From c006e820fe5e2bb18f4709867146e44c3ad2ba00 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Thu, 19 Jan 2023 19:33:26 +0900 Subject: [PATCH 01/22] Add tags to aws resources. --- pkg/app/piped/executor/ecs/deploy.go | 4 +-- pkg/app/piped/executor/ecs/ecs.go | 29 ++++++++++++-------- pkg/app/piped/platformprovider/ecs/client.go | 10 +++++-- pkg/app/piped/platformprovider/ecs/ecs.go | 17 ++++++++++-- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index c5be37077a..f2a272c5f8 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -86,7 +86,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { } if ecsInput.IsStandaloneTask() { - if !runStandaloneTask(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { + if !runStandaloneTask(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS @@ -102,7 +102,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { return model.StageStatus_STAGE_FAILURE } - if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + if !sync(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { return model.StageStatus_STAGE_FAILURE } diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index a97b33d233..732be3c60c 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -127,15 +127,16 @@ func loadTargetGroups(in *executor.Input, appCfg *config.ECSApplicationSpec, ds return primary, canary, true } -func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) { - td, err := cli.RegisterTaskDefinition(ctx, taskDefinition) +func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { + + td, err := cli.RegisterTaskDefinition(ctx, taskDefinition, tags) if err != nil { return nil, fmt.Errorf("unable to register ECS task definition of family %s: %v", *taskDefinition.Family, err) } 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, tags []types.Tag) (*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: %v", *serviceDefinition.ServiceName, err) @@ -148,7 +149,7 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef return nil, fmt.Errorf("failed to update ECS service %s: %v", *serviceDefinition.ServiceName, err) } } else { - service, err = cli.CreateService(ctx, serviceDefinition) + service, err = cli.CreateService(ctx, serviceDefinition, tags) if err != nil { return nil, fmt.Errorf("failed to create ECS service %s: %v", *serviceDefinition.ServiceName, err) } @@ -159,6 +160,7 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef func runStandaloneTask( ctx context.Context, + appID string, in *executor.Input, cloudProviderName string, cloudProviderCfg *config.PlatformProviderECSConfig, @@ -172,7 +174,8 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition) + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false @@ -223,22 +226,24 @@ 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, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func sync(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, 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) return false } + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition) + td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false } in.LogPersister.Infof("Start applying the ECS service definition") - service, err := applyServiceDefinition(ctx, client, serviceDefinition) + service, err := applyServiceDefinition(ctx, client, serviceDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err) return false @@ -254,22 +259,24 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string, return true } -func rollout(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollout(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, 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) return false } + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition) + td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false } in.LogPersister.Infof("Start applying the ECS service definition") - service, err := applyServiceDefinition(ctx, client, serviceDefinition) + service, err := applyServiceDefinition(ctx, client, serviceDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err) return false diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 105f2659c5..7e6d4dba4d 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -71,10 +71,13 @@ func newClient(region, profile, credentialsFile, roleARN, tokenPath string, logg return c, nil } -func (c *client) CreateService(ctx context.Context, service types.Service) (*types.Service, error) { +func (c *client) CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) { if service.DeploymentController == nil || service.DeploymentController.Type != types.DeploymentControllerTypeExternal { return nil, fmt.Errorf("failed to create ECS service %s: deployment controller of type EXTERNAL is required", *service.ServiceName) } + + tags = append(tags, service.Tags...) + input := &ecs.CreateServiceInput{ Cluster: service.ClusterArn, ServiceName: service.ServiceName, @@ -90,7 +93,7 @@ func (c *client) CreateService(ctx context.Context, service types.Service) (*typ Role: service.RoleArn, SchedulingStrategy: service.SchedulingStrategy, ServiceRegistries: service.ServiceRegistries, - Tags: service.Tags, + Tags: tags, } output, err := c.ecsClient.CreateService(ctx, input) @@ -130,7 +133,7 @@ func (c *client) UpdateService(ctx context.Context, service types.Service) (*typ return output.Service, nil } -func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) { +func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { input := &ecs.RegisterTaskDefinitionInput{ Family: taskDefinition.Family, ContainerDefinitions: taskDefinition.ContainerDefinitions, @@ -142,6 +145,7 @@ func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition type // Requires defined at task level in case Fargate is used. Cpu: taskDefinition.Cpu, Memory: taskDefinition.Memory, + Tags: tags, // TODO: Support tags for registering task definition. } output, err := c.ecsClient.RegisterTaskDefinition(ctx, input) diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 4eaee15a0d..6aeebfdeef 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -19,6 +19,7 @@ import ( "path/filepath" "sync" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs/types" "go.uber.org/zap" "golang.org/x/sync/singleflight" @@ -26,6 +27,10 @@ import ( "github.com/pipe-cd/pipecd/pkg/config" ) +const ( + LabelApplication = "pipecd-dev-application" // The application this resource belongs to. +) + // Client is wrapper of ECS client. type Client interface { ECS @@ -34,9 +39,9 @@ type Client interface { type ECS interface { ServiceExists(ctx context.Context, clusterName string, servicesName string) (bool, error) - CreateService(ctx context.Context, service types.Service) (*types.Service, error) + CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) UpdateService(ctx context.Context, service types.Service) (*types.Service, error) - RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) + RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration) error GetPrimaryTaskSet(ctx context.Context, service types.Service) (*types.TaskSet, error) CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) @@ -109,3 +114,11 @@ var defaultRegistry = ®istry{ func DefaultRegistry() Registry { return defaultRegistry } + +func CreateTags(keyValue map[string]string) []types.Tag { + tags := make([]types.Tag, len(keyValue)) + for key, value := range keyValue { + tags = append(tags, types.Tag{Key: aws.String(key), Value: aws.String(value)}) + } + return tags +} From 13fc5bc1939f84de7b4ba66480d8f05d56f9aa10 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Thu, 19 Jan 2023 19:38:54 +0900 Subject: [PATCH 02/22] Small fix --- pkg/app/piped/executor/ecs/deploy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index f2a272c5f8..3fa8800f0d 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -128,7 +128,7 @@ func (e *deployExecutor) ensurePrimaryRollout(ctx context.Context) model.StageSt return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { return model.StageStatus_STAGE_FAILURE } @@ -154,7 +154,7 @@ func (e *deployExecutor) ensureCanaryRollout(ctx context.Context) model.StageSta return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { + if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { return model.StageStatus_STAGE_FAILURE } From a973d112817f9f9b2ff567bee7d67d4cd0d2dcc2 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Thu, 19 Jan 2023 19:50:58 +0900 Subject: [PATCH 03/22] Small fix --- pkg/app/piped/executor/ecs/rollback.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/app/piped/executor/ecs/rollback.go b/pkg/app/piped/executor/ecs/rollback.go index 92fa7b4b43..f51db951e1 100644 --- a/pkg/app/piped/executor/ecs/rollback.go +++ b/pkg/app/piped/executor/ecs/rollback.go @@ -87,14 +87,14 @@ func (e *rollbackExecutor) ensureRollback(ctx context.Context) model.StageStatus return model.StageStatus_STAGE_FAILURE } - if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { + if !rollback(ctx, e.Deployment.ApplicationId, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS } -func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollback(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { in.LogPersister.Infof("Start rollback the ECS service and task family: %s and %s to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family) client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger) if err != nil { @@ -102,10 +102,12 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri return false } + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + // Re-register TaskDef to get TaskDefArn. // Consider using DescribeServices and get services[0].taskSets[0].taskDefinition (taskDefinition of PRIMARY taskSet) // then store it in metadata store and use for rollback instead. - td, err := client.RegisterTaskDefinition(ctx, taskDefinition) + td, err := client.RegisterTaskDefinition(ctx, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to register new revision of ECS task definition %s: %v", *taskDefinition.Family, err) return false From ca0a60db085d02d53a051e38bfa9f9732f2bfdab Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 12:18:11 +0900 Subject: [PATCH 04/22] Fix initial slice length. --- pkg/app/piped/platformprovider/ecs/ecs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 6aeebfdeef..a0db7f24fb 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -28,7 +28,7 @@ import ( ) const ( - LabelApplication = "pipecd-dev-application" // The application this resource belongs to. + LabelApplication string = "pipecd-dev-application" // The application this resource belongs to. ) // Client is wrapper of ECS client. @@ -116,7 +116,7 @@ func DefaultRegistry() Registry { } func CreateTags(keyValue map[string]string) []types.Tag { - tags := make([]types.Tag, len(keyValue)) + tags := make([]types.Tag, 0, len(keyValue)) for key, value := range keyValue { tags = append(tags, types.Tag{Key: aws.String(key), Value: aws.String(value)}) } From b2ce28711c49e290155a35e511e1414e74d7fd4b Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 12:36:00 +0900 Subject: [PATCH 05/22] Add TagResource function --- pkg/app/piped/platformprovider/ecs/client.go | 12 ++++++++++++ pkg/app/piped/platformprovider/ecs/ecs.go | 1 + 2 files changed, 13 insertions(+) diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 7e6d4dba4d..6ffdfa9c42 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -345,3 +345,15 @@ func (c *client) ModifyListener(ctx context.Context, listenerArn string, routing _, err := c.elbClient.ModifyListener(ctx, input) return err } + +func (c *client) TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error { + input := &ecs.TagResourceInput{ + ResourceArn: resourceArn, + Tags: tags, + } + _, err := c.ecsClient.TagResource(ctx, input) + if err != nil { + return fmt.Errorf("failed to update tag of resource %s: %w", *resourceArn, err) + } + return nil +} diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index a0db7f24fb..4679661ac2 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -47,6 +47,7 @@ type ECS interface { CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) DeleteTaskSet(ctx context.Context, service types.Service, taskSetArn string) error UpdateServicePrimaryTaskSet(ctx context.Context, service types.Service, taskSet types.TaskSet) (*types.TaskSet, error) + TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error } type ELB interface { From c2a85d1a04426dad6bee59b3a62f85f3052f70fb Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 15:42:36 +0900 Subject: [PATCH 06/22] Fix deploy process --- pkg/app/piped/executor/ecs/deploy.go | 12 ++++++---- pkg/app/piped/executor/ecs/ecs.go | 21 ++++++++-------- pkg/app/piped/executor/ecs/rollback.go | 7 +++--- pkg/app/piped/platformprovider/ecs/client.go | 25 ++++++++++---------- pkg/app/piped/platformprovider/ecs/ecs.go | 8 +++---- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index 3fa8800f0d..58e4e89b3e 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -19,6 +19,7 @@ import ( "github.com/pipe-cd/pipecd/pkg/app/piped/deploysource" "github.com/pipe-cd/pipecd/pkg/app/piped/executor" + provider "github.com/pipe-cd/pipecd/pkg/app/piped/platformprovider/ecs" "github.com/pipe-cd/pipecd/pkg/config" "github.com/pipe-cd/pipecd/pkg/model" ) @@ -79,6 +80,7 @@ func (e *deployExecutor) Execute(sig executor.StopSignal) model.StageStatus { func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { ecsInput := e.appCfg.Input + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) taskDefinition, ok := loadTaskDefinition(&e.Input, ecsInput.TaskDefinitionFile, e.deploySource) if !ok { @@ -86,7 +88,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { } if ecsInput.IsStandaloneTask() { - if !runStandaloneTask(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { + if !runStandaloneTask(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput, tags) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS @@ -102,7 +104,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { return model.StageStatus_STAGE_FAILURE } - if !sync(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary, tags) { return model.StageStatus_STAGE_FAILURE } @@ -128,7 +130,8 @@ func (e *deployExecutor) ensurePrimaryRollout(ctx context.Context) model.StageSt return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) + if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary, tags) { return model.StageStatus_STAGE_FAILURE } @@ -154,7 +157,8 @@ func (e *deployExecutor) ensureCanaryRollout(ctx context.Context) model.StageSta return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) + if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary, tags) { return model.StageStatus_STAGE_FAILURE } diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 732be3c60c..0d60d39500 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -137,19 +137,22 @@ func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinitio } func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service, tags []types.Tag) (*types.Service, error) { - found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) + service, found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) if err != nil { return nil, fmt.Errorf("unable to validate service name %s: %v", *serviceDefinition.ServiceName, err) } - var service *types.Service if found { + if err = cli.TagResource(ctx, *service.ServiceArn, tags); err != nil { + return nil, fmt.Errorf("failed to update tags of service %s: %v", *serviceDefinition.ServiceName, err) + } service, err = cli.UpdateService(ctx, serviceDefinition) if err != nil { return nil, fmt.Errorf("failed to update ECS service %s: %v", *serviceDefinition.ServiceName, err) } } else { - service, err = cli.CreateService(ctx, serviceDefinition, tags) + serviceDefinition.Tags = append(serviceDefinition.Tags, tags...) + service, err = cli.CreateService(ctx, serviceDefinition) if err != nil { return nil, fmt.Errorf("failed to create ECS service %s: %v", *serviceDefinition.ServiceName, err) } @@ -160,12 +163,12 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef func runStandaloneTask( ctx context.Context, - appID string, in *executor.Input, cloudProviderName string, cloudProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, ecsInput *config.ECSDeploymentInput, + tags []types.Tag, ) bool { client, err := provider.DefaultRegistry().Client(cloudProviderName, cloudProviderCfg, in.Logger) if err != nil { @@ -174,7 +177,6 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) @@ -187,6 +189,7 @@ func runStandaloneTask( ecsInput.ClusterArn, ecsInput.LaunchType, &ecsInput.AwsVpcConfiguration, + tags, ) if err != nil { in.LogPersister.Errorf("Failed to run ECS task: %v", err) @@ -226,15 +229,13 @@ func createPrimaryTaskSet(ctx context.Context, client provider.Client, service t return nil } -func sync(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func sync(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) 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) return false } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) - in.LogPersister.Infof("Start applying the ECS task definition") td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { @@ -259,15 +260,13 @@ func sync(ctx context.Context, appID string, in *executor.Input, platformProvide return true } -func rollout(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollout(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) 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) return false } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) - in.LogPersister.Infof("Start applying the ECS task definition") td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { diff --git a/pkg/app/piped/executor/ecs/rollback.go b/pkg/app/piped/executor/ecs/rollback.go index f51db951e1..738e4b37ab 100644 --- a/pkg/app/piped/executor/ecs/rollback.go +++ b/pkg/app/piped/executor/ecs/rollback.go @@ -87,14 +87,15 @@ func (e *rollbackExecutor) ensureRollback(ctx context.Context) model.StageStatus return model.StageStatus_STAGE_FAILURE } - if !rollback(ctx, e.Deployment.ApplicationId, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) + if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary, tags) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS } -func rollback(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) bool { in.LogPersister.Infof("Start rollback the ECS service and task family: %s and %s to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family) client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger) if err != nil { @@ -102,8 +103,6 @@ func rollback(ctx context.Context, appID string, in *executor.Input, platformPro return false } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) - // Re-register TaskDef to get TaskDefArn. // Consider using DescribeServices and get services[0].taskSets[0].taskDefinition (taskDefinition of PRIMARY taskSet) // then store it in metadata store and use for rollback instead. diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 6ffdfa9c42..da2c69fc24 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -71,13 +71,11 @@ func newClient(region, profile, credentialsFile, roleARN, tokenPath string, logg return c, nil } -func (c *client) CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) { +func (c *client) CreateService(ctx context.Context, service types.Service) (*types.Service, error) { if service.DeploymentController == nil || service.DeploymentController.Type != types.DeploymentControllerTypeExternal { return nil, fmt.Errorf("failed to create ECS service %s: deployment controller of type EXTERNAL is required", *service.ServiceName) } - tags = append(tags, service.Tags...) - input := &ecs.CreateServiceInput{ Cluster: service.ClusterArn, ServiceName: service.ServiceName, @@ -93,7 +91,7 @@ func (c *client) CreateService(ctx context.Context, service types.Service, tags Role: service.RoleArn, SchedulingStrategy: service.SchedulingStrategy, ServiceRegistries: service.ServiceRegistries, - Tags: tags, + Tags: service.Tags, } output, err := c.ecsClient.CreateService(ctx, input) @@ -155,7 +153,7 @@ func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition type return output.TaskDefinition, nil } -func (c *client) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *appconfig.ECSVpcConfiguration) error { +func (c *client) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *appconfig.ECSVpcConfiguration, tags []types.Tag) error { if taskDefinition.TaskDefinitionArn == nil { return fmt.Errorf("failed to run task of task family %s: no task definition provided", *taskDefinition.Family) } @@ -164,6 +162,7 @@ func (c *client) RunTask(ctx context.Context, taskDefinition types.TaskDefinitio TaskDefinition: taskDefinition.Family, Cluster: aws.String(clusterArn), LaunchType: types.LaunchType(launchType), + Tags: tags, } if len(awsVpcConfiguration.Subnets) > 0 { @@ -255,7 +254,7 @@ func (c *client) UpdateServicePrimaryTaskSet(ctx context.Context, service types. return output.TaskSet, nil } -func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceName string) (bool, error) { +func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceName string) (*types.Service, bool, error) { input := &ecs.DescribeServicesInput{ Cluster: aws.String(clusterName), Services: []string{serviceName}, @@ -265,17 +264,17 @@ func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceN var nfe *types.ResourceNotFoundException if errors.As(err, &nfe) { // Only in case ResourceNotFound error occurred, the FunctionName is available for create so do not raise error. - return false, nil + return &types.Service{}, false, nil } - return false, err + return &types.Service{}, false, err } // Note: In case of cluster's existing serviceName is set to inactive status, it's safe to recreate the service with the same serviceName. for _, service := range output.Services { if *service.ServiceName == serviceName && *service.Status == "ACTIVE" { - return true, nil + return &service, true, nil } } - return false, nil + return &types.Service{}, false, nil } func (c *client) GetListener(ctx context.Context, targetGroup types.LoadBalancer) (string, error) { @@ -346,14 +345,14 @@ func (c *client) ModifyListener(ctx context.Context, listenerArn string, routing return err } -func (c *client) TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error { +func (c *client) TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error { input := &ecs.TagResourceInput{ - ResourceArn: resourceArn, + ResourceArn: aws.String(resourceArn), Tags: tags, } _, err := c.ecsClient.TagResource(ctx, input) if err != nil { - return fmt.Errorf("failed to update tag of resource %s: %w", *resourceArn, err) + return fmt.Errorf("failed to update tag of resource %s: %w", resourceArn, err) } return nil } diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 4679661ac2..1a4bda348b 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -38,16 +38,16 @@ type Client interface { } type ECS interface { - ServiceExists(ctx context.Context, clusterName string, servicesName string) (bool, error) - CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) + ServiceExists(ctx context.Context, clusterName string, servicesName string) (*types.Service, bool, error) + CreateService(ctx context.Context, service types.Service) (*types.Service, error) UpdateService(ctx context.Context, service types.Service) (*types.Service, error) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) - RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration) error + RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration, tags []types.Tag) error GetPrimaryTaskSet(ctx context.Context, service types.Service) (*types.TaskSet, error) CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) DeleteTaskSet(ctx context.Context, service types.Service, taskSetArn string) error UpdateServicePrimaryTaskSet(ctx context.Context, service types.Service, taskSet types.TaskSet) (*types.TaskSet, error) - TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error + TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error } type ELB interface { From 9a9ee5b0f143dfb7142cb15a7b9ce976174e59c8 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Thu, 19 Jan 2023 19:33:26 +0900 Subject: [PATCH 07/22] Add tags to aws resources. --- pkg/app/piped/executor/ecs/deploy.go | 4 +-- pkg/app/piped/executor/ecs/ecs.go | 29 ++++++++++++-------- pkg/app/piped/platformprovider/ecs/client.go | 10 +++++-- pkg/app/piped/platformprovider/ecs/ecs.go | 17 ++++++++++-- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index c5be37077a..f2a272c5f8 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -86,7 +86,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { } if ecsInput.IsStandaloneTask() { - if !runStandaloneTask(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { + if !runStandaloneTask(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS @@ -102,7 +102,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { return model.StageStatus_STAGE_FAILURE } - if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + if !sync(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { return model.StageStatus_STAGE_FAILURE } diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index a97b33d233..732be3c60c 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -127,15 +127,16 @@ func loadTargetGroups(in *executor.Input, appCfg *config.ECSApplicationSpec, ds return primary, canary, true } -func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) { - td, err := cli.RegisterTaskDefinition(ctx, taskDefinition) +func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { + + td, err := cli.RegisterTaskDefinition(ctx, taskDefinition, tags) if err != nil { return nil, fmt.Errorf("unable to register ECS task definition of family %s: %v", *taskDefinition.Family, err) } 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, tags []types.Tag) (*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: %v", *serviceDefinition.ServiceName, err) @@ -148,7 +149,7 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef return nil, fmt.Errorf("failed to update ECS service %s: %v", *serviceDefinition.ServiceName, err) } } else { - service, err = cli.CreateService(ctx, serviceDefinition) + service, err = cli.CreateService(ctx, serviceDefinition, tags) if err != nil { return nil, fmt.Errorf("failed to create ECS service %s: %v", *serviceDefinition.ServiceName, err) } @@ -159,6 +160,7 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef func runStandaloneTask( ctx context.Context, + appID string, in *executor.Input, cloudProviderName string, cloudProviderCfg *config.PlatformProviderECSConfig, @@ -172,7 +174,8 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition) + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false @@ -223,22 +226,24 @@ 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, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func sync(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, 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) return false } + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition) + td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false } in.LogPersister.Infof("Start applying the ECS service definition") - service, err := applyServiceDefinition(ctx, client, serviceDefinition) + service, err := applyServiceDefinition(ctx, client, serviceDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err) return false @@ -254,22 +259,24 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string, return true } -func rollout(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollout(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, 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) return false } + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition) + td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false } in.LogPersister.Infof("Start applying the ECS service definition") - service, err := applyServiceDefinition(ctx, client, serviceDefinition) + service, err := applyServiceDefinition(ctx, client, serviceDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err) return false diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 105f2659c5..7e6d4dba4d 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -71,10 +71,13 @@ func newClient(region, profile, credentialsFile, roleARN, tokenPath string, logg return c, nil } -func (c *client) CreateService(ctx context.Context, service types.Service) (*types.Service, error) { +func (c *client) CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) { if service.DeploymentController == nil || service.DeploymentController.Type != types.DeploymentControllerTypeExternal { return nil, fmt.Errorf("failed to create ECS service %s: deployment controller of type EXTERNAL is required", *service.ServiceName) } + + tags = append(tags, service.Tags...) + input := &ecs.CreateServiceInput{ Cluster: service.ClusterArn, ServiceName: service.ServiceName, @@ -90,7 +93,7 @@ func (c *client) CreateService(ctx context.Context, service types.Service) (*typ Role: service.RoleArn, SchedulingStrategy: service.SchedulingStrategy, ServiceRegistries: service.ServiceRegistries, - Tags: service.Tags, + Tags: tags, } output, err := c.ecsClient.CreateService(ctx, input) @@ -130,7 +133,7 @@ func (c *client) UpdateService(ctx context.Context, service types.Service) (*typ return output.Service, nil } -func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) { +func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { input := &ecs.RegisterTaskDefinitionInput{ Family: taskDefinition.Family, ContainerDefinitions: taskDefinition.ContainerDefinitions, @@ -142,6 +145,7 @@ func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition type // Requires defined at task level in case Fargate is used. Cpu: taskDefinition.Cpu, Memory: taskDefinition.Memory, + Tags: tags, // TODO: Support tags for registering task definition. } output, err := c.ecsClient.RegisterTaskDefinition(ctx, input) diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 4eaee15a0d..6aeebfdeef 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -19,6 +19,7 @@ import ( "path/filepath" "sync" + "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ecs/types" "go.uber.org/zap" "golang.org/x/sync/singleflight" @@ -26,6 +27,10 @@ import ( "github.com/pipe-cd/pipecd/pkg/config" ) +const ( + LabelApplication = "pipecd-dev-application" // The application this resource belongs to. +) + // Client is wrapper of ECS client. type Client interface { ECS @@ -34,9 +39,9 @@ type Client interface { type ECS interface { ServiceExists(ctx context.Context, clusterName string, servicesName string) (bool, error) - CreateService(ctx context.Context, service types.Service) (*types.Service, error) + CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) UpdateService(ctx context.Context, service types.Service) (*types.Service, error) - RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) + RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration) error GetPrimaryTaskSet(ctx context.Context, service types.Service) (*types.TaskSet, error) CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) @@ -109,3 +114,11 @@ var defaultRegistry = ®istry{ func DefaultRegistry() Registry { return defaultRegistry } + +func CreateTags(keyValue map[string]string) []types.Tag { + tags := make([]types.Tag, len(keyValue)) + for key, value := range keyValue { + tags = append(tags, types.Tag{Key: aws.String(key), Value: aws.String(value)}) + } + return tags +} From 74c29dcf9044784be53ce7338cfedccbf2ccb60a Mon Sep 17 00:00:00 2001 From: Tomoki Date: Thu, 19 Jan 2023 19:38:54 +0900 Subject: [PATCH 08/22] Small fix --- pkg/app/piped/executor/ecs/deploy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index f2a272c5f8..3fa8800f0d 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -128,7 +128,7 @@ func (e *deployExecutor) ensurePrimaryRollout(ctx context.Context) model.StageSt return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { return model.StageStatus_STAGE_FAILURE } @@ -154,7 +154,7 @@ func (e *deployExecutor) ensureCanaryRollout(ctx context.Context) model.StageSta return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { + if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { return model.StageStatus_STAGE_FAILURE } From 9643e01ec4644317375b22ef04ec4eb7030a150c Mon Sep 17 00:00:00 2001 From: Tomoki Date: Thu, 19 Jan 2023 19:50:58 +0900 Subject: [PATCH 09/22] Small fix --- pkg/app/piped/executor/ecs/rollback.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/app/piped/executor/ecs/rollback.go b/pkg/app/piped/executor/ecs/rollback.go index 92fa7b4b43..f51db951e1 100644 --- a/pkg/app/piped/executor/ecs/rollback.go +++ b/pkg/app/piped/executor/ecs/rollback.go @@ -87,14 +87,14 @@ func (e *rollbackExecutor) ensureRollback(ctx context.Context) model.StageStatus return model.StageStatus_STAGE_FAILURE } - if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { + if !rollback(ctx, e.Deployment.ApplicationId, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS } -func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollback(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { in.LogPersister.Infof("Start rollback the ECS service and task family: %s and %s to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family) client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger) if err != nil { @@ -102,10 +102,12 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri return false } + tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) + // Re-register TaskDef to get TaskDefArn. // Consider using DescribeServices and get services[0].taskSets[0].taskDefinition (taskDefinition of PRIMARY taskSet) // then store it in metadata store and use for rollback instead. - td, err := client.RegisterTaskDefinition(ctx, taskDefinition) + td, err := client.RegisterTaskDefinition(ctx, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to register new revision of ECS task definition %s: %v", *taskDefinition.Family, err) return false From b072278a95e7c887c83d5990bf23103235867219 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 12:18:11 +0900 Subject: [PATCH 10/22] Fix initial slice length. --- pkg/app/piped/platformprovider/ecs/ecs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 6aeebfdeef..a0db7f24fb 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -28,7 +28,7 @@ import ( ) const ( - LabelApplication = "pipecd-dev-application" // The application this resource belongs to. + LabelApplication string = "pipecd-dev-application" // The application this resource belongs to. ) // Client is wrapper of ECS client. @@ -116,7 +116,7 @@ func DefaultRegistry() Registry { } func CreateTags(keyValue map[string]string) []types.Tag { - tags := make([]types.Tag, len(keyValue)) + tags := make([]types.Tag, 0, len(keyValue)) for key, value := range keyValue { tags = append(tags, types.Tag{Key: aws.String(key), Value: aws.String(value)}) } From 09a423aa253271320f00f7cac28e8debaaecf367 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 12:36:00 +0900 Subject: [PATCH 11/22] Add TagResource function --- pkg/app/piped/platformprovider/ecs/client.go | 12 ++++++++++++ pkg/app/piped/platformprovider/ecs/ecs.go | 1 + 2 files changed, 13 insertions(+) diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 7e6d4dba4d..6ffdfa9c42 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -345,3 +345,15 @@ func (c *client) ModifyListener(ctx context.Context, listenerArn string, routing _, err := c.elbClient.ModifyListener(ctx, input) return err } + +func (c *client) TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error { + input := &ecs.TagResourceInput{ + ResourceArn: resourceArn, + Tags: tags, + } + _, err := c.ecsClient.TagResource(ctx, input) + if err != nil { + return fmt.Errorf("failed to update tag of resource %s: %w", *resourceArn, err) + } + return nil +} diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index a0db7f24fb..4679661ac2 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -47,6 +47,7 @@ type ECS interface { CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) DeleteTaskSet(ctx context.Context, service types.Service, taskSetArn string) error UpdateServicePrimaryTaskSet(ctx context.Context, service types.Service, taskSet types.TaskSet) (*types.TaskSet, error) + TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error } type ELB interface { From 33831ebd005ec8a1200408d1ea9dfc7ae8a541f8 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 15:42:36 +0900 Subject: [PATCH 12/22] Fix deploy process --- pkg/app/piped/executor/ecs/deploy.go | 12 ++++++---- pkg/app/piped/executor/ecs/ecs.go | 21 ++++++++-------- pkg/app/piped/executor/ecs/rollback.go | 7 +++--- pkg/app/piped/platformprovider/ecs/client.go | 25 ++++++++++---------- pkg/app/piped/platformprovider/ecs/ecs.go | 8 +++---- 5 files changed, 37 insertions(+), 36 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index 3fa8800f0d..58e4e89b3e 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -19,6 +19,7 @@ import ( "github.com/pipe-cd/pipecd/pkg/app/piped/deploysource" "github.com/pipe-cd/pipecd/pkg/app/piped/executor" + provider "github.com/pipe-cd/pipecd/pkg/app/piped/platformprovider/ecs" "github.com/pipe-cd/pipecd/pkg/config" "github.com/pipe-cd/pipecd/pkg/model" ) @@ -79,6 +80,7 @@ func (e *deployExecutor) Execute(sig executor.StopSignal) model.StageStatus { func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { ecsInput := e.appCfg.Input + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) taskDefinition, ok := loadTaskDefinition(&e.Input, ecsInput.TaskDefinitionFile, e.deploySource) if !ok { @@ -86,7 +88,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { } if ecsInput.IsStandaloneTask() { - if !runStandaloneTask(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { + if !runStandaloneTask(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput, tags) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS @@ -102,7 +104,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { return model.StageStatus_STAGE_FAILURE } - if !sync(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary, tags) { return model.StageStatus_STAGE_FAILURE } @@ -128,7 +130,8 @@ func (e *deployExecutor) ensurePrimaryRollout(ctx context.Context) model.StageSt return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) + if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary, tags) { return model.StageStatus_STAGE_FAILURE } @@ -154,7 +157,8 @@ func (e *deployExecutor) ensureCanaryRollout(ctx context.Context) model.StageSta return model.StageStatus_STAGE_FAILURE } - if !rollout(ctx, e.Deployment.ApplicationId, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) + if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary, tags) { return model.StageStatus_STAGE_FAILURE } diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 732be3c60c..0d60d39500 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -137,19 +137,22 @@ func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinitio } func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service, tags []types.Tag) (*types.Service, error) { - found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) + service, found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) if err != nil { return nil, fmt.Errorf("unable to validate service name %s: %v", *serviceDefinition.ServiceName, err) } - var service *types.Service if found { + if err = cli.TagResource(ctx, *service.ServiceArn, tags); err != nil { + return nil, fmt.Errorf("failed to update tags of service %s: %v", *serviceDefinition.ServiceName, err) + } service, err = cli.UpdateService(ctx, serviceDefinition) if err != nil { return nil, fmt.Errorf("failed to update ECS service %s: %v", *serviceDefinition.ServiceName, err) } } else { - service, err = cli.CreateService(ctx, serviceDefinition, tags) + serviceDefinition.Tags = append(serviceDefinition.Tags, tags...) + service, err = cli.CreateService(ctx, serviceDefinition) if err != nil { return nil, fmt.Errorf("failed to create ECS service %s: %v", *serviceDefinition.ServiceName, err) } @@ -160,12 +163,12 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef func runStandaloneTask( ctx context.Context, - appID string, in *executor.Input, cloudProviderName string, cloudProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, ecsInput *config.ECSDeploymentInput, + tags []types.Tag, ) bool { client, err := provider.DefaultRegistry().Client(cloudProviderName, cloudProviderCfg, in.Logger) if err != nil { @@ -174,7 +177,6 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) @@ -187,6 +189,7 @@ func runStandaloneTask( ecsInput.ClusterArn, ecsInput.LaunchType, &ecsInput.AwsVpcConfiguration, + tags, ) if err != nil { in.LogPersister.Errorf("Failed to run ECS task: %v", err) @@ -226,15 +229,13 @@ func createPrimaryTaskSet(ctx context.Context, client provider.Client, service t return nil } -func sync(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func sync(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) 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) return false } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) - in.LogPersister.Infof("Start applying the ECS task definition") td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { @@ -259,15 +260,13 @@ func sync(ctx context.Context, appID string, in *executor.Input, platformProvide return true } -func rollout(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollout(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) 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) return false } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) - in.LogPersister.Infof("Start applying the ECS task definition") td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { diff --git a/pkg/app/piped/executor/ecs/rollback.go b/pkg/app/piped/executor/ecs/rollback.go index f51db951e1..738e4b37ab 100644 --- a/pkg/app/piped/executor/ecs/rollback.go +++ b/pkg/app/piped/executor/ecs/rollback.go @@ -87,14 +87,15 @@ func (e *rollbackExecutor) ensureRollback(ctx context.Context) model.StageStatus return model.StageStatus_STAGE_FAILURE } - if !rollback(ctx, e.Deployment.ApplicationId, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { + tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) + if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary, tags) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS } -func rollback(ctx context.Context, appID string, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { +func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) bool { in.LogPersister.Infof("Start rollback the ECS service and task family: %s and %s to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family) client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger) if err != nil { @@ -102,8 +103,6 @@ func rollback(ctx context.Context, appID string, in *executor.Input, platformPro return false } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: appID}) - // Re-register TaskDef to get TaskDefArn. // Consider using DescribeServices and get services[0].taskSets[0].taskDefinition (taskDefinition of PRIMARY taskSet) // then store it in metadata store and use for rollback instead. diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 6ffdfa9c42..da2c69fc24 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -71,13 +71,11 @@ func newClient(region, profile, credentialsFile, roleARN, tokenPath string, logg return c, nil } -func (c *client) CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) { +func (c *client) CreateService(ctx context.Context, service types.Service) (*types.Service, error) { if service.DeploymentController == nil || service.DeploymentController.Type != types.DeploymentControllerTypeExternal { return nil, fmt.Errorf("failed to create ECS service %s: deployment controller of type EXTERNAL is required", *service.ServiceName) } - tags = append(tags, service.Tags...) - input := &ecs.CreateServiceInput{ Cluster: service.ClusterArn, ServiceName: service.ServiceName, @@ -93,7 +91,7 @@ func (c *client) CreateService(ctx context.Context, service types.Service, tags Role: service.RoleArn, SchedulingStrategy: service.SchedulingStrategy, ServiceRegistries: service.ServiceRegistries, - Tags: tags, + Tags: service.Tags, } output, err := c.ecsClient.CreateService(ctx, input) @@ -155,7 +153,7 @@ func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition type return output.TaskDefinition, nil } -func (c *client) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *appconfig.ECSVpcConfiguration) error { +func (c *client) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *appconfig.ECSVpcConfiguration, tags []types.Tag) error { if taskDefinition.TaskDefinitionArn == nil { return fmt.Errorf("failed to run task of task family %s: no task definition provided", *taskDefinition.Family) } @@ -164,6 +162,7 @@ func (c *client) RunTask(ctx context.Context, taskDefinition types.TaskDefinitio TaskDefinition: taskDefinition.Family, Cluster: aws.String(clusterArn), LaunchType: types.LaunchType(launchType), + Tags: tags, } if len(awsVpcConfiguration.Subnets) > 0 { @@ -255,7 +254,7 @@ func (c *client) UpdateServicePrimaryTaskSet(ctx context.Context, service types. return output.TaskSet, nil } -func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceName string) (bool, error) { +func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceName string) (*types.Service, bool, error) { input := &ecs.DescribeServicesInput{ Cluster: aws.String(clusterName), Services: []string{serviceName}, @@ -265,17 +264,17 @@ func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceN var nfe *types.ResourceNotFoundException if errors.As(err, &nfe) { // Only in case ResourceNotFound error occurred, the FunctionName is available for create so do not raise error. - return false, nil + return &types.Service{}, false, nil } - return false, err + return &types.Service{}, false, err } // Note: In case of cluster's existing serviceName is set to inactive status, it's safe to recreate the service with the same serviceName. for _, service := range output.Services { if *service.ServiceName == serviceName && *service.Status == "ACTIVE" { - return true, nil + return &service, true, nil } } - return false, nil + return &types.Service{}, false, nil } func (c *client) GetListener(ctx context.Context, targetGroup types.LoadBalancer) (string, error) { @@ -346,14 +345,14 @@ func (c *client) ModifyListener(ctx context.Context, listenerArn string, routing return err } -func (c *client) TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error { +func (c *client) TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error { input := &ecs.TagResourceInput{ - ResourceArn: resourceArn, + ResourceArn: aws.String(resourceArn), Tags: tags, } _, err := c.ecsClient.TagResource(ctx, input) if err != nil { - return fmt.Errorf("failed to update tag of resource %s: %w", *resourceArn, err) + return fmt.Errorf("failed to update tag of resource %s: %w", resourceArn, err) } return nil } diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 4679661ac2..1a4bda348b 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -38,16 +38,16 @@ type Client interface { } type ECS interface { - ServiceExists(ctx context.Context, clusterName string, servicesName string) (bool, error) - CreateService(ctx context.Context, service types.Service, tags []types.Tag) (*types.Service, error) + ServiceExists(ctx context.Context, clusterName string, servicesName string) (*types.Service, bool, error) + CreateService(ctx context.Context, service types.Service) (*types.Service, error) UpdateService(ctx context.Context, service types.Service) (*types.Service, error) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) - RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration) error + RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration, tags []types.Tag) error GetPrimaryTaskSet(ctx context.Context, service types.Service) (*types.TaskSet, error) CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) DeleteTaskSet(ctx context.Context, service types.Service, taskSetArn string) error UpdateServicePrimaryTaskSet(ctx context.Context, service types.Service, taskSet types.TaskSet) (*types.TaskSet, error) - TagResource(ctx context.Context, resourceArn *string, tags []types.Tag) error + TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error } type ELB interface { From c4ae0f550a0c2ff807bc1d235a22ad9677481198 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 20 Jan 2023 16:52:43 +0900 Subject: [PATCH 13/22] Delete Unnecessary blank lines --- pkg/app/piped/executor/ecs/ecs.go | 1 - pkg/app/piped/platformprovider/ecs/client.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 0d60d39500..22e4143d9a 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -128,7 +128,6 @@ func loadTargetGroups(in *executor.Input, appCfg *config.ECSApplicationSpec, ds } func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { - td, err := cli.RegisterTaskDefinition(ctx, taskDefinition, tags) if err != nil { return nil, fmt.Errorf("unable to register ECS task definition of family %s: %v", *taskDefinition.Family, err) diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index da2c69fc24..8e48027dbb 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -75,7 +75,6 @@ func (c *client) CreateService(ctx context.Context, service types.Service) (*typ if service.DeploymentController == nil || service.DeploymentController.Type != types.DeploymentControllerTypeExternal { return nil, fmt.Errorf("failed to create ECS service %s: deployment controller of type EXTERNAL is required", *service.ServiceName) } - input := &ecs.CreateServiceInput{ Cluster: service.ClusterArn, ServiceName: service.ServiceName, @@ -93,7 +92,6 @@ func (c *client) CreateService(ctx context.Context, service types.Service) (*typ ServiceRegistries: service.ServiceRegistries, Tags: service.Tags, } - output, err := c.ecsClient.CreateService(ctx, input) if err != nil { return nil, fmt.Errorf("failed to create ECS service %s: %w", *service.ServiceName, err) From 5882609b70e97e324e13570c1e801b60ac254d12 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Mon, 23 Jan 2023 11:10:43 +0900 Subject: [PATCH 14/22] Change how to add task's tags --- pkg/app/piped/executor/ecs/deploy.go | 12 ++++------ pkg/app/piped/executor/ecs/ecs.go | 25 ++++++++++---------- pkg/app/piped/executor/ecs/rollback.go | 7 +++--- pkg/app/piped/platformprovider/ecs/client.go | 1 - 4 files changed, 20 insertions(+), 25 deletions(-) diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index 58e4e89b3e..c5be37077a 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -19,7 +19,6 @@ import ( "github.com/pipe-cd/pipecd/pkg/app/piped/deploysource" "github.com/pipe-cd/pipecd/pkg/app/piped/executor" - provider "github.com/pipe-cd/pipecd/pkg/app/piped/platformprovider/ecs" "github.com/pipe-cd/pipecd/pkg/config" "github.com/pipe-cd/pipecd/pkg/model" ) @@ -80,7 +79,6 @@ func (e *deployExecutor) Execute(sig executor.StopSignal) model.StageStatus { func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { ecsInput := e.appCfg.Input - tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) taskDefinition, ok := loadTaskDefinition(&e.Input, ecsInput.TaskDefinitionFile, e.deploySource) if !ok { @@ -88,7 +86,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { } if ecsInput.IsStandaloneTask() { - if !runStandaloneTask(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput, tags) { + if !runStandaloneTask(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, &ecsInput) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS @@ -104,7 +102,7 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { return model.StageStatus_STAGE_FAILURE } - if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary, tags) { + if !sync(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { return model.StageStatus_STAGE_FAILURE } @@ -130,8 +128,7 @@ func (e *deployExecutor) ensurePrimaryRollout(ctx context.Context) model.StageSt return model.StageStatus_STAGE_FAILURE } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) - if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary, tags) { + if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, primary) { return model.StageStatus_STAGE_FAILURE } @@ -157,8 +154,7 @@ func (e *deployExecutor) ensureCanaryRollout(ctx context.Context) model.StageSta return model.StageStatus_STAGE_FAILURE } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) - if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary, tags) { + if !rollout(ctx, &e.Input, e.platformProviderName, e.platformProviderCfg, taskDefinition, servicedefinition, canary) { return model.StageStatus_STAGE_FAILURE } diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 22e4143d9a..365ee391a9 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -92,6 +92,11 @@ func loadServiceDefinition(in *executor.Input, serviceDefinitionFile string, ds return types.Service{}, false } + serviceDefinition.Tags = append( + serviceDefinition.Tags, + provider.CreateTags(map[string]string{provider.LabelApplication: in.Deployment.ApplicationId})..., + ) + in.LogPersister.Infof("Successfully loaded the ECS service definition at commit %s", ds.Revision) return serviceDefinition, true } @@ -135,22 +140,18 @@ func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinitio return td, nil } -func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service, tags []types.Tag) (*types.Service, error) { +func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service) (*types.Service, error) { service, found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) if err != nil { return nil, fmt.Errorf("unable to validate service name %s: %v", *serviceDefinition.ServiceName, err) } if found { - if err = cli.TagResource(ctx, *service.ServiceArn, tags); err != nil { - return nil, fmt.Errorf("failed to update tags of service %s: %v", *serviceDefinition.ServiceName, err) - } service, err = cli.UpdateService(ctx, serviceDefinition) if err != nil { return nil, fmt.Errorf("failed to update ECS service %s: %v", *serviceDefinition.ServiceName, err) } } else { - serviceDefinition.Tags = append(serviceDefinition.Tags, tags...) service, err = cli.CreateService(ctx, serviceDefinition) if err != nil { return nil, fmt.Errorf("failed to create ECS service %s: %v", *serviceDefinition.ServiceName, err) @@ -167,7 +168,6 @@ func runStandaloneTask( cloudProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, ecsInput *config.ECSDeploymentInput, - tags []types.Tag, ) bool { client, err := provider.DefaultRegistry().Client(cloudProviderName, cloudProviderCfg, in.Logger) if err != nil { @@ -176,6 +176,7 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") + tags := provider.CreateTags(map[string]string{provider.LabelApplication: in.Application.Id}) td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) @@ -228,7 +229,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, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) bool { +func sync(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, 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) @@ -236,14 +237,14 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string, } in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) + td, err := applyTaskDefinition(ctx, client, taskDefinition, serviceDefinition.Tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false } in.LogPersister.Infof("Start applying the ECS service definition") - service, err := applyServiceDefinition(ctx, client, serviceDefinition, tags) + service, err := applyServiceDefinition(ctx, client, serviceDefinition) if err != nil { in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err) return false @@ -259,7 +260,7 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string, return true } -func rollout(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) bool { +func rollout(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, 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) @@ -267,14 +268,14 @@ func rollout(ctx context.Context, in *executor.Input, platformProviderName strin } in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) + td, err := applyTaskDefinition(ctx, client, taskDefinition, serviceDefinition.Tags) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false } in.LogPersister.Infof("Start applying the ECS service definition") - service, err := applyServiceDefinition(ctx, client, serviceDefinition, tags) + service, err := applyServiceDefinition(ctx, client, serviceDefinition) if err != nil { in.LogPersister.Errorf("Failed to apply service %s: %v", *serviceDefinition.ServiceName, err) return false diff --git a/pkg/app/piped/executor/ecs/rollback.go b/pkg/app/piped/executor/ecs/rollback.go index 738e4b37ab..964a176e07 100644 --- a/pkg/app/piped/executor/ecs/rollback.go +++ b/pkg/app/piped/executor/ecs/rollback.go @@ -87,15 +87,14 @@ func (e *rollbackExecutor) ensureRollback(ctx context.Context) model.StageStatus return model.StageStatus_STAGE_FAILURE } - tags := provider.CreateTags(map[string]string{provider.LabelApplication: e.Deployment.ApplicationId}) - if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary, tags) { + if !rollback(ctx, &e.Input, platformProviderName, platformProviderCfg, taskDefinition, serviceDefinition, primary) { return model.StageStatus_STAGE_FAILURE } return model.StageStatus_STAGE_SUCCESS } -func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer, tags []types.Tag) bool { +func rollback(ctx context.Context, in *executor.Input, platformProviderName string, platformProviderCfg *config.PlatformProviderECSConfig, taskDefinition types.TaskDefinition, serviceDefinition types.Service, targetGroup *types.LoadBalancer) bool { in.LogPersister.Infof("Start rollback the ECS service and task family: %s and %s to original stage", *serviceDefinition.ServiceName, *taskDefinition.Family) client, err := provider.DefaultRegistry().Client(platformProviderName, platformProviderCfg, in.Logger) if err != nil { @@ -106,7 +105,7 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri // Re-register TaskDef to get TaskDefArn. // Consider using DescribeServices and get services[0].taskSets[0].taskDefinition (taskDefinition of PRIMARY taskSet) // then store it in metadata store and use for rollback instead. - td, err := client.RegisterTaskDefinition(ctx, taskDefinition, tags) + td, err := client.RegisterTaskDefinition(ctx, taskDefinition, serviceDefinition.Tags) if err != nil { in.LogPersister.Errorf("Failed to register new revision of ECS task definition %s: %v", *taskDefinition.Family, err) return false diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 8e48027dbb..f8f52ec327 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -142,7 +142,6 @@ func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition type Cpu: taskDefinition.Cpu, Memory: taskDefinition.Memory, Tags: tags, - // TODO: Support tags for registering task definition. } output, err := c.ecsClient.RegisterTaskDefinition(ctx, input) if err != nil { From 14954a75389940e354fd389cd0b3d5a0be3b2432 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Mon, 23 Jan 2023 11:16:36 +0900 Subject: [PATCH 15/22] Delete TagResource method --- pkg/app/piped/platformprovider/ecs/client.go | 12 ------------ pkg/app/piped/platformprovider/ecs/ecs.go | 1 - 2 files changed, 13 deletions(-) diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index f8f52ec327..2ab77cab3e 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -341,15 +341,3 @@ func (c *client) ModifyListener(ctx context.Context, listenerArn string, routing _, err := c.elbClient.ModifyListener(ctx, input) return err } - -func (c *client) TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error { - input := &ecs.TagResourceInput{ - ResourceArn: aws.String(resourceArn), - Tags: tags, - } - _, err := c.ecsClient.TagResource(ctx, input) - if err != nil { - return fmt.Errorf("failed to update tag of resource %s: %w", resourceArn, err) - } - return nil -} diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 1a4bda348b..68be9c5063 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -47,7 +47,6 @@ type ECS interface { CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) DeleteTaskSet(ctx context.Context, service types.Service, taskSetArn string) error UpdateServicePrimaryTaskSet(ctx context.Context, service types.Service, taskSet types.TaskSet) (*types.TaskSet, error) - TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error } type ELB interface { From 9b8a215fd4f610f54549ac293598c662f567e33c Mon Sep 17 00:00:00 2001 From: Tomoki Date: Mon, 23 Jan 2023 11:24:25 +0900 Subject: [PATCH 16/22] Fix based on linter --- pkg/app/piped/executor/ecs/ecs.go | 3 ++- pkg/app/piped/platformprovider/ecs/client.go | 10 +++++----- pkg/app/piped/platformprovider/ecs/ecs.go | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 365ee391a9..852c8b80a3 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -141,11 +141,12 @@ func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinitio } func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDefinition types.Service) (*types.Service, error) { - service, found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) + found, err := cli.ServiceExists(ctx, *serviceDefinition.ClusterArn, *serviceDefinition.ServiceName) if err != nil { return nil, fmt.Errorf("unable to validate service name %s: %v", *serviceDefinition.ServiceName, err) } + var service *types.Service if found { service, err = cli.UpdateService(ctx, serviceDefinition) if err != nil { diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 2ab77cab3e..7448f4bb9a 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -251,7 +251,7 @@ func (c *client) UpdateServicePrimaryTaskSet(ctx context.Context, service types. return output.TaskSet, nil } -func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceName string) (*types.Service, bool, error) { +func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceName string) (bool, error) { input := &ecs.DescribeServicesInput{ Cluster: aws.String(clusterName), Services: []string{serviceName}, @@ -261,17 +261,17 @@ func (c *client) ServiceExists(ctx context.Context, clusterName string, serviceN var nfe *types.ResourceNotFoundException if errors.As(err, &nfe) { // Only in case ResourceNotFound error occurred, the FunctionName is available for create so do not raise error. - return &types.Service{}, false, nil + return false, nil } - return &types.Service{}, false, err + return false, err } // Note: In case of cluster's existing serviceName is set to inactive status, it's safe to recreate the service with the same serviceName. for _, service := range output.Services { if *service.ServiceName == serviceName && *service.Status == "ACTIVE" { - return &service, true, nil + return true, nil } } - return &types.Service{}, false, nil + return false, nil } func (c *client) GetListener(ctx context.Context, targetGroup types.LoadBalancer) (string, error) { diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 68be9c5063..d2767dbd35 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -38,7 +38,7 @@ type Client interface { } type ECS interface { - ServiceExists(ctx context.Context, clusterName string, servicesName string) (*types.Service, bool, 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) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) From 17697c35119eb2d14766ab4724891fbf152d87ad Mon Sep 17 00:00:00 2001 From: Tomoki Date: Mon, 23 Jan 2023 14:07:53 +0900 Subject: [PATCH 17/22] Apply review --- pkg/app/piped/executor/ecs/ecs.go | 10 +++++----- pkg/app/piped/executor/ecs/rollback.go | 2 +- pkg/app/piped/platformprovider/ecs/client.go | 4 ++-- pkg/app/piped/platformprovider/ecs/ecs.go | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 852c8b80a3..4410338727 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -132,8 +132,8 @@ func loadTargetGroups(in *executor.Input, appCfg *config.ECSApplicationSpec, ds return primary, canary, true } -func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { - td, err := cli.RegisterTaskDefinition(ctx, taskDefinition, tags) +func applyTaskDefinition(ctx context.Context, cli provider.Client, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) { + td, err := cli.RegisterTaskDefinition(ctx, taskDefinition) if err != nil { return nil, fmt.Errorf("unable to register ECS task definition of family %s: %v", *taskDefinition.Family, err) } @@ -178,7 +178,7 @@ func runStandaloneTask( in.LogPersister.Infof("Start applying the ECS task definition") tags := provider.CreateTags(map[string]string{provider.LabelApplication: in.Application.Id}) - td, err := applyTaskDefinition(ctx, client, taskDefinition, tags) + td, err := applyTaskDefinition(ctx, client, taskDefinition) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false @@ -238,7 +238,7 @@ func sync(ctx context.Context, in *executor.Input, platformProviderName string, } in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition, serviceDefinition.Tags) + td, err := applyTaskDefinition(ctx, client, taskDefinition) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false @@ -269,7 +269,7 @@ func rollout(ctx context.Context, in *executor.Input, platformProviderName strin } in.LogPersister.Infof("Start applying the ECS task definition") - td, err := applyTaskDefinition(ctx, client, taskDefinition, serviceDefinition.Tags) + td, err := applyTaskDefinition(ctx, client, taskDefinition) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) return false diff --git a/pkg/app/piped/executor/ecs/rollback.go b/pkg/app/piped/executor/ecs/rollback.go index 964a176e07..92fa7b4b43 100644 --- a/pkg/app/piped/executor/ecs/rollback.go +++ b/pkg/app/piped/executor/ecs/rollback.go @@ -105,7 +105,7 @@ func rollback(ctx context.Context, in *executor.Input, platformProviderName stri // Re-register TaskDef to get TaskDefArn. // Consider using DescribeServices and get services[0].taskSets[0].taskDefinition (taskDefinition of PRIMARY taskSet) // then store it in metadata store and use for rollback instead. - td, err := client.RegisterTaskDefinition(ctx, taskDefinition, serviceDefinition.Tags) + td, err := client.RegisterTaskDefinition(ctx, taskDefinition) if err != nil { in.LogPersister.Errorf("Failed to register new revision of ECS task definition %s: %v", *taskDefinition.Family, err) return false diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 7448f4bb9a..56221ec361 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -129,7 +129,7 @@ func (c *client) UpdateService(ctx context.Context, service types.Service) (*typ return output.Service, nil } -func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) { +func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) { input := &ecs.RegisterTaskDefinitionInput{ Family: taskDefinition.Family, ContainerDefinitions: taskDefinition.ContainerDefinitions, @@ -141,7 +141,7 @@ func (c *client) RegisterTaskDefinition(ctx context.Context, taskDefinition type // Requires defined at task level in case Fargate is used. Cpu: taskDefinition.Cpu, Memory: taskDefinition.Memory, - Tags: tags, + // TODO: Support tags for registering task definition. } output, err := c.ecsClient.RegisterTaskDefinition(ctx, input) if err != nil { diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index d2767dbd35..d65a964073 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -41,7 +41,7 @@ type ECS interface { 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) - RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition, tags []types.Tag) (*types.TaskDefinition, error) + RegisterTaskDefinition(ctx context.Context, taskDefinition types.TaskDefinition) (*types.TaskDefinition, error) RunTask(ctx context.Context, taskDefinition types.TaskDefinition, clusterArn string, launchType string, awsVpcConfiguration *config.ECSVpcConfiguration, tags []types.Tag) error GetPrimaryTaskSet(ctx context.Context, service types.Service) (*types.TaskSet, error) CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) From defc1ba5ddadc2fac16370f3285848bd03d61ba0 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 27 Jan 2023 10:12:34 +0900 Subject: [PATCH 18/22] add builtin-labels --- pkg/app/piped/executor/ecs/ecs.go | 14 ++++++++++++-- pkg/app/piped/platformprovider/ecs/ecs.go | 4 ++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 4410338727..43e3bfb012 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -94,7 +94,12 @@ func loadServiceDefinition(in *executor.Input, serviceDefinitionFile string, ds serviceDefinition.Tags = append( serviceDefinition.Tags, - provider.CreateTags(map[string]string{provider.LabelApplication: in.Deployment.ApplicationId})..., + provider.CreateTags(map[string]string{ + provider.LabelManagedBy: provider.ManagedByPiped, + provider.LabelPiped: in.PipedConfig.PipedID, + provider.LabelApplication: in.Deployment.ApplicationId, + provider.LabelCommitHash: in.Deployment.CommitHash(), + })..., ) in.LogPersister.Infof("Successfully loaded the ECS service definition at commit %s", ds.Revision) @@ -177,7 +182,12 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") - tags := provider.CreateTags(map[string]string{provider.LabelApplication: in.Application.Id}) + tags := provider.CreateTags(map[string]string{ + provider.LabelManagedBy: provider.ManagedByPiped, + provider.LabelPiped: in.PipedConfig.PipedID, + provider.LabelApplication: in.Deployment.ApplicationId, + provider.LabelCommitHash: in.Deployment.CommitHash(), + }) td, err := applyTaskDefinition(ctx, client, taskDefinition) if err != nil { in.LogPersister.Errorf("Failed to apply ECS task definition: %v", err) diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index d65a964073..442d551928 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -28,7 +28,11 @@ import ( ) const ( + LabelManagedBy string = "pipecd-dev-managed-by" // Always be piped. + LabelPiped string = "pipecd-dev-piped" // The id of piped handling this application. LabelApplication string = "pipecd-dev-application" // The application this resource belongs to. + LabelCommitHash string = "pipecd-dev-commit-hash" // Hash value of the deployed commit. + ManagedByPiped string = "piped" ) // Client is wrapper of ECS client. From d517e333a8fd9eeb07c1a680133471c88a531d71 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 27 Jan 2023 10:41:26 +0900 Subject: [PATCH 19/22] Add tagResource func --- pkg/app/piped/executor/ecs/ecs.go | 4 ++++ pkg/app/piped/platformprovider/ecs/client.go | 12 ++++++++++++ pkg/app/piped/platformprovider/ecs/ecs.go | 1 + 3 files changed, 17 insertions(+) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 43e3bfb012..5573c5383f 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -157,6 +157,10 @@ func applyServiceDefinition(ctx context.Context, cli provider.Client, serviceDef if err != nil { return nil, fmt.Errorf("failed to update ECS service %s: %v", *serviceDefinition.ServiceName, err) } + if err := cli.TagResource(ctx, *service.ServiceArn, serviceDefinition.Tags); err != nil { + return nil, fmt.Errorf("failed to update tags of ECS service %s: %v", *serviceDefinition.ServiceName, err) + } + } else { service, err = cli.CreateService(ctx, serviceDefinition) if err != nil { diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 56221ec361..5fbd68588c 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -341,3 +341,15 @@ func (c *client) ModifyListener(ctx context.Context, listenerArn string, routing _, err := c.elbClient.ModifyListener(ctx, input) return err } + +func (c *client) TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error { + input := &ecs.TagResourceInput{ + ResourceArn: aws.String(resourceArn), + Tags: tags, + } + _, err := c.ecsClient.TagResource(ctx, input) + if err != nil { + return fmt.Errorf("failed to update tag of resource %s: %w", resourceArn, err) + } + return nil +} diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index 442d551928..c773d00318 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -51,6 +51,7 @@ type ECS interface { CreateTaskSet(ctx context.Context, service types.Service, taskDefinition types.TaskDefinition, targetGroup *types.LoadBalancer, scale int) (*types.TaskSet, error) DeleteTaskSet(ctx context.Context, service types.Service, taskSetArn string) error UpdateServicePrimaryTaskSet(ctx context.Context, service types.Service, taskSet types.TaskSet) (*types.TaskSet, error) + TagResource(ctx context.Context, resourceArn string, tags []types.Tag) error } type ELB interface { From 6669ad153123def9d4b7947a8c43b00ce233abd8 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 27 Jan 2023 13:45:10 +0900 Subject: [PATCH 20/22] Set default value of PropagateTags --- pkg/app/piped/executor/ecs/ecs.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 5573c5383f..e5477cf391 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -92,6 +92,10 @@ func loadServiceDefinition(in *executor.Input, serviceDefinitionFile string, ds return types.Service{}, false } + if serviceDefinition.PropagateTags == "" { + serviceDefinition.PropagateTags = types.PropagateTagsService + } + serviceDefinition.Tags = append( serviceDefinition.Tags, provider.CreateTags(map[string]string{ From 9833e37e9d7101c12b8af55499d828c58bbfbf97 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 27 Jan 2023 13:48:30 +0900 Subject: [PATCH 21/22] Apply review --- pkg/app/piped/executor/ecs/ecs.go | 4 ++-- pkg/app/piped/platformprovider/ecs/ecs.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index e5477cf391..0c5f9c58d7 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -98,7 +98,7 @@ func loadServiceDefinition(in *executor.Input, serviceDefinitionFile string, ds serviceDefinition.Tags = append( serviceDefinition.Tags, - provider.CreateTags(map[string]string{ + provider.MakeTags(map[string]string{ provider.LabelManagedBy: provider.ManagedByPiped, provider.LabelPiped: in.PipedConfig.PipedID, provider.LabelApplication: in.Deployment.ApplicationId, @@ -190,7 +190,7 @@ func runStandaloneTask( } in.LogPersister.Infof("Start applying the ECS task definition") - tags := provider.CreateTags(map[string]string{ + tags := provider.MakeTags(map[string]string{ provider.LabelManagedBy: provider.ManagedByPiped, provider.LabelPiped: in.PipedConfig.PipedID, provider.LabelApplication: in.Deployment.ApplicationId, diff --git a/pkg/app/piped/platformprovider/ecs/ecs.go b/pkg/app/piped/platformprovider/ecs/ecs.go index c773d00318..7da090b32c 100644 --- a/pkg/app/piped/platformprovider/ecs/ecs.go +++ b/pkg/app/piped/platformprovider/ecs/ecs.go @@ -120,10 +120,10 @@ func DefaultRegistry() Registry { return defaultRegistry } -func CreateTags(keyValue map[string]string) []types.Tag { - tags := make([]types.Tag, 0, len(keyValue)) - for key, value := range keyValue { - tags = append(tags, types.Tag{Key: aws.String(key), Value: aws.String(value)}) +func MakeTags(tags map[string]string) []types.Tag { + resourceTags := make([]types.Tag, 0, len(tags)) + for key, value := range tags { + resourceTags = append(resourceTags, types.Tag{Key: aws.String(key), Value: aws.String(value)}) } - return tags + return resourceTags } From 94c3ba1bc8356905624160f2659e2a2bc7bdfaf5 Mon Sep 17 00:00:00 2001 From: Tomoki Date: Fri, 27 Jan 2023 15:32:45 +0900 Subject: [PATCH 22/22] Apply review --- pkg/app/piped/executor/ecs/ecs.go | 4 ---- pkg/app/piped/platformprovider/ecs/client.go | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/pkg/app/piped/executor/ecs/ecs.go b/pkg/app/piped/executor/ecs/ecs.go index 0c5f9c58d7..63675cd743 100644 --- a/pkg/app/piped/executor/ecs/ecs.go +++ b/pkg/app/piped/executor/ecs/ecs.go @@ -92,10 +92,6 @@ func loadServiceDefinition(in *executor.Input, serviceDefinitionFile string, ds return types.Service{}, false } - if serviceDefinition.PropagateTags == "" { - serviceDefinition.PropagateTags = types.PropagateTagsService - } - serviceDefinition.Tags = append( serviceDefinition.Tags, provider.MakeTags(map[string]string{ diff --git a/pkg/app/piped/platformprovider/ecs/client.go b/pkg/app/piped/platformprovider/ecs/client.go index 5fbd68588c..d439faeca9 100644 --- a/pkg/app/piped/platformprovider/ecs/client.go +++ b/pkg/app/piped/platformprovider/ecs/client.go @@ -86,7 +86,7 @@ func (c *client) CreateService(ctx context.Context, service types.Service) (*typ PlacementConstraints: service.PlacementConstraints, PlacementStrategy: service.PlacementStrategy, PlatformVersion: service.PlatformVersion, - PropagateTags: service.PropagateTags, + PropagateTags: types.PropagateTagsService, Role: service.RoleArn, SchedulingStrategy: service.SchedulingStrategy, ServiceRegistries: service.ServiceRegistries,