From 904b0d98e692667563385de020d5efd3d20d822b Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Sat, 19 Nov 2022 14:08:22 -0800 Subject: [PATCH 01/12] set placement to private --- internal/pkg/cli/svc_init.go | 94 ++++++++++++++++++- internal/pkg/cli/svc_init_test.go | 30 ++++++ internal/pkg/initialize/workload.go | 51 +++++----- internal/pkg/manifest/backend_svc.go | 21 ++++- internal/pkg/manifest/lb_web_svc.go | 17 +++- internal/pkg/manifest/lb_web_svc_test.go | 2 + internal/pkg/manifest/rd_web_svc.go | 21 ++++- internal/pkg/manifest/worker_svc.go | 23 ++++- .../workloads/services/lb-web/manifest.yml | 11 ++- 9 files changed, 232 insertions(+), 38 deletions(-) diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 45d23a76036..23fe5cb6413 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -12,6 +12,7 @@ import ( "github.com/aws/aws-sdk-go/service/ssm" "github.com/aws/copilot-cli/internal/pkg/aws/identity" + "github.com/aws/copilot-cli/internal/pkg/describe" "github.com/dustin/go-humanize/english" "github.com/aws/aws-sdk-go/aws" @@ -152,7 +153,8 @@ type initSvcOpts struct { manifestExists bool // Init a Dockerfile parser using fs and input path - dockerfile func(string) dockerfileParser + dockerfile func(string) dockerfileParser + initEnvDescribers func(string) (envDescriber, error) } func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { @@ -200,6 +202,17 @@ func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { opts.df = dockerfile.New(opts.fs, opts.dockerfilePath) return opts.df } + opts.initEnvDescribers = func(envName string) (envDescriber, error) { + envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ + App: opts.appName, + Env: envName, + ConfigStore: opts.store, + }) + if err != nil { + return nil, err + } + return envDescriber, nil + } return opts, nil } @@ -293,6 +306,18 @@ func (o *initSvcOpts) Ask() error { return err } } + // if man, _ := o.IsSubnetsOnlyPrivate(); man != nil { + // var fmtEnv string + // var fmtString string + // for k, v := range man.EnvOverridePlacement { + // // if v == "" { + // // return fmt.Errorf("zero value of string") + // // } + // fmtString = fmtString + v + // fmtEnv = fmtEnv + strconv.Itoa(k) + // } + // return fmt.Errorf("myError is\n%s\n%s", fmtEnv, fmtString) + // } if err := o.askSvcPort(); err != nil { return err } @@ -323,6 +348,10 @@ func (o *initSvcOpts) Execute() error { o.platform = &platform } } + envs, err := o.isSubnetsOnlyPrivate() + if err != nil { + return err + } manifestPath, err := o.init.Service(&initialize.ServiceProps{ WorkloadProps: initialize.WorkloadProps{ App: o.appName, @@ -333,7 +362,8 @@ func (o *initSvcOpts) Execute() error { Platform: manifest.PlatformArgsOrString{ PlatformString: o.platform, }, - Topics: o.topics, + Topics: o.topics, + PrivateOnlyEnvironments: envs, }, Port: o.port, HealthCheck: hc, @@ -711,6 +741,66 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error }, nil } +// func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { +// envs, err := o.store.ListEnvironments(o.appName) +// if err != nil { +// return nil, err +// } +// var privateOnlyEnvs []string +// for i := range envs { +// envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ +// App: o.appName, +// Env: envs[i].Name, +// ConfigStore: o.store, +// }) +// if err != nil { +// return nil, fmt.Errorf("initiate env describer: %w", err) +// } +// mft, err := envDescriber.Manifest() +// if err != nil { +// return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) +// } +// envConfig, err := manifest.UnmarshalEnvironment(mft) +// if err != nil { +// return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) +// } +// subnets := envConfig.Network.VPC.Subnets + +// if len(subnets.Public) == 0 && len(subnets.Private) != 0 { +// privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) +// } +// } +// return privateOnlyEnvs, nil +// } + +func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { + envs, err := o.store.ListEnvironments(o.appName) + if err != nil { + return nil, err + } + var privateOnlyEnvs []string + for i := range envs { + envDescribers, err := o.initEnvDescribers(envs[i].Name) + if err != nil { + return nil, fmt.Errorf("initiate env describer: %w", err) + } + mft, err := envDescribers.Manifest() + if err != nil { + return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) + } + envConfig, err := manifest.UnmarshalEnvironment(mft) + if err != nil { + return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) + } + subnets := envConfig.Network.VPC.Subnets + + if len(subnets.Public) == 0 && len(subnets.Private) != 0 { + privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) + } + } + return privateOnlyEnvs, nil +} + func svcTypePromptOpts() []prompt.Option { var options []prompt.Option for _, svcType := range manifest.ServiceTypes() { diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index db479519e7d..bdc7bc92d32 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -33,6 +33,7 @@ type initSvcMocks struct { mockDockerEngine *mocks.MockdockerEngine mockMftReader *mocks.MockmanifestReader mockStore *mocks.Mockstore + mockEnvDescriber *mocks.MockenvDescriber } func TestSvcInitOpts_Validate(t *testing.T) { @@ -1135,3 +1136,32 @@ func TestSvcInitOpts_Execute(t *testing.T) { }) } } + +// func Test_isSubnetsOnlyPrivate(t *testing.T) { +// testCases := map[string]struct { +// inAppName string +// setupMocks func(mocks initSvcMocks) +// wantedPrivateOnlyEnvs []string +// wantedErr error +// }{ +// "no error": { +// inAppName: "phonetool", +// setupMocks: func(mocks initSvcMocks) { +// mocks.mockStore.EXPECT().ListEnvironments("phonetool").Return([]*config.Environment{ +// { +// Name: "metrics", +// }, +// { +// Name: "payments", +// }, +// }) +// for i := 0; i < 2; i++ { +// //fmt.Println("w") +// mocks.mockEnvDescriber.EXPECT().Manifest().Return([]byte("hello"), nil) + +// } +// }, +// }, +// } + +// } diff --git a/internal/pkg/initialize/workload.go b/internal/pkg/initialize/workload.go index acb4600c3cc..448c955cbde 100644 --- a/internal/pkg/initialize/workload.go +++ b/internal/pkg/initialize/workload.go @@ -60,14 +60,15 @@ type Prog interface { // WorkloadProps contains the information needed to represent a Workload (job or service). type WorkloadProps struct { - App string - Type string - Name string - DockerfilePath string - Image string - Platform manifest.PlatformArgsOrString - Topics []manifest.TopicSubscription - Queue manifest.SQSQueue + App string + Type string + Name string + DockerfilePath string + Image string + Platform manifest.PlatformArgsOrString + Topics []manifest.TopicSubscription + Queue manifest.SQSQueue + PrivateOnlyEnvironments []string } // JobProps contains the information needed to represent a Job. @@ -315,11 +316,12 @@ func (w *WorkloadInitializer) newLoadBalancedWebServiceManifest(i *ServiceProps) Dockerfile: i.DockerfilePath, Image: i.Image, }, - Path: "/", - Port: i.Port, - HTTPVersion: httpVersion, - HealthCheck: i.HealthCheck, - Platform: i.Platform, + Path: "/", + Port: i.Port, + HTTPVersion: httpVersion, + HealthCheck: i.HealthCheck, + Platform: i.Platform, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, } existingSvcs, err := w.Store.ListServices(i.App) if err != nil { @@ -343,9 +345,10 @@ func (w *WorkloadInitializer) newRequestDrivenWebServiceManifest(i *ServiceProps Dockerfile: i.DockerfilePath, Image: i.Image, }, - Port: i.Port, - Platform: i.Platform, - Private: i.Private, + Port: i.Port, + Platform: i.Platform, + Private: i.Private, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, } return manifest.NewRequestDrivenWebService(props) } @@ -357,9 +360,10 @@ func newBackendServiceManifest(i *ServiceProps) (*manifest.BackendService, error Dockerfile: i.DockerfilePath, Image: i.Image, }, - Port: i.Port, - HealthCheck: i.HealthCheck, - Platform: i.Platform, + Port: i.Port, + HealthCheck: i.HealthCheck, + Platform: i.Platform, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }), nil } @@ -370,10 +374,11 @@ func newWorkerServiceManifest(i *ServiceProps) (*manifest.WorkerService, error) Dockerfile: i.DockerfilePath, Image: i.Image, }, - HealthCheck: i.HealthCheck, - Platform: i.Platform, - Topics: i.Topics, - Queue: i.Queue, + HealthCheck: i.HealthCheck, + Platform: i.Platform, + Topics: i.Topics, + Queue: i.Queue, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }), nil } diff --git a/internal/pkg/manifest/backend_svc.go b/internal/pkg/manifest/backend_svc.go index e064eabcfc8..2c4a7c32d37 100644 --- a/internal/pkg/manifest/backend_svc.go +++ b/internal/pkg/manifest/backend_svc.go @@ -41,9 +41,10 @@ type BackendServiceConfig struct { // BackendServiceProps represents the configuration needed to create a backend service. type BackendServiceProps struct { WorkloadProps - Port uint16 - HealthCheck ContainerHealthCheck // Optional healthcheck configuration. - Platform PlatformArgsOrString // Optional platform configuration. + Port uint16 + PrivateOnlyEnvironments []string + HealthCheck ContainerHealthCheck // Optional healthcheck configuration. + Platform PlatformArgsOrString // Optional platform configuration. } // NewBackendService applies the props to a default backend service configuration with @@ -62,6 +63,19 @@ func NewBackendService(props BackendServiceProps) *BackendService { svc.BackendServiceConfig.TaskConfig.Memory = aws.Int(MinWindowsTaskMemory) } svc.parser = template.New() + for _, v := range props.PrivateOnlyEnvironments { + if v != "" { + svc.Environments[v] = &BackendServiceConfig{ + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + } + } + } return svc } @@ -191,5 +205,6 @@ func newDefaultBackendService() *BackendService { }, }, }, + Environments: map[string]*BackendServiceConfig{}, } } diff --git a/internal/pkg/manifest/lb_web_svc.go b/internal/pkg/manifest/lb_web_svc.go index 3809d5173cd..a5e7af38fdf 100644 --- a/internal/pkg/manifest/lb_web_svc.go +++ b/internal/pkg/manifest/lb_web_svc.go @@ -62,8 +62,9 @@ type LoadBalancedWebServiceConfig struct { // LoadBalancedWebServiceProps contains properties for creating a new load balanced fargate service manifest. type LoadBalancedWebServiceProps struct { *WorkloadProps - Path string - Port uint16 + Path string + Port uint16 + PrivateOnlyEnvironments []string HTTPVersion string // Optional http protocol version such as gRPC, HTTP2. HealthCheck ContainerHealthCheck // Optional healthcheck configuration. @@ -90,6 +91,17 @@ func NewLoadBalancedWebService(props *LoadBalancedWebServiceProps) *LoadBalanced } svc.RoutingRule.Path = aws.String(props.Path) svc.parser = template.New() + for _, v := range props.PrivateOnlyEnvironments { + svc.Environments[v] = &LoadBalancedWebServiceConfig{ + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + } + } return svc } @@ -135,6 +147,7 @@ func newDefaultLoadBalancedWebService() *LoadBalancedWebService { }, }, }, + Environments: map[string]*LoadBalancedWebServiceConfig{}, } } diff --git a/internal/pkg/manifest/lb_web_svc_test.go b/internal/pkg/manifest/lb_web_svc_test.go index 0227218b60e..4870ca47d7f 100644 --- a/internal/pkg/manifest/lb_web_svc_test.go +++ b/internal/pkg/manifest/lb_web_svc_test.go @@ -77,6 +77,7 @@ func TestNewHTTPLoadBalancedWebService(t *testing.T) { }, }, }, + Environments: map[string]*LoadBalancedWebServiceConfig{}, }, }, "overrides default settings when optional configuration is provided": { @@ -153,6 +154,7 @@ func TestNewHTTPLoadBalancedWebService(t *testing.T) { }, }, }, + Environments: map[string]*LoadBalancedWebServiceConfig{}, }, }, } diff --git a/internal/pkg/manifest/rd_web_svc.go b/internal/pkg/manifest/rd_web_svc.go index 52d0d81e075..f00f49686a3 100644 --- a/internal/pkg/manifest/rd_web_svc.go +++ b/internal/pkg/manifest/rd_web_svc.go @@ -102,9 +102,10 @@ type AppRunnerInstanceConfig struct { // RequestDrivenWebServiceProps contains properties for creating a new request-driven web service manifest. type RequestDrivenWebServiceProps struct { *WorkloadProps - Port uint16 - Platform PlatformArgsOrString - Private bool + Port uint16 + PrivateOnlyEnvironments []string + Platform PlatformArgsOrString + Private bool } // NewRequestDrivenWebService creates a new Request-Driven Web Service manifest with default values. @@ -119,6 +120,19 @@ func NewRequestDrivenWebService(props *RequestDrivenWebServiceProps) *RequestDri svc.Private = BasicToUnion[*bool, VPCEndpoint](aws.Bool(true)) svc.Network.VPC.Placement.PlacementString = (*PlacementString)(aws.String("private")) } + for _, v := range props.PrivateOnlyEnvironments { + if v != "" { + svc.Environments[v] = &RequestDrivenWebServiceConfig{ + Network: RequestDrivenWebServiceNetworkConfig{ + VPC: rdwsVpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + } + } + } svc.parser = template.New() return svc } @@ -199,5 +213,6 @@ func newDefaultRequestDrivenWebService() *RequestDrivenWebService { Memory: aws.Int(2048), }, }, + Environments: map[string]*RequestDrivenWebServiceConfig{}, } } diff --git a/internal/pkg/manifest/worker_svc.go b/internal/pkg/manifest/worker_svc.go index 0dc91613495..1307c100543 100644 --- a/internal/pkg/manifest/worker_svc.go +++ b/internal/pkg/manifest/worker_svc.go @@ -187,6 +187,7 @@ func (q *DeadLetterQueue) IsEmpty() bool { // WorkerServiceProps represents the configuration needed to create a worker service. type WorkerServiceProps struct { WorkloadProps + PrivateOnlyEnvironments []string HealthCheck ContainerHealthCheck // Optional healthcheck configuration. Platform PlatformArgsOrString // Optional platform configuration. @@ -213,15 +214,28 @@ func NewWorkerService(props WorkerServiceProps) *WorkerService { } svc.WorkerServiceConfig.Subscribe.Topics = props.Topics svc.WorkerServiceConfig.Platform = props.Platform + for _, v := range props.PrivateOnlyEnvironments { + if v != "" { + svc.Environments[v] = &WorkerServiceConfig{ + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + } + } + } svc.parser = template.New() return svc } // setSubscriptionQueueDefaults function modifies the manifest to have -// 1. FIFO Topic names without ".fifo" suffix. -// 2. If there are both FIFO and Standard topic subscriptions are specified then set -// default events queue to FIFO and add standard topic-specific queue for all the standard topic subscriptions. -// 3. If there are only Standard topic subscriptions are specified then do nothing and return. +// 1. FIFO Topic names without ".fifo" suffix. +// 2. If there are both FIFO and Standard topic subscriptions are specified then set +// default events queue to FIFO and add standard topic-specific queue for all the standard topic subscriptions. +// 3. If there are only Standard topic subscriptions are specified then do nothing and return. func setSubscriptionQueueDefaults(topics []TopicSubscription, eventsQueue *SQSQueue) { var isFIFOEnabled bool for _, topic := range topics { @@ -353,5 +367,6 @@ func newDefaultWorkerService() *WorkerService { }, }, }, + Environments: map[string]*WorkerServiceConfig{}, } } diff --git a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml index 8cef9c72bad..9710bf11981 100644 --- a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml @@ -58,4 +58,13 @@ exec: true # Enable running commands in your container. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +{{- if .Environments}} +environments: {{ range $key, $value := .Environments}} + {{$key}}: + network: + vpc: + placement: {{$value.Network.VPC.Placement.PlacementString}} + {{- end}} +{{- end}} + From f8b93f11211a179b68b09414837dd81b0bea751a Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Mon, 21 Nov 2022 17:20:35 -0800 Subject: [PATCH 02/12] placement private with failing tests --- internal/pkg/cli/svc_init.go | 45 +- internal/pkg/cli/svc_init_test.go | 555 +++++++++--------- internal/pkg/initialize/workload.go | 26 +- internal/pkg/manifest/backend_svc.go | 16 +- internal/pkg/manifest/backend_svc_test.go | 14 + internal/pkg/manifest/lb_web_svc.go | 4 +- internal/pkg/manifest/lb_web_svc_test.go | 15 +- .../marshal_manifest_integration_test.go | 30 +- internal/pkg/manifest/rd_web_svc.go | 16 +- internal/pkg/manifest/rd_web_svc_test.go | 14 + internal/pkg/manifest/svc_test.go | 2 + ...> backend-svc-nohealthcheck-placement.yml} | 14 +- .../manifest/testdata/lb-svc-placement.yml | 49 ++ internal/pkg/manifest/testdata/placements.yml | 49 ++ ...l => worker-svc-nosubscribe-placement.yml} | 14 +- internal/pkg/manifest/worker_svc.go | 16 +- internal/pkg/manifest/worker_svc_test.go | 14 + .../workloads/services/backend/manifest.yml | 18 +- .../workloads/services/lb-web/manifest.yml | 19 +- .../workloads/services/rd-web/manifest.yml | 17 + .../workloads/services/worker/manifest.yml | 18 +- 21 files changed, 605 insertions(+), 360 deletions(-) rename internal/pkg/manifest/testdata/{backend-svc-nohealthcheck.yml => backend-svc-nohealthcheck-placement.yml} (75%) create mode 100644 internal/pkg/manifest/testdata/lb-svc-placement.yml create mode 100644 internal/pkg/manifest/testdata/placements.yml rename internal/pkg/manifest/testdata/{worker-svc-nosubscribe.yml => worker-svc-nosubscribe-placement.yml} (76%) diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 23fe5cb6413..1d9a4ce7c08 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -306,18 +306,6 @@ func (o *initSvcOpts) Ask() error { return err } } - // if man, _ := o.IsSubnetsOnlyPrivate(); man != nil { - // var fmtEnv string - // var fmtString string - // for k, v := range man.EnvOverridePlacement { - // // if v == "" { - // // return fmt.Errorf("zero value of string") - // // } - // fmtString = fmtString + v - // fmtEnv = fmtEnv + strconv.Itoa(k) - // } - // return fmt.Errorf("myError is\n%s\n%s", fmtEnv, fmtString) - // } if err := o.askSvcPort(); err != nil { return err } @@ -362,12 +350,12 @@ func (o *initSvcOpts) Execute() error { Platform: manifest.PlatformArgsOrString{ PlatformString: o.platform, }, - Topics: o.topics, - PrivateOnlyEnvironments: envs, + Topics: o.topics, }, - Port: o.port, - HealthCheck: hc, - Private: strings.EqualFold(o.ingressType, ingressTypeEnvironment), + Port: o.port, + HealthCheck: hc, + Private: strings.EqualFold(o.ingressType, ingressTypeEnvironment), + PrivateOnlyEnvironments: envs, }) if err != nil { return err @@ -741,6 +729,7 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error }, nil } +// isSubnetsOnlyPrivate returns the list of deployed environment names whose subnets are only private. // func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { // envs, err := o.store.ListEnvironments(o.appName) // if err != nil { @@ -764,15 +753,17 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error // if err != nil { // return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) // } +// // workspace.ReadEnvironmentManifest() // subnets := envConfig.Network.VPC.Subnets // if len(subnets.Public) == 0 && len(subnets.Private) != 0 { // privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) // } // } -// return privateOnlyEnvs, nil +// return privateOnlyEnvs, err // } +// isSubnetsOnlyPrivate returns the list of deployed environment names whose subnets are only private. func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { envs, err := o.store.ListEnvironments(o.appName) if err != nil { @@ -780,25 +771,23 @@ func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { } var privateOnlyEnvs []string for i := range envs { - envDescribers, err := o.initEnvDescribers(envs[i].Name) + envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ + App: o.appName, + Env: envs[i].Name, + ConfigStore: o.store, + }) if err != nil { return nil, fmt.Errorf("initiate env describer: %w", err) } - mft, err := envDescribers.Manifest() + out, err := envDescriber.Outputs() if err != nil { return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) } - envConfig, err := manifest.UnmarshalEnvironment(mft) - if err != nil { - return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) - } - subnets := envConfig.Network.VPC.Subnets - - if len(subnets.Public) == 0 && len(subnets.Private) != 0 { + if out["PublicSubnets"] == "" && out["PrivateSubnets"] != "" { privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) } } - return privateOnlyEnvs, nil + return privateOnlyEnvs, err } func svcTypePromptOpts() []prompt.Option { diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index bdc7bc92d32..8a4f213e2cc 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -9,7 +9,6 @@ import ( "path/filepath" "testing" - "github.com/aws/aws-sdk-go/aws" "github.com/aws/copilot-cli/internal/pkg/config" "github.com/aws/copilot-cli/internal/pkg/deploy" "github.com/aws/copilot-cli/internal/pkg/docker/dockerengine" @@ -33,7 +32,6 @@ type initSvcMocks struct { mockDockerEngine *mocks.MockdockerEngine mockMftReader *mocks.MockmanifestReader mockStore *mocks.Mockstore - mockEnvDescriber *mocks.MockenvDescriber } func TestSvcInitOpts_Validate(t *testing.T) { @@ -772,6 +770,8 @@ func TestSvcInitOpts_Execute(t *testing.T) { mockDockerfile func(m *mocks.MockdockerfileParser) mockDockerEngine func(m *mocks.MockdockerEngine) mockTopicSel func(m *mocks.MocktopicSelector) + mockStore func(m *mocks.Mockstore) + //mockEnvDescriber func(m *mocks.MockenvDescriber) inSvcPort uint16 inSvcType string inSvcName string @@ -791,153 +791,16 @@ func TestSvcInitOpts_Execute(t *testing.T) { inSvcPort: 80, - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Load Balanced Web Service", - DockerfilePath: "./Dockerfile", - Platform: manifest.PlatformArgsOrString{}, - }, - Port: 80, - }).Return("manifest/path", nil) - }, - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("linux", "amd64", nil) - }, - - wantedManifestPath: "manifest/path", - }, - "backend service": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.BackendServiceType, - - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Backend Service", - DockerfilePath: "./Dockerfile", - Platform: manifest.PlatformArgsOrString{}, - }, - }).Return("manifest/path", nil) - }, - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("linux", "amd64", nil) - }, - - wantedManifestPath: "manifest/path", - }, - "doesn't attempt to detect and populate the platform if manifest already exists": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.LoadBalancedWebServiceType, - inSvcPort: 80, - inManifestExists: true, - - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Times(0) - m.EXPECT().GetPlatform().Times(0) - }, - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Load Balanced Web Service", - DockerfilePath: "./Dockerfile", - }, - Port: 80, - }).Return("manifest/path", nil) - }, - - wantedManifestPath: "manifest/path", - }, - "doesn't complain if docker is unavailable": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.LoadBalancedWebServiceType, - - inSvcPort: 80, - - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(&dockerengine.ErrDockerDaemonNotResponsive{}) - m.EXPECT().GetPlatform().Times(0) - }, - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Load Balanced Web Service", - DockerfilePath: "./Dockerfile", - }, - Port: 80, - }).Return("manifest/path", nil) - }, - - wantedManifestPath: "manifest/path", - }, - "windows platform": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.LoadBalancedWebServiceType, - - inSvcPort: 80, - - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Load Balanced Web Service", - DockerfilePath: "./Dockerfile", - Platform: manifest.PlatformArgsOrString{ - PlatformString: (*manifest.PlatformString)(aws.String("windows/x86_64")), - }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + Name: "test", + Region: "us-west-2", + AccountID: "123456789", }, - Port: 80, - }).Return("manifest/path", nil) - }, - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("windows", "amd64", nil) + }, nil) + m.EXPECT().GetEnvironment("sample", "test").Return(&config.Environment{Name: "test", Region: "us-west-2"}, nil) }, - - wantedManifestPath: "manifest/path", - }, - "ARM architecture redirects to X86_64": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.LoadBalancedWebServiceType, - - inSvcPort: 80, - mockSvcInit: func(m *mocks.MocksvcInitializer) { m.EXPECT().Service(&initialize.ServiceProps{ WorkloadProps: initialize.WorkloadProps{ @@ -945,38 +808,9 @@ func TestSvcInitOpts_Execute(t *testing.T) { Name: "frontend", Type: "Load Balanced Web Service", DockerfilePath: "./Dockerfile", - Platform: manifest.PlatformArgsOrString{ - PlatformString: (*manifest.PlatformString)(aws.String("linux/x86_64")), - }, - }, - Port: 80, - }).Return("manifest/path", nil) - }, - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("linux", "arm", nil) - }, - - wantedManifestPath: "manifest/path", - }, - "worker service": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.WorkerServiceType, - - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Worker Service", - DockerfilePath: "./Dockerfile", Platform: manifest.PlatformArgsOrString{}, }, + Port: 80, }).Return("manifest/path", nil) }, mockDockerfile: func(m *mocks.MockdockerfileParser) { @@ -986,100 +820,277 @@ func TestSvcInitOpts_Execute(t *testing.T) { m.EXPECT().CheckDockerEngineRunning().Return(nil) m.EXPECT().GetPlatform().Return("linux", "amd64", nil) }, - mockTopicSel: func(m *mocks.MocktopicSelector) { - m.EXPECT().Topics( - gomock.Eq(svcInitPublisherPrompt), - gomock.Eq(svcInitPublisherHelpPrompt), - gomock.Any(), - ).Return([]manifest.TopicSubscription{ - { - Name: aws.String("thetopic"), - Service: aws.String("theservice"), - }, - }, nil) - }, wantedManifestPath: "manifest/path", }, - "doesn't parse dockerfile if image specified (backend)": { - inAppName: "sample", - inSvcName: "backend", - inDockerfilePath: "", - inImage: "nginx:latest", - inSvcType: manifest.BackendServiceType, - - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "backend", - Type: "Backend Service", - Image: "nginx:latest", - Platform: manifest.PlatformArgsOrString{}, - }, - }).Return("manifest/path", nil) - }, - mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. - wantedManifestPath: "manifest/path", - }, - "doesn't parse dockerfile if image specified (lb-web)": { - inAppName: "sample", - inSvcName: "frontend", - inDockerfilePath: "", - inImage: "nginx:latest", - inSvcType: manifest.LoadBalancedWebServiceType, - - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(&initialize.ServiceProps{ - WorkloadProps: initialize.WorkloadProps{ - App: "sample", - Name: "frontend", - Type: "Load Balanced Web Service", - Image: "nginx:latest", - Platform: manifest.PlatformArgsOrString{}, - }, - }).Return("manifest/path", nil) - }, - mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. - - wantedManifestPath: "manifest/path", - }, - "return error if platform detection fails": { - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("", "", errors.New("some error")) - }, - wantedErr: errors.New("get docker engine platform: some error"), - }, - "return error if Windows platform attempted with RDWS": { - inAppName: "sample", - inSvcName: "appRunner", - inDockerfilePath: "./Dockerfile", - inSvcType: manifest.RequestDrivenWebServiceType, - - inSvcPort: 80, - - mockDockerfile: func(m *mocks.MockdockerfileParser) { - m.EXPECT().GetHealthCheck().Return(nil, nil) - }, - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("windows", "amd64", nil) - }, - - wantedErr: errors.New("redirect docker engine platform: Windows is not supported for App Runner services"), - }, - "failure": { - mockDockerEngine: func(m *mocks.MockdockerEngine) { - m.EXPECT().CheckDockerEngineRunning().Return(nil) - m.EXPECT().GetPlatform().Return("linux", "amd64", nil) - }, - mockSvcInit: func(m *mocks.MocksvcInitializer) { - m.EXPECT().Service(gomock.Any()).Return("", errors.New("some error")) - }, - wantedErr: errors.New("some error"), - }, + // "backend service": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.BackendServiceType, + + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Backend Service", + // DockerfilePath: "./Dockerfile", + // Platform: manifest.PlatformArgsOrString{}, + // }, + // }).Return("manifest/path", nil) + // }, + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + // }, + + // wantedManifestPath: "manifest/path", + // }, + // "doesn't attempt to detect and populate the platform if manifest already exists": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.LoadBalancedWebServiceType, + // inSvcPort: 80, + // inManifestExists: true, + + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Times(0) + // m.EXPECT().GetPlatform().Times(0) + // }, + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Load Balanced Web Service", + // DockerfilePath: "./Dockerfile", + // }, + // Port: 80, + // }).Return("manifest/path", nil) + // }, + + // wantedManifestPath: "manifest/path", + // }, + // "doesn't complain if docker is unavailable": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.LoadBalancedWebServiceType, + + // inSvcPort: 80, + + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(&dockerengine.ErrDockerDaemonNotResponsive{}) + // m.EXPECT().GetPlatform().Times(0) + // }, + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Load Balanced Web Service", + // DockerfilePath: "./Dockerfile", + // }, + // Port: 80, + // }).Return("manifest/path", nil) + // }, + + // wantedManifestPath: "manifest/path", + // }, + // "windows platform": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.LoadBalancedWebServiceType, + + // inSvcPort: 80, + + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Load Balanced Web Service", + // DockerfilePath: "./Dockerfile", + // Platform: manifest.PlatformArgsOrString{ + // PlatformString: (*manifest.PlatformString)(aws.String("windows/x86_64")), + // }, + // }, + // Port: 80, + // }).Return("manifest/path", nil) + // }, + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("windows", "amd64", nil) + // }, + + // wantedManifestPath: "manifest/path", + // }, + // "ARM architecture redirects to X86_64": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.LoadBalancedWebServiceType, + + // inSvcPort: 80, + + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Load Balanced Web Service", + // DockerfilePath: "./Dockerfile", + // Platform: manifest.PlatformArgsOrString{ + // PlatformString: (*manifest.PlatformString)(aws.String("linux/x86_64")), + // }, + // }, + // Port: 80, + // }).Return("manifest/path", nil) + // }, + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("linux", "arm", nil) + // }, + + // wantedManifestPath: "manifest/path", + // }, + // "worker service": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.WorkerServiceType, + + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Worker Service", + // DockerfilePath: "./Dockerfile", + // Platform: manifest.PlatformArgsOrString{}, + // }, + // }).Return("manifest/path", nil) + // }, + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + // }, + // mockTopicSel: func(m *mocks.MocktopicSelector) { + // m.EXPECT().Topics( + // gomock.Eq(svcInitPublisherPrompt), + // gomock.Eq(svcInitPublisherHelpPrompt), + // gomock.Any(), + // ).Return([]manifest.TopicSubscription{ + // { + // Name: aws.String("thetopic"), + // Service: aws.String("theservice"), + // }, + // }, nil) + // }, + + // wantedManifestPath: "manifest/path", + // }, + // "doesn't parse dockerfile if image specified (backend)": { + // inAppName: "sample", + // inSvcName: "backend", + // inDockerfilePath: "", + // inImage: "nginx:latest", + // inSvcType: manifest.BackendServiceType, + + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "backend", + // Type: "Backend Service", + // Image: "nginx:latest", + // Platform: manifest.PlatformArgsOrString{}, + // }, + // }).Return("manifest/path", nil) + // }, + // mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. + + // wantedManifestPath: "manifest/path", + // }, + // "doesn't parse dockerfile if image specified (lb-web)": { + // inAppName: "sample", + // inSvcName: "frontend", + // inDockerfilePath: "", + // inImage: "nginx:latest", + // inSvcType: manifest.LoadBalancedWebServiceType, + + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(&initialize.ServiceProps{ + // WorkloadProps: initialize.WorkloadProps{ + // App: "sample", + // Name: "frontend", + // Type: "Load Balanced Web Service", + // Image: "nginx:latest", + // Platform: manifest.PlatformArgsOrString{}, + // }, + // }).Return("manifest/path", nil) + // }, + // mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. + + // wantedManifestPath: "manifest/path", + // }, + // "return error if platform detection fails": { + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("", "", errors.New("some error")) + // }, + // wantedErr: errors.New("get docker engine platform: some error"), + // }, + // "return error if Windows platform attempted with RDWS": { + // inAppName: "sample", + // inSvcName: "appRunner", + // inDockerfilePath: "./Dockerfile", + // inSvcType: manifest.RequestDrivenWebServiceType, + + // inSvcPort: 80, + + // mockDockerfile: func(m *mocks.MockdockerfileParser) { + // m.EXPECT().GetHealthCheck().Return(nil, nil) + // }, + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("windows", "amd64", nil) + // }, + + // wantedErr: errors.New("redirect docker engine platform: Windows is not supported for App Runner services"), + // }, + // "failure": { + // mockDockerEngine: func(m *mocks.MockdockerEngine) { + // m.EXPECT().CheckDockerEngineRunning().Return(nil) + // m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + // }, + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(gomock.Any()).Return("", errors.New("some error")) + // }, + // wantedErr: errors.New("some error"), + // }, } for name, tc := range testCases { @@ -1092,6 +1103,12 @@ func TestSvcInitOpts_Execute(t *testing.T) { mockDockerfile := mocks.NewMockdockerfileParser(ctrl) mockDockerEngine := mocks.NewMockdockerEngine(ctrl) mockTopicSel := mocks.NewMocktopicSelector(ctrl) + mockStore := mocks.NewMockstore(ctrl) + //mockEnvDescriber := mocks.NewMockenvDescriber(ctrl) + + if tc.mockStore != nil { + tc.mockStore(mockStore) + } if tc.mockSvcInit != nil { tc.mockSvcInit(mockSvcInitializer) @@ -1102,6 +1119,9 @@ func TestSvcInitOpts_Execute(t *testing.T) { if tc.mockDockerEngine != nil { tc.mockDockerEngine(mockDockerEngine) } + // if tc.mockEnvDescriber != nil { + // tc.mockEnvDescriber(mockEnvDescriber) + // } opts := initSvcOpts{ initSvcVars: initSvcVars{ initWkldVars: initWkldVars{ @@ -1119,6 +1139,7 @@ func TestSvcInitOpts_Execute(t *testing.T) { }, df: mockDockerfile, dockerEngine: mockDockerEngine, + store: mockStore, topicSel: mockTopicSel, manifestExists: tc.inManifestExists, } diff --git a/internal/pkg/initialize/workload.go b/internal/pkg/initialize/workload.go index 448c955cbde..2987472f268 100644 --- a/internal/pkg/initialize/workload.go +++ b/internal/pkg/initialize/workload.go @@ -60,15 +60,14 @@ type Prog interface { // WorkloadProps contains the information needed to represent a Workload (job or service). type WorkloadProps struct { - App string - Type string - Name string - DockerfilePath string - Image string - Platform manifest.PlatformArgsOrString - Topics []manifest.TopicSubscription - Queue manifest.SQSQueue - PrivateOnlyEnvironments []string + App string + Type string + Name string + DockerfilePath string + Image string + Platform manifest.PlatformArgsOrString + Topics []manifest.TopicSubscription + Queue manifest.SQSQueue } // JobProps contains the information needed to represent a Job. @@ -83,10 +82,11 @@ type JobProps struct { // ServiceProps contains the information needed to represent a Service (port, HealthCheck, and workload common props). type ServiceProps struct { WorkloadProps - Port uint16 - HealthCheck manifest.ContainerHealthCheck - Private bool - appDomain *string + Port uint16 + HealthCheck manifest.ContainerHealthCheck + Private bool + appDomain *string + PrivateOnlyEnvironments []string } // WorkloadInitializer holds the clients necessary to initialize either a diff --git a/internal/pkg/manifest/backend_svc.go b/internal/pkg/manifest/backend_svc.go index 2c4a7c32d37..72ca2e941f3 100644 --- a/internal/pkg/manifest/backend_svc.go +++ b/internal/pkg/manifest/backend_svc.go @@ -63,17 +63,15 @@ func NewBackendService(props BackendServiceProps) *BackendService { svc.BackendServiceConfig.TaskConfig.Memory = aws.Int(MinWindowsTaskMemory) } svc.parser = template.New() - for _, v := range props.PrivateOnlyEnvironments { - if v != "" { - svc.Environments[v] = &BackendServiceConfig{ - Network: NetworkConfig{ - VPC: vpcConfig{ - Placement: PlacementArgOrString{ - PlacementString: placementStringP(PrivateSubnetPlacement), - }, + for _, envName := range props.PrivateOnlyEnvironments { + svc.Environments[envName] = &BackendServiceConfig{ + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), }, }, - } + }, } } return svc diff --git a/internal/pkg/manifest/backend_svc_test.go b/internal/pkg/manifest/backend_svc_test.go index 84ac90473cd..a41feac8900 100644 --- a/internal/pkg/manifest/backend_svc_test.go +++ b/internal/pkg/manifest/backend_svc_test.go @@ -25,6 +25,9 @@ func TestNewBackendSvc(t *testing.T) { Name: "subscribers", Dockerfile: "./subscribers/Dockerfile", }, + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, wantedManifest: &BackendService{ Workload: Workload{ @@ -61,6 +64,17 @@ func TestNewBackendSvc(t *testing.T) { }, }, }, + Environments: map[string]*BackendServiceConfig{ + "metrics": { + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + }, + }, }, }, "with custom healthcheck command": { diff --git a/internal/pkg/manifest/lb_web_svc.go b/internal/pkg/manifest/lb_web_svc.go index a5e7af38fdf..83eaea0fbb7 100644 --- a/internal/pkg/manifest/lb_web_svc.go +++ b/internal/pkg/manifest/lb_web_svc.go @@ -91,8 +91,8 @@ func NewLoadBalancedWebService(props *LoadBalancedWebServiceProps) *LoadBalanced } svc.RoutingRule.Path = aws.String(props.Path) svc.parser = template.New() - for _, v := range props.PrivateOnlyEnvironments { - svc.Environments[v] = &LoadBalancedWebServiceConfig{ + for _, envName := range props.PrivateOnlyEnvironments { + svc.Environments[envName] = &LoadBalancedWebServiceConfig{ Network: NetworkConfig{ VPC: vpcConfig{ Placement: PlacementArgOrString{ diff --git a/internal/pkg/manifest/lb_web_svc_test.go b/internal/pkg/manifest/lb_web_svc_test.go index 4870ca47d7f..bf7cb681231 100644 --- a/internal/pkg/manifest/lb_web_svc_test.go +++ b/internal/pkg/manifest/lb_web_svc_test.go @@ -94,6 +94,9 @@ func TestNewHTTPLoadBalancedWebService(t *testing.T) { Command: []string{"CMD", "curl -f http://localhost:8080 || exit 1"}, }, Platform: PlatformArgsOrString{PlatformString: (*PlatformString)(aws.String("windows/amd64"))}, + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, wanted: &LoadBalancedWebService{ @@ -154,7 +157,17 @@ func TestNewHTTPLoadBalancedWebService(t *testing.T) { }, }, }, - Environments: map[string]*LoadBalancedWebServiceConfig{}, + Environments: map[string]*LoadBalancedWebServiceConfig{ + "metrics": { + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + }, + }, }, }, } diff --git a/internal/pkg/manifest/marshal_manifest_integration_test.go b/internal/pkg/manifest/marshal_manifest_integration_test.go index 0f76c1db2a6..20bdc2cc68c 100644 --- a/internal/pkg/manifest/marshal_manifest_integration_test.go +++ b/internal/pkg/manifest/marshal_manifest_integration_test.go @@ -36,6 +36,22 @@ func TestLoadBalancedWebService_InitialManifestIntegration(t *testing.T) { }, wantedTestdata: "lb-svc.yml", }, + "with environments private": { + inProps: LoadBalancedWebServiceProps{ + WorkloadProps: &WorkloadProps{ + Name: "frontend", + Dockerfile: "./frontend/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{}, + }, + PrivateOnlyEnvironments: []string{ + "phonetool", + }, + }, + wantedTestdata: "lb-svc-placement.yml", + }, } for name, tc := range testCases { @@ -62,7 +78,7 @@ func TestBackendSvc_InitialManifestIntegration(t *testing.T) { wantedTestdata string }{ - "without healthcheck and port": { + "without healthcheck and port and with private only environments": { inProps: BackendServiceProps{ WorkloadProps: WorkloadProps{ Name: "subscribers", @@ -75,8 +91,11 @@ func TestBackendSvc_InitialManifestIntegration(t *testing.T) { Arch: nil, }, }, + PrivateOnlyEnvironments: []string{ + "phonetool", + }, }, - wantedTestdata: "backend-svc-nohealthcheck.yml", + wantedTestdata: "backend-svc-nohealthcheck-placement.yml", }, "with custom healthcheck command": { inProps: BackendServiceProps{ @@ -121,7 +140,7 @@ func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { wantedTestdata string }{ - "without subscribe": { + "without subscribe and with private only environments": { inProps: WorkerServiceProps{ WorkloadProps: WorkloadProps{ Name: "testers", @@ -134,8 +153,11 @@ func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { Arch: nil, }, }, + PrivateOnlyEnvironments: []string{ + "phonetool", + }, }, - wantedTestdata: "worker-svc-nosubscribe.yml", + wantedTestdata: "worker-svc-nosubscribe-placement.yml", }, "with subscribe": { inProps: WorkerServiceProps{ diff --git a/internal/pkg/manifest/rd_web_svc.go b/internal/pkg/manifest/rd_web_svc.go index f00f49686a3..fe3211d7404 100644 --- a/internal/pkg/manifest/rd_web_svc.go +++ b/internal/pkg/manifest/rd_web_svc.go @@ -120,17 +120,15 @@ func NewRequestDrivenWebService(props *RequestDrivenWebServiceProps) *RequestDri svc.Private = BasicToUnion[*bool, VPCEndpoint](aws.Bool(true)) svc.Network.VPC.Placement.PlacementString = (*PlacementString)(aws.String("private")) } - for _, v := range props.PrivateOnlyEnvironments { - if v != "" { - svc.Environments[v] = &RequestDrivenWebServiceConfig{ - Network: RequestDrivenWebServiceNetworkConfig{ - VPC: rdwsVpcConfig{ - Placement: PlacementArgOrString{ - PlacementString: placementStringP(PrivateSubnetPlacement), - }, + for _, envName := range props.PrivateOnlyEnvironments { + svc.Environments[envName] = &RequestDrivenWebServiceConfig{ + Network: RequestDrivenWebServiceNetworkConfig{ + VPC: rdwsVpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), }, }, - } + }, } } svc.parser = template.New() diff --git a/internal/pkg/manifest/rd_web_svc_test.go b/internal/pkg/manifest/rd_web_svc_test.go index 845265b812e..d25c6452ccb 100644 --- a/internal/pkg/manifest/rd_web_svc_test.go +++ b/internal/pkg/manifest/rd_web_svc_test.go @@ -30,6 +30,9 @@ func TestNewRequestDrivenWebService(t *testing.T) { Dockerfile: "./Dockerfile", }, Port: uint16(80), + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, wantedStruct: &RequestDrivenWebService{ @@ -53,6 +56,17 @@ func TestNewRequestDrivenWebService(t *testing.T) { Memory: aws.Int(2048), }, }, + Environments: map[string]*RequestDrivenWebServiceConfig{ + "metrics": { + Network: RequestDrivenWebServiceNetworkConfig{ + VPC: rdwsVpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + }, + }, }, }, } diff --git a/internal/pkg/manifest/svc_test.go b/internal/pkg/manifest/svc_test.go index 46b52e046c4..111f043714a 100644 --- a/internal/pkg/manifest/svc_test.go +++ b/internal/pkg/manifest/svc_test.go @@ -287,6 +287,7 @@ secrets: }, }, }, + Environments: map[string]*BackendServiceConfig{}, } require.Equal(t, wantedManifest, actualManifest) }, @@ -375,6 +376,7 @@ subscribe: }, }, }, + Environments: map[string]*WorkerServiceConfig{}, } require.Equal(t, wantedManifest, actualManifest) }, diff --git a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck.yml b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml similarity index 75% rename from internal/pkg/manifest/testdata/backend-svc-nohealthcheck.yml rename to internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml index 7df0ca52386..b900e2d8ebf 100644 --- a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck.yml +++ b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml @@ -30,8 +30,12 @@ exec: true # Enable running commands in your container. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. # You can override any of the values defined above by environment. -#environments: -# test: -# count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file +environments: + phonetool: + network: + vpc: + placement: 'private' # Set placement to private for environments containing only on private subnets. +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/lb-svc-placement.yml b/internal/pkg/manifest/testdata/lb-svc-placement.yml new file mode 100644 index 00000000000..55b45a651e1 --- /dev/null +++ b/internal/pkg/manifest/testdata/lb-svc-placement.yml @@ -0,0 +1,49 @@ +# The manifest for the "frontend" service. +# Read the full specification for the "Load Balanced Web Service" type at: +# https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/ + +# Your service name will be used in naming your resources like log groups, ECS services, etc. +name: frontend +type: Load Balanced Web Service + +# Distribute traffic to your service. +http: + # Requests to this path will be forwarded to your service. + # To match all requests you can use the "/" path. + path: '' + # You can specify a custom health check path. The default is "/". + # healthcheck: '/' + +# Configuration for your containers and service. +image: + # Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#image-build + build: ./frontend/Dockerfile + # Port exposed through your container to route traffic to it. + port: 0 + +cpu: 256 # Number of CPU units for the task. +memory: 512 # Amount of memory in MiB used by the task. +count: 1 # Number of tasks that should be running in your service. +exec: true # Enable running commands in your container. + +# storage: + # readonly_fs: true # Limit to read-only access to mounted root filesystems. + +# Optional fields for more advanced use-cases. +# +#variables: # Pass environment variables as key value pairs. +# LOG_LEVEL: info + +#secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. +# GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. + +# You can override any of the values defined above by environment. +environments: + phonetool: + network: + vpc: + placement: 'private' # Set placement to private for environments containing only on private subnets. +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/placements.yml b/internal/pkg/manifest/testdata/placements.yml new file mode 100644 index 00000000000..55b45a651e1 --- /dev/null +++ b/internal/pkg/manifest/testdata/placements.yml @@ -0,0 +1,49 @@ +# The manifest for the "frontend" service. +# Read the full specification for the "Load Balanced Web Service" type at: +# https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/ + +# Your service name will be used in naming your resources like log groups, ECS services, etc. +name: frontend +type: Load Balanced Web Service + +# Distribute traffic to your service. +http: + # Requests to this path will be forwarded to your service. + # To match all requests you can use the "/" path. + path: '' + # You can specify a custom health check path. The default is "/". + # healthcheck: '/' + +# Configuration for your containers and service. +image: + # Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#image-build + build: ./frontend/Dockerfile + # Port exposed through your container to route traffic to it. + port: 0 + +cpu: 256 # Number of CPU units for the task. +memory: 512 # Amount of memory in MiB used by the task. +count: 1 # Number of tasks that should be running in your service. +exec: true # Enable running commands in your container. + +# storage: + # readonly_fs: true # Limit to read-only access to mounted root filesystems. + +# Optional fields for more advanced use-cases. +# +#variables: # Pass environment variables as key value pairs. +# LOG_LEVEL: info + +#secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. +# GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. + +# You can override any of the values defined above by environment. +environments: + phonetool: + network: + vpc: + placement: 'private' # Set placement to private for environments containing only on private subnets. +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/worker-svc-nosubscribe.yml b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml similarity index 76% rename from internal/pkg/manifest/testdata/worker-svc-nosubscribe.yml rename to internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml index 099c1678af0..5077180af75 100644 --- a/internal/pkg/manifest/testdata/worker-svc-nosubscribe.yml +++ b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml @@ -36,8 +36,12 @@ exec: true # Enable running commands in your container. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. # You can override any of the values defined above by environment. -#environments: -# test: -# count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file +environments: + phonetool: + network: + vpc: + placement: 'private' # Set placement to private for environments containing only on private subnets. +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/worker_svc.go b/internal/pkg/manifest/worker_svc.go index 1307c100543..03bd1b90fbf 100644 --- a/internal/pkg/manifest/worker_svc.go +++ b/internal/pkg/manifest/worker_svc.go @@ -214,17 +214,15 @@ func NewWorkerService(props WorkerServiceProps) *WorkerService { } svc.WorkerServiceConfig.Subscribe.Topics = props.Topics svc.WorkerServiceConfig.Platform = props.Platform - for _, v := range props.PrivateOnlyEnvironments { - if v != "" { - svc.Environments[v] = &WorkerServiceConfig{ - Network: NetworkConfig{ - VPC: vpcConfig{ - Placement: PlacementArgOrString{ - PlacementString: placementStringP(PrivateSubnetPlacement), - }, + for _, envName := range props.PrivateOnlyEnvironments { + svc.Environments[envName] = &WorkerServiceConfig{ + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), }, }, - } + }, } } svc.parser = template.New() diff --git a/internal/pkg/manifest/worker_svc_test.go b/internal/pkg/manifest/worker_svc_test.go index 110dd4cf198..ecef3fdcb6f 100644 --- a/internal/pkg/manifest/worker_svc_test.go +++ b/internal/pkg/manifest/worker_svc_test.go @@ -62,6 +62,9 @@ func TestNewWorkerSvc(t *testing.T) { Name: "testers", Dockerfile: "./testers/Dockerfile", }, + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, wantedManifest: &WorkerService{ Workload: Workload{ @@ -97,6 +100,17 @@ func TestNewWorkerSvc(t *testing.T) { }, }, }, + Environments: map[string]*WorkerServiceConfig{ + "metrics": { + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + }, + }, }, }, "should return a worker service instance with subscribe": { diff --git a/internal/pkg/template/templates/workloads/services/backend/manifest.yml b/internal/pkg/template/templates/workloads/services/backend/manifest.yml index f6e95c749d1..51d45a578af 100644 --- a/internal/pkg/template/templates/workloads/services/backend/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/backend/manifest.yml @@ -58,9 +58,25 @@ exec: true # Enable running commands in your container. #secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. +{{- if not .Environments}} + # You can override any of the values defined above by environment. #environments: # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +{{- else}} + +# You can override any of the values defined above by environment. +environments: {{ range $key, $value := .Environments}} + {{$key}}: + network: + vpc: + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. +{{- end}} +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +{{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml index 9710bf11981..67749603337 100644 --- a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml @@ -53,18 +53,25 @@ exec: true # Enable running commands in your container. #secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. +{{- if not .Environments}} + # You can override any of the values defined above by environment. #environments: # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. -{{- if .Environments}} -environments: {{ range $key, $value := .Environments}} +{{- else}} + +# You can override any of the values defined above by environment. +environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: {{$value.Network.VPC.Placement.PlacementString}} - {{- end}} -{{- end}} - + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. +{{- end}} +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +{{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml index fcbf04061a7..f5966d490ec 100644 --- a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml @@ -66,8 +66,25 @@ network: # tags: # Pass tags as key value pairs. # project: project-name +{{- if not .Environments}} + # You can override any of the values defined above by environment. # environments: # test: # variables: # LOG_LEVEL: debug # Log level for the "test" environment. +{{- else}} + +# You can override any of the values defined above by environment. +environments: {{ range $key, $value := .Environments}} + {{$key}}: + network: + vpc: + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. +{{- end}} +# You can override any of the values defined above by environment. +# environments: +# test: +# variables: +# LOG_LEVEL: debug # Log level for the "test" environment. +{{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/worker/manifest.yml b/internal/pkg/template/templates/workloads/services/worker/manifest.yml index 160b5715368..37bf668f809 100644 --- a/internal/pkg/template/templates/workloads/services/worker/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/worker/manifest.yml @@ -73,9 +73,25 @@ subscribe: #secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. +{{- if not .Environments}} + # You can override any of the values defined above by environment. #environments: # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +{{- else}} + +# You can override any of the values defined above by environment. +environments: {{ range $key, $value := .Environments}} + {{$key}}: + network: + vpc: + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. +{{- end}} +# test: +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +{{- end}} \ No newline at end of file From e5dfa17bb643ea4ac97756347e401f807e1e54f7 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 22 Nov 2022 13:15:14 -0800 Subject: [PATCH 03/12] fix unit tests --- internal/pkg/cli/svc_init.go | 69 +-- internal/pkg/cli/svc_init_test.go | 764 +++++++++++++++++----------- internal/pkg/initialize/workload.go | 26 +- 3 files changed, 490 insertions(+), 369 deletions(-) diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 1d9a4ce7c08..68d005a06a3 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -153,8 +153,9 @@ type initSvcOpts struct { manifestExists bool // Init a Dockerfile parser using fs and input path - dockerfile func(string) dockerfileParser - initEnvDescribers func(string) (envDescriber, error) + dockerfile func(string) dockerfileParser + // Init a new EnvDescriber using environment name. + envDescriber func(string) (envDescriber, error) } func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { @@ -202,7 +203,7 @@ func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { opts.df = dockerfile.New(opts.fs, opts.dockerfilePath) return opts.df } - opts.initEnvDescribers = func(envName string) (envDescriber, error) { + opts.envDescriber = func(envName string) (envDescriber, error) { envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ App: opts.appName, Env: envName, @@ -350,12 +351,12 @@ func (o *initSvcOpts) Execute() error { Platform: manifest.PlatformArgsOrString{ PlatformString: o.platform, }, - Topics: o.topics, + Topics: o.topics, + PrivateOnlyEnvironments: envs, }, - Port: o.port, - HealthCheck: hc, - Private: strings.EqualFold(o.ingressType, ingressTypeEnvironment), - PrivateOnlyEnvironments: envs, + Port: o.port, + HealthCheck: hc, + Private: strings.EqualFold(o.ingressType, ingressTypeEnvironment), }) if err != nil { return err @@ -729,41 +730,7 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error }, nil } -// isSubnetsOnlyPrivate returns the list of deployed environment names whose subnets are only private. -// func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { -// envs, err := o.store.ListEnvironments(o.appName) -// if err != nil { -// return nil, err -// } -// var privateOnlyEnvs []string -// for i := range envs { -// envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ -// App: o.appName, -// Env: envs[i].Name, -// ConfigStore: o.store, -// }) -// if err != nil { -// return nil, fmt.Errorf("initiate env describer: %w", err) -// } -// mft, err := envDescriber.Manifest() -// if err != nil { -// return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) -// } -// envConfig, err := manifest.UnmarshalEnvironment(mft) -// if err != nil { -// return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) -// } -// // workspace.ReadEnvironmentManifest() -// subnets := envConfig.Network.VPC.Subnets - -// if len(subnets.Public) == 0 && len(subnets.Private) != 0 { -// privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) -// } -// } -// return privateOnlyEnvs, err -// } - -// isSubnetsOnlyPrivate returns the list of deployed environment names whose subnets are only private. +// isSubnetsOnlyPrivate returns the list of environment names deployed on private only subnets. func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { envs, err := o.store.ListEnvironments(o.appName) if err != nil { @@ -771,19 +738,21 @@ func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { } var privateOnlyEnvs []string for i := range envs { - envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ - App: o.appName, - Env: envs[i].Name, - ConfigStore: o.store, - }) + envDescriber, err := o.envDescriber(envs[i].Name) if err != nil { return nil, fmt.Errorf("initiate env describer: %w", err) } - out, err := envDescriber.Outputs() + mft, err := envDescriber.Manifest() if err != nil { return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) } - if out["PublicSubnets"] == "" && out["PrivateSubnets"] != "" { + envConfig, err := manifest.UnmarshalEnvironment(mft) + if err != nil { + return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) + } + subnets := envConfig.Network.VPC.Subnets + + if len(subnets.Public) == 0 && len(subnets.Private) != 0 { privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) } } diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index 8a4f213e2cc..33f6d8a5e83 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -9,6 +9,7 @@ import ( "path/filepath" "testing" + "github.com/aws/aws-sdk-go/aws" "github.com/aws/copilot-cli/internal/pkg/config" "github.com/aws/copilot-cli/internal/pkg/deploy" "github.com/aws/copilot-cli/internal/pkg/docker/dockerengine" @@ -771,7 +772,7 @@ func TestSvcInitOpts_Execute(t *testing.T) { mockDockerEngine func(m *mocks.MockdockerEngine) mockTopicSel func(m *mocks.MocktopicSelector) mockStore func(m *mocks.Mockstore) - //mockEnvDescriber func(m *mocks.MockenvDescriber) + mockEnvDescriber func(m *mocks.MockenvDescriber) inSvcPort uint16 inSvcType string inSvcName string @@ -791,306 +792,483 @@ func TestSvcInitOpts_Execute(t *testing.T) { inSvcPort: 80, + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + DockerfilePath: "./Dockerfile", + Platform: manifest.PlatformArgsOrString{}, + }, + Port: 80, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + }, mockStore: func(m *mocks.Mockstore) { m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ { - Name: "test", - Region: "us-west-2", - AccountID: "123456789", + App: "sample", + Name: "test", }, }, nil) - m.EXPECT().GetEnvironment("sample", "test").Return(&config.Environment{Name: "test", Region: "us-west-2"}, nil) }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + wantedManifestPath: "manifest/path", + }, + "backend service": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.BackendServiceType, + mockSvcInit: func(m *mocks.MocksvcInitializer) { m.EXPECT().Service(&initialize.ServiceProps{ WorkloadProps: initialize.WorkloadProps{ App: "sample", Name: "frontend", - Type: "Load Balanced Web Service", + Type: "Backend Service", DockerfilePath: "./Dockerfile", Platform: manifest.PlatformArgsOrString{}, }, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + wantedManifestPath: "manifest/path", + }, + "doesn't attempt to detect and populate the platform if manifest already exists": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.LoadBalancedWebServiceType, + inSvcPort: 80, + inManifestExists: true, + + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Times(0) + m.EXPECT().GetPlatform().Times(0) + }, + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + DockerfilePath: "./Dockerfile", + }, + Port: 80, + }).Return("manifest/path", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + + wantedManifestPath: "manifest/path", + }, + "doesn't complain if docker is unavailable": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.LoadBalancedWebServiceType, + + inSvcPort: 80, + + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(&dockerengine.ErrDockerDaemonNotResponsive{}) + m.EXPECT().GetPlatform().Times(0) + }, + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + DockerfilePath: "./Dockerfile", + }, + Port: 80, + }).Return("manifest/path", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + + wantedManifestPath: "manifest/path", + }, + "windows platform": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.LoadBalancedWebServiceType, + + inSvcPort: 80, + + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + DockerfilePath: "./Dockerfile", + Platform: manifest.PlatformArgsOrString{ + PlatformString: (*manifest.PlatformString)(aws.String("windows/x86_64")), + }, + }, + Port: 80, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("windows", "amd64", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + + wantedManifestPath: "manifest/path", + }, + "ARM architecture redirects to X86_64": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.LoadBalancedWebServiceType, + + inSvcPort: 80, + + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + DockerfilePath: "./Dockerfile", + Platform: manifest.PlatformArgsOrString{ + PlatformString: (*manifest.PlatformString)(aws.String("linux/x86_64")), + }, + }, Port: 80, }).Return("manifest/path", nil) }, mockDockerfile: func(m *mocks.MockdockerfileParser) { m.EXPECT().GetHealthCheck().Return(nil, nil) }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "arm", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + + wantedManifestPath: "manifest/path", + }, + "worker service": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.WorkerServiceType, + + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Worker Service", + DockerfilePath: "./Dockerfile", + Platform: manifest.PlatformArgsOrString{}, + }, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, mockDockerEngine: func(m *mocks.MockdockerEngine) { m.EXPECT().CheckDockerEngineRunning().Return(nil) m.EXPECT().GetPlatform().Return("linux", "amd64", nil) }, + mockTopicSel: func(m *mocks.MocktopicSelector) { + m.EXPECT().Topics( + gomock.Eq(svcInitPublisherPrompt), + gomock.Eq(svcInitPublisherHelpPrompt), + gomock.Any(), + ).Return([]manifest.TopicSubscription{ + { + Name: aws.String("thetopic"), + Service: aws.String("theservice"), + }, + }, nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, wantedManifestPath: "manifest/path", }, + "doesn't parse dockerfile if image specified (backend)": { + inAppName: "sample", + inSvcName: "backend", + inDockerfilePath: "", + inImage: "nginx:latest", + inSvcType: manifest.BackendServiceType, + + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "backend", + Type: "Backend Service", + Image: "nginx:latest", + Platform: manifest.PlatformArgsOrString{}, + }, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, + + wantedManifestPath: "manifest/path", + }, + "doesn't parse dockerfile if image specified (lb-web)": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "", + inImage: "nginx:latest", + inSvcType: manifest.LoadBalancedWebServiceType, + + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + Image: "nginx:latest", + Platform: manifest.PlatformArgsOrString{}, + }, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, - // "backend service": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.BackendServiceType, - - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Backend Service", - // DockerfilePath: "./Dockerfile", - // Platform: manifest.PlatformArgsOrString{}, - // }, - // }).Return("manifest/path", nil) - // }, - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("linux", "amd64", nil) - // }, - - // wantedManifestPath: "manifest/path", - // }, - // "doesn't attempt to detect and populate the platform if manifest already exists": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.LoadBalancedWebServiceType, - // inSvcPort: 80, - // inManifestExists: true, - - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Times(0) - // m.EXPECT().GetPlatform().Times(0) - // }, - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Load Balanced Web Service", - // DockerfilePath: "./Dockerfile", - // }, - // Port: 80, - // }).Return("manifest/path", nil) - // }, - - // wantedManifestPath: "manifest/path", - // }, - // "doesn't complain if docker is unavailable": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.LoadBalancedWebServiceType, - - // inSvcPort: 80, - - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(&dockerengine.ErrDockerDaemonNotResponsive{}) - // m.EXPECT().GetPlatform().Times(0) - // }, - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Load Balanced Web Service", - // DockerfilePath: "./Dockerfile", - // }, - // Port: 80, - // }).Return("manifest/path", nil) - // }, - - // wantedManifestPath: "manifest/path", - // }, - // "windows platform": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.LoadBalancedWebServiceType, - - // inSvcPort: 80, - - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Load Balanced Web Service", - // DockerfilePath: "./Dockerfile", - // Platform: manifest.PlatformArgsOrString{ - // PlatformString: (*manifest.PlatformString)(aws.String("windows/x86_64")), - // }, - // }, - // Port: 80, - // }).Return("manifest/path", nil) - // }, - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("windows", "amd64", nil) - // }, - - // wantedManifestPath: "manifest/path", - // }, - // "ARM architecture redirects to X86_64": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.LoadBalancedWebServiceType, - - // inSvcPort: 80, - - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Load Balanced Web Service", - // DockerfilePath: "./Dockerfile", - // Platform: manifest.PlatformArgsOrString{ - // PlatformString: (*manifest.PlatformString)(aws.String("linux/x86_64")), - // }, - // }, - // Port: 80, - // }).Return("manifest/path", nil) - // }, - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("linux", "arm", nil) - // }, - - // wantedManifestPath: "manifest/path", - // }, - // "worker service": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.WorkerServiceType, - - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Worker Service", - // DockerfilePath: "./Dockerfile", - // Platform: manifest.PlatformArgsOrString{}, - // }, - // }).Return("manifest/path", nil) - // }, - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("linux", "amd64", nil) - // }, - // mockTopicSel: func(m *mocks.MocktopicSelector) { - // m.EXPECT().Topics( - // gomock.Eq(svcInitPublisherPrompt), - // gomock.Eq(svcInitPublisherHelpPrompt), - // gomock.Any(), - // ).Return([]manifest.TopicSubscription{ - // { - // Name: aws.String("thetopic"), - // Service: aws.String("theservice"), - // }, - // }, nil) - // }, - - // wantedManifestPath: "manifest/path", - // }, - // "doesn't parse dockerfile if image specified (backend)": { - // inAppName: "sample", - // inSvcName: "backend", - // inDockerfilePath: "", - // inImage: "nginx:latest", - // inSvcType: manifest.BackendServiceType, - - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "backend", - // Type: "Backend Service", - // Image: "nginx:latest", - // Platform: manifest.PlatformArgsOrString{}, - // }, - // }).Return("manifest/path", nil) - // }, - // mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. - - // wantedManifestPath: "manifest/path", - // }, - // "doesn't parse dockerfile if image specified (lb-web)": { - // inAppName: "sample", - // inSvcName: "frontend", - // inDockerfilePath: "", - // inImage: "nginx:latest", - // inSvcType: manifest.LoadBalancedWebServiceType, - - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(&initialize.ServiceProps{ - // WorkloadProps: initialize.WorkloadProps{ - // App: "sample", - // Name: "frontend", - // Type: "Load Balanced Web Service", - // Image: "nginx:latest", - // Platform: manifest.PlatformArgsOrString{}, - // }, - // }).Return("manifest/path", nil) - // }, - // mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. - - // wantedManifestPath: "manifest/path", - // }, - // "return error if platform detection fails": { - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("", "", errors.New("some error")) - // }, - // wantedErr: errors.New("get docker engine platform: some error"), - // }, - // "return error if Windows platform attempted with RDWS": { - // inAppName: "sample", - // inSvcName: "appRunner", - // inDockerfilePath: "./Dockerfile", - // inSvcType: manifest.RequestDrivenWebServiceType, - - // inSvcPort: 80, - - // mockDockerfile: func(m *mocks.MockdockerfileParser) { - // m.EXPECT().GetHealthCheck().Return(nil, nil) - // }, - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("windows", "amd64", nil) - // }, - - // wantedErr: errors.New("redirect docker engine platform: Windows is not supported for App Runner services"), - // }, - // "failure": { - // mockDockerEngine: func(m *mocks.MockdockerEngine) { - // m.EXPECT().CheckDockerEngineRunning().Return(nil) - // m.EXPECT().GetPlatform().Return("linux", "amd64", nil) - // }, - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(gomock.Any()).Return("", errors.New("some error")) - // }, - // wantedErr: errors.New("some error"), - // }, + wantedManifestPath: "manifest/path", + }, + "return error if platform detection fails": { + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("", "", errors.New("some error")) + }, + wantedErr: errors.New("get docker engine platform: some error"), + }, + "return error if Windows platform attempted with RDWS": { + inAppName: "sample", + inSvcName: "appRunner", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.RequestDrivenWebServiceType, + + inSvcPort: 80, + + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("windows", "amd64", nil) + }, + wantedErr: errors.New("redirect docker engine platform: Windows is not supported for App Runner services"), + }, + "failure if appname is not provided": { + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + }, + // mockSvcInit: func(m *mocks.MocksvcInitializer) { + // m.EXPECT().Service(gomock.Any()).Return("", errors.New("some error")) + // }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("").Return(nil, errors.New("some error")) + }, + wantedErr: errors.New("some error"), + }, + "return environment with only private subnets": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.LoadBalancedWebServiceType, + + inSvcPort: 80, + + mockSvcInit: func(m *mocks.MocksvcInitializer) { + m.EXPECT().Service(&initialize.ServiceProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "frontend", + Type: "Load Balanced Web Service", + DockerfilePath: "./Dockerfile", + Platform: manifest.PlatformArgsOrString{}, + PrivateOnlyEnvironments: []string{ + "test", + }, + }, + Port: 80, + }).Return("manifest/path", nil) + }, + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment +network: + vpc: + id: 'vpc-mockid' + subnets: + private: + - id: 'subnet-1' + - id: 'subnet-2' + - id: 'subnet-3' + - id: 'subnet-4'`), nil) + }, + wantedManifestPath: "manifest/path", + }, + "error if fail to read the manifest": { + inAppName: "sample", + inSvcName: "frontend", + inDockerfilePath: "./Dockerfile", + inSvcType: manifest.LoadBalancedWebServiceType, + + inSvcPort: 80, + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(nil, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return(nil, errors.New("failed to read manifest")) + }, + wantedErr: errors.New("read the manifest used to deploy environment test: failed to read manifest"), + }, } for name, tc := range testCases { @@ -1104,7 +1282,7 @@ func TestSvcInitOpts_Execute(t *testing.T) { mockDockerEngine := mocks.NewMockdockerEngine(ctrl) mockTopicSel := mocks.NewMocktopicSelector(ctrl) mockStore := mocks.NewMockstore(ctrl) - //mockEnvDescriber := mocks.NewMockenvDescriber(ctrl) + mockEnvDescriber := mocks.NewMockenvDescriber(ctrl) if tc.mockStore != nil { tc.mockStore(mockStore) @@ -1119,9 +1297,9 @@ func TestSvcInitOpts_Execute(t *testing.T) { if tc.mockDockerEngine != nil { tc.mockDockerEngine(mockDockerEngine) } - // if tc.mockEnvDescriber != nil { - // tc.mockEnvDescriber(mockEnvDescriber) - // } + if tc.mockEnvDescriber != nil { + tc.mockEnvDescriber(mockEnvDescriber) + } opts := initSvcOpts{ initSvcVars: initSvcVars{ initWkldVars: initWkldVars{ @@ -1142,6 +1320,9 @@ func TestSvcInitOpts_Execute(t *testing.T) { store: mockStore, topicSel: mockTopicSel, manifestExists: tc.inManifestExists, + envDescriber: func(s string) (envDescriber, error) { + return mockEnvDescriber, nil + }, } // WHEN @@ -1157,32 +1338,3 @@ func TestSvcInitOpts_Execute(t *testing.T) { }) } } - -// func Test_isSubnetsOnlyPrivate(t *testing.T) { -// testCases := map[string]struct { -// inAppName string -// setupMocks func(mocks initSvcMocks) -// wantedPrivateOnlyEnvs []string -// wantedErr error -// }{ -// "no error": { -// inAppName: "phonetool", -// setupMocks: func(mocks initSvcMocks) { -// mocks.mockStore.EXPECT().ListEnvironments("phonetool").Return([]*config.Environment{ -// { -// Name: "metrics", -// }, -// { -// Name: "payments", -// }, -// }) -// for i := 0; i < 2; i++ { -// //fmt.Println("w") -// mocks.mockEnvDescriber.EXPECT().Manifest().Return([]byte("hello"), nil) - -// } -// }, -// }, -// } - -// } diff --git a/internal/pkg/initialize/workload.go b/internal/pkg/initialize/workload.go index 2987472f268..448c955cbde 100644 --- a/internal/pkg/initialize/workload.go +++ b/internal/pkg/initialize/workload.go @@ -60,14 +60,15 @@ type Prog interface { // WorkloadProps contains the information needed to represent a Workload (job or service). type WorkloadProps struct { - App string - Type string - Name string - DockerfilePath string - Image string - Platform manifest.PlatformArgsOrString - Topics []manifest.TopicSubscription - Queue manifest.SQSQueue + App string + Type string + Name string + DockerfilePath string + Image string + Platform manifest.PlatformArgsOrString + Topics []manifest.TopicSubscription + Queue manifest.SQSQueue + PrivateOnlyEnvironments []string } // JobProps contains the information needed to represent a Job. @@ -82,11 +83,10 @@ type JobProps struct { // ServiceProps contains the information needed to represent a Service (port, HealthCheck, and workload common props). type ServiceProps struct { WorkloadProps - Port uint16 - HealthCheck manifest.ContainerHealthCheck - Private bool - appDomain *string - PrivateOnlyEnvironments []string + Port uint16 + HealthCheck manifest.ContainerHealthCheck + Private bool + appDomain *string } // WorkloadInitializer holds the clients necessary to initialize either a From 93eaaab31df6b5e7720cb1239a90b0dc2113bc11 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 22 Nov 2022 17:58:40 -0800 Subject: [PATCH 04/12] implement placement private for job --- internal/pkg/cli/job_init.go | 49 ++ internal/pkg/cli/job_init_test.go | 116 +++ internal/pkg/cli/svc_init.go | 1 + internal/pkg/cli/svc_init_test.go | 5 +- internal/pkg/initialize/workload.go | 11 +- internal/pkg/manifest/job.go | 23 +- .../marshal_manifest_integration_test.go | 814 +++++++++--------- .../workloads/jobs/scheduled-job/manifest.yml | 18 +- .../workloads/services/backend/manifest.yml | 5 +- .../workloads/services/lb-web/manifest.yml | 5 +- .../workloads/services/rd-web/manifest.yml | 4 +- .../workloads/services/worker/manifest.yml | 5 +- 12 files changed, 627 insertions(+), 429 deletions(-) diff --git a/internal/pkg/cli/job_init.go b/internal/pkg/cli/job_init.go index 2266d81fdde..23ae099e1ab 100644 --- a/internal/pkg/cli/job_init.go +++ b/internal/pkg/cli/job_init.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ssm" "github.com/aws/copilot-cli/internal/pkg/aws/identity" + "github.com/aws/copilot-cli/internal/pkg/describe" "github.com/aws/copilot-cli/internal/pkg/docker/dockerfile" @@ -79,6 +80,8 @@ type initJobOpts struct { // Init a Dockerfile parser using fs and input path initParser func(string) dockerfileParser + // Init a new EnvDescriber using environment name. + envDescriber func(string) (envDescriber, error) } func newInitJobOpts(vars initJobVars) (*initJobOpts, error) { @@ -117,6 +120,17 @@ func newInitJobOpts(vars initJobVars) (*initJobOpts, error) { initParser: func(path string) dockerfileParser { return dockerfile.New(fs, path) }, + envDescriber: func(envName string) (envDescriber, error) { + envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ + App: vars.appName, + Env: envName, + ConfigStore: store, + }) + if err != nil { + return nil, err + } + return envDescriber, nil + }, wsAppName: tryReadingAppName(), }, nil } @@ -211,6 +225,35 @@ func (o *initJobOpts) Ask() error { return nil } +// isSubnetsOnlyPrivate returns the list of environment names deployed on private only subnets. +func (o *initJobOpts) isSubnetsOnlyPrivate() ([]string, error) { + envs, err := o.store.ListEnvironments(o.appName) + if err != nil { + return nil, err + } + var privateOnlyEnvs []string + for i := range envs { + envDescriber, err := o.envDescriber(envs[i].Name) + if err != nil { + return nil, fmt.Errorf("initiate env describer: %w", err) + } + mft, err := envDescriber.Manifest() + if err != nil { + return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) + } + envConfig, err := manifest.UnmarshalEnvironment(mft) + if err != nil { + return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) + } + subnets := envConfig.Network.VPC.Subnets + + if len(subnets.Public) == 0 && len(subnets.Private) != 0 { + privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) + } + } + return privateOnlyEnvs, err +} + // Execute writes the job's manifest file, creates an ECR repo, and stores the name in SSM. func (o *initJobOpts) Execute() error { // Check for a valid healthcheck and add it to the opts. @@ -232,6 +275,11 @@ func (o *initJobOpts) Execute() error { o.platform = &platform } } + // Environments that are deployed to the VPC has only private subnets. + envs, err := o.isSubnetsOnlyPrivate() + if err != nil { + return err + } manifestPath, err := o.init.Job(&initialize.JobProps{ WorkloadProps: initialize.WorkloadProps{ App: o.appName, @@ -242,6 +290,7 @@ func (o *initJobOpts) Execute() error { Platform: manifest.PlatformArgsOrString{ PlatformString: o.platform, }, + PrivateOnlyEnvironments: envs, }, Schedule: o.schedule, diff --git a/internal/pkg/cli/job_init_test.go b/internal/pkg/cli/job_init_test.go index 627499ce329..b79ee300a7e 100644 --- a/internal/pkg/cli/job_init_test.go +++ b/internal/pkg/cli/job_init_test.go @@ -552,12 +552,25 @@ type: Scheduled Job`), nil) } func TestJobInitOpts_Execute(t *testing.T) { + mockmanifest := []byte(`name: test +type: Environment +network: + vpc: + id: 'vpc-mockid' + subnets: + private: + - id: 'subnet-1' + - id: 'subnet-2' + - id: 'subnet-3' + - id: 'subnet-4'`) second := time.Second zero := 0 testCases := map[string]struct { mockJobInit func(m *mocks.MockjobInitializer) mockDockerfile func(m *mocks.MockdockerfileParser) mockDockerEngine func(m *mocks.MockdockerEngine) + mockStore func(m *mocks.Mockstore) + mockEnvDescriber func(m *mocks.MockenvDescriber) inApp string inName string @@ -610,12 +623,27 @@ func TestJobInitOpts_Execute(t *testing.T) { }, }).Return("manifest/path", nil) }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, }, "fail to init job": { mockDockerEngine: func(m *mocks.MockdockerEngine) { m.EXPECT().CheckDockerEngineRunning().Return(nil) m.EXPECT().GetPlatform().Return("linux", "amd64", nil) }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("").Return(nil, nil) + }, mockJobInit: func(m *mocks.MockjobInitializer) { m.EXPECT().Job(gomock.Any()).Return("", errors.New("some error")) }, @@ -662,6 +690,18 @@ func TestJobInitOpts_Execute(t *testing.T) { }, }).Return("manifest/path", nil) }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(`name: test +type: Environment`), nil) + }, }, "doesn't complain if docker is unavailable": { inApp: "sample", @@ -703,6 +743,17 @@ func TestJobInitOpts_Execute(t *testing.T) { }, }).Return("manifest/path", nil) }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return([]byte(``), nil) + }, }, "return error if platform detection fails": { mockDockerEngine: func(m *mocks.MockdockerEngine) { @@ -711,6 +762,59 @@ func TestJobInitOpts_Execute(t *testing.T) { }, wantedErr: errors.New("get docker engine platform: some error"), }, + "environments with only private subnets": { + inApp: "sample", + inName: "mailer", + inType: manifest.ScheduledJobType, + inDf: "./Dockerfile", + inSchedule: "@hourly", + wantedManifestPath: "manifest/path", + + mockDockerfile: func(m *mocks.MockdockerfileParser) { + m.EXPECT().GetHealthCheck().Return(&dockerfile.HealthCheck{ + Cmd: []string{"mockCommand"}, + Interval: second, + Timeout: second, + StartPeriod: second, + Retries: zero, + }, nil) + }, + mockDockerEngine: func(m *mocks.MockdockerEngine) { + m.EXPECT().CheckDockerEngineRunning().Return(nil) + m.EXPECT().GetPlatform().Return("linux", "amd64", nil) + }, + mockJobInit: func(m *mocks.MockjobInitializer) { + m.EXPECT().Job(&initialize.JobProps{ + WorkloadProps: initialize.WorkloadProps{ + App: "sample", + Name: "mailer", + Type: "Scheduled Job", + DockerfilePath: "./Dockerfile", + Platform: manifest.PlatformArgsOrString{}, + PrivateOnlyEnvironments: []string{"test"}, + }, + Schedule: "@hourly", + HealthCheck: manifest.ContainerHealthCheck{ + Command: []string{"mockCommand"}, + Interval: &second, + Retries: &zero, + Timeout: &second, + StartPeriod: &second, + }, + }).Return("manifest/path", nil) + }, + mockStore: func(m *mocks.Mockstore) { + m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ + { + App: "sample", + Name: "test", + }, + }, nil) + }, + mockEnvDescriber: func(m *mocks.MockenvDescriber) { + m.EXPECT().Manifest().Return(mockmanifest, nil) + }, + }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { @@ -721,6 +825,8 @@ func TestJobInitOpts_Execute(t *testing.T) { mockJobInitializer := mocks.NewMockjobInitializer(ctrl) mockDockerfile := mocks.NewMockdockerfileParser(ctrl) mockDockerEngine := mocks.NewMockdockerEngine(ctrl) + mockStore := mocks.NewMockstore(ctrl) + mockEnvDescriber := mocks.NewMockenvDescriber(ctrl) if tc.mockJobInit != nil { tc.mockJobInit(mockJobInitializer) @@ -731,6 +837,12 @@ func TestJobInitOpts_Execute(t *testing.T) { if tc.mockDockerEngine != nil { tc.mockDockerEngine(mockDockerEngine) } + if tc.mockStore != nil { + tc.mockStore(mockStore) + } + if tc.mockEnvDescriber != nil { + tc.mockEnvDescriber(mockEnvDescriber) + } opts := initJobOpts{ initJobVars: initJobVars{ @@ -748,6 +860,10 @@ func TestJobInitOpts_Execute(t *testing.T) { }, dockerEngine: mockDockerEngine, manifestExists: tc.inManifestExists, + store: mockStore, + envDescriber: func(s string) (envDescriber, error) { + return mockEnvDescriber, nil + }, } // WHEN diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 68d005a06a3..6142f84287a 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -337,6 +337,7 @@ func (o *initSvcOpts) Execute() error { o.platform = &platform } } + // Environments that are deployed to the VPC has only private subnets. envs, err := o.isSubnetsOnlyPrivate() if err != nil { return err diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index 33f6d8a5e83..3e09ed1beb1 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -820,8 +820,7 @@ func TestSvcInitOpts_Execute(t *testing.T) { }, nil) }, mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().Manifest().Return([]byte(``), nil) }, wantedManifestPath: "manifest/path", }, @@ -1189,7 +1188,7 @@ type: Environment`), nil) }, wantedErr: errors.New("some error"), }, - "return environment with only private subnets": { + "environments with only private subnets": { inAppName: "sample", inSvcName: "frontend", inDockerfilePath: "./Dockerfile", diff --git a/internal/pkg/initialize/workload.go b/internal/pkg/initialize/workload.go index 448c955cbde..9fdec0cd402 100644 --- a/internal/pkg/initialize/workload.go +++ b/internal/pkg/initialize/workload.go @@ -276,11 +276,12 @@ func newJobManifest(i *JobProps) (encoding.BinaryMarshaler, error) { Dockerfile: i.DockerfilePath, Image: i.Image, }, - HealthCheck: i.HealthCheck, - Platform: i.Platform, - Schedule: i.Schedule, - Timeout: i.Timeout, - Retries: i.Retries, + HealthCheck: i.HealthCheck, + Platform: i.Platform, + Schedule: i.Schedule, + Timeout: i.Timeout, + Retries: i.Retries, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }), nil default: return nil, fmt.Errorf("job type %s doesn't have a manifest", i.Type) diff --git a/internal/pkg/manifest/job.go b/internal/pkg/manifest/job.go index 6984424f809..5f5801b9ecb 100644 --- a/internal/pkg/manifest/job.go +++ b/internal/pkg/manifest/job.go @@ -67,11 +67,12 @@ type JobFailureHandlerConfig struct { // ScheduledJobProps contains properties for creating a new scheduled job manifest. type ScheduledJobProps struct { *WorkloadProps - Schedule string - Timeout string - HealthCheck ContainerHealthCheck // Optional healthcheck configuration. - Platform PlatformArgsOrString // Optional platform configuration. - Retries int + Schedule string + Timeout string + HealthCheck ContainerHealthCheck // Optional healthcheck configuration. + Platform PlatformArgsOrString // Optional platform configuration. + Retries int + PrivateOnlyEnvironments []string } // NewScheduledJob creates a new scheduled job object. @@ -92,6 +93,17 @@ func NewScheduledJob(props *ScheduledJobProps) *ScheduledJob { job.Retries = aws.Int(props.Retries) } job.Timeout = stringP(props.Timeout) + for _, envName := range props.PrivateOnlyEnvironments { + job.Environments[envName] = &ScheduledJobConfig{ + Network: NetworkConfig{ + VPC: vpcConfig{ + Placement: PlacementArgOrString{ + PlacementString: placementStringP(PrivateSubnetPlacement), + }, + }, + }, + } + } job.parser = template.New() return job } @@ -178,5 +190,6 @@ func newDefaultScheduledJob() *ScheduledJob { }, }, }, + Environments: map[string]*ScheduledJobConfig{}, } } diff --git a/internal/pkg/manifest/marshal_manifest_integration_test.go b/internal/pkg/manifest/marshal_manifest_integration_test.go index 20bdc2cc68c..0d7a7f1c32b 100644 --- a/internal/pkg/manifest/marshal_manifest_integration_test.go +++ b/internal/pkg/manifest/marshal_manifest_integration_test.go @@ -36,7 +36,7 @@ func TestLoadBalancedWebService_InitialManifestIntegration(t *testing.T) { }, wantedTestdata: "lb-svc.yml", }, - "with environments private": { + "with placement private": { inProps: LoadBalancedWebServiceProps{ WorkloadProps: &WorkloadProps{ Name: "frontend", @@ -72,409 +72,409 @@ func TestLoadBalancedWebService_InitialManifestIntegration(t *testing.T) { } } -func TestBackendSvc_InitialManifestIntegration(t *testing.T) { - testCases := map[string]struct { - inProps BackendServiceProps - - wantedTestdata string - }{ - "without healthcheck and port and with private only environments": { - inProps: BackendServiceProps{ - WorkloadProps: WorkloadProps{ - Name: "subscribers", - Dockerfile: "./subscribers/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{ - OSFamily: nil, - Arch: nil, - }, - }, - PrivateOnlyEnvironments: []string{ - "phonetool", - }, - }, - wantedTestdata: "backend-svc-nohealthcheck-placement.yml", - }, - "with custom healthcheck command": { - inProps: BackendServiceProps{ - WorkloadProps: WorkloadProps{ - Name: "subscribers", - Image: "flask-sample", - }, - HealthCheck: ContainerHealthCheck{ - Command: []string{"CMD-SHELL", "curl -f http://localhost:8080 || exit 1"}, - Interval: durationp(6 * time.Second), - Retries: aws.Int(0), - Timeout: durationp(20 * time.Second), - StartPeriod: durationp(15 * time.Second), - }, - Port: 8080, - }, - wantedTestdata: "backend-svc-customhealthcheck.yml", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - // GIVEN - path := filepath.Join("testdata", tc.wantedTestdata) - wantedBytes, err := os.ReadFile(path) - require.NoError(t, err) - manifest := NewBackendService(tc.inProps) - - // WHEN - tpl, err := manifest.MarshalBinary() - require.NoError(t, err) - - // THEN - require.Equal(t, string(wantedBytes), string(tpl)) - }) - } -} - -func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { - testCases := map[string]struct { - inProps WorkerServiceProps - - wantedTestdata string - }{ - "without subscribe and with private only environments": { - inProps: WorkerServiceProps{ - WorkloadProps: WorkloadProps{ - Name: "testers", - Dockerfile: "./testers/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{ - OSFamily: nil, - Arch: nil, - }, - }, - PrivateOnlyEnvironments: []string{ - "phonetool", - }, - }, - wantedTestdata: "worker-svc-nosubscribe-placement.yml", - }, - "with subscribe": { - inProps: WorkerServiceProps{ - WorkloadProps: WorkloadProps{ - Name: "testers", - Dockerfile: "./testers/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{ - OSFamily: nil, - Arch: nil, - }, - }, - Topics: []TopicSubscription{ - { - Name: aws.String("testTopic"), - Service: aws.String("service4TestTopic"), - }, - { - Name: aws.String("testTopic2"), - Service: aws.String("service4TestTopic2"), - }, - }, - }, - wantedTestdata: "worker-svc-subscribe.yml", - }, - "with fifo topic subscription with default fifo queue": { - inProps: WorkerServiceProps{ - WorkloadProps: WorkloadProps{ - Name: "testers", - Dockerfile: "./testers/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{ - OSFamily: nil, - Arch: nil, - }, - }, - Topics: []TopicSubscription{ - { - Name: aws.String("testTopic.fifo"), - Service: aws.String("service4TestTopic"), - }, - }, - }, - wantedTestdata: "worker-svc-with-default-fifo-queue.yml", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - // GIVEN - path := filepath.Join("testdata", tc.wantedTestdata) - wantedBytes, err := os.ReadFile(path) - require.NoError(t, err) - manifest := NewWorkerService(tc.inProps) - - // WHEN - tpl, err := manifest.MarshalBinary() - require.NoError(t, err) - - // THEN - require.Equal(t, string(wantedBytes), string(tpl)) - }) - } -} - -func TestScheduledJob_InitialManifestIntegration(t *testing.T) { - testCases := map[string]struct { - inProps ScheduledJobProps - - wantedTestData string - }{ - "without timeout or retries": { - inProps: ScheduledJobProps{ - WorkloadProps: &WorkloadProps{ - Name: "cuteness-aggregator", - Image: "copilot/cuteness-aggregator", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{}, - }, - Schedule: "@weekly", - }, - wantedTestData: "scheduled-job-no-timeout-or-retries.yml", - }, - "fully specified using cron schedule": { - inProps: ScheduledJobProps{ - WorkloadProps: &WorkloadProps{ - Name: "cuteness-aggregator", - Dockerfile: "./cuteness-aggregator/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{}, - }, - Schedule: "0 */2 * * *", - Retries: 3, - Timeout: "1h30m", - }, - wantedTestData: "scheduled-job-fully-specified.yml", - }, - "with timeout and no retries": { - inProps: ScheduledJobProps{ - WorkloadProps: &WorkloadProps{ - Name: "cuteness-aggregator", - Dockerfile: "./cuteness-aggregator/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{}, - }, - Schedule: "@every 5h", - Retries: 0, - Timeout: "3h", - }, - wantedTestData: "scheduled-job-no-retries.yml", - }, - "with retries and no timeout": { - inProps: ScheduledJobProps{ - WorkloadProps: &WorkloadProps{ - Name: "cuteness-aggregator", - Dockerfile: "./cuteness-aggregator/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{}, - }, - Schedule: "@every 5h", - Retries: 5, - }, - wantedTestData: "scheduled-job-no-timeout.yml", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - // GIVEN - path := filepath.Join("testdata", tc.wantedTestData) - wantedBytes, err := os.ReadFile(path) - require.NoError(t, err) - manifest := NewScheduledJob(&tc.inProps) - - // WHEN - tpl, err := manifest.MarshalBinary() - require.NoError(t, err) - - // THEN - require.Equal(t, string(wantedBytes), string(tpl)) - }) - } -} - -func TestEnvironment_InitialManifestIntegration(t *testing.T) { - testCases := map[string]struct { - inProps EnvironmentProps - wantedTestData string - }{ - "fully configured with customized vpc resources": { - inProps: EnvironmentProps{ - Name: "test", - CustomConfig: &config.CustomizeEnv{ - VPCConfig: &config.AdjustVPC{ - CIDR: "mock-cidr-0", - AZs: []string{"mock-az-1", "mock-az-2"}, - PublicSubnetCIDRs: []string{"mock-cidr-1", "mock-cidr-2"}, - PrivateSubnetCIDRs: []string{"mock-cidr-3", "mock-cidr-4"}, - }, - ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, - InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, - }, - Telemetry: &config.Telemetry{ - EnableContainerInsights: false, - }, - }, - wantedTestData: "environment-adjust-vpc.yml", - }, - "fully configured with customized vpc resources including imported private subnets": { - inProps: EnvironmentProps{ - Name: "test", - CustomConfig: &config.CustomizeEnv{ - ImportVPC: &config.ImportVPC{ - ID: "mock-vpc-id", - PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, - }, - ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, - InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, - EnableInternalALBVPCIngress: false, - }, - - Telemetry: &config.Telemetry{ - EnableContainerInsights: false, - }, - }, - wantedTestData: "environment-adjust-vpc-private-subnets.yml", - }, - "fully configured with imported vpc resources": { - inProps: EnvironmentProps{ - Name: "test", - CustomConfig: &config.CustomizeEnv{ - ImportVPC: &config.ImportVPC{ - ID: "mock-vpc-id", - PublicSubnetIDs: []string{"mock-subnet-id-1", "mock-subnet-id-2"}, - PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, - }, - ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, - }, - Telemetry: &config.Telemetry{ - EnableContainerInsights: true, - }, - }, - wantedTestData: "environment-import-vpc.yml", - }, - "basic manifest": { - inProps: EnvironmentProps{ - Name: "test", - }, - wantedTestData: "environment-default.yml", - }, - } - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - // GIVEN - path := filepath.Join("testdata", tc.wantedTestData) - wantedBytes, err := os.ReadFile(path) - require.NoError(t, err) - manifest := NewEnvironment(&tc.inProps) - - // WHEN - tpl, err := manifest.MarshalBinary() - require.NoError(t, err) - - // THEN - require.Equal(t, string(wantedBytes), string(tpl)) - }) - } -} - -func TestPipelineManifest_InitialManifest_Integration(t *testing.T) { - testCases := map[string]struct { - inProvider Provider - inStages []PipelineStage - - wantedTestData string - wantedError error - }{ - "basic pipeline manifest": { - inProvider: &githubProvider{ - properties: &GitHubProperties{ - RepositoryURL: "mock-url", - Branch: "main", - }, - }, - inStages: []PipelineStage{ - { - Name: "test", - }, - { - Name: "prod", - }, - }, - wantedTestData: "pipeline-basic.yml", - }, - "environment pipeline manifest with template configurations": { - inProvider: &githubProvider{ - properties: &GitHubProperties{ - RepositoryURL: "mock-url", - Branch: "main", - }, - }, - inStages: []PipelineStage{ - { - Name: "test", - Deployments: Deployments{ - "deploy-env": &Deployment{ - TemplatePath: "infrastructure/test.env.yml", - TemplateConfig: "infrastructure/test.env.params.json", - StackName: "app-test", - }, - }, - }, - { - Name: "prod", - Deployments: Deployments{ - "deploy-env": &Deployment{ - TemplatePath: "infrastructure/prod.env.yml", - TemplateConfig: "infrastructure/prod.env.params.json", - StackName: "app-prod", - }, - }, - }, - }, - wantedTestData: "pipeline-environment.yml", - }, - } - - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - // GIVEN - ctrl := gomock.NewController(t) - defer ctrl.Finish() - path := filepath.Join("testdata", tc.wantedTestData) - wantedBytes, err := os.ReadFile(path) - require.NoError(t, err) - - manifest, err := NewPipeline("mock-pipeline", tc.inProvider, tc.inStages) - require.NoError(t, err) - - // WHEN - b, err := manifest.MarshalBinary() - - // THEN - require.Equal(t, string(wantedBytes), string(b)) - require.NoError(t, err) - }) - } -} +// func TestBackendSvc_InitialManifestIntegration(t *testing.T) { +// testCases := map[string]struct { +// inProps BackendServiceProps + +// wantedTestdata string +// }{ +// "without healthcheck and port and with private only environments": { +// inProps: BackendServiceProps{ +// WorkloadProps: WorkloadProps{ +// Name: "subscribers", +// Dockerfile: "./subscribers/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{ +// OSFamily: nil, +// Arch: nil, +// }, +// }, +// PrivateOnlyEnvironments: []string{ +// "phonetool", +// }, +// }, +// wantedTestdata: "backend-svc-nohealthcheck-placement.yml", +// }, +// "with custom healthcheck command": { +// inProps: BackendServiceProps{ +// WorkloadProps: WorkloadProps{ +// Name: "subscribers", +// Image: "flask-sample", +// }, +// HealthCheck: ContainerHealthCheck{ +// Command: []string{"CMD-SHELL", "curl -f http://localhost:8080 || exit 1"}, +// Interval: durationp(6 * time.Second), +// Retries: aws.Int(0), +// Timeout: durationp(20 * time.Second), +// StartPeriod: durationp(15 * time.Second), +// }, +// Port: 8080, +// }, +// wantedTestdata: "backend-svc-customhealthcheck.yml", +// }, +// } + +// for name, tc := range testCases { +// t.Run(name, func(t *testing.T) { +// // GIVEN +// path := filepath.Join("testdata", tc.wantedTestdata) +// wantedBytes, err := os.ReadFile(path) +// require.NoError(t, err) +// manifest := NewBackendService(tc.inProps) + +// // WHEN +// tpl, err := manifest.MarshalBinary() +// require.NoError(t, err) + +// // THEN +// require.Equal(t, string(wantedBytes), string(tpl)) +// }) +// } +// } + +// func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { +// testCases := map[string]struct { +// inProps WorkerServiceProps + +// wantedTestdata string +// }{ +// "without subscribe and with private only environments": { +// inProps: WorkerServiceProps{ +// WorkloadProps: WorkloadProps{ +// Name: "testers", +// Dockerfile: "./testers/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{ +// OSFamily: nil, +// Arch: nil, +// }, +// }, +// PrivateOnlyEnvironments: []string{ +// "phonetool", +// }, +// }, +// wantedTestdata: "worker-svc-nosubscribe-placement.yml", +// }, +// "with subscribe": { +// inProps: WorkerServiceProps{ +// WorkloadProps: WorkloadProps{ +// Name: "testers", +// Dockerfile: "./testers/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{ +// OSFamily: nil, +// Arch: nil, +// }, +// }, +// Topics: []TopicSubscription{ +// { +// Name: aws.String("testTopic"), +// Service: aws.String("service4TestTopic"), +// }, +// { +// Name: aws.String("testTopic2"), +// Service: aws.String("service4TestTopic2"), +// }, +// }, +// }, +// wantedTestdata: "worker-svc-subscribe.yml", +// }, +// "with fifo topic subscription with default fifo queue": { +// inProps: WorkerServiceProps{ +// WorkloadProps: WorkloadProps{ +// Name: "testers", +// Dockerfile: "./testers/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{ +// OSFamily: nil, +// Arch: nil, +// }, +// }, +// Topics: []TopicSubscription{ +// { +// Name: aws.String("testTopic.fifo"), +// Service: aws.String("service4TestTopic"), +// }, +// }, +// }, +// wantedTestdata: "worker-svc-with-default-fifo-queue.yml", +// }, +// } + +// for name, tc := range testCases { +// t.Run(name, func(t *testing.T) { +// // GIVEN +// path := filepath.Join("testdata", tc.wantedTestdata) +// wantedBytes, err := os.ReadFile(path) +// require.NoError(t, err) +// manifest := NewWorkerService(tc.inProps) + +// // WHEN +// tpl, err := manifest.MarshalBinary() +// require.NoError(t, err) + +// // THEN +// require.Equal(t, string(wantedBytes), string(tpl)) +// }) +// } +// } + +// func TestScheduledJob_InitialManifestIntegration(t *testing.T) { +// testCases := map[string]struct { +// inProps ScheduledJobProps + +// wantedTestData string +// }{ +// "without timeout or retries": { +// inProps: ScheduledJobProps{ +// WorkloadProps: &WorkloadProps{ +// Name: "cuteness-aggregator", +// Image: "copilot/cuteness-aggregator", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{}, +// }, +// Schedule: "@weekly", +// }, +// wantedTestData: "scheduled-job-no-timeout-or-retries.yml", +// }, +// "fully specified using cron schedule": { +// inProps: ScheduledJobProps{ +// WorkloadProps: &WorkloadProps{ +// Name: "cuteness-aggregator", +// Dockerfile: "./cuteness-aggregator/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{}, +// }, +// Schedule: "0 */2 * * *", +// Retries: 3, +// Timeout: "1h30m", +// }, +// wantedTestData: "scheduled-job-fully-specified.yml", +// }, +// "with timeout and no retries": { +// inProps: ScheduledJobProps{ +// WorkloadProps: &WorkloadProps{ +// Name: "cuteness-aggregator", +// Dockerfile: "./cuteness-aggregator/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{}, +// }, +// Schedule: "@every 5h", +// Retries: 0, +// Timeout: "3h", +// }, +// wantedTestData: "scheduled-job-no-retries.yml", +// }, +// "with retries and no timeout": { +// inProps: ScheduledJobProps{ +// WorkloadProps: &WorkloadProps{ +// Name: "cuteness-aggregator", +// Dockerfile: "./cuteness-aggregator/Dockerfile", +// }, +// Platform: PlatformArgsOrString{ +// PlatformString: nil, +// PlatformArgs: PlatformArgs{}, +// }, +// Schedule: "@every 5h", +// Retries: 5, +// }, +// wantedTestData: "scheduled-job-no-timeout.yml", +// }, +// } + +// for name, tc := range testCases { +// t.Run(name, func(t *testing.T) { +// // GIVEN +// path := filepath.Join("testdata", tc.wantedTestData) +// wantedBytes, err := os.ReadFile(path) +// require.NoError(t, err) +// manifest := NewScheduledJob(&tc.inProps) + +// // WHEN +// tpl, err := manifest.MarshalBinary() +// require.NoError(t, err) + +// // THEN +// require.Equal(t, string(wantedBytes), string(tpl)) +// }) +// } +// } + +// func TestEnvironment_InitialManifestIntegration(t *testing.T) { +// testCases := map[string]struct { +// inProps EnvironmentProps +// wantedTestData string +// }{ +// "fully configured with customized vpc resources": { +// inProps: EnvironmentProps{ +// Name: "test", +// CustomConfig: &config.CustomizeEnv{ +// VPCConfig: &config.AdjustVPC{ +// CIDR: "mock-cidr-0", +// AZs: []string{"mock-az-1", "mock-az-2"}, +// PublicSubnetCIDRs: []string{"mock-cidr-1", "mock-cidr-2"}, +// PrivateSubnetCIDRs: []string{"mock-cidr-3", "mock-cidr-4"}, +// }, +// ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, +// InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, +// }, +// Telemetry: &config.Telemetry{ +// EnableContainerInsights: false, +// }, +// }, +// wantedTestData: "environment-adjust-vpc.yml", +// }, +// "fully configured with customized vpc resources including imported private subnets": { +// inProps: EnvironmentProps{ +// Name: "test", +// CustomConfig: &config.CustomizeEnv{ +// ImportVPC: &config.ImportVPC{ +// ID: "mock-vpc-id", +// PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, +// }, +// ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, +// InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, +// EnableInternalALBVPCIngress: false, +// }, + +// Telemetry: &config.Telemetry{ +// EnableContainerInsights: false, +// }, +// }, +// wantedTestData: "environment-adjust-vpc-private-subnets.yml", +// }, +// "fully configured with imported vpc resources": { +// inProps: EnvironmentProps{ +// Name: "test", +// CustomConfig: &config.CustomizeEnv{ +// ImportVPC: &config.ImportVPC{ +// ID: "mock-vpc-id", +// PublicSubnetIDs: []string{"mock-subnet-id-1", "mock-subnet-id-2"}, +// PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, +// }, +// ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, +// }, +// Telemetry: &config.Telemetry{ +// EnableContainerInsights: true, +// }, +// }, +// wantedTestData: "environment-import-vpc.yml", +// }, +// "basic manifest": { +// inProps: EnvironmentProps{ +// Name: "test", +// }, +// wantedTestData: "environment-default.yml", +// }, +// } +// for name, tc := range testCases { +// t.Run(name, func(t *testing.T) { +// // GIVEN +// path := filepath.Join("testdata", tc.wantedTestData) +// wantedBytes, err := os.ReadFile(path) +// require.NoError(t, err) +// manifest := NewEnvironment(&tc.inProps) + +// // WHEN +// tpl, err := manifest.MarshalBinary() +// require.NoError(t, err) + +// // THEN +// require.Equal(t, string(wantedBytes), string(tpl)) +// }) +// } +// } + +// func TestPipelineManifest_InitialManifest_Integration(t *testing.T) { +// testCases := map[string]struct { +// inProvider Provider +// inStages []PipelineStage + +// wantedTestData string +// wantedError error +// }{ +// "basic pipeline manifest": { +// inProvider: &githubProvider{ +// properties: &GitHubProperties{ +// RepositoryURL: "mock-url", +// Branch: "main", +// }, +// }, +// inStages: []PipelineStage{ +// { +// Name: "test", +// }, +// { +// Name: "prod", +// }, +// }, +// wantedTestData: "pipeline-basic.yml", +// }, +// "environment pipeline manifest with template configurations": { +// inProvider: &githubProvider{ +// properties: &GitHubProperties{ +// RepositoryURL: "mock-url", +// Branch: "main", +// }, +// }, +// inStages: []PipelineStage{ +// { +// Name: "test", +// Deployments: Deployments{ +// "deploy-env": &Deployment{ +// TemplatePath: "infrastructure/test.env.yml", +// TemplateConfig: "infrastructure/test.env.params.json", +// StackName: "app-test", +// }, +// }, +// }, +// { +// Name: "prod", +// Deployments: Deployments{ +// "deploy-env": &Deployment{ +// TemplatePath: "infrastructure/prod.env.yml", +// TemplateConfig: "infrastructure/prod.env.params.json", +// StackName: "app-prod", +// }, +// }, +// }, +// }, +// wantedTestData: "pipeline-environment.yml", +// }, +// } + +// for name, tc := range testCases { +// t.Run(name, func(t *testing.T) { +// // GIVEN +// ctrl := gomock.NewController(t) +// defer ctrl.Finish() +// path := filepath.Join("testdata", tc.wantedTestData) +// wantedBytes, err := os.ReadFile(path) +// require.NoError(t, err) + +// manifest, err := NewPipeline("mock-pipeline", tc.inProvider, tc.inStages) +// require.NoError(t, err) + +// // WHEN +// b, err := manifest.MarshalBinary() + +// // THEN +// require.Equal(t, string(wantedBytes), string(b)) +// require.NoError(t, err) +// }) +// } +// } diff --git a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml index 646b2612e4c..9364b36dcaf 100644 --- a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml +++ b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml @@ -46,7 +46,23 @@ platform: {{.Platform.PlatformString}} # See https://aws.github.io/copilot-cli #secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. +{{- if not .Environments}} + +# You can override any of the values defined above by environment. +#environments: +# prod: +# cpu: 2048 # Larger CPU value for prod environment. +{{- else}} + +# Set placement to private for environments containing only private subnets. +environments: {{ range $key, $value := .Environments}} + {{$key}}: + network: + vpc: + placement: '{{$value.Network.VPC.Placement.PlacementString}}' +{{- end}} # You can override any of the values defined above by environment. #environments: # prod: -# cpu: 2048 # Larger CPU value for prod environment +# cpu: 2048 # Larger CPU value for prod environment. +{{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/backend/manifest.yml b/internal/pkg/template/templates/workloads/services/backend/manifest.yml index 51d45a578af..21b20d4e7db 100644 --- a/internal/pkg/template/templates/workloads/services/backend/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/backend/manifest.yml @@ -68,13 +68,14 @@ exec: true # Enable running commands in your container. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- else}} -# You can override any of the values defined above by environment. +# Set placement to private for environments containing only private subnets. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. -{{- end}} +{{- end}} +# You can override any of the values defined above by environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml index 67749603337..4252cdc54c1 100644 --- a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml @@ -63,13 +63,14 @@ exec: true # Enable running commands in your container. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- else}} -# You can override any of the values defined above by environment. +# Placement is set to private for environments containing only private subnets. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' {{- end}} +# You can override any of the values defined above by environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml index f5966d490ec..d8b1d186f8a 100644 --- a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml @@ -75,12 +75,12 @@ network: # LOG_LEVEL: debug # Log level for the "test" environment. {{- else}} -# You can override any of the values defined above by environment. +# Set placement to private for environments containing only private subnets. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' {{- end}} # You can override any of the values defined above by environment. # environments: diff --git a/internal/pkg/template/templates/workloads/services/worker/manifest.yml b/internal/pkg/template/templates/workloads/services/worker/manifest.yml index 37bf668f809..5a2d1ce2b0f 100644 --- a/internal/pkg/template/templates/workloads/services/worker/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/worker/manifest.yml @@ -83,13 +83,14 @@ subscribe: # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- else}} -# You can override any of the values defined above by environment. +# Set placement to private for environments containing only private subnets. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. -{{- end}} +{{- end}} +# You can override any of the values defined above by environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. From aaaf725384311481ba9d25dc922b3f0b364b5257 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 22 Nov 2022 22:29:45 -0800 Subject: [PATCH 05/12] add manifest integration tests --- internal/pkg/cli/job_init.go | 4 +- internal/pkg/cli/job_init_test.go | 2 +- internal/pkg/cli/svc_init.go | 4 +- internal/pkg/cli/svc_init_test.go | 7 +- .../marshal_manifest_integration_test.go | 859 +++++++++--------- .../backend-svc-nohealthcheck-placement.yml | 2 +- ...ement.yml => lb-svc-placement-private.yml} | 4 +- internal/pkg/manifest/testdata/placements.yml | 49 - .../testdata/rdws-placement-private.yml | 56 ++ ...heduled-job-fully-specified-placement.yml} | 10 +- .../testdata/scheduled-job-no-retries.yml | 2 +- .../scheduled-job-no-timeout-or-retries.yml | 2 +- .../testdata/scheduled-job-no-timeout.yml | 2 +- .../worker-svc-nosubscribe-placement.yml | 2 +- internal/pkg/manifest/worker_svc.go | 6 +- .../workloads/jobs/scheduled-job/manifest.yml | 12 +- .../workloads/services/backend/manifest.yml | 5 +- .../workloads/services/lb-web/manifest.yml | 7 +- .../workloads/services/rd-web/manifest.yml | 8 +- .../workloads/services/worker/manifest.yml | 5 +- 20 files changed, 547 insertions(+), 501 deletions(-) rename internal/pkg/manifest/testdata/{lb-svc-placement.yml => lb-svc-placement-private.yml} (93%) delete mode 100644 internal/pkg/manifest/testdata/placements.yml create mode 100644 internal/pkg/manifest/testdata/rdws-placement-private.yml rename internal/pkg/manifest/testdata/{scheduled-job-fully-specified.yml => scheduled-job-fully-specified-placement.yml} (86%) diff --git a/internal/pkg/cli/job_init.go b/internal/pkg/cli/job_init.go index 23ae099e1ab..365625fb387 100644 --- a/internal/pkg/cli/job_init.go +++ b/internal/pkg/cli/job_init.go @@ -225,7 +225,7 @@ func (o *initJobOpts) Ask() error { return nil } -// isSubnetsOnlyPrivate returns the list of environment names deployed on private only subnets. +// isSubnetsOnlyPrivate returns the list of environments names deployed that contains only private subnets. func (o *initJobOpts) isSubnetsOnlyPrivate() ([]string, error) { envs, err := o.store.ListEnvironments(o.appName) if err != nil { @@ -275,7 +275,7 @@ func (o *initJobOpts) Execute() error { o.platform = &platform } } - // Environments that are deployed to the VPC has only private subnets. + // Environments that are deployed have​ only private subnets. envs, err := o.isSubnetsOnlyPrivate() if err != nil { return err diff --git a/internal/pkg/cli/job_init_test.go b/internal/pkg/cli/job_init_test.go index b79ee300a7e..f5e395dd8fc 100644 --- a/internal/pkg/cli/job_init_test.go +++ b/internal/pkg/cli/job_init_test.go @@ -762,7 +762,7 @@ type: Environment`), nil) }, wantedErr: errors.New("get docker engine platform: some error"), }, - "environments with only private subnets": { + "initialize a job in environments with only private subnets": { inApp: "sample", inName: "mailer", inType: manifest.ScheduledJobType, diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 6142f84287a..814a287d00c 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -337,7 +337,7 @@ func (o *initSvcOpts) Execute() error { o.platform = &platform } } - // Environments that are deployed to the VPC has only private subnets. + // Environments that are deployed have​ only private subnets. envs, err := o.isSubnetsOnlyPrivate() if err != nil { return err @@ -731,7 +731,7 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error }, nil } -// isSubnetsOnlyPrivate returns the list of environment names deployed on private only subnets. +// isSubnetsOnlyPrivate returns the list of environments names deployed that contains only private subnets. func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { envs, err := o.store.ListEnvironments(o.appName) if err != nil { diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index 3e09ed1beb1..6d6d31c9453 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -1175,20 +1175,17 @@ type: Environment`), nil) }, wantedErr: errors.New("redirect docker engine platform: Windows is not supported for App Runner services"), }, - "failure if appname is not provided": { + "failure": { mockDockerEngine: func(m *mocks.MockdockerEngine) { m.EXPECT().CheckDockerEngineRunning().Return(nil) m.EXPECT().GetPlatform().Return("linux", "amd64", nil) }, - // mockSvcInit: func(m *mocks.MocksvcInitializer) { - // m.EXPECT().Service(gomock.Any()).Return("", errors.New("some error")) - // }, mockStore: func(m *mocks.Mockstore) { m.EXPECT().ListEnvironments("").Return(nil, errors.New("some error")) }, wantedErr: errors.New("some error"), }, - "environments with only private subnets": { + "initalize a service in environments with only private subnets": { inAppName: "sample", inSvcName: "frontend", inDockerfilePath: "./Dockerfile", diff --git a/internal/pkg/manifest/marshal_manifest_integration_test.go b/internal/pkg/manifest/marshal_manifest_integration_test.go index 0d7a7f1c32b..7666bfe7ecd 100644 --- a/internal/pkg/manifest/marshal_manifest_integration_test.go +++ b/internal/pkg/manifest/marshal_manifest_integration_test.go @@ -50,7 +50,7 @@ func TestLoadBalancedWebService_InitialManifestIntegration(t *testing.T) { "phonetool", }, }, - wantedTestdata: "lb-svc-placement.yml", + wantedTestdata: "lb-svc-placement-private.yml", }, } @@ -72,409 +72,454 @@ func TestLoadBalancedWebService_InitialManifestIntegration(t *testing.T) { } } -// func TestBackendSvc_InitialManifestIntegration(t *testing.T) { -// testCases := map[string]struct { -// inProps BackendServiceProps - -// wantedTestdata string -// }{ -// "without healthcheck and port and with private only environments": { -// inProps: BackendServiceProps{ -// WorkloadProps: WorkloadProps{ -// Name: "subscribers", -// Dockerfile: "./subscribers/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{ -// OSFamily: nil, -// Arch: nil, -// }, -// }, -// PrivateOnlyEnvironments: []string{ -// "phonetool", -// }, -// }, -// wantedTestdata: "backend-svc-nohealthcheck-placement.yml", -// }, -// "with custom healthcheck command": { -// inProps: BackendServiceProps{ -// WorkloadProps: WorkloadProps{ -// Name: "subscribers", -// Image: "flask-sample", -// }, -// HealthCheck: ContainerHealthCheck{ -// Command: []string{"CMD-SHELL", "curl -f http://localhost:8080 || exit 1"}, -// Interval: durationp(6 * time.Second), -// Retries: aws.Int(0), -// Timeout: durationp(20 * time.Second), -// StartPeriod: durationp(15 * time.Second), -// }, -// Port: 8080, -// }, -// wantedTestdata: "backend-svc-customhealthcheck.yml", -// }, -// } - -// for name, tc := range testCases { -// t.Run(name, func(t *testing.T) { -// // GIVEN -// path := filepath.Join("testdata", tc.wantedTestdata) -// wantedBytes, err := os.ReadFile(path) -// require.NoError(t, err) -// manifest := NewBackendService(tc.inProps) - -// // WHEN -// tpl, err := manifest.MarshalBinary() -// require.NoError(t, err) - -// // THEN -// require.Equal(t, string(wantedBytes), string(tpl)) -// }) -// } -// } - -// func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { -// testCases := map[string]struct { -// inProps WorkerServiceProps - -// wantedTestdata string -// }{ -// "without subscribe and with private only environments": { -// inProps: WorkerServiceProps{ -// WorkloadProps: WorkloadProps{ -// Name: "testers", -// Dockerfile: "./testers/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{ -// OSFamily: nil, -// Arch: nil, -// }, -// }, -// PrivateOnlyEnvironments: []string{ -// "phonetool", -// }, -// }, -// wantedTestdata: "worker-svc-nosubscribe-placement.yml", -// }, -// "with subscribe": { -// inProps: WorkerServiceProps{ -// WorkloadProps: WorkloadProps{ -// Name: "testers", -// Dockerfile: "./testers/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{ -// OSFamily: nil, -// Arch: nil, -// }, -// }, -// Topics: []TopicSubscription{ -// { -// Name: aws.String("testTopic"), -// Service: aws.String("service4TestTopic"), -// }, -// { -// Name: aws.String("testTopic2"), -// Service: aws.String("service4TestTopic2"), -// }, -// }, -// }, -// wantedTestdata: "worker-svc-subscribe.yml", -// }, -// "with fifo topic subscription with default fifo queue": { -// inProps: WorkerServiceProps{ -// WorkloadProps: WorkloadProps{ -// Name: "testers", -// Dockerfile: "./testers/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{ -// OSFamily: nil, -// Arch: nil, -// }, -// }, -// Topics: []TopicSubscription{ -// { -// Name: aws.String("testTopic.fifo"), -// Service: aws.String("service4TestTopic"), -// }, -// }, -// }, -// wantedTestdata: "worker-svc-with-default-fifo-queue.yml", -// }, -// } - -// for name, tc := range testCases { -// t.Run(name, func(t *testing.T) { -// // GIVEN -// path := filepath.Join("testdata", tc.wantedTestdata) -// wantedBytes, err := os.ReadFile(path) -// require.NoError(t, err) -// manifest := NewWorkerService(tc.inProps) - -// // WHEN -// tpl, err := manifest.MarshalBinary() -// require.NoError(t, err) - -// // THEN -// require.Equal(t, string(wantedBytes), string(tpl)) -// }) -// } -// } - -// func TestScheduledJob_InitialManifestIntegration(t *testing.T) { -// testCases := map[string]struct { -// inProps ScheduledJobProps - -// wantedTestData string -// }{ -// "without timeout or retries": { -// inProps: ScheduledJobProps{ -// WorkloadProps: &WorkloadProps{ -// Name: "cuteness-aggregator", -// Image: "copilot/cuteness-aggregator", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{}, -// }, -// Schedule: "@weekly", -// }, -// wantedTestData: "scheduled-job-no-timeout-or-retries.yml", -// }, -// "fully specified using cron schedule": { -// inProps: ScheduledJobProps{ -// WorkloadProps: &WorkloadProps{ -// Name: "cuteness-aggregator", -// Dockerfile: "./cuteness-aggregator/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{}, -// }, -// Schedule: "0 */2 * * *", -// Retries: 3, -// Timeout: "1h30m", -// }, -// wantedTestData: "scheduled-job-fully-specified.yml", -// }, -// "with timeout and no retries": { -// inProps: ScheduledJobProps{ -// WorkloadProps: &WorkloadProps{ -// Name: "cuteness-aggregator", -// Dockerfile: "./cuteness-aggregator/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{}, -// }, -// Schedule: "@every 5h", -// Retries: 0, -// Timeout: "3h", -// }, -// wantedTestData: "scheduled-job-no-retries.yml", -// }, -// "with retries and no timeout": { -// inProps: ScheduledJobProps{ -// WorkloadProps: &WorkloadProps{ -// Name: "cuteness-aggregator", -// Dockerfile: "./cuteness-aggregator/Dockerfile", -// }, -// Platform: PlatformArgsOrString{ -// PlatformString: nil, -// PlatformArgs: PlatformArgs{}, -// }, -// Schedule: "@every 5h", -// Retries: 5, -// }, -// wantedTestData: "scheduled-job-no-timeout.yml", -// }, -// } - -// for name, tc := range testCases { -// t.Run(name, func(t *testing.T) { -// // GIVEN -// path := filepath.Join("testdata", tc.wantedTestData) -// wantedBytes, err := os.ReadFile(path) -// require.NoError(t, err) -// manifest := NewScheduledJob(&tc.inProps) - -// // WHEN -// tpl, err := manifest.MarshalBinary() -// require.NoError(t, err) - -// // THEN -// require.Equal(t, string(wantedBytes), string(tpl)) -// }) -// } -// } - -// func TestEnvironment_InitialManifestIntegration(t *testing.T) { -// testCases := map[string]struct { -// inProps EnvironmentProps -// wantedTestData string -// }{ -// "fully configured with customized vpc resources": { -// inProps: EnvironmentProps{ -// Name: "test", -// CustomConfig: &config.CustomizeEnv{ -// VPCConfig: &config.AdjustVPC{ -// CIDR: "mock-cidr-0", -// AZs: []string{"mock-az-1", "mock-az-2"}, -// PublicSubnetCIDRs: []string{"mock-cidr-1", "mock-cidr-2"}, -// PrivateSubnetCIDRs: []string{"mock-cidr-3", "mock-cidr-4"}, -// }, -// ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, -// InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, -// }, -// Telemetry: &config.Telemetry{ -// EnableContainerInsights: false, -// }, -// }, -// wantedTestData: "environment-adjust-vpc.yml", -// }, -// "fully configured with customized vpc resources including imported private subnets": { -// inProps: EnvironmentProps{ -// Name: "test", -// CustomConfig: &config.CustomizeEnv{ -// ImportVPC: &config.ImportVPC{ -// ID: "mock-vpc-id", -// PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, -// }, -// ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, -// InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, -// EnableInternalALBVPCIngress: false, -// }, - -// Telemetry: &config.Telemetry{ -// EnableContainerInsights: false, -// }, -// }, -// wantedTestData: "environment-adjust-vpc-private-subnets.yml", -// }, -// "fully configured with imported vpc resources": { -// inProps: EnvironmentProps{ -// Name: "test", -// CustomConfig: &config.CustomizeEnv{ -// ImportVPC: &config.ImportVPC{ -// ID: "mock-vpc-id", -// PublicSubnetIDs: []string{"mock-subnet-id-1", "mock-subnet-id-2"}, -// PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, -// }, -// ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, -// }, -// Telemetry: &config.Telemetry{ -// EnableContainerInsights: true, -// }, -// }, -// wantedTestData: "environment-import-vpc.yml", -// }, -// "basic manifest": { -// inProps: EnvironmentProps{ -// Name: "test", -// }, -// wantedTestData: "environment-default.yml", -// }, -// } -// for name, tc := range testCases { -// t.Run(name, func(t *testing.T) { -// // GIVEN -// path := filepath.Join("testdata", tc.wantedTestData) -// wantedBytes, err := os.ReadFile(path) -// require.NoError(t, err) -// manifest := NewEnvironment(&tc.inProps) - -// // WHEN -// tpl, err := manifest.MarshalBinary() -// require.NoError(t, err) - -// // THEN -// require.Equal(t, string(wantedBytes), string(tpl)) -// }) -// } -// } - -// func TestPipelineManifest_InitialManifest_Integration(t *testing.T) { -// testCases := map[string]struct { -// inProvider Provider -// inStages []PipelineStage - -// wantedTestData string -// wantedError error -// }{ -// "basic pipeline manifest": { -// inProvider: &githubProvider{ -// properties: &GitHubProperties{ -// RepositoryURL: "mock-url", -// Branch: "main", -// }, -// }, -// inStages: []PipelineStage{ -// { -// Name: "test", -// }, -// { -// Name: "prod", -// }, -// }, -// wantedTestData: "pipeline-basic.yml", -// }, -// "environment pipeline manifest with template configurations": { -// inProvider: &githubProvider{ -// properties: &GitHubProperties{ -// RepositoryURL: "mock-url", -// Branch: "main", -// }, -// }, -// inStages: []PipelineStage{ -// { -// Name: "test", -// Deployments: Deployments{ -// "deploy-env": &Deployment{ -// TemplatePath: "infrastructure/test.env.yml", -// TemplateConfig: "infrastructure/test.env.params.json", -// StackName: "app-test", -// }, -// }, -// }, -// { -// Name: "prod", -// Deployments: Deployments{ -// "deploy-env": &Deployment{ -// TemplatePath: "infrastructure/prod.env.yml", -// TemplateConfig: "infrastructure/prod.env.params.json", -// StackName: "app-prod", -// }, -// }, -// }, -// }, -// wantedTestData: "pipeline-environment.yml", -// }, -// } - -// for name, tc := range testCases { -// t.Run(name, func(t *testing.T) { -// // GIVEN -// ctrl := gomock.NewController(t) -// defer ctrl.Finish() -// path := filepath.Join("testdata", tc.wantedTestData) -// wantedBytes, err := os.ReadFile(path) -// require.NoError(t, err) - -// manifest, err := NewPipeline("mock-pipeline", tc.inProvider, tc.inStages) -// require.NoError(t, err) - -// // WHEN -// b, err := manifest.MarshalBinary() - -// // THEN -// require.Equal(t, string(wantedBytes), string(b)) -// require.NoError(t, err) -// }) -// } -// } +func TestBackendSvc_InitialManifestIntegration(t *testing.T) { + testCases := map[string]struct { + inProps BackendServiceProps + + wantedTestdata string + }{ + "without healthcheck and port and with private only environments": { + inProps: BackendServiceProps{ + WorkloadProps: WorkloadProps{ + Name: "subscribers", + Dockerfile: "./subscribers/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{ + OSFamily: nil, + Arch: nil, + }, + }, + PrivateOnlyEnvironments: []string{ + "phonetool", + }, + }, + wantedTestdata: "backend-svc-nohealthcheck-placement.yml", + }, + "with custom healthcheck command": { + inProps: BackendServiceProps{ + WorkloadProps: WorkloadProps{ + Name: "subscribers", + Image: "flask-sample", + }, + HealthCheck: ContainerHealthCheck{ + Command: []string{"CMD-SHELL", "curl -f http://localhost:8080 || exit 1"}, + Interval: durationp(6 * time.Second), + Retries: aws.Int(0), + Timeout: durationp(20 * time.Second), + StartPeriod: durationp(15 * time.Second), + }, + Port: 8080, + }, + wantedTestdata: "backend-svc-customhealthcheck.yml", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // GIVEN + path := filepath.Join("testdata", tc.wantedTestdata) + wantedBytes, err := os.ReadFile(path) + require.NoError(t, err) + manifest := NewBackendService(tc.inProps) + + // WHEN + tpl, err := manifest.MarshalBinary() + require.NoError(t, err) + + // THEN + require.Equal(t, string(wantedBytes), string(tpl)) + }) + } +} + +func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { + testCases := map[string]struct { + inProps WorkerServiceProps + + wantedTestdata string + }{ + "without subscribe and with private only environments": { + inProps: WorkerServiceProps{ + WorkloadProps: WorkloadProps{ + Name: "testers", + Dockerfile: "./testers/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{ + OSFamily: nil, + Arch: nil, + }, + }, + PrivateOnlyEnvironments: []string{ + "phonetool", + }, + }, + wantedTestdata: "worker-svc-nosubscribe-placement.yml", + }, + "with subscribe": { + inProps: WorkerServiceProps{ + WorkloadProps: WorkloadProps{ + Name: "testers", + Dockerfile: "./testers/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{ + OSFamily: nil, + Arch: nil, + }, + }, + Topics: []TopicSubscription{ + { + Name: aws.String("testTopic"), + Service: aws.String("service4TestTopic"), + }, + { + Name: aws.String("testTopic2"), + Service: aws.String("service4TestTopic2"), + }, + }, + }, + wantedTestdata: "worker-svc-subscribe.yml", + }, + "with fifo topic subscription with default fifo queue": { + inProps: WorkerServiceProps{ + WorkloadProps: WorkloadProps{ + Name: "testers", + Dockerfile: "./testers/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{ + OSFamily: nil, + Arch: nil, + }, + }, + Topics: []TopicSubscription{ + { + Name: aws.String("testTopic.fifo"), + Service: aws.String("service4TestTopic"), + }, + }, + }, + wantedTestdata: "worker-svc-with-default-fifo-queue.yml", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // GIVEN + path := filepath.Join("testdata", tc.wantedTestdata) + wantedBytes, err := os.ReadFile(path) + require.NoError(t, err) + manifest := NewWorkerService(tc.inProps) + + // WHEN + tpl, err := manifest.MarshalBinary() + require.NoError(t, err) + + // THEN + require.Equal(t, string(wantedBytes), string(tpl)) + }) + } +} + +func TestScheduledJob_InitialManifestIntegration(t *testing.T) { + testCases := map[string]struct { + inProps ScheduledJobProps + + wantedTestData string + }{ + "without timeout or retries": { + inProps: ScheduledJobProps{ + WorkloadProps: &WorkloadProps{ + Name: "cuteness-aggregator", + Image: "copilot/cuteness-aggregator", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{}, + }, + Schedule: "@weekly", + }, + wantedTestData: "scheduled-job-no-timeout-or-retries.yml", + }, + "fully specified using cron schedule with placement set to private": { + inProps: ScheduledJobProps{ + WorkloadProps: &WorkloadProps{ + Name: "cuteness-aggregator", + Dockerfile: "./cuteness-aggregator/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{}, + }, + Schedule: "0 */2 * * *", + Retries: 3, + Timeout: "1h30m", + PrivateOnlyEnvironments: []string{ + "phonetool", + }, + }, + wantedTestData: "scheduled-job-fully-specified-placement.yml", + }, + "with timeout and no retries": { + inProps: ScheduledJobProps{ + WorkloadProps: &WorkloadProps{ + Name: "cuteness-aggregator", + Dockerfile: "./cuteness-aggregator/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{}, + }, + Schedule: "@every 5h", + Retries: 0, + Timeout: "3h", + }, + wantedTestData: "scheduled-job-no-retries.yml", + }, + "with retries and no timeout": { + inProps: ScheduledJobProps{ + WorkloadProps: &WorkloadProps{ + Name: "cuteness-aggregator", + Dockerfile: "./cuteness-aggregator/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{}, + }, + Schedule: "@every 5h", + Retries: 5, + }, + wantedTestData: "scheduled-job-no-timeout.yml", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // GIVEN + path := filepath.Join("testdata", tc.wantedTestData) + wantedBytes, err := os.ReadFile(path) + require.NoError(t, err) + manifest := NewScheduledJob(&tc.inProps) + + // WHEN + tpl, err := manifest.MarshalBinary() + require.NoError(t, err) + + // THEN + require.Equal(t, string(wantedBytes), string(tpl)) + }) + } +} + +func TestEnvironment_InitialManifestIntegration(t *testing.T) { + testCases := map[string]struct { + inProps EnvironmentProps + wantedTestData string + }{ + "fully configured with customized vpc resources": { + inProps: EnvironmentProps{ + Name: "test", + CustomConfig: &config.CustomizeEnv{ + VPCConfig: &config.AdjustVPC{ + CIDR: "mock-cidr-0", + AZs: []string{"mock-az-1", "mock-az-2"}, + PublicSubnetCIDRs: []string{"mock-cidr-1", "mock-cidr-2"}, + PrivateSubnetCIDRs: []string{"mock-cidr-3", "mock-cidr-4"}, + }, + ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, + InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, + }, + Telemetry: &config.Telemetry{ + EnableContainerInsights: false, + }, + }, + wantedTestData: "environment-adjust-vpc.yml", + }, + "fully configured with customized vpc resources including imported private subnets": { + inProps: EnvironmentProps{ + Name: "test", + CustomConfig: &config.CustomizeEnv{ + ImportVPC: &config.ImportVPC{ + ID: "mock-vpc-id", + PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, + }, + ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, + InternalALBSubnets: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, + EnableInternalALBVPCIngress: false, + }, + + Telemetry: &config.Telemetry{ + EnableContainerInsights: false, + }, + }, + wantedTestData: "environment-adjust-vpc-private-subnets.yml", + }, + "fully configured with imported vpc resources": { + inProps: EnvironmentProps{ + Name: "test", + CustomConfig: &config.CustomizeEnv{ + ImportVPC: &config.ImportVPC{ + ID: "mock-vpc-id", + PublicSubnetIDs: []string{"mock-subnet-id-1", "mock-subnet-id-2"}, + PrivateSubnetIDs: []string{"mock-subnet-id-3", "mock-subnet-id-4"}, + }, + ImportCertARNs: []string{"mock-cert-1", "mock-cert-2"}, + }, + Telemetry: &config.Telemetry{ + EnableContainerInsights: true, + }, + }, + wantedTestData: "environment-import-vpc.yml", + }, + "basic manifest": { + inProps: EnvironmentProps{ + Name: "test", + }, + wantedTestData: "environment-default.yml", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // GIVEN + path := filepath.Join("testdata", tc.wantedTestData) + wantedBytes, err := os.ReadFile(path) + require.NoError(t, err) + manifest := NewEnvironment(&tc.inProps) + + // WHEN + tpl, err := manifest.MarshalBinary() + require.NoError(t, err) + + // THEN + require.Equal(t, string(wantedBytes), string(tpl)) + }) + } +} + +func TestPipelineManifest_InitialManifest_Integration(t *testing.T) { + testCases := map[string]struct { + inProvider Provider + inStages []PipelineStage + + wantedTestData string + wantedError error + }{ + "basic pipeline manifest": { + inProvider: &githubProvider{ + properties: &GitHubProperties{ + RepositoryURL: "mock-url", + Branch: "main", + }, + }, + inStages: []PipelineStage{ + { + Name: "test", + }, + { + Name: "prod", + }, + }, + wantedTestData: "pipeline-basic.yml", + }, + "environment pipeline manifest with template configurations": { + inProvider: &githubProvider{ + properties: &GitHubProperties{ + RepositoryURL: "mock-url", + Branch: "main", + }, + }, + inStages: []PipelineStage{ + { + Name: "test", + Deployments: Deployments{ + "deploy-env": &Deployment{ + TemplatePath: "infrastructure/test.env.yml", + TemplateConfig: "infrastructure/test.env.params.json", + StackName: "app-test", + }, + }, + }, + { + Name: "prod", + Deployments: Deployments{ + "deploy-env": &Deployment{ + TemplatePath: "infrastructure/prod.env.yml", + TemplateConfig: "infrastructure/prod.env.params.json", + StackName: "app-prod", + }, + }, + }, + }, + wantedTestData: "pipeline-environment.yml", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // GIVEN + ctrl := gomock.NewController(t) + defer ctrl.Finish() + path := filepath.Join("testdata", tc.wantedTestData) + wantedBytes, err := os.ReadFile(path) + require.NoError(t, err) + + manifest, err := NewPipeline("mock-pipeline", tc.inProvider, tc.inStages) + require.NoError(t, err) + + // WHEN + b, err := manifest.MarshalBinary() + + // THEN + require.Equal(t, string(wantedBytes), string(b)) + require.NoError(t, err) + }) + } +} + +func TestRequestDrivenSvc_InitialManifestIntegration(t *testing.T) { + testCases := map[string]struct { + inProps RequestDrivenWebServiceProps + + wantedTestdata string + }{ + "with placement private": { + inProps: RequestDrivenWebServiceProps{ + WorkloadProps: &WorkloadProps{ + Name: "testers", + Dockerfile: "./testers/Dockerfile", + }, + Platform: PlatformArgsOrString{ + PlatformString: nil, + PlatformArgs: PlatformArgs{}, + }, + Port: 8000, + PrivateOnlyEnvironments: []string{ + "phonetool", + }, + }, + wantedTestdata: "rdws-placement-private.yml", + }, + } + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + // GIVEN + path := filepath.Join("testdata", tc.wantedTestdata) + wantedBytes, err := os.ReadFile(path) + require.NoError(t, err) + manifest := NewRequestDrivenWebService(&tc.inProps) + + // WHEN + tpl, err := manifest.MarshalBinary() + require.NoError(t, err) + + // THEN + require.Equal(t, string(wantedBytes), string(tpl)) + }) + } +} diff --git a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml index b900e2d8ebf..f627dd85e87 100644 --- a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml +++ b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml @@ -34,7 +34,7 @@ environments: phonetool: network: vpc: - placement: 'private' # Set placement to private for environments containing only on private subnets. + placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/manifest/testdata/lb-svc-placement.yml b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml similarity index 93% rename from internal/pkg/manifest/testdata/lb-svc-placement.yml rename to internal/pkg/manifest/testdata/lb-svc-placement-private.yml index 55b45a651e1..f06dce59ecf 100644 --- a/internal/pkg/manifest/testdata/lb-svc-placement.yml +++ b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml @@ -38,11 +38,11 @@ exec: true # Enable running commands in your container. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. # You can override any of the values defined above by environment. -environments: +environments: phonetool: network: vpc: - placement: 'private' # Set placement to private for environments containing only on private subnets. + placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/manifest/testdata/placements.yml b/internal/pkg/manifest/testdata/placements.yml deleted file mode 100644 index 55b45a651e1..00000000000 --- a/internal/pkg/manifest/testdata/placements.yml +++ /dev/null @@ -1,49 +0,0 @@ -# The manifest for the "frontend" service. -# Read the full specification for the "Load Balanced Web Service" type at: -# https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/ - -# Your service name will be used in naming your resources like log groups, ECS services, etc. -name: frontend -type: Load Balanced Web Service - -# Distribute traffic to your service. -http: - # Requests to this path will be forwarded to your service. - # To match all requests you can use the "/" path. - path: '' - # You can specify a custom health check path. The default is "/". - # healthcheck: '/' - -# Configuration for your containers and service. -image: - # Docker build arguments. For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/lb-web-service/#image-build - build: ./frontend/Dockerfile - # Port exposed through your container to route traffic to it. - port: 0 - -cpu: 256 # Number of CPU units for the task. -memory: 512 # Amount of memory in MiB used by the task. -count: 1 # Number of tasks that should be running in your service. -exec: true # Enable running commands in your container. - -# storage: - # readonly_fs: true # Limit to read-only access to mounted root filesystems. - -# Optional fields for more advanced use-cases. -# -#variables: # Pass environment variables as key value pairs. -# LOG_LEVEL: info - -#secrets: # Pass secrets from AWS Systems Manager (SSM) Parameter Store. -# GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. - -# You can override any of the values defined above by environment. -environments: - phonetool: - network: - vpc: - placement: 'private' # Set placement to private for environments containing only on private subnets. -# test: -# count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/rdws-placement-private.yml b/internal/pkg/manifest/testdata/rdws-placement-private.yml new file mode 100644 index 00000000000..778ad365bfb --- /dev/null +++ b/internal/pkg/manifest/testdata/rdws-placement-private.yml @@ -0,0 +1,56 @@ +# The manifest for the "testers" service. +# Read the full specification for the "Request-Driven Web Service" type at: +# https://aws.github.io/copilot-cli/docs/manifest/rd-web-service/ + +# Your service name will be used in naming your resources like log groups, App Runner services, etc. +name: testers +# The "architecture" of the service you're running. +type: Request-Driven Web Service + +image: + # Docker build arguments. + # For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/rd-web-service/#image-build + build: ./testers/Dockerfile + # Port exposed through your container to route traffic to it. + port: 8000 + +# http: +# healthcheck: +# path: / +# healthy_threshold: 3 +# unhealthy_threshold: 5 +# interval: 10s +# timeout: 5s + +# Number of CPU units for the task. +cpu: 1024 +# Amount of memory in MiB used by the task. +memory: 2048 + +# Connect your App Runner service to your environment's VPC. +# network: +# vpc: +# placement: private + + +# Enable tracing for the service. +# observability: +# tracing: awsxray + +# Optional fields for more advanced use-cases. +# +# variables: # Pass environment variables as key value pairs. +# LOG_LEVEL: info +# +# tags: # Pass tags as key value pairs. +# project: project-name + +# You can override any of the values defined above by environment. +environments: + phonetool: + network: + vpc: + placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. +# test: +# variables: +# LOG_LEVEL: debug # Log level for the "test" environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/scheduled-job-fully-specified.yml b/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml similarity index 86% rename from internal/pkg/manifest/testdata/scheduled-job-fully-specified.yml rename to internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml index 22142a7566b..a9ef46b7f30 100644 --- a/internal/pkg/manifest/testdata/scheduled-job-fully-specified.yml +++ b/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml @@ -31,6 +31,10 @@ memory: 512 # Amount of memory in MiB used by the task. # GITHUB_TOKEN: GITHUB_TOKEN # The key is the name of the environment variable, the value is the name of the SSM parameter. # You can override any of the values defined above by environment. -#environments: -# prod: -# cpu: 2048 # Larger CPU value for prod environment +environments: + phonetool: + network: + vpc: + placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. +# prod: +# cpu: 2048 # Larger CPU value for prod environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/scheduled-job-no-retries.yml b/internal/pkg/manifest/testdata/scheduled-job-no-retries.yml index f1a4f43a00e..76324e7bb78 100644 --- a/internal/pkg/manifest/testdata/scheduled-job-no-retries.yml +++ b/internal/pkg/manifest/testdata/scheduled-job-no-retries.yml @@ -33,4 +33,4 @@ memory: 512 # Amount of memory in MiB used by the task. # You can override any of the values defined above by environment. #environments: # prod: -# cpu: 2048 # Larger CPU value for prod environment +# cpu: 2048 # Larger CPU value for prod environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/scheduled-job-no-timeout-or-retries.yml b/internal/pkg/manifest/testdata/scheduled-job-no-timeout-or-retries.yml index 4075da2a11f..bcfa4aabf26 100644 --- a/internal/pkg/manifest/testdata/scheduled-job-no-timeout-or-retries.yml +++ b/internal/pkg/manifest/testdata/scheduled-job-no-timeout-or-retries.yml @@ -32,4 +32,4 @@ memory: 512 # Amount of memory in MiB used by the task. # You can override any of the values defined above by environment. #environments: # prod: -# cpu: 2048 # Larger CPU value for prod environment +# cpu: 2048 # Larger CPU value for prod environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/scheduled-job-no-timeout.yml b/internal/pkg/manifest/testdata/scheduled-job-no-timeout.yml index bd815fb9705..ed3abb69766 100644 --- a/internal/pkg/manifest/testdata/scheduled-job-no-timeout.yml +++ b/internal/pkg/manifest/testdata/scheduled-job-no-timeout.yml @@ -33,4 +33,4 @@ memory: 512 # Amount of memory in MiB used by the task. # You can override any of the values defined above by environment. #environments: # prod: -# cpu: 2048 # Larger CPU value for prod environment +# cpu: 2048 # Larger CPU value for prod environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml index 5077180af75..cb28f3efc8d 100644 --- a/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml +++ b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml @@ -40,7 +40,7 @@ environments: phonetool: network: vpc: - placement: 'private' # Set placement to private for environments containing only on private subnets. + placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/manifest/worker_svc.go b/internal/pkg/manifest/worker_svc.go index 03bd1b90fbf..c46c296a80d 100644 --- a/internal/pkg/manifest/worker_svc.go +++ b/internal/pkg/manifest/worker_svc.go @@ -230,10 +230,10 @@ func NewWorkerService(props WorkerServiceProps) *WorkerService { } // setSubscriptionQueueDefaults function modifies the manifest to have -// 1. FIFO Topic names without ".fifo" suffix. -// 2. If there are both FIFO and Standard topic subscriptions are specified then set +// 1. FIFO Topic names without ".fifo" suffix. +// 2. If there are both FIFO and Standard topic subscriptions are specified then set // default events queue to FIFO and add standard topic-specific queue for all the standard topic subscriptions. -// 3. If there are only Standard topic subscriptions are specified then do nothing and return. +// 3. If there are only Standard topic subscriptions are specified then do nothing and return. func setSubscriptionQueueDefaults(topics []TopicSubscription, eventsQueue *SQSQueue) { var isFIFOEnabled bool for _, topic := range topics { diff --git a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml index 9364b36dcaf..2a59f1dc611 100644 --- a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml +++ b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml @@ -51,18 +51,16 @@ platform: {{.Platform.PlatformString}} # See https://aws.github.io/copilot-cli # You can override any of the values defined above by environment. #environments: # prod: -# cpu: 2048 # Larger CPU value for prod environment. +# cpu: 2048 # Larger CPU value for prod environment. {{- else}} -# Set placement to private for environments containing only private subnets. +# You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. {{- end}} -# You can override any of the values defined above by environment. -#environments: -# prod: -# cpu: 2048 # Larger CPU value for prod environment. +# prod: +# cpu: 2048 # Larger CPU value for prod environment. {{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/backend/manifest.yml b/internal/pkg/template/templates/workloads/services/backend/manifest.yml index 21b20d4e7db..e9f4a1ea49f 100644 --- a/internal/pkg/template/templates/workloads/services/backend/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/backend/manifest.yml @@ -68,14 +68,13 @@ exec: true # Enable running commands in your container. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- else}} -# Set placement to private for environments containing only private subnets. +# You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. {{- end}} -# You can override any of the values defined above by environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml index 4252cdc54c1..170e8c0a5ac 100644 --- a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml @@ -63,14 +63,13 @@ exec: true # Enable running commands in your container. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- else}} -# Placement is set to private for environments containing only private subnets. -environments: {{ range $key, $value := .Environments}} +# You can override any of the values defined above by environment. +environments: {{range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. {{- end}} -# You can override any of the values defined above by environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml index d8b1d186f8a..81ca641b8bb 100644 --- a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml @@ -75,16 +75,14 @@ network: # LOG_LEVEL: debug # Log level for the "test" environment. {{- else}} -# Set placement to private for environments containing only private subnets. +# You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. {{- end}} -# You can override any of the values defined above by environment. -# environments: # test: # variables: -# LOG_LEVEL: debug # Log level for the "test" environment. +# LOG_LEVEL: debug # Log level for the "test" environment. {{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/worker/manifest.yml b/internal/pkg/template/templates/workloads/services/worker/manifest.yml index 5a2d1ce2b0f..fab618ca6cb 100644 --- a/internal/pkg/template/templates/workloads/services/worker/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/worker/manifest.yml @@ -83,14 +83,13 @@ subscribe: # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- else}} -# Set placement to private for environments containing only private subnets. +# You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Set placement to private for environments containing only on private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. {{- end}} -# You can override any of the values defined above by environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. From a8ea76d57fe9c650dd6c302ea96dbb72bd93d1be Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 22 Nov 2022 22:38:53 -0800 Subject: [PATCH 06/12] remove space --- internal/pkg/manifest/worker_svc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/manifest/worker_svc.go b/internal/pkg/manifest/worker_svc.go index c46c296a80d..1a16d927e93 100644 --- a/internal/pkg/manifest/worker_svc.go +++ b/internal/pkg/manifest/worker_svc.go @@ -232,7 +232,7 @@ func NewWorkerService(props WorkerServiceProps) *WorkerService { // setSubscriptionQueueDefaults function modifies the manifest to have // 1. FIFO Topic names without ".fifo" suffix. // 2. If there are both FIFO and Standard topic subscriptions are specified then set -// default events queue to FIFO and add standard topic-specific queue for all the standard topic subscriptions. +// default events queue to FIFO and add standard topic-specific queue for all the standard topic subscriptions. // 3. If there are only Standard topic subscriptions are specified then do nothing and return. func setSubscriptionQueueDefaults(topics []TopicSubscription, eventsQueue *SQSQueue) { var isFIFOEnabled bool From 22a77e01cde77315f73190395794437c2b8718c9 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Mon, 28 Nov 2022 14:52:47 -0800 Subject: [PATCH 07/12] address @Janice fb and fix indentations --- internal/pkg/cli/job_init.go | 6 +++--- internal/pkg/cli/svc_init.go | 6 +++--- .../testdata/backend-svc-nohealthcheck-placement.yml | 4 ++-- .../pkg/manifest/testdata/lb-svc-placement-private.yml | 4 ++-- .../pkg/manifest/testdata/rdws-placement-private.yml | 2 +- .../scheduled-job-fully-specified-placement.yml | 2 +- .../testdata/worker-svc-nosubscribe-placement.yml | 6 +++--- .../workloads/jobs/scheduled-job/manifest.yml | 4 +++- .../templates/workloads/services/backend/manifest.yml | 8 +++++--- .../templates/workloads/services/lb-web/manifest.yml | 8 +++++--- .../templates/workloads/services/rd-web/manifest.yml | 6 ++++-- .../templates/workloads/services/worker/manifest.yml | 10 ++++++---- 12 files changed, 38 insertions(+), 28 deletions(-) diff --git a/internal/pkg/cli/job_init.go b/internal/pkg/cli/job_init.go index 365625fb387..9f14a6cb542 100644 --- a/internal/pkg/cli/job_init.go +++ b/internal/pkg/cli/job_init.go @@ -225,8 +225,8 @@ func (o *initJobOpts) Ask() error { return nil } -// isSubnetsOnlyPrivate returns the list of environments names deployed that contains only private subnets. -func (o *initJobOpts) isSubnetsOnlyPrivate() ([]string, error) { +// hasOnlyPrivateSubnets returns the list of environments names deployed that contains only private subnets. +func (o *initJobOpts) hasOnlyPrivateSubnets() ([]string, error) { envs, err := o.store.ListEnvironments(o.appName) if err != nil { return nil, err @@ -276,7 +276,7 @@ func (o *initJobOpts) Execute() error { } } // Environments that are deployed have​ only private subnets. - envs, err := o.isSubnetsOnlyPrivate() + envs, err := o.hasOnlyPrivateSubnets() if err != nil { return err } diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 814a287d00c..66fc3f3cd39 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -338,7 +338,7 @@ func (o *initSvcOpts) Execute() error { } } // Environments that are deployed have​ only private subnets. - envs, err := o.isSubnetsOnlyPrivate() + envs, err := o.hasOnlyPrivateSubnets() if err != nil { return err } @@ -731,8 +731,8 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error }, nil } -// isSubnetsOnlyPrivate returns the list of environments names deployed that contains only private subnets. -func (o *initSvcOpts) isSubnetsOnlyPrivate() ([]string, error) { +// hasOnlyPrivateSubnets returns the list of environments names deployed that contains only private subnets. +func (o *initSvcOpts) hasOnlyPrivateSubnets() ([]string, error) { envs, err := o.store.ListEnvironments(o.appName) if err != nil { return nil, err diff --git a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml index f627dd85e87..b5a87b5ca3e 100644 --- a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml +++ b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml @@ -34,8 +34,8 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. + placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. # test: # count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/lb-svc-placement-private.yml b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml index f06dce59ecf..218a9464e15 100644 --- a/internal/pkg/manifest/testdata/lb-svc-placement-private.yml +++ b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml @@ -42,8 +42,8 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. + placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. # test: # count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/rdws-placement-private.yml b/internal/pkg/manifest/testdata/rdws-placement-private.yml index 778ad365bfb..a1a4b9052dc 100644 --- a/internal/pkg/manifest/testdata/rdws-placement-private.yml +++ b/internal/pkg/manifest/testdata/rdws-placement-private.yml @@ -50,7 +50,7 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. + placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. # test: # variables: # LOG_LEVEL: debug # Log level for the "test" environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml b/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml index a9ef46b7f30..1b2f97236a4 100644 --- a/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml +++ b/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml @@ -35,6 +35,6 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. + placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. # prod: # cpu: 2048 # Larger CPU value for prod environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml index cb28f3efc8d..dfe83b47dea 100644 --- a/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml +++ b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml @@ -40,8 +40,8 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to private for environment "phonetool" containing only private subnets. + placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. # test: -# count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. # rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml index 2a59f1dc611..ffbd15f3a03 100644 --- a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml +++ b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml @@ -57,9 +57,11 @@ platform: {{.Platform.PlatformString}} # See https://aws.github.io/copilot-cli # You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: +{{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. +{{- end}} {{- end}} # prod: # cpu: 2048 # Larger CPU value for prod environment. diff --git a/internal/pkg/template/templates/workloads/services/backend/manifest.yml b/internal/pkg/template/templates/workloads/services/backend/manifest.yml index e9f4a1ea49f..9dc339f6971 100644 --- a/internal/pkg/template/templates/workloads/services/backend/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/backend/manifest.yml @@ -71,12 +71,14 @@ exec: true # Enable running commands in your container. # You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: +{{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. +{{- end}} {{- end}} # test: # count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml index 170e8c0a5ac..a5216721166 100644 --- a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml @@ -66,12 +66,14 @@ exec: true # Enable running commands in your container. # You can override any of the values defined above by environment. environments: {{range $key, $value := .Environments}} {{$key}}: +{{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. +{{- end}} {{- end}} # test: # count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- end}} \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml index 81ca641b8bb..45b63f46d6a 100644 --- a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml @@ -78,10 +78,12 @@ network: # You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: +{{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. -{{- end}} + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. +{{- end}} +{{- end}} # test: # variables: # LOG_LEVEL: debug # Log level for the "test" environment. diff --git a/internal/pkg/template/templates/workloads/services/worker/manifest.yml b/internal/pkg/template/templates/workloads/services/worker/manifest.yml index fab618ca6cb..7d58eecc95d 100644 --- a/internal/pkg/template/templates/workloads/services/worker/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/worker/manifest.yml @@ -86,12 +86,14 @@ subscribe: # You can override any of the values defined above by environment. environments: {{ range $key, $value := .Environments}} {{$key}}: +{{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to private for environment "{{$key}}" containing only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. +{{- end}} {{- end}} # test: -# count: 2 # Number of tasks to run for the "test" environment. -# deployment: # The deployment strategy for the "test" environment. -# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. +# count: 2 # Number of tasks to run for the "test" environment. +# deployment: # The deployment strategy for the "test" environment. +# rolling: 'recreate' # Stops existing tasks before new ones are started for faster deployments. {{- end}} \ No newline at end of file From f52ff87241a33821760dcba29e5b9012d66b0859 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Mon, 28 Nov 2022 15:19:43 -0800 Subject: [PATCH 08/12] fix integ tests --- internal/pkg/manifest/testdata/lb-svc-placement-private.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/pkg/manifest/testdata/lb-svc-placement-private.yml b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml index 218a9464e15..b2cec37c16a 100644 --- a/internal/pkg/manifest/testdata/lb-svc-placement-private.yml +++ b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml @@ -25,6 +25,8 @@ cpu: 256 # Number of CPU units for the task. memory: 512 # Amount of memory in MiB used by the task. count: 1 # Number of tasks that should be running in your service. exec: true # Enable running commands in your container. +network: + connect: true # Enable Service Connect for intra-environment traffic between services. # storage: # readonly_fs: true # Limit to read-only access to mounted root filesystems. From 56e36a4a12bb2df1f9bae34259e1e8c335516247 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Thu, 1 Dec 2022 12:05:57 -0800 Subject: [PATCH 09/12] address Wanxian and Penghao fb --- internal/pkg/cli/job_init.go | 31 +++-- internal/pkg/cli/job_init_test.go | 38 +----- internal/pkg/cli/svc_init.go | 39 +----- internal/pkg/cli/svc_init_test.go | 126 ++++-------------- internal/pkg/initialize/workload.go | 73 +++++----- internal/pkg/manifest/backend_svc.go | 7 +- internal/pkg/manifest/backend_svc_test.go | 6 +- internal/pkg/manifest/job.go | 11 +- internal/pkg/manifest/lb_web_svc.go | 5 +- internal/pkg/manifest/lb_web_svc_test.go | 6 +- .../marshal_manifest_integration_test.go | 66 ++------- internal/pkg/manifest/rd_web_svc.go | 19 +-- internal/pkg/manifest/rd_web_svc_test.go | 14 -- .../backend-svc-nohealthcheck-placement.yml | 2 +- .../testdata/lb-svc-placement-private.yml | 2 +- .../testdata/rdws-placement-private.yml | 56 -------- ...cheduled-job-fully-specified-placement.yml | 2 +- .../worker-svc-nosubscribe-placement.yml | 2 +- internal/pkg/manifest/worker_svc.go | 1 - internal/pkg/manifest/worker_svc_test.go | 6 +- internal/pkg/manifest/workload.go | 7 +- .../workloads/jobs/scheduled-job/manifest.yml | 2 +- .../workloads/services/backend/manifest.yml | 2 +- .../workloads/services/lb-web/manifest.yml | 2 +- .../workloads/services/rd-web/manifest.yml | 19 +-- .../workloads/services/worker/manifest.yml | 2 +- 26 files changed, 132 insertions(+), 414 deletions(-) delete mode 100644 internal/pkg/manifest/testdata/rdws-placement-private.yml diff --git a/internal/pkg/cli/job_init.go b/internal/pkg/cli/job_init.go index 39648bf2ca2..a93711bcb3f 100644 --- a/internal/pkg/cli/job_init.go +++ b/internal/pkg/cli/job_init.go @@ -82,7 +82,7 @@ type initJobOpts struct { // Init a Dockerfile parser using fs and input path initParser func(string) dockerfileParser // Init a new EnvDescriber using environment name. - envDescriber func(string) (envDescriber, error) + initEnvDescriber func(string, string) (envDescriber, error) } func newInitJobOpts(vars initJobVars) (*initJobOpts, error) { @@ -124,14 +124,14 @@ func newInitJobOpts(vars initJobVars) (*initJobOpts, error) { initParser: func(path string) dockerfileParser { return dockerfile.New(fs, path) }, - envDescriber: func(envName string) (envDescriber, error) { + initEnvDescriber: func(appName string, envName string) (envDescriber, error) { envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ - App: vars.appName, + App: appName, Env: envName, ConfigStore: store, }) if err != nil { - return nil, err + return nil, fmt.Errorf("initiate env describer: %w", err) } return envDescriber, nil }, @@ -228,30 +228,30 @@ func (o *initJobOpts) Ask() error { return nil } -// hasOnlyPrivateSubnets returns the list of environments names deployed that contains only private subnets. -func (o *initJobOpts) hasOnlyPrivateSubnets() ([]string, error) { - envs, err := o.store.ListEnvironments(o.appName) +// envsWithPrivateSubnetsOnly returns the list of environments names deployed that contains only private subnets. +func envsWithPrivateSubnetsOnly(store store, initEnvDescriber func(string, string) (envDescriber, error), appName string) ([]string, error) { + envs, err := store.ListEnvironments(appName) if err != nil { - return nil, err + return nil, fmt.Errorf("list environments for application %s: %w", appName, err) } var privateOnlyEnvs []string - for i := range envs { - envDescriber, err := o.envDescriber(envs[i].Name) + for _, env := range envs { + envDescriber, err := initEnvDescriber(appName, env.Name) if err != nil { - return nil, fmt.Errorf("initiate env describer: %w", err) + return nil, err } mft, err := envDescriber.Manifest() if err != nil { - return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) + return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", env.Name, err) } envConfig, err := manifest.UnmarshalEnvironment(mft) if err != nil { - return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) + return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", env.Name, err) } subnets := envConfig.Network.VPC.Subnets if len(subnets.Public) == 0 && len(subnets.Private) != 0 { - privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) + privateOnlyEnvs = append(privateOnlyEnvs, env.Name) } } return privateOnlyEnvs, err @@ -278,8 +278,7 @@ func (o *initJobOpts) Execute() error { o.platform = &platform } } - // Environments that are deployed have​ only private subnets. - envs, err := o.hasOnlyPrivateSubnets() + envs, err := envsWithPrivateSubnetsOnly(o.store, o.initEnvDescriber, o.appName) if err != nil { return err } diff --git a/internal/pkg/cli/job_init_test.go b/internal/pkg/cli/job_init_test.go index d05551d6ffe..a15a2bed9d9 100644 --- a/internal/pkg/cli/job_init_test.go +++ b/internal/pkg/cli/job_init_test.go @@ -550,7 +550,7 @@ type: Scheduled Job`), nil) } func TestJobInitOpts_Execute(t *testing.T) { - mockmanifest := []byte(`name: test + mockEnvironmentManifest := []byte(`name: test type: Environment network: vpc: @@ -622,16 +622,7 @@ network: }).Return("manifest/path", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, }, "fail to init job": { @@ -689,16 +680,7 @@ type: Environment`), nil) }).Return("manifest/path", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, }, "doesn't complain if docker is unavailable": { @@ -742,15 +724,7 @@ type: Environment`), nil) }).Return("manifest/path", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(``), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, }, "return error if platform detection fails": { @@ -810,7 +784,7 @@ type: Environment`), nil) }, nil) }, mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return(mockmanifest, nil) + m.EXPECT().Manifest().Return(mockEnvironmentManifest, nil) }, }, } @@ -859,7 +833,7 @@ type: Environment`), nil) dockerEngine: mockDockerEngine, manifestExists: tc.inManifestExists, store: mockStore, - envDescriber: func(s string) (envDescriber, error) { + initEnvDescriber: func(string, string) (envDescriber, error) { return mockEnvDescriber, nil }, } diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 8282fcf33b9..6aa5e534f0b 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -155,7 +155,7 @@ type initSvcOpts struct { // Init a Dockerfile parser using fs and input path dockerfile func(string) dockerfileParser // Init a new EnvDescriber using environment name. - envDescriber func(string) (envDescriber, error) + initEnvDescriber func(string, string) (envDescriber, error) } func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { @@ -207,14 +207,14 @@ func newInitSvcOpts(vars initSvcVars) (*initSvcOpts, error) { opts.df = dockerfile.New(opts.fs, opts.dockerfilePath) return opts.df } - opts.envDescriber = func(envName string) (envDescriber, error) { + opts.initEnvDescriber = func(appName string, envName string) (envDescriber, error) { envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ - App: opts.appName, + App: appName, Env: envName, ConfigStore: opts.store, }) if err != nil { - return nil, err + return nil, fmt.Errorf("initiate env describer: %w", err) } return envDescriber, nil } @@ -342,7 +342,7 @@ func (o *initSvcOpts) Execute() error { } } // Environments that are deployed have​ only private subnets. - envs, err := o.hasOnlyPrivateSubnets() + envs, err := envsWithPrivateSubnetsOnly(o.store, o.initEnvDescriber, o.appName) if err != nil { return err } @@ -738,35 +738,6 @@ func parseHealthCheck(df dockerfileParser) (manifest.ContainerHealthCheck, error }, nil } -// hasOnlyPrivateSubnets returns the list of environments names deployed that contains only private subnets. -func (o *initSvcOpts) hasOnlyPrivateSubnets() ([]string, error) { - envs, err := o.store.ListEnvironments(o.appName) - if err != nil { - return nil, err - } - var privateOnlyEnvs []string - for i := range envs { - envDescriber, err := o.envDescriber(envs[i].Name) - if err != nil { - return nil, fmt.Errorf("initiate env describer: %w", err) - } - mft, err := envDescriber.Manifest() - if err != nil { - return nil, fmt.Errorf("read the manifest used to deploy environment %s: %w", envs[i].Name, err) - } - envConfig, err := manifest.UnmarshalEnvironment(mft) - if err != nil { - return nil, fmt.Errorf("unmarshal the manifest used to deploy environment %s: %w", envs[i].Name, err) - } - subnets := envConfig.Network.VPC.Subnets - - if len(subnets.Public) == 0 && len(subnets.Private) != 0 { - privateOnlyEnvs = append(privateOnlyEnvs, envs[i].Name) - } - } - return privateOnlyEnvs, err -} - func svcTypePromptOpts() []prompt.Option { var options []prompt.Option for _, svcType := range manifest.ServiceTypes() { diff --git a/internal/pkg/cli/svc_init_test.go b/internal/pkg/cli/svc_init_test.go index 6d6d31c9453..760add5544f 100644 --- a/internal/pkg/cli/svc_init_test.go +++ b/internal/pkg/cli/svc_init_test.go @@ -766,6 +766,17 @@ type: Request-Driven Web Service`), nil) } func TestSvcInitOpts_Execute(t *testing.T) { + mockEnvironmentManifest := []byte(`name: test +type: Environment +network: + vpc: + id: 'vpc-mockid' + subnets: + private: + - id: 'subnet-1' + - id: 'subnet-2' + - id: 'subnet-3' + - id: 'subnet-4'`) testCases := map[string]struct { mockSvcInit func(m *mocks.MocksvcInitializer) mockDockerfile func(m *mocks.MockdockerfileParser) @@ -812,15 +823,7 @@ func TestSvcInitOpts_Execute(t *testing.T) { m.EXPECT().GetPlatform().Return("linux", "amd64", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(``), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", }, @@ -849,16 +852,7 @@ func TestSvcInitOpts_Execute(t *testing.T) { m.EXPECT().GetPlatform().Return("linux", "amd64", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", }, @@ -889,18 +883,8 @@ type: Environment`), nil) }).Return("manifest/path", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, - wantedManifestPath: "manifest/path", }, "doesn't complain if docker is unavailable": { @@ -930,16 +914,7 @@ type: Environment`), nil) }).Return("manifest/path", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", @@ -974,16 +949,7 @@ type: Environment`), nil) m.EXPECT().GetPlatform().Return("windows", "amd64", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", @@ -1018,16 +984,7 @@ type: Environment`), nil) m.EXPECT().GetPlatform().Return("linux", "arm", nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", @@ -1069,16 +1026,7 @@ type: Environment`), nil) }, nil) }, mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", @@ -1103,16 +1051,7 @@ type: Environment`), nil) }, mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", @@ -1137,16 +1076,7 @@ type: Environment`), nil) }, mockDockerfile: func(m *mocks.MockdockerfileParser) {}, // Be sure that no dockerfile parsing happens. mockStore: func(m *mocks.Mockstore) { - m.EXPECT().ListEnvironments("sample").Return([]*config.Environment{ - { - App: "sample", - Name: "test", - }, - }, nil) - }, - mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment`), nil) + m.EXPECT().ListEnvironments("sample").Return(nil, nil) }, wantedManifestPath: "manifest/path", @@ -1183,7 +1113,7 @@ type: Environment`), nil) mockStore: func(m *mocks.Mockstore) { m.EXPECT().ListEnvironments("").Return(nil, errors.New("some error")) }, - wantedErr: errors.New("some error"), + wantedErr: errors.New("list environments for application : some error"), }, "initalize a service in environments with only private subnets": { inAppName: "sample", @@ -1224,17 +1154,7 @@ type: Environment`), nil) }, nil) }, mockEnvDescriber: func(m *mocks.MockenvDescriber) { - m.EXPECT().Manifest().Return([]byte(`name: test -type: Environment -network: - vpc: - id: 'vpc-mockid' - subnets: - private: - - id: 'subnet-1' - - id: 'subnet-2' - - id: 'subnet-3' - - id: 'subnet-4'`), nil) + m.EXPECT().Manifest().Return(mockEnvironmentManifest, nil) }, wantedManifestPath: "manifest/path", }, @@ -1316,7 +1236,7 @@ network: store: mockStore, topicSel: mockTopicSel, manifestExists: tc.inManifestExists, - envDescriber: func(s string) (envDescriber, error) { + initEnvDescriber: func(string, string) (envDescriber, error) { return mockEnvDescriber, nil }, } diff --git a/internal/pkg/initialize/workload.go b/internal/pkg/initialize/workload.go index 9fdec0cd402..e3a5d76d0ff 100644 --- a/internal/pkg/initialize/workload.go +++ b/internal/pkg/initialize/workload.go @@ -272,16 +272,16 @@ func newJobManifest(i *JobProps) (encoding.BinaryMarshaler, error) { case manifest.ScheduledJobType: return manifest.NewScheduledJob(&manifest.ScheduledJobProps{ WorkloadProps: &manifest.WorkloadProps{ - Name: i.Name, - Dockerfile: i.DockerfilePath, - Image: i.Image, + Name: i.Name, + Dockerfile: i.DockerfilePath, + Image: i.Image, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }, - HealthCheck: i.HealthCheck, - Platform: i.Platform, - Schedule: i.Schedule, - Timeout: i.Timeout, - Retries: i.Retries, - PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, + HealthCheck: i.HealthCheck, + Platform: i.Platform, + Schedule: i.Schedule, + Timeout: i.Timeout, + Retries: i.Retries, }), nil default: return nil, fmt.Errorf("job type %s doesn't have a manifest", i.Type) @@ -313,16 +313,16 @@ func (w *WorkloadInitializer) newLoadBalancedWebServiceManifest(i *ServiceProps) } props := &manifest.LoadBalancedWebServiceProps{ WorkloadProps: &manifest.WorkloadProps{ - Name: i.Name, - Dockerfile: i.DockerfilePath, - Image: i.Image, + Name: i.Name, + Dockerfile: i.DockerfilePath, + Image: i.Image, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }, - Path: "/", - Port: i.Port, - HTTPVersion: httpVersion, - HealthCheck: i.HealthCheck, - Platform: i.Platform, - PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, + Path: "/", + Port: i.Port, + HTTPVersion: httpVersion, + HealthCheck: i.HealthCheck, + Platform: i.Platform, } existingSvcs, err := w.Store.ListServices(i.App) if err != nil { @@ -346,10 +346,9 @@ func (w *WorkloadInitializer) newRequestDrivenWebServiceManifest(i *ServiceProps Dockerfile: i.DockerfilePath, Image: i.Image, }, - Port: i.Port, - Platform: i.Platform, - Private: i.Private, - PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, + Port: i.Port, + Platform: i.Platform, + Private: i.Private, } return manifest.NewRequestDrivenWebService(props) } @@ -357,29 +356,29 @@ func (w *WorkloadInitializer) newRequestDrivenWebServiceManifest(i *ServiceProps func newBackendServiceManifest(i *ServiceProps) (*manifest.BackendService, error) { return manifest.NewBackendService(manifest.BackendServiceProps{ WorkloadProps: manifest.WorkloadProps{ - Name: i.Name, - Dockerfile: i.DockerfilePath, - Image: i.Image, + Name: i.Name, + Dockerfile: i.DockerfilePath, + Image: i.Image, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }, - Port: i.Port, - HealthCheck: i.HealthCheck, - Platform: i.Platform, - PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, + Port: i.Port, + HealthCheck: i.HealthCheck, + Platform: i.Platform, }), nil } func newWorkerServiceManifest(i *ServiceProps) (*manifest.WorkerService, error) { return manifest.NewWorkerService(manifest.WorkerServiceProps{ WorkloadProps: manifest.WorkloadProps{ - Name: i.Name, - Dockerfile: i.DockerfilePath, - Image: i.Image, + Name: i.Name, + Dockerfile: i.DockerfilePath, + Image: i.Image, + PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, }, - HealthCheck: i.HealthCheck, - Platform: i.Platform, - Topics: i.Topics, - Queue: i.Queue, - PrivateOnlyEnvironments: i.PrivateOnlyEnvironments, + HealthCheck: i.HealthCheck, + Platform: i.Platform, + Topics: i.Topics, + Queue: i.Queue, }), nil } diff --git a/internal/pkg/manifest/backend_svc.go b/internal/pkg/manifest/backend_svc.go index 728be8d7cc4..0d9bf2708ef 100644 --- a/internal/pkg/manifest/backend_svc.go +++ b/internal/pkg/manifest/backend_svc.go @@ -41,10 +41,9 @@ type BackendServiceConfig struct { // BackendServiceProps represents the configuration needed to create a backend service. type BackendServiceProps struct { WorkloadProps - Port uint16 - PrivateOnlyEnvironments []string - HealthCheck ContainerHealthCheck // Optional healthcheck configuration. - Platform PlatformArgsOrString // Optional platform configuration. + Port uint16 + HealthCheck ContainerHealthCheck // Optional healthcheck configuration. + Platform PlatformArgsOrString // Optional platform configuration. } // NewBackendService applies the props to a default backend service configuration with diff --git a/internal/pkg/manifest/backend_svc_test.go b/internal/pkg/manifest/backend_svc_test.go index b60a210c26f..c6c3d93e141 100644 --- a/internal/pkg/manifest/backend_svc_test.go +++ b/internal/pkg/manifest/backend_svc_test.go @@ -24,9 +24,9 @@ func TestNewBackendSvc(t *testing.T) { WorkloadProps: WorkloadProps{ Name: "subscribers", Dockerfile: "./subscribers/Dockerfile", - }, - PrivateOnlyEnvironments: []string{ - "metrics", + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, }, wantedManifest: &BackendService{ diff --git a/internal/pkg/manifest/job.go b/internal/pkg/manifest/job.go index 5f5801b9ecb..fc9112bf976 100644 --- a/internal/pkg/manifest/job.go +++ b/internal/pkg/manifest/job.go @@ -67,12 +67,11 @@ type JobFailureHandlerConfig struct { // ScheduledJobProps contains properties for creating a new scheduled job manifest. type ScheduledJobProps struct { *WorkloadProps - Schedule string - Timeout string - HealthCheck ContainerHealthCheck // Optional healthcheck configuration. - Platform PlatformArgsOrString // Optional platform configuration. - Retries int - PrivateOnlyEnvironments []string + Schedule string + Timeout string + HealthCheck ContainerHealthCheck // Optional healthcheck configuration. + Platform PlatformArgsOrString // Optional platform configuration. + Retries int } // NewScheduledJob creates a new scheduled job object. diff --git a/internal/pkg/manifest/lb_web_svc.go b/internal/pkg/manifest/lb_web_svc.go index 9977284e6bb..f5c1e152169 100644 --- a/internal/pkg/manifest/lb_web_svc.go +++ b/internal/pkg/manifest/lb_web_svc.go @@ -62,9 +62,8 @@ type LoadBalancedWebServiceConfig struct { // LoadBalancedWebServiceProps contains properties for creating a new load balanced fargate service manifest. type LoadBalancedWebServiceProps struct { *WorkloadProps - Path string - Port uint16 - PrivateOnlyEnvironments []string + Path string + Port uint16 HTTPVersion string // Optional http protocol version such as gRPC, HTTP2. HealthCheck ContainerHealthCheck // Optional healthcheck configuration. diff --git a/internal/pkg/manifest/lb_web_svc_test.go b/internal/pkg/manifest/lb_web_svc_test.go index d822a15a533..0f9781cf5cb 100644 --- a/internal/pkg/manifest/lb_web_svc_test.go +++ b/internal/pkg/manifest/lb_web_svc_test.go @@ -85,6 +85,9 @@ func TestNewHTTPLoadBalancedWebService(t *testing.T) { WorkloadProps: &WorkloadProps{ Name: "subscribers", Dockerfile: "./subscribers/Dockerfile", + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, Path: "/", Port: 80, @@ -94,9 +97,6 @@ func TestNewHTTPLoadBalancedWebService(t *testing.T) { Command: []string{"CMD", "curl -f http://localhost:8080 || exit 1"}, }, Platform: PlatformArgsOrString{PlatformString: (*PlatformString)(aws.String("windows/amd64"))}, - PrivateOnlyEnvironments: []string{ - "metrics", - }, }, wanted: &LoadBalancedWebService{ diff --git a/internal/pkg/manifest/marshal_manifest_integration_test.go b/internal/pkg/manifest/marshal_manifest_integration_test.go index 7666bfe7ecd..66e37d0c11a 100644 --- a/internal/pkg/manifest/marshal_manifest_integration_test.go +++ b/internal/pkg/manifest/marshal_manifest_integration_test.go @@ -41,14 +41,14 @@ func TestLoadBalancedWebService_InitialManifestIntegration(t *testing.T) { WorkloadProps: &WorkloadProps{ Name: "frontend", Dockerfile: "./frontend/Dockerfile", + PrivateOnlyEnvironments: []string{ + "phonetool", + }, }, Platform: PlatformArgsOrString{ PlatformString: nil, PlatformArgs: PlatformArgs{}, }, - PrivateOnlyEnvironments: []string{ - "phonetool", - }, }, wantedTestdata: "lb-svc-placement-private.yml", }, @@ -83,6 +83,9 @@ func TestBackendSvc_InitialManifestIntegration(t *testing.T) { WorkloadProps: WorkloadProps{ Name: "subscribers", Dockerfile: "./subscribers/Dockerfile", + PrivateOnlyEnvironments: []string{ + "phonetool", + }, }, Platform: PlatformArgsOrString{ PlatformString: nil, @@ -91,9 +94,6 @@ func TestBackendSvc_InitialManifestIntegration(t *testing.T) { Arch: nil, }, }, - PrivateOnlyEnvironments: []string{ - "phonetool", - }, }, wantedTestdata: "backend-svc-nohealthcheck-placement.yml", }, @@ -145,6 +145,9 @@ func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { WorkloadProps: WorkloadProps{ Name: "testers", Dockerfile: "./testers/Dockerfile", + PrivateOnlyEnvironments: []string{ + "phonetool", + }, }, Platform: PlatformArgsOrString{ PlatformString: nil, @@ -153,9 +156,6 @@ func TestWorkerSvc_InitialManifestIntegration(t *testing.T) { Arch: nil, }, }, - PrivateOnlyEnvironments: []string{ - "phonetool", - }, }, wantedTestdata: "worker-svc-nosubscribe-placement.yml", }, @@ -252,6 +252,9 @@ func TestScheduledJob_InitialManifestIntegration(t *testing.T) { WorkloadProps: &WorkloadProps{ Name: "cuteness-aggregator", Dockerfile: "./cuteness-aggregator/Dockerfile", + PrivateOnlyEnvironments: []string{ + "phonetool", + }, }, Platform: PlatformArgsOrString{ PlatformString: nil, @@ -260,9 +263,6 @@ func TestScheduledJob_InitialManifestIntegration(t *testing.T) { Schedule: "0 */2 * * *", Retries: 3, Timeout: "1h30m", - PrivateOnlyEnvironments: []string{ - "phonetool", - }, }, wantedTestData: "scheduled-job-fully-specified-placement.yml", }, @@ -481,45 +481,3 @@ func TestPipelineManifest_InitialManifest_Integration(t *testing.T) { }) } } - -func TestRequestDrivenSvc_InitialManifestIntegration(t *testing.T) { - testCases := map[string]struct { - inProps RequestDrivenWebServiceProps - - wantedTestdata string - }{ - "with placement private": { - inProps: RequestDrivenWebServiceProps{ - WorkloadProps: &WorkloadProps{ - Name: "testers", - Dockerfile: "./testers/Dockerfile", - }, - Platform: PlatformArgsOrString{ - PlatformString: nil, - PlatformArgs: PlatformArgs{}, - }, - Port: 8000, - PrivateOnlyEnvironments: []string{ - "phonetool", - }, - }, - wantedTestdata: "rdws-placement-private.yml", - }, - } - for name, tc := range testCases { - t.Run(name, func(t *testing.T) { - // GIVEN - path := filepath.Join("testdata", tc.wantedTestdata) - wantedBytes, err := os.ReadFile(path) - require.NoError(t, err) - manifest := NewRequestDrivenWebService(&tc.inProps) - - // WHEN - tpl, err := manifest.MarshalBinary() - require.NoError(t, err) - - // THEN - require.Equal(t, string(wantedBytes), string(tpl)) - }) - } -} diff --git a/internal/pkg/manifest/rd_web_svc.go b/internal/pkg/manifest/rd_web_svc.go index fe3211d7404..52d0d81e075 100644 --- a/internal/pkg/manifest/rd_web_svc.go +++ b/internal/pkg/manifest/rd_web_svc.go @@ -102,10 +102,9 @@ type AppRunnerInstanceConfig struct { // RequestDrivenWebServiceProps contains properties for creating a new request-driven web service manifest. type RequestDrivenWebServiceProps struct { *WorkloadProps - Port uint16 - PrivateOnlyEnvironments []string - Platform PlatformArgsOrString - Private bool + Port uint16 + Platform PlatformArgsOrString + Private bool } // NewRequestDrivenWebService creates a new Request-Driven Web Service manifest with default values. @@ -120,17 +119,6 @@ func NewRequestDrivenWebService(props *RequestDrivenWebServiceProps) *RequestDri svc.Private = BasicToUnion[*bool, VPCEndpoint](aws.Bool(true)) svc.Network.VPC.Placement.PlacementString = (*PlacementString)(aws.String("private")) } - for _, envName := range props.PrivateOnlyEnvironments { - svc.Environments[envName] = &RequestDrivenWebServiceConfig{ - Network: RequestDrivenWebServiceNetworkConfig{ - VPC: rdwsVpcConfig{ - Placement: PlacementArgOrString{ - PlacementString: placementStringP(PrivateSubnetPlacement), - }, - }, - }, - } - } svc.parser = template.New() return svc } @@ -211,6 +199,5 @@ func newDefaultRequestDrivenWebService() *RequestDrivenWebService { Memory: aws.Int(2048), }, }, - Environments: map[string]*RequestDrivenWebServiceConfig{}, } } diff --git a/internal/pkg/manifest/rd_web_svc_test.go b/internal/pkg/manifest/rd_web_svc_test.go index d25c6452ccb..845265b812e 100644 --- a/internal/pkg/manifest/rd_web_svc_test.go +++ b/internal/pkg/manifest/rd_web_svc_test.go @@ -30,9 +30,6 @@ func TestNewRequestDrivenWebService(t *testing.T) { Dockerfile: "./Dockerfile", }, Port: uint16(80), - PrivateOnlyEnvironments: []string{ - "metrics", - }, }, wantedStruct: &RequestDrivenWebService{ @@ -56,17 +53,6 @@ func TestNewRequestDrivenWebService(t *testing.T) { Memory: aws.Int(2048), }, }, - Environments: map[string]*RequestDrivenWebServiceConfig{ - "metrics": { - Network: RequestDrivenWebServiceNetworkConfig{ - VPC: rdwsVpcConfig{ - Placement: PlacementArgOrString{ - PlacementString: placementStringP(PrivateSubnetPlacement), - }, - }, - }, - }, - }, }, }, } diff --git a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml index b5a87b5ca3e..eb1fdd9781f 100644 --- a/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml +++ b/internal/pkg/manifest/testdata/backend-svc-nohealthcheck-placement.yml @@ -34,7 +34,7 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. + placement: 'private' # The tasks will be placed on private subnets for the "phonetool" environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/manifest/testdata/lb-svc-placement-private.yml b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml index b2cec37c16a..2b427ddd4e8 100644 --- a/internal/pkg/manifest/testdata/lb-svc-placement-private.yml +++ b/internal/pkg/manifest/testdata/lb-svc-placement-private.yml @@ -44,7 +44,7 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. + placement: 'private' # The tasks will be placed on private subnets for the "phonetool" environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/manifest/testdata/rdws-placement-private.yml b/internal/pkg/manifest/testdata/rdws-placement-private.yml deleted file mode 100644 index a1a4b9052dc..00000000000 --- a/internal/pkg/manifest/testdata/rdws-placement-private.yml +++ /dev/null @@ -1,56 +0,0 @@ -# The manifest for the "testers" service. -# Read the full specification for the "Request-Driven Web Service" type at: -# https://aws.github.io/copilot-cli/docs/manifest/rd-web-service/ - -# Your service name will be used in naming your resources like log groups, App Runner services, etc. -name: testers -# The "architecture" of the service you're running. -type: Request-Driven Web Service - -image: - # Docker build arguments. - # For additional overrides: https://aws.github.io/copilot-cli/docs/manifest/rd-web-service/#image-build - build: ./testers/Dockerfile - # Port exposed through your container to route traffic to it. - port: 8000 - -# http: -# healthcheck: -# path: / -# healthy_threshold: 3 -# unhealthy_threshold: 5 -# interval: 10s -# timeout: 5s - -# Number of CPU units for the task. -cpu: 1024 -# Amount of memory in MiB used by the task. -memory: 2048 - -# Connect your App Runner service to your environment's VPC. -# network: -# vpc: -# placement: private - - -# Enable tracing for the service. -# observability: -# tracing: awsxray - -# Optional fields for more advanced use-cases. -# -# variables: # Pass environment variables as key value pairs. -# LOG_LEVEL: info -# -# tags: # Pass tags as key value pairs. -# project: project-name - -# You can override any of the values defined above by environment. -environments: - phonetool: - network: - vpc: - placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. -# test: -# variables: -# LOG_LEVEL: debug # Log level for the "test" environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml b/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml index 1b2f97236a4..a5340b596a7 100644 --- a/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml +++ b/internal/pkg/manifest/testdata/scheduled-job-fully-specified-placement.yml @@ -35,6 +35,6 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. + placement: 'private' # The tasks will be placed on private subnets for the "phonetool" environment. # prod: # cpu: 2048 # Larger CPU value for prod environment. \ No newline at end of file diff --git a/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml index dfe83b47dea..c29043c7b5e 100644 --- a/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml +++ b/internal/pkg/manifest/testdata/worker-svc-nosubscribe-placement.yml @@ -40,7 +40,7 @@ environments: phonetool: network: vpc: - placement: 'private' # Placement is set to 'private' for environment "phonetool", which contains only private subnets. + placement: 'private' # The tasks will be placed on private subnets for the "phonetool" environment. # test: # count: 2 # Number of tasks to run for the "test" environment. # deployment: # The deployment strategy for the "test" environment. diff --git a/internal/pkg/manifest/worker_svc.go b/internal/pkg/manifest/worker_svc.go index 1a16d927e93..554241b52af 100644 --- a/internal/pkg/manifest/worker_svc.go +++ b/internal/pkg/manifest/worker_svc.go @@ -187,7 +187,6 @@ func (q *DeadLetterQueue) IsEmpty() bool { // WorkerServiceProps represents the configuration needed to create a worker service. type WorkerServiceProps struct { WorkloadProps - PrivateOnlyEnvironments []string HealthCheck ContainerHealthCheck // Optional healthcheck configuration. Platform PlatformArgsOrString // Optional platform configuration. diff --git a/internal/pkg/manifest/worker_svc_test.go b/internal/pkg/manifest/worker_svc_test.go index ecef3fdcb6f..f985b094fc5 100644 --- a/internal/pkg/manifest/worker_svc_test.go +++ b/internal/pkg/manifest/worker_svc_test.go @@ -61,9 +61,9 @@ func TestNewWorkerSvc(t *testing.T) { WorkloadProps: WorkloadProps{ Name: "testers", Dockerfile: "./testers/Dockerfile", - }, - PrivateOnlyEnvironments: []string{ - "metrics", + PrivateOnlyEnvironments: []string{ + "metrics", + }, }, }, wantedManifest: &WorkerService{ diff --git a/internal/pkg/manifest/workload.go b/internal/pkg/manifest/workload.go index 6fd28c81196..740f15df453 100644 --- a/internal/pkg/manifest/workload.go +++ b/internal/pkg/manifest/workload.go @@ -115,9 +115,10 @@ func UnmarshalWorkload(in []byte) (DynamicWorkload, error) { // WorkloadProps contains properties for creating a new workload manifest. type WorkloadProps struct { - Name string - Dockerfile string - Image string + Name string + Dockerfile string + Image string + PrivateOnlyEnvironments []string } // Workload holds the basic data that every workload manifest file needs to have. diff --git a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml index ffbd15f3a03..5df13fa2b72 100644 --- a/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml +++ b/internal/pkg/template/templates/workloads/jobs/scheduled-job/manifest.yml @@ -60,7 +60,7 @@ environments: {{ range $key, $value := .Environments}} {{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # The tasks will be placed on private subnets for the "{{$key}}" environment. {{- end}} {{- end}} # prod: diff --git a/internal/pkg/template/templates/workloads/services/backend/manifest.yml b/internal/pkg/template/templates/workloads/services/backend/manifest.yml index 748dd41692a..d8749bcd5b6 100644 --- a/internal/pkg/template/templates/workloads/services/backend/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/backend/manifest.yml @@ -77,7 +77,7 @@ environments: {{ range $key, $value := .Environments}} {{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # The tasks will be placed on private subnets for the "{{$key}}" environment. {{- end}} {{- end}} # test: diff --git a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml index f673f37a996..6391e7f31c3 100644 --- a/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/lb-web/manifest.yml @@ -70,7 +70,7 @@ environments: {{range $key, $value := .Environments}} {{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # The tasks will be placed on private subnets for the "{{$key}}" environment. {{- end}} {{- end}} # test: diff --git a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml index 45b63f46d6a..75defbf70dd 100644 --- a/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/rd-web/manifest.yml @@ -66,25 +66,8 @@ network: # tags: # Pass tags as key value pairs. # project: project-name -{{- if not .Environments}} - # You can override any of the values defined above by environment. # environments: # test: # variables: -# LOG_LEVEL: debug # Log level for the "test" environment. -{{- else}} - -# You can override any of the values defined above by environment. -environments: {{ range $key, $value := .Environments}} - {{$key}}: -{{- if $value.Network.VPC.Placement.PlacementString}} - network: - vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. -{{- end}} -{{- end}} -# test: -# variables: -# LOG_LEVEL: debug # Log level for the "test" environment. -{{- end}} \ No newline at end of file +# LOG_LEVEL: debug # Log level for the "test" environment. \ No newline at end of file diff --git a/internal/pkg/template/templates/workloads/services/worker/manifest.yml b/internal/pkg/template/templates/workloads/services/worker/manifest.yml index 7d58eecc95d..9eacb738076 100644 --- a/internal/pkg/template/templates/workloads/services/worker/manifest.yml +++ b/internal/pkg/template/templates/workloads/services/worker/manifest.yml @@ -89,7 +89,7 @@ environments: {{ range $key, $value := .Environments}} {{- if $value.Network.VPC.Placement.PlacementString}} network: vpc: - placement: '{{$value.Network.VPC.Placement.PlacementString}}' # Placement is set to 'private' for environment "{{$key}}", which contains only private subnets. + placement: '{{$value.Network.VPC.Placement.PlacementString}}' # The tasks will be placed on private subnets for the "{{$key}}" environment. {{- end}} {{- end}} # test: From 8d9e5ad607269ebebfdf48f202a0f57b844efce5 Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 6 Dec 2022 01:00:32 -0800 Subject: [PATCH 10/12] addr fb --- internal/pkg/cli/svc_init.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index 6aa5e534f0b..ca8d80916c5 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -341,7 +341,7 @@ func (o *initSvcOpts) Execute() error { o.platform = &platform } } - // Environments that are deployed have​ only private subnets. + // Environments that are deployed and have​ only private subnets. envs, err := envsWithPrivateSubnetsOnly(o.store, o.initEnvDescriber, o.appName) if err != nil { return err From f01eedc7da682cd708568c034bcf55372529f3eb Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 6 Dec 2022 01:06:49 -0800 Subject: [PATCH 11/12] add comments to initEnvDescrber --- internal/pkg/cli/job_init.go | 2 +- internal/pkg/cli/svc_init.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/cli/job_init.go b/internal/pkg/cli/job_init.go index a93711bcb3f..0da394b2625 100644 --- a/internal/pkg/cli/job_init.go +++ b/internal/pkg/cli/job_init.go @@ -81,7 +81,7 @@ type initJobOpts struct { // Init a Dockerfile parser using fs and input path initParser func(string) dockerfileParser - // Init a new EnvDescriber using environment name. + // Init a new EnvDescriber using environment name and app name. initEnvDescriber func(string, string) (envDescriber, error) } diff --git a/internal/pkg/cli/svc_init.go b/internal/pkg/cli/svc_init.go index ca8d80916c5..e234a21f162 100644 --- a/internal/pkg/cli/svc_init.go +++ b/internal/pkg/cli/svc_init.go @@ -154,7 +154,7 @@ type initSvcOpts struct { // Init a Dockerfile parser using fs and input path dockerfile func(string) dockerfileParser - // Init a new EnvDescriber using environment name. + // Init a new EnvDescriber using environment name and app name. initEnvDescriber func(string, string) (envDescriber, error) } From a6b2892b531d218e58df0bc724baba2a3dd190ef Mon Sep 17 00:00:00 2001 From: Adithya Kolla Date: Tue, 6 Dec 2022 10:14:01 -0800 Subject: [PATCH 12/12] initialize EnvDescriber in init for svc and job --- internal/pkg/cli/init.go | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/internal/pkg/cli/init.go b/internal/pkg/cli/init.go index d6d83868c64..7c798b1f0ef 100644 --- a/internal/pkg/cli/init.go +++ b/internal/pkg/cli/init.go @@ -11,6 +11,7 @@ import ( awscfn "github.com/aws/copilot-cli/internal/pkg/aws/cloudformation" "github.com/aws/copilot-cli/internal/pkg/aws/iam" + "github.com/aws/copilot-cli/internal/pkg/describe" "github.com/aws/copilot-cli/internal/pkg/docker/dockerfile" "github.com/aws/copilot-cli/internal/pkg/deploy" @@ -264,6 +265,17 @@ func newInitOpts(vars initVars) (*initOpts, error) { initParser: func(s string) dockerfileParser { return dockerfile.New(fs, s) }, + initEnvDescriber: func(appName string, envName string) (envDescriber, error) { + envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ + App: appName, + Env: envName, + ConfigStore: configStore, + }) + if err != nil { + return nil, fmt.Errorf("initiate env describer: %w", err) + } + return envDescriber, nil + }, } o.initWlCmd = &opts o.schedule = &opts.schedule // Surfaced via pointer for logging @@ -292,6 +304,17 @@ func newInitOpts(vars initVars) (*initOpts, error) { opts.df = dockerfile.New(opts.fs, opts.dockerfilePath) return opts.df } + opts.initEnvDescriber = func(appName string, envName string) (envDescriber, error) { + envDescriber, err := describe.NewEnvDescriber(describe.NewEnvDescriberConfig{ + App: appName, + Env: envName, + ConfigStore: opts.store, + }) + if err != nil { + return nil, fmt.Errorf("initiate env describer: %w", err) + } + return envDescriber, nil + } o.initWlCmd = &opts o.port = &opts.port // Surfaced via pointer for logging. o.initWkldVars = &opts.initWkldVars