Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ var (
LifecycleStageIndex = lifecycleStageIndex
ValidateLifecycleStageSupported = validateLifecycleStageSupported
EvaluateDeploymentStatus = evaluateDeploymentStatus
PausedHookIDs = pausedHookIDs
)

type WaitDeploymentResult = waitDeploymentResult
Expand Down
36 changes: 30 additions & 6 deletions wait.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,17 +460,27 @@ func evaluateDeploymentStatus(dp *types.ServiceDeployment, done func(*types.Serv
// deploymentPaused reports whether a pause lifecycle hook of the deployment is
// awaiting action.
func (d *App) deploymentPaused(dp *types.ServiceDeployment) bool {
ids := pausedHookIDs(dp)
for _, id := range ids {
d.LogInfo("deployment paused at lifecycle hook",
"hook_id", id,
"lifecycle_stage", string(dp.LifecycleStage),
)
}
return len(ids) > 0
}

// pausedHookIDs returns the IDs of pause lifecycle hooks of the deployment
// that are awaiting action.
func pausedHookIDs(dp *types.ServiceDeployment) []string {
var ids []string
for _, hook := range dp.LifecycleHookDetails {
if hook.TargetType == types.DeploymentLifecycleHookTargetTypePause &&
hook.Status == types.DeploymentLifecycleHookStatusAwaitingAction {
d.LogInfo("deployment paused at lifecycle hook",
"hook_id", aws.ToString(hook.HookId),
"lifecycle_stage", string(dp.LifecycleStage),
)
return true
ids = append(ids, aws.ToString(hook.HookId))
}
}
return false
return ids
}

// WaitServiceDeployLifecycleStage returns a waitFunc that waits until the
Expand All @@ -492,13 +502,27 @@ func (d *App) WaitServiceDeployLifecycleStage(stage types.ServiceDeploymentLifec
d.LogWarn("deployment strategy is not set; a ROLLING deployment reports no lifecycle stage, so this waits until the deployment completes")
}
d.LogInfo("Waiting for service deployment lifecycle stage...", "stage", string(stage))
notified := map[string]struct{}{}
// Stages such as PRODUCTION_TRAFFIC_SHIFT are transient and can be
// skipped between polls, so compare positions rather than equality.
return d.waitServiceDeployment(ctx, knownDeploymentArn, func(dp *types.ServiceDeployment) bool {
if lifecycleStageIndex(dp.LifecycleStage) >= target {
d.LogInfo("service deployment reached the lifecycle stage", "stage", string(dp.LifecycleStage))
return true
}
// A pause lifecycle hook awaiting action blocks the deployment, so
// the target stage never arrives until the deployment is continued.
for _, id := range pausedHookIDs(dp) {
if _, ok := notified[id]; ok {
continue
}
notified[id] = struct{}{}
d.LogWarn("deployment is paused at a lifecycle hook; the target lifecycle stage will not be reached until the deployment is continued (run `ecspresso continue`)",
"hook_id", id,
"lifecycle_stage", string(dp.LifecycleStage),
"target_stage", string(stage),
)
}
return false
})
}
Expand Down
31 changes: 31 additions & 0 deletions wait_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,40 @@ import (

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/google/go-cmp/cmp"
"github.com/kayac/ecspresso/v2"
)

func TestPausedHookIDs(t *testing.T) {
dp := &types.ServiceDeployment{
LifecycleHookDetails: []types.DeploymentLifecycleHookDetail{
{
HookId: aws.String("hook-awaiting"),
TargetType: types.DeploymentLifecycleHookTargetTypePause,
Status: types.DeploymentLifecycleHookStatusAwaitingAction,
},
{
HookId: aws.String("hook-in-progress"),
TargetType: types.DeploymentLifecycleHookTargetTypePause,
Status: types.DeploymentLifecycleHookStatusInProgress,
},
{
HookId: aws.String("hook-lambda"),
TargetType: types.DeploymentLifecycleHookTargetTypeAwsLambda,
Status: types.DeploymentLifecycleHookStatusAwaitingAction,
},
},
}
want := []string{"hook-awaiting"}
if diff := cmp.Diff(want, ecspresso.PausedHookIDs(dp)); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}

if got := ecspresso.PausedHookIDs(&types.ServiceDeployment{}); got != nil {
t.Errorf("expected nil for no hooks, got %v", got)
}
}

func TestLifecycleStageIndex(t *testing.T) {
// The waiter compares positions instead of equality, so pin the full
// ordering of the stages within the deployment lifecycle.
Expand Down