diff --git a/pkg/app/piped/executor/ecs/deploy.go b/pkg/app/piped/executor/ecs/deploy.go index ca40ecabdc..14f8c517bc 100644 --- a/pkg/app/piped/executor/ecs/deploy.go +++ b/pkg/app/piped/executor/ecs/deploy.go @@ -17,6 +17,8 @@ package ecs import ( "context" + "github.com/aws/aws-sdk-go-v2/service/ecs/types" + "github.com/pipe-cd/pipecd/pkg/app/piped/deploysource" "github.com/pipe-cd/pipecd/pkg/app/piped/executor" "github.com/pipe-cd/pipecd/pkg/config" @@ -97,9 +99,13 @@ func (e *deployExecutor) ensureSync(ctx context.Context) model.StageStatus { return model.StageStatus_STAGE_FAILURE } - primary, _, ok := loadTargetGroups(&e.Input, e.appCfg, e.deploySource) - if !ok { - return model.StageStatus_STAGE_FAILURE + var primary *types.LoadBalancer + // When the service is not accessed via ELB, the target group is not used. + if ecsInput.IsAccessedViaELB() { + primary, _, ok = loadTargetGroups(&e.Input, e.appCfg, e.deploySource) + if !ok { + return model.StageStatus_STAGE_FAILURE + } } recreate := e.appCfg.QuickSync.Recreate diff --git a/pkg/config/application_ecs.go b/pkg/config/application_ecs.go index fb0e5e72d3..14deab78c6 100644 --- a/pkg/config/application_ecs.go +++ b/pkg/config/application_ecs.go @@ -16,6 +16,12 @@ package config import ( "encoding/json" + "fmt" +) + +const ( + AccessTypeELB string = "ELB" + AccessTypeServiceDiscovery string = "SERVICE_DISCOVERY" ) // ECSApplicationSpec represents an application configuration for ECS application. @@ -32,6 +38,11 @@ func (s *ECSApplicationSpec) Validate() error { if err := s.GenericApplicationSpec.Validate(); err != nil { return err } + + if err := s.Input.validate(); err != nil { + return err + } + return nil } @@ -57,12 +68,22 @@ type ECSDeploymentInput struct { // Run standalone task during deployment. // Default is true. RunStandaloneTask *bool `json:"runStandaloneTask" default:"true"` + // How the ECS service is accessed. + // Possible values are: + // - ELB - The service is accessed via ELB and target groups. + // - SERVICE_DISCOVERY - The service is accessed via ECS Service Discovery. + // Default is ELB. + AccessType string `json:"accessType" default:"ELB"` } func (in *ECSDeploymentInput) IsStandaloneTask() bool { return in.ServiceDefinitionFile == "" } +func (in *ECSDeploymentInput) IsAccessedViaELB() bool { + return in.AccessType == AccessTypeELB +} + type ECSVpcConfiguration struct { Subnets []string AssignPublicIP string @@ -121,3 +142,13 @@ func (opts ECSTrafficRoutingStageOptions) Percentage() (primary, canary int) { canary = 0 return } + +func (in *ECSDeploymentInput) validate() error { + switch in.AccessType { + case AccessTypeELB, AccessTypeServiceDiscovery: + break + default: + return fmt.Errorf("invalid accessType: %s", in.AccessType) + } + return nil +} diff --git a/pkg/config/application_ecs_test.go b/pkg/config/application_ecs_test.go index 2b63f733e1..305a4bf437 100644 --- a/pkg/config/application_ecs_test.go +++ b/pkg/config/application_ecs_test.go @@ -16,6 +16,7 @@ package config import ( "encoding/json" + "fmt" "testing" "time" @@ -64,10 +65,79 @@ func TestECSApplicationConfig(t *testing.T) { LaunchType: "FARGATE", AutoRollback: newBoolPointer(true), RunStandaloneTask: newBoolPointer(true), + AccessType: "ELB", }, }, expectedError: nil, }, + { + fileName: "testdata/application/ecs-app-service-discovery.yaml", + expectedKind: KindECSApp, + expectedAPIVersion: "pipecd.dev/v1beta1", + expectedSpec: &ECSApplicationSpec{ + GenericApplicationSpec: GenericApplicationSpec{ + Timeout: Duration(6 * time.Hour), + Trigger: Trigger{ + OnCommit: OnCommit{ + Disabled: false, + }, + OnCommand: OnCommand{ + Disabled: false, + }, + OnOutOfSync: OnOutOfSync{ + Disabled: newBoolPointer(true), + MinWindow: Duration(5 * time.Minute), + }, + OnChain: OnChain{ + Disabled: newBoolPointer(true), + }, + }, + }, + Input: ECSDeploymentInput{ + ServiceDefinitionFile: "/path/to/servicedef.yaml", + TaskDefinitionFile: "/path/to/taskdef.yaml", + LaunchType: "FARGATE", + AutoRollback: newBoolPointer(true), + RunStandaloneTask: newBoolPointer(true), + AccessType: "SERVICE_DISCOVERY", + }, + }, + expectedError: nil, + }, + { + fileName: "testdata/application/ecs-app-invalid-access-type.yaml", + expectedKind: KindECSApp, + expectedAPIVersion: "pipecd.dev/v1beta1", + expectedSpec: &ECSApplicationSpec{ + GenericApplicationSpec: GenericApplicationSpec{ + Timeout: Duration(6 * time.Hour), + Trigger: Trigger{ + OnCommit: OnCommit{ + Disabled: false, + }, + OnCommand: OnCommand{ + Disabled: false, + }, + OnOutOfSync: OnOutOfSync{ + Disabled: newBoolPointer(true), + MinWindow: Duration(5 * time.Minute), + }, + OnChain: OnChain{ + Disabled: newBoolPointer(true), + }, + }, + }, + Input: ECSDeploymentInput{ + ServiceDefinitionFile: "/path/to/servicedef.yaml", + TaskDefinitionFile: "/path/to/taskdef.yaml", + LaunchType: "FARGATE", + AutoRollback: newBoolPointer(true), + RunStandaloneTask: newBoolPointer(true), + AccessType: "XXX", + }, + }, + expectedError: fmt.Errorf("invalid accessType: XXX"), + }, } for _, tc := range testcases { t.Run(tc.fileName, func(t *testing.T) { diff --git a/pkg/config/testdata/application/ecs-app-invalid-access-type.yaml b/pkg/config/testdata/application/ecs-app-invalid-access-type.yaml new file mode 100644 index 0000000000..4f41eb974e --- /dev/null +++ b/pkg/config/testdata/application/ecs-app-invalid-access-type.yaml @@ -0,0 +1,7 @@ +apiVersion: pipecd.dev/v1beta1 +kind: ECSApp +spec: + input: + serviceDefinitionFile: /path/to/servicedef.yaml + taskDefinitionFile: /path/to/taskdef.yaml + accessType: XXX \ No newline at end of file diff --git a/pkg/config/testdata/application/ecs-app-service-discovery.yaml b/pkg/config/testdata/application/ecs-app-service-discovery.yaml new file mode 100644 index 0000000000..cc9da20611 --- /dev/null +++ b/pkg/config/testdata/application/ecs-app-service-discovery.yaml @@ -0,0 +1,7 @@ +apiVersion: pipecd.dev/v1beta1 +kind: ECSApp +spec: + input: + serviceDefinitionFile: /path/to/servicedef.yaml + taskDefinitionFile: /path/to/taskdef.yaml + accessType: SERVICE_DISCOVERY \ No newline at end of file