From 77ac3d45bcc27c0e0b956fe05dbeaee6827b4b7b Mon Sep 17 00:00:00 2001 From: Austin Ely Date: Tue, 9 Feb 2021 22:20:40 +0000 Subject: [PATCH 1/4] feat(deploy): enable mounting EFS filesystems in manifest --- .../cloudformation/stack/backend_svc.go | 5 +++++ .../deploy/cloudformation/stack/lb_web_svc.go | 7 +++++++ .../cloudformation/stack/scheduled_job.go | 6 ++++++ .../cloudformation/stack/transformers.go | 19 ++++++++++++------- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/internal/pkg/deploy/cloudformation/stack/backend_svc.go b/internal/pkg/deploy/cloudformation/stack/backend_svc.go index 9325b9ed14b..1b94cdfacc7 100644 --- a/internal/pkg/deploy/cloudformation/stack/backend_svc.go +++ b/internal/pkg/deploy/cloudformation/stack/backend_svc.go @@ -83,6 +83,10 @@ func (s *BackendService) Template() (string, error) { if err != nil { return "", fmt.Errorf("convert the Auto Scaling configuration for service %s: %w", s.name, err) } + storage, err := convertStorageOpts(&s.manifest.Storage) + if err != nil { + return "", fmt.Errorf("convert storage options for service %s: %w", s.name, err) + } content, err := s.parser.ParseBackendService(template.WorkloadOpts{ Variables: s.manifest.BackendServiceConfig.Variables, Secrets: s.manifest.BackendServiceConfig.Secrets, @@ -92,6 +96,7 @@ func (s *BackendService) Template() (string, error) { HealthCheck: s.manifest.BackendServiceConfig.ImageConfig.HealthCheckOpts(), LogConfig: convertLogging(s.manifest.Logging), DesiredCountLambda: desiredCountLambda.String(), + Storage: storage, }) if err != nil { return "", fmt.Errorf("parse backend service template: %w", err) diff --git a/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go b/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go index 1c57feb673d..ce11ac25db9 100644 --- a/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go +++ b/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go @@ -112,6 +112,12 @@ func (s *LoadBalancedWebService) Template() (string, error) { if err != nil { return "", fmt.Errorf("convert the Auto Scaling configuration for service %s: %w", s.name, err) } + + storage, err := convertStorageOpts(&s.manifest.Storage) + if err != nil { + return "", fmt.Errorf("convert storage options for service %s: %w", s.name, err) + } + content, err := s.parser.ParseLoadBalancedWebService(template.WorkloadOpts{ Variables: s.manifest.Variables, Secrets: s.manifest.Secrets, @@ -124,6 +130,7 @@ func (s *LoadBalancedWebService) Template() (string, error) { RulePriorityLambda: rulePriorityLambda.String(), DesiredCountLambda: desiredCountLambda.String(), EnvControllerLambda: envControllerLambda.String(), + Storage: storage, }) if err != nil { return "", err diff --git a/internal/pkg/deploy/cloudformation/stack/scheduled_job.go b/internal/pkg/deploy/cloudformation/stack/scheduled_job.go index 727b9268c97..64f00c0287f 100644 --- a/internal/pkg/deploy/cloudformation/stack/scheduled_job.go +++ b/internal/pkg/deploy/cloudformation/stack/scheduled_job.go @@ -137,6 +137,11 @@ func (j *ScheduledJob) Template() (string, error) { return "", fmt.Errorf("convert retry/timeout config for job %s: %w", j.name, err) } + storage, err := convertStorageOpts(&j.manifest.Storage) + if err != nil { + return "", fmt.Errorf("convert storage options for job %s: %w", j.name, err) + } + content, err := j.parser.ParseScheduledJob(template.WorkloadOpts{ Variables: j.manifest.Variables, Secrets: j.manifest.Secrets, @@ -145,6 +150,7 @@ func (j *ScheduledJob) Template() (string, error) { ScheduleExpression: schedule, StateMachine: stateMachine, LogConfig: convertLogging(j.manifest.Logging), + Storage: storage, }) if err != nil { return "", fmt.Errorf("parse scheduled job template: %w", err) diff --git a/internal/pkg/deploy/cloudformation/stack/transformers.go b/internal/pkg/deploy/cloudformation/stack/transformers.go index 93f00d2866d..03502f10947 100644 --- a/internal/pkg/deploy/cloudformation/stack/transformers.go +++ b/internal/pkg/deploy/cloudformation/stack/transformers.go @@ -54,14 +54,19 @@ func convertSidecar(s map[string]*manifest.SidecarConfig) ([]*template.SidecarOp if err != nil { return nil, err } + mp, err := renderSidecarMountPoints(config.MountPoints) + if err != nil { + return nil, err + } sidecars = append(sidecars, &template.SidecarOpts{ - Name: aws.String(name), - Image: config.Image, - Port: port, - Protocol: protocol, - CredsParam: config.CredsParam, - Secrets: config.Secrets, - Variables: config.Variables, + Name: aws.String(name), + Image: config.Image, + Port: port, + Protocol: protocol, + CredsParam: config.CredsParam, + Secrets: config.Secrets, + Variables: config.Variables, + MountPoints: mp, }) } return sidecars, nil From ee1503da211d95cd894becfbddee9f5642dedb16 Mon Sep 17 00:00:00 2001 From: Austin Ely Date: Fri, 19 Feb 2021 23:55:55 +0000 Subject: [PATCH 2/4] chore(template): ensure that storage renders nil if empty; add tests --- .../cloudformation/stack/backend_svc.go | 2 +- .../deploy/cloudformation/stack/lb_web_svc.go | 2 +- .../cloudformation/stack/scheduled_job.go | 2 +- .../stack/testdata/workloads/job-manifest.yml | 8 +++++++ .../testdata/workloads/job-test.stack.yml | 23 ++++++++++++++++++- .../cloudformation/stack/transformers.go | 3 +++ internal/pkg/manifest/lb_web_svc_test.go | 10 ++++---- internal/pkg/manifest/workload.go | 2 +- templates/workloads/jobs/scheduled-job/cf.yml | 6 ++--- .../workloads/partials/cf/mount-points.yml | 2 +- templates/workloads/partials/cf/sidecars.yml | 2 +- templates/workloads/partials/cf/volumes.yml | 6 ++--- templates/workloads/services/backend/cf.yml | 6 ++--- templates/workloads/services/lb-web/cf.yml | 6 ++--- 14 files changed, 56 insertions(+), 24 deletions(-) diff --git a/internal/pkg/deploy/cloudformation/stack/backend_svc.go b/internal/pkg/deploy/cloudformation/stack/backend_svc.go index 1b94cdfacc7..fd67ef40405 100644 --- a/internal/pkg/deploy/cloudformation/stack/backend_svc.go +++ b/internal/pkg/deploy/cloudformation/stack/backend_svc.go @@ -83,7 +83,7 @@ func (s *BackendService) Template() (string, error) { if err != nil { return "", fmt.Errorf("convert the Auto Scaling configuration for service %s: %w", s.name, err) } - storage, err := convertStorageOpts(&s.manifest.Storage) + storage, err := convertStorageOpts(s.manifest.Storage) if err != nil { return "", fmt.Errorf("convert storage options for service %s: %w", s.name, err) } diff --git a/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go b/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go index ce11ac25db9..bd149066d02 100644 --- a/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go +++ b/internal/pkg/deploy/cloudformation/stack/lb_web_svc.go @@ -113,7 +113,7 @@ func (s *LoadBalancedWebService) Template() (string, error) { return "", fmt.Errorf("convert the Auto Scaling configuration for service %s: %w", s.name, err) } - storage, err := convertStorageOpts(&s.manifest.Storage) + storage, err := convertStorageOpts(s.manifest.Storage) if err != nil { return "", fmt.Errorf("convert storage options for service %s: %w", s.name, err) } diff --git a/internal/pkg/deploy/cloudformation/stack/scheduled_job.go b/internal/pkg/deploy/cloudformation/stack/scheduled_job.go index 64f00c0287f..21c995b2457 100644 --- a/internal/pkg/deploy/cloudformation/stack/scheduled_job.go +++ b/internal/pkg/deploy/cloudformation/stack/scheduled_job.go @@ -137,7 +137,7 @@ func (j *ScheduledJob) Template() (string, error) { return "", fmt.Errorf("convert retry/timeout config for job %s: %w", j.name, err) } - storage, err := convertStorageOpts(&j.manifest.Storage) + storage, err := convertStorageOpts(j.manifest.Storage) if err != nil { return "", fmt.Errorf("convert storage options for job %s: %w", j.name, err) } diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml index 2b245da196d..590792bb943 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml @@ -24,6 +24,14 @@ retries: 3 # Optional. The timeout after which to stop the job if it's still running. You can use the units (h, m, s). timeout: 1h +storage: + volumes: + myEFSVolume: + path: '/var/www' + read_only: true + efs: + id: fs-12345 + # Optional fields for more advanced use-cases. # #variables: # Pass environment variables as key value pairs. diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml index 42cd9d5ee5c..426d1e58c41 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml @@ -73,6 +73,19 @@ Resources: awslogs-region: !Ref AWS::Region awslogs-group: !Ref LogGroup awslogs-stream-prefix: copilot + + MountPoints: + - ContainerPath: /var/www + ReadOnly: true + SourceVolume: myEFSVolume + Volumes: + - Name: myEFSVolume + EFSVolumeConfiguration: + FilesystemId: fs-12345 + RootDirectory: / + TransitEncryption: ENABLED + AuthorizationConfig: + IAM: DISABLED ExecutionRole: @@ -145,7 +158,15 @@ Resources: StringEquals: 'iam:ResourceTag/copilot-application': !Sub '${AppName}' 'iam:ResourceTag/copilot-environment': !Sub '${EnvName}' - + - PolicyName: 'GrantEFSAccessfs-12345' + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Action: + - 'elasticfilesystem:ClientMount' + Resource: + - !Sub 'arn:aws:elasticfilesystem:${AWS::Region}:${AWS::AccountId}:file-system/fs-12345' Rule: Metadata: diff --git a/internal/pkg/deploy/cloudformation/stack/transformers.go b/internal/pkg/deploy/cloudformation/stack/transformers.go index 03502f10947..32d27e482b1 100644 --- a/internal/pkg/deploy/cloudformation/stack/transformers.go +++ b/internal/pkg/deploy/cloudformation/stack/transformers.go @@ -159,6 +159,9 @@ func logConfigOpts(lc *manifest.Logging) *template.LogConfigOpts { // convertStorageOpts converts a manifest Storage field into template data structures which can be used // to execute CFN templates func convertStorageOpts(in *manifest.Storage) (*template.StorageOpts, error) { + if in == nil { + return nil, nil + } v, err := renderVolumes(in.Volumes) if err != nil { return nil, err diff --git a/internal/pkg/manifest/lb_web_svc_test.go b/internal/pkg/manifest/lb_web_svc_test.go index 4dc019612f7..74de8d3a3e1 100644 --- a/internal/pkg/manifest/lb_web_svc_test.go +++ b/internal/pkg/manifest/lb_web_svc_test.go @@ -159,7 +159,7 @@ func TestLoadBalancedWebService_ApplyEnv(t *testing.T) { Count: Count{ Value: aws.Int(1), }, - Storage: Storage{ + Storage: &Storage{ Volumes: map[string]Volume{ "myEFSVolume": { MountPointOpts: MountPointOpts{ @@ -205,7 +205,7 @@ func TestLoadBalancedWebService_ApplyEnv(t *testing.T) { Count: Count{ Value: aws.Int(1), }, - Storage: Storage{ + Storage: &Storage{ Volumes: map[string]Volume{ "myEFSVolume": { MountPointOpts: MountPointOpts{ @@ -259,7 +259,7 @@ func TestLoadBalancedWebService_ApplyEnv(t *testing.T) { "GITHUB_TOKEN": "1111", "TWILIO_TOKEN": "1111", }, - Storage: Storage{ + Storage: &Storage{ Volumes: map[string]Volume{ "myEFSVolume": { MountPointOpts: MountPointOpts{ @@ -311,7 +311,7 @@ func TestLoadBalancedWebService_ApplyEnv(t *testing.T) { Variables: map[string]string{ "DDB_TABLE_NAME": "awards-prod", }, - Storage: Storage{ + Storage: &Storage{ Volumes: map[string]Volume{ "myEFSVolume": { EFS: EFSVolumeConfiguration{ @@ -385,7 +385,7 @@ func TestLoadBalancedWebService_ApplyEnv(t *testing.T) { "GITHUB_TOKEN": "1111", "TWILIO_TOKEN": "1111", }, - Storage: Storage{ + Storage: &Storage{ Volumes: map[string]Volume{ "myEFSVolume": { MountPointOpts: MountPointOpts{ diff --git a/internal/pkg/manifest/workload.go b/internal/pkg/manifest/workload.go index 2ba9b14c20c..8fc9719ae8d 100644 --- a/internal/pkg/manifest/workload.go +++ b/internal/pkg/manifest/workload.go @@ -215,7 +215,7 @@ type TaskConfig struct { Count Count `yaml:"count"` Variables map[string]string `yaml:"variables"` Secrets map[string]string `yaml:"secrets"` - Storage Storage `yaml:"storage"` + Storage *Storage `yaml:"storage"` } // WorkloadProps contains properties for creating a new workload manifest. diff --git a/templates/workloads/jobs/scheduled-job/cf.yml b/templates/workloads/jobs/scheduled-job/cf.yml index 0d3c118b4b2..a1da49adf68 100644 --- a/templates/workloads/jobs/scheduled-job/cf.yml +++ b/templates/workloads/jobs/scheduled-job/cf.yml @@ -44,11 +44,11 @@ Resources: {{include "envvars" . | indent 10}} {{include "secrets" . | indent 10}} {{include "logconfig" . | indent 10}} -{{- if .Storage}} +{{- if .Storage -}} {{include "mount-points" . | indent 10}} -{{- end}} +{{- end -}} {{include "sidecars" . | indent 8}} -{{- if .Storage}} +{{- if .Storage -}} {{include "volumes" . | indent 6}} {{- end}} {{include "executionrole" . | indent 2}} diff --git a/templates/workloads/partials/cf/mount-points.yml b/templates/workloads/partials/cf/mount-points.yml index b6699b58852..fba74870902 100644 --- a/templates/workloads/partials/cf/mount-points.yml +++ b/templates/workloads/partials/cf/mount-points.yml @@ -1,7 +1,7 @@ {{- if .Storage.MountPoints}} MountPoints: {{- range $mp := .Storage.MountPoints}} - - ContainerPath: {{$mp.ContainerPath}} + - ContainerPath: '{{$mp.ContainerPath}}' ReadOnly: {{$mp.ReadOnly}} SourceVolume: {{$mp.SourceVolume}} {{- end -}} diff --git a/templates/workloads/partials/cf/sidecars.yml b/templates/workloads/partials/cf/sidecars.yml index c0cab21eb9e..f7f0424f9af 100644 --- a/templates/workloads/partials/cf/sidecars.yml +++ b/templates/workloads/partials/cf/sidecars.yml @@ -50,7 +50,7 @@ {{- range $mp := $sidecar.MountPoints}} - SourceVolume: {{$mp.SourceVolume}} ReadOnly: {{$mp.ReadOnly}} - ContainerPath: {{$mp.ContainerPath}} + ContainerPath: '{{$mp.ContainerPath}}' {{- end}} {{- end}} {{- end}} \ No newline at end of file diff --git a/templates/workloads/partials/cf/volumes.yml b/templates/workloads/partials/cf/volumes.yml index 11ffcbc415f..55b844c3cb9 100644 --- a/templates/workloads/partials/cf/volumes.yml +++ b/templates/workloads/partials/cf/volumes.yml @@ -4,7 +4,7 @@ Volumes: - Name: {{$vol.Name}} EFSVolumeConfiguration: FilesystemId: {{$vol.Filesystem}} - RootDirectory: {{$vol.RootDirectory}} + RootDirectory: '{{$vol.RootDirectory}}' TransitEncryption: ENABLED {{- if or $vol.AccessPointID $vol.IAM}} AuthorizationConfig: @@ -15,5 +15,5 @@ Volumes: IAM: {{$vol.IAM}} {{- end}} {{- end}} -{{- end}} -{{- end}} +{{- end -}} +{{- end -}} diff --git a/templates/workloads/services/backend/cf.yml b/templates/workloads/services/backend/cf.yml index 2c7a4294805..df3047af240 100644 --- a/templates/workloads/services/backend/cf.yml +++ b/templates/workloads/services/backend/cf.yml @@ -56,11 +56,11 @@ Resources: StartPeriod: {{.HealthCheck.StartPeriod}} Timeout: {{.HealthCheck.Timeout}} {{- end}} -{{- if .Storage}} +{{- if .Storage -}} {{include "mount-points" . | indent 10}} -{{- end}} +{{- end -}} {{include "sidecars" . | indent 8}} -{{- if .Storage}} +{{- if .Storage -}} {{include "volumes" . | indent 6}} {{- end}} {{include "executionrole" . | indent 2}} diff --git a/templates/workloads/services/lb-web/cf.yml b/templates/workloads/services/lb-web/cf.yml index 76596944161..2241d5578d3 100644 --- a/templates/workloads/services/lb-web/cf.yml +++ b/templates/workloads/services/lb-web/cf.yml @@ -67,11 +67,11 @@ Resources: Value: !GetAtt EnvControllerAction.PublicLoadBalancerDNSName {{include "secrets" . | indent 10}} {{include "logconfig" . | indent 10}} -{{- if .Storage}} +{{- if .Storage -}} {{include "mount-points" . | indent 10}} -{{- end}} +{{- end -}} {{include "sidecars" . | indent 8}} -{{if .Storage}} +{{if .Storage -}} {{include "volumes" . | indent 6}} {{- end}} {{include "executionrole" . | indent 2}} From 350f992dcb6d4f4b62dfdbc66f6425dcce23df1b Mon Sep 17 00:00:00 2001 From: Austin Ely Date: Wed, 24 Feb 2021 00:40:21 +0000 Subject: [PATCH 3/4] feat: enable mounting EFS volumes in copilot jobs, services, and sidecars --- .../stack/testdata/workloads/job-test.stack.yml | 2 ++ internal/pkg/template/template_functions.go | 2 +- internal/pkg/template/template_functions_test.go | 6 +++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml index 426d1e58c41..4cac96a06d8 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml @@ -66,6 +66,8 @@ Resources: Value: !Sub '${EnvName}' - Name: COPILOT_SERVICE_NAME Value: !Sub '${WorkloadName}' + - Name: COPILOT_MOUNT_POINTS + Value: '{"myEFSVolume":"/var/www"}' LogConfiguration: LogDriver: awslogs diff --git a/internal/pkg/template/template_functions.go b/internal/pkg/template/template_functions.go index 63b5f9fbf8f..4a39c80eccd 100644 --- a/internal/pkg/template/template_functions.go +++ b/internal/pkg/template/template_functions.go @@ -101,7 +101,7 @@ func QuotePSliceFunc(elems []*string) []string { // generateMountPointJSON turns a list of MountPoint objects into a JSON string: // `{"myEFSVolume": "/var/www", "myEBSVolume": "/usr/data"}` // This function must be called on an array of correctly constructed MountPoint objects. -func generateMountPointJSON(mountPoints []MountPoint) string { +func generateMountPointJSON(mountPoints []*MountPoint) string { volumeMap := make(map[string]string) for _, mp := range mountPoints { diff --git a/internal/pkg/template/template_functions_test.go b/internal/pkg/template/template_functions_test.go index 1666248f6fa..b7385febccc 100644 --- a/internal/pkg/template/template_functions_test.go +++ b/internal/pkg/template/template_functions_test.go @@ -233,7 +233,7 @@ func TestQuotePSliceFunc(t *testing.T) { } func TestGenerateMountPointJSON(t *testing.T) { - require.Equal(t, `{"myEFSVolume":"/var/www"}`, generateMountPointJSON([]MountPoint{{ContainerPath: aws.String("/var/www"), SourceVolume: aws.String("myEFSVolume")}}), "JSON should render correctly") - require.Equal(t, "{}", generateMountPointJSON([]MountPoint{}), "nil list of arguments should render ") - require.Equal(t, "{}", generateMountPointJSON([]MountPoint{{SourceVolume: aws.String("fromEFS")}}), "empty paths should not get injected") + require.Equal(t, `{"myEFSVolume":"/var/www"}`, generateMountPointJSON([]*MountPoint{{ContainerPath: aws.String("/var/www"), SourceVolume: aws.String("myEFSVolume")}}), "JSON should render correctly") + require.Equal(t, "{}", generateMountPointJSON([]*MountPoint{}), "nil list of arguments should render ") + require.Equal(t, "{}", generateMountPointJSON([]*MountPoint{{SourceVolume: aws.String("fromEFS")}}), "empty paths should not get injected") } From 282a9840d9562556b7cdf4ad42ef28ae60e82d6a Mon Sep 17 00:00:00 2001 From: Austin Ely Date: Wed, 24 Feb 2021 01:13:38 +0000 Subject: [PATCH 4/4] test(stack): add integ test for efs volume in sidecar --- .../stack/testdata/workloads/job-manifest.yml | 11 ++++++++- .../testdata/workloads/job-test.stack.yml | 24 +++++++++++++++++-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml index 590792bb943..ef73b679220 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-manifest.yml @@ -27,11 +27,20 @@ timeout: 1h storage: volumes: myEFSVolume: - path: '/var/www' + path: '/etc/mount1' read_only: true efs: id: fs-12345 +sidecars: + nginx: + image: public.ecr.aws/nginx/nginx + port: 8080 + mount_points: + - source_volume: myEFSVolume + path: '/var/www' + variables: + NGINX_PORT: 8080 # Optional fields for more advanced use-cases. # #variables: # Pass environment variables as key value pairs. diff --git a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml index 4cac96a06d8..53f70432947 100644 --- a/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml +++ b/internal/pkg/deploy/cloudformation/stack/testdata/workloads/job-test.stack.yml @@ -67,7 +67,7 @@ Resources: - Name: COPILOT_SERVICE_NAME Value: !Sub '${WorkloadName}' - Name: COPILOT_MOUNT_POINTS - Value: '{"myEFSVolume":"/var/www"}' + Value: '{"myEFSVolume":"/etc/mount1"}' LogConfiguration: LogDriver: awslogs @@ -77,9 +77,29 @@ Resources: awslogs-stream-prefix: copilot MountPoints: - - ContainerPath: /var/www + - ContainerPath: /etc/mount1 ReadOnly: true SourceVolume: myEFSVolume + - Name: nginx + Image: 'public.ecr.aws/nginx/nginx' + LogConfiguration: + LogDriver: awslogs + Options: + awslogs-group: !Ref LogGroup + awslogs-region: !Ref AWS::Region + awslogs-stream-prefix: copilot + MountPoints: + - ContainerPath: '/var/www' + ReadOnly: true + SourceVolume: myEFSVolume + PortMappings: + - ContainerPort: 8080 + Environment: + - Name: NGINX_PORT + Value: '8080' + - Name: COPILOT_MOUNT_POINTS + Value: '{"myEFSVolume":"/var/www"}' + Volumes: - Name: myEFSVolume EFSVolumeConfiguration: