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
8 changes: 7 additions & 1 deletion pkg/app/piped/executor/ecs/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package ecs

import (
"context"
"strconv"

"github.com/aws/aws-sdk-go-v2/service/ecs/types"

Expand Down Expand Up @@ -109,7 +110,12 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus {
}

recreate := e.appCfg.QuickSync.Recreate
if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, recreate, taskDefinition, servicedefinition, primary) {
forceNewDeployment := e.appCfg.QuickSync.ForceNewDeployment

// Store force new deployment flag to metadata store.
e.Input.MetadataStore.Shared().Put(ctx, forceNewDeploymentKey, strconv.FormatBool(forceNewDeployment))

if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, recreate, forceNewDeployment, taskDefinition, servicedefinition, primary) {
return model.StageStatus_STAGE_FAILURE
}

Expand Down
18 changes: 12 additions & 6 deletions pkg/app/piped/executor/ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const (
canaryScaleMetadataKey = "canary-scale"
currentListenersKey = "current-listeners"
canaryTargetGroupArnKey = "canary-target-group-arn"
// Force new deployment flag metadata key.
forceNewDeploymentKey = "force-new-deployment"
)

type registerer interface {
Expand Down Expand Up @@ -147,15 +149,15 @@ func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinitio
return td, nil
}

func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service) (*types.Service, error) {
func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service, forceNewDeployment bool) (*types.Service, error) {
found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName)
if err != nil {
return nil, fmt.Errorf("unable to validate service name %s: %w", *serviceDefinition.ServiceName, err)
}

var service *types.Service
if found {
service, err = cli.UpdateService(ctx, serviceDefinition)
service, err = cli.UpdateService(ctx, serviceDefinition, forceNewDeployment)
if err != nil {
return nil, fmt.Errorf("failed to update ECS service %s: %w", *serviceDefinition.ServiceName, err)
}
Expand Down Expand Up @@ -291,7 +293,7 @@ func createPrimaryTaskSet(ctx context.Context, client provider.Client, service t
return nil
}

func sync(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, recreate bool, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool {
func sync(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, recreate bool, forceNewDeployment bool, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool {
client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger)
if err != nil {
in.LogPersister.Errorf("Unable to create ECS client for the provider %s: %v", platformProviderName, err)
Expand All @@ -306,7 +308,7 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string,
}

in.LogPersister.Infof("Start applying the ECS service definition")
service, err := applyServiceDefinition(ctx, client, serviceDefinition)
service, err := applyServiceDefinition(ctx, client, serviceDefinition, forceNewDeployment)
if err != nil {
in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err)
return false
Expand All @@ -330,7 +332,7 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string,
// Scale up the service tasks count back to its desired.
in.LogPersister.Infof("Scale up ECS desired tasks count back to %d", cnt)
service.DesiredCount = cnt
if _, err = client.UpdateService(ctx, *service); err != nil {
if _, err = client.UpdateService(ctx, *service, forceNewDeployment); err != nil {
in.LogPersister.Errorf("Failed to turning back service tasks: %v", err)
return false
}
Expand Down Expand Up @@ -367,7 +369,11 @@ func rollout(ctx context.Context, in *executor.Input, platformProviderName strin
}

in.LogPersister.Infof("Start applying the ECS service definition")
service, err := applyServiceDefinition(ctx, client, serviceDefinition)

// forceNewDeployment is false since this configuration only available for QuickSync strategy.
forceNewDeployment := false

service, err := applyServiceDefinition(ctx, client, serviceDefinition, forceNewDeployment)
if err != nil {
in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err)
return false
Expand Down
9 changes: 8 additions & 1 deletion pkg/app/piped/executor/ecs/rollback.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,15 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri
return false
}

// Retrieve force new deployment flag from metadata store.
forceNewDeployment := false
val, ok := in.MetadataStore.Shared().Get(forceNewDeploymentKey)
if ok && val == "true" {
forceNewDeployment = true
}

// Rollback ECS service configuration to previous state including commit-hash of the tag.
service, err := applyServiceDefinition(ctx, client, serviceDefinition)
service, err := applyServiceDefinition(ctx, client, serviceDefinition, forceNewDeployment)
if err != nil {
in.LogPersister.Errorf("Unable to rollback ECS service %s configuration to previous stage: %v", *serviceDefinition.ServiceName, err)
return false
Expand Down
3 changes: 2 additions & 1 deletion pkg/app/piped/platformprovider/ecs/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,13 +144,14 @@ func (c *client) PruneServiceTasks(ctx context.Context, service types.Service) e
return nil
}

func (c *client) UpdateService(ctx context.Context, service types.Service) (*types.Service, error) {
func (c *client) UpdateService(ctx context.Context, service types.Service, forceNewDeployment bool) (*types.Service, error) {
if service.LaunchType != "" && service.CapacityProviderStrategy != nil {
return nil, fmt.Errorf("failed to update ECS service %s: launch type and capacity provider strategy cannot be specified together", *service.ServiceName)
}
input := &ecs.UpdateServiceInput{
Cluster: service.ClusterArn,
Service: service.ServiceName,
ForceNewDeployment: forceNewDeployment,
EnableExecuteCommand: aws.Bool(service.EnableExecuteCommand),
PlacementStrategy: service.PlacementStrategy,
// TODO: Support update other properties of service.
Expand Down
2 changes: 1 addition & 1 deletion pkg/app/piped/platformprovider/ecs/ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type ECS interface {
ListClusters(ctx context.Context) ([]string, error)
ServiceExists(ctx context.Context, clusterName string, servicesName string) (bool, error)
CreateService(ctx context.Context, service types.Service) (*types.Service, error)
UpdateService(ctx context.Context, service types.Service) (*types.Service, error)
UpdateService(ctx context.Context, service types.Service, forceNewDeployment bool) (*types.Service, error)
PruneServiceTasks(ctx context.Context, service types.Service) error
WaitServiceStable(ctx context.Context, service types.Service) error
GetServices(ctx context.Context, clusterName string) ([]*types.Service, error)
Expand Down
5 changes: 5 additions & 0 deletions pkg/config/application_ecs.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,11 @@ type ECSSyncStageOptions struct {
// If this is set, the application may be unavailable for a short of time during the deployment.
// Default is false.
Recreate bool `json:"recreate"`
// Whether to force a new deployment or not.
// If this is set, the new service deployment will be forced to start even if the service is already in a desired state.
// This is useful when you want to update the service configuration.
// Default is false.
ForceNewDeployment bool `json:"forceNewDeployment"`
}

// ECSCanaryRolloutStageOptions contains all configurable values for a ECS_CANARY_ROLLOUT stage.
Expand Down