Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5 from /issues/4
Browse files Browse the repository at this point in the history
feat: --skipCanaryオプション追加
keroxp authored Sep 6, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents a9fa87d + ac915b3 commit a1f9e75
Showing 5 changed files with 58 additions and 7 deletions.
15 changes: 11 additions & 4 deletions cli/cage/main.go
Original file line number Diff line number Diff line change
@@ -28,11 +28,12 @@ func main() {
AvailabilityThreshold: aws.Float64(-1.0),
ResponseTimeThreshold: aws.Float64(-1.0),
RollOutPeriod: aws.Int64(-1),
SkipCanary: aws.Bool(false),
}
configPath := ""
app := cli.NewApp()
app.Name = "canarycage"
app.Version = "1.0.0-alpha2"
app.Version = "1.1.0-alpha"
app.Description = "A gradual roll-out deployment tool for AWS ECS"
app.Flags = []cli.Flag{
cli.StringFlag{
@@ -46,7 +47,7 @@ func main() {
Usage: "generate config file skeleton json",
},
cli.BoolFlag{
Name: "dryRun",
Name: "dryRun",
Usage: "describe roll out plan without affecting any resources",
},
cli.StringFlag{
@@ -113,10 +114,16 @@ func main() {
Value: 300,
Destination: envars.RollOutPeriod,
},
cli.BoolFlag{
Name: "skipCanary",
EnvVar: cage.SkipCanaryKey,
Usage: "skip canary test. ensuring only healthy tasks.",
Destination: envars.SkipCanary,
},
}
app.Action = func(ctx *cli.Context) {
if ctx.Bool("skeleton") {
d, err := json.MarshalIndent(envars,"","\t")
d, err := json.MarshalIndent(envars, "", "\t")
if err != nil {
log.Fatalf("failed to marshal json due to: %s", err)
}
@@ -151,7 +158,7 @@ func DryRun(envars *cage.Envars) {
log.Infof("== [DRY RUN] ==")
d, _ := json.MarshalIndent(envars, "", "\t")
log.Infof("envars = \n%s", string(d))
if envars.NextTaskDefinitionArn == nil{
if envars.NextTaskDefinitionArn == nil {
log.Info("create NEXT task definition with provided json")
}
log.Infof("create NEXT service '%s' with desiredCount=1", *envars.NextServiceName)
5 changes: 5 additions & 0 deletions env.go
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ type Envars struct {
AvailabilityThreshold *float64 `json:"availabilityThreshold" type:"double"`
ResponseTimeThreshold *float64 `json:"responseTimeThreshold" type:"double"`
RollOutPeriod *int64 `json:"rollOutPeriod" type:"integer"`
SkipCanary *bool `json:"skipCanary" type:"bool"`
}

// required
@@ -35,6 +36,7 @@ const RegionKey = "CAGE_AWS_REGION"
const AvailabilityThresholdKey = "CAGE_AVAILABILITY_THRESHOLD"
const ResponseTimeThresholdKey = "CAGE_RESPONSE_TIME_THRESHOLD"
const RollOutPeriodKey = "CAGE_ROLL_OUT_PERIOD"
const SkipCanaryKey = "CAGE_SKIP_CANARY"

const kAvailabilityThresholdDefaultValue = 0.9970
const kResponseTimeThresholdDefaultValue = 1.0
@@ -81,5 +83,8 @@ func EnsureEnvars(
if period := *dest.RollOutPeriod; !(60 <= period && float64(period) != math.NaN() && float64(period) != math.Inf(0)) {
return NewErrorf("--rollOutPeriod [%s] must be lesser than 60, but got '%d'", RollOutPeriodKey, period)
}
if dest.SkipCanary == nil {
dest.SkipCanary = aws.Bool(false)
}
return nil
}
8 changes: 6 additions & 2 deletions rollout.go
Original file line number Diff line number Diff line change
@@ -158,7 +158,9 @@ func (envars *Envars) RollOut(
return err
}
// Phase2: service-nextのperiodic healthを計測
if err := envars.CanaryTest(ctx.Cw, tg.LoadBalancerArns[0], tg.TargetGroupArn, totalRollOutCnt); err != nil {
if *envars.SkipCanary {
log.Infof("🤫 %dth canary test skipped.", totalRollOutCnt)
} else if err := envars.CanaryTest(ctx.Cw, tg.LoadBalancerArns[0], tg.TargetGroupArn, totalRollOutCnt); err != nil {
return err
}
// Phase3: service-currentからタスクを指定数消す
@@ -195,7 +197,9 @@ func (envars *Envars) RollOut(
if oldTaskCount == 0 && newTaskCount >= originalDesiredCount {
// ロールアウトが終わったら最終検証を行う
log.Infof("estimated roll out completed. Do final canary test...")
if err := envars.CanaryTest(ctx.Cw, tg.LoadBalancerArns[0], tg.TargetGroupArn, totalRollOutCnt); err != nil {
if *envars.SkipCanary {
log.Infof("😑 final canary test skipped...")
} else if err := envars.CanaryTest(ctx.Cw, tg.LoadBalancerArns[0], tg.TargetGroupArn, totalRollOutCnt); err != nil {
log.Errorf("final canary test has failed due to: %s", err)
return err
}
18 changes: 17 additions & 1 deletion rollout_test.go
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ func TestEnvars_StartGradualRollOut(t *testing.T) {
t.Fatalf("%s", err)
}
assert.Nil(t, result.HandledError)
assert.False(t, *result.Rolledback)
assert.False(t, *result.Rolledback)
assert.Equal(t, int64(1), mctx.ServiceSize())
assert.Equal(t, v, mctx.TaskSize())
}
@@ -189,6 +189,22 @@ func TestEnvars_StartGradualRollOut3(t *testing.T) {
assert.Equal(t, int64(4), mocker.TaskSize())
}

func TestEnvars_StartGradualRollOut4(t *testing.T) {
// skipCanaryオプションを追加した場合canaryTestは行わない
envars := DefaultEnvars()
envars.SkipCanary = aws.Bool(true)
ctrl := gomock.NewController(t)
_, ctx := envars.Setup(ctrl, 2)
cwMock := mock_cloudwatch.NewMockCloudWatchAPI(ctrl)
cwMock.EXPECT().GetMetricStatistics(gomock.Any()).Times(0)
ctx.Cw = cwMock
if res, err := envars.StartGradualRollOut(ctx); err != nil {
t.Fatalf(err.Error())
} else if res.HandledError != nil {
t.Fatalf(err.Error())
}
}

func TestEnvars_Rollback(t *testing.T) {
log.SetLevel(log.DebugLevel)
newTimer = fakeTimer
19 changes: 19 additions & 0 deletions test-integration/integration_test.go
Original file line number Diff line number Diff line change
@@ -234,6 +234,25 @@ func TestHealthyToHealthy(t *testing.T) {
assert.False(t, *result.Rolledback)
}

func TestHealthyToHealthySkipCanary(t *testing.T) {
ctx := setup()
envars := setupEnvars()
envars.SkipCanary = aws.Bool(true)
envars.NextTaskDefinitionArn = aws.String(kHealthyTDArn)
envars.CurrentServiceName = aws.String(kCurrentServiceName)
envars.NextServiceName = aws.String(kNextServiceName)
defer func() {
cleanupService(ctx.Ecs, envars, envars.CurrentServiceName)
cleanupService(ctx.Ecs, envars, envars.NextServiceName)
}()
result, err := testInternal(t, envars)
if err != nil {
t.Fatalf(err.Error())
}
assert.Nil(t, result.HandledError)
assert.False(t, *result.Rolledback)
}

func testAbnormal(t *testing.T, tdarn string, servicePostfix string) error {
log.SetLevel(log.InfoLevel)
ctx := setup()

0 comments on commit a1f9e75

Please sign in to comment.