Skip to content
This repository was archived by the owner on May 30, 2024. It is now read-only.

Commit d3b016c

Browse files
author
noah
committed
Add IsFreezed method
1 parent 1d01093 commit d3b016c

File tree

4 files changed

+169
-8
lines changed

4 files changed

+169
-8
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ require (
1212
github.com/gin-contrib/cors v1.3.1
1313
github.com/gin-contrib/sse v0.1.0
1414
github.com/gin-gonic/gin v1.7.7
15+
github.com/gitploy-io/cronexpr v0.2.1
1516
github.com/go-sql-driver/mysql v1.5.1-0.20200311113236-681ffa848bae
1617
github.com/golang/mock v1.6.0
1718
github.com/google/go-github/v32 v32.1.0

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm
104104
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
105105
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
106106
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
107+
github.com/gitploy-io/cronexpr v0.2.0 h1:BmYX3+QNB11Bevp2z98taCOiNDhMo/E1pBeO6OzojAs=
108+
github.com/gitploy-io/cronexpr v0.2.0/go.mod h1:Uep5sbzUSocMZvJ1s0lNI9zi37s5iUI1llkw3vRGK9M=
109+
github.com/gitploy-io/cronexpr v0.2.1 h1:usx6GTAQh2q3E4S8jx5RWGGkr4LSxLH0mBcebGZGv+c=
110+
github.com/gitploy-io/cronexpr v0.2.1/go.mod h1:Uep5sbzUSocMZvJ1s0lNI9zi37s5iUI1llkw3vRGK9M=
107111
github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368/go.mod h1:7xCgX1lzlrXPHkfvn3EhumqHkmSlzt8at9q7v0ax19c=
108112
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
109113
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=

model/extent/config.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package extent
22

33
import (
4+
"fmt"
45
"regexp"
56
"strconv"
7+
"strings"
8+
"time"
69

710
"github.com/drone/envsubst"
11+
"github.com/gitploy-io/cronexpr"
812
"gopkg.in/yaml.v3"
913

1014
eutil "github.com/gitploy-io/gitploy/pkg/e"
@@ -38,13 +42,21 @@ type (
3842
// Review is the configuration of Review,
3943
// It is disabled when it is empty.
4044
Review *Review `json:"review,omitempty" yaml:"review"`
45+
46+
FreezeWindows []FreezeWindow `json:"freeze_windows" yaml:"freeze_windows"`
4147
}
4248

4349
Review struct {
4450
Enabled bool `json:"enabled" yaml:"enabled"`
4551
Reviewers []string `json:"reviewers" yaml:"reviewers"`
4652
}
4753

54+
FreezeWindow struct {
55+
Start string `json:"start" yaml:"start"`
56+
Duration string `json:"duration" yaml:"duration"`
57+
Location string `json:"location" yaml:"location"`
58+
}
59+
4860
EvalValues struct {
4961
IsRollback bool
5062
}
@@ -131,12 +143,12 @@ func (c *Config) GetEnv(name string) *Env {
131143
return nil
132144
}
133145

134-
// IsProductionEnvironment check whether the environment is production or not.
146+
// IsProductionEnvironment verifies whether the environment is production or not.
135147
func (e *Env) IsProductionEnvironment() bool {
136148
return e.ProductionEnvironment != nil && *e.ProductionEnvironment
137149
}
138150

139-
// IsDeployableRef validate the ref is deployable.
151+
// IsDeployableRef verifies the ref is deployable.
140152
func (e *Env) IsDeployableRef(ref string) (bool, error) {
141153
if e.DeployableRef == nil {
142154
return true, nil
@@ -150,7 +162,7 @@ func (e *Env) IsDeployableRef(ref string) (bool, error) {
150162
return matched, nil
151163
}
152164

153-
// IsAutoDeployOn validate the ref is matched with 'auto_deploy_on'.
165+
// IsAutoDeployOn verifies the ref is matched with 'auto_deploy_on'.
154166
func (e *Env) IsAutoDeployOn(ref string) (bool, error) {
155167
if e.AutoDeployOn == nil {
156168
return false, nil
@@ -168,3 +180,40 @@ func (e *Env) IsAutoDeployOn(ref string) (bool, error) {
168180
func (e *Env) HasReview() bool {
169181
return e.Review != nil && e.Review.Enabled
170182
}
183+
184+
// IsFreezed verifies whether the current time is in a freeze window.
185+
// It returns an error when parsing an expression is failed.
186+
func (e *Env) IsFreezed(t time.Time) (bool, error) {
187+
if len(e.FreezeWindows) == 0 {
188+
return false, nil
189+
}
190+
191+
for _, w := range e.FreezeWindows {
192+
s, err := cronexpr.ParseInLocation(strings.TrimSpace(w.Start), w.Location)
193+
if err != nil {
194+
return false, eutil.NewErrorWithMessage(
195+
eutil.ErrorCodeConfigInvalid,
196+
fmt.Sprintf("The crontab expression of the freeze window is invalid."),
197+
err,
198+
)
199+
}
200+
201+
d, err := time.ParseDuration(w.Duration)
202+
if err != nil {
203+
return false, eutil.NewErrorWithMessage(
204+
eutil.ErrorCodeConfigInvalid,
205+
fmt.Sprintf("The duration of the freeze window is invalid."),
206+
err,
207+
)
208+
}
209+
210+
// Add one minute to include the starting time.
211+
start := s.Prev(t.Add(time.Minute))
212+
end := start.Add(d)
213+
if t.After(start) && t.Before(end) {
214+
return true, nil
215+
}
216+
}
217+
218+
return false, nil
219+
}

model/extent/config_test.go

Lines changed: 112 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ package extent
33
import (
44
"reflect"
55
"testing"
6+
"time"
67

78
"github.com/AlekSi/pointer"
89
"github.com/davecgh/go-spew/spew"
910
)
1011

1112
func TestUnmarshalYAML(t *testing.T) {
12-
t.Run("unmarhsal the required_context field", func(tt *testing.T) {
13+
t.Run("Unmarhsal the required_context field", func(tt *testing.T) {
1314
s := `
1415
envs:
1516
- name: dev
@@ -37,7 +38,7 @@ envs:
3738
}
3839
})
3940

40-
t.Run("unmarshal auto_merge: false ", func(tt *testing.T) {
41+
t.Run("Unmarshal 'auto_merge: false'", func(tt *testing.T) {
4142
s := `
4243
envs:
4344
- name: dev
@@ -64,7 +65,7 @@ envs:
6465
}
6566
})
6667

67-
t.Run("unmarshal auto_merge: true", func(tt *testing.T) {
68+
t.Run("Unmarshal 'auto_merge: true'", func(tt *testing.T) {
6869
s := `
6970
envs:
7071
- name: dev
@@ -92,7 +93,7 @@ envs:
9293
}
9394

9495
func TestConfig_Eval(t *testing.T) {
95-
t.Run("Evaluate the configuration.", func(t *testing.T) {
96+
t.Run("Umarshal the task with the variable template.", func(t *testing.T) {
9697
s := `
9798
envs:
9899
- name: dev
@@ -122,7 +123,7 @@ envs:
122123
}
123124
})
124125

125-
t.Run("Evaluate the configuration with the regexp.", func(t *testing.T) {
126+
t.Run("Unmarshal the deployable_ref field with a regexp.", func(t *testing.T) {
126127
s := `
127128
envs:
128129
- name: dev
@@ -153,6 +154,38 @@ envs:
153154
t.Errorf("Config = %v expected %v", spew.Sdump(c), spew.Sdump(e))
154155
}
155156
})
157+
158+
t.Run("Unmarshal the freeze_windows field", func(t *testing.T) {
159+
s := `
160+
envs:
161+
- name: dev
162+
freeze_windows:
163+
- start: "55 23 * * *"
164+
duration: "10m"`
165+
166+
c := &Config{}
167+
if err := UnmarshalYAML([]byte(s), c); err != nil {
168+
t.Fatalf("Failed to parse the configuration file: %v", err)
169+
}
170+
171+
e := &Config{
172+
Envs: []*Env{
173+
{
174+
Name: "dev",
175+
FreezeWindows: []FreezeWindow{
176+
{
177+
Start: "55 23 * * *",
178+
Duration: "10m",
179+
},
180+
},
181+
},
182+
},
183+
source: []byte(s),
184+
}
185+
if !reflect.DeepEqual(c, e) {
186+
t.Errorf("Config = %v expected %v", spew.Sdump(c), spew.Sdump(e))
187+
}
188+
})
156189
}
157190

158191
func TestEnv_IsProductionEnvironment(t *testing.T) {
@@ -224,3 +257,77 @@ func TestEnv_IsDeployableRef(t *testing.T) {
224257
}
225258
})
226259
}
260+
261+
func TestEnv_IsFreezed(t *testing.T) {
262+
t.Run("Return true when the time is in the window", func(t *testing.T) {
263+
runs := []struct {
264+
t time.Time
265+
e *Env
266+
want bool
267+
}{
268+
{
269+
t: time.Date(2012, 12, 1, 23, 55, 10, 0, time.UTC),
270+
e: &Env{
271+
FreezeWindows: []FreezeWindow{
272+
{
273+
Start: "55 23 * Dec *",
274+
Duration: "10m",
275+
},
276+
},
277+
},
278+
want: true,
279+
},
280+
{
281+
t: time.Date(2012, 1, 1, 0, 3, 0, 0, time.UTC),
282+
e: &Env{
283+
FreezeWindows: []FreezeWindow{
284+
{
285+
Start: "55 23 * Dec *",
286+
Duration: "10m",
287+
},
288+
},
289+
},
290+
want: true,
291+
},
292+
}
293+
e := &Env{
294+
FreezeWindows: []FreezeWindow{
295+
{
296+
Start: "55 23 * Dec *",
297+
Duration: "10m",
298+
},
299+
},
300+
}
301+
302+
for _, r := range runs {
303+
freezed, err := e.IsFreezed(r.t)
304+
if err != nil {
305+
t.Fatalf("IsFreezed returns an error: %s", err)
306+
}
307+
308+
if freezed != r.want {
309+
t.Fatalf("IsFreezed = %v, wanted %v", freezed, r.want)
310+
}
311+
}
312+
})
313+
314+
t.Run("Return false when the time is out of the window", func(t *testing.T) {
315+
e := &Env{
316+
FreezeWindows: []FreezeWindow{
317+
{
318+
Start: "55 23 * Dec *",
319+
Duration: "10m",
320+
},
321+
},
322+
}
323+
324+
freezed, err := e.IsFreezed(time.Date(2012, 1, 1, 0, 10, 0, 0, time.UTC))
325+
if err != nil {
326+
t.Fatalf("IsFreezed returns an error: %s", err)
327+
}
328+
329+
if freezed != false {
330+
t.Fatalf("IsFreezed = %v, wanted %v", freezed, false)
331+
}
332+
})
333+
}

0 commit comments

Comments
 (0)