Skip to content

Commit

Permalink
feat: add labels to allow runtime labels on containers (aws#2186)
Browse files Browse the repository at this point in the history
<!-- Provide summary of changes -->
Incorporates and builds on top of aws#2165. This addresses @efekarakus' feedback and adds a test. 

<!-- Issue number, if available. E.g. "Fixes aws#31", "Addresses aws#42, 77" -->

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
  • Loading branch information
bvtujo authored and thrau committed Dec 9, 2022
1 parent dd8a6ff commit 4049bf0
Show file tree
Hide file tree
Showing 21 changed files with 145 additions and 75 deletions.
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/backend_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ func (s *BackendService) Template() (string, error) {
WorkloadType: manifest.BackendServiceType,
HealthCheck: s.manifest.BackendServiceConfig.ImageConfig.HealthCheckOpts(),
LogConfig: convertLogging(s.manifest.Logging),
DockerLabels: s.manifest.ImageConfig.DockerLabels,
DesiredCountLambda: desiredCountLambda.String(),
EnvControllerLambda: envControllerLambda.String(),
Storage: storage,
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/lb_web_svc.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ func (s *LoadBalancedWebService) Template() (string, error) {
NestedStack: outputs,
Sidecars: sidecars,
LogConfig: convertLogging(s.manifest.Logging),
DockerLabels: s.manifest.ImageConfig.DockerLabels,
Autoscaling: autoscaling,
ExecuteCommand: convertExecuteCommand(&s.manifest.ExecuteCommand),
WorkloadType: manifest.LoadBalancedWebServiceType,
Expand Down
1 change: 1 addition & 0 deletions internal/pkg/deploy/cloudformation/stack/scheduled_job.go
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ func (j *ScheduledJob) Template() (string, error) {
ScheduleExpression: schedule,
StateMachine: stateMachine,
LogConfig: convertLogging(j.manifest.Logging),
DockerLabels: j.manifest.ImageConfig.DockerLabels,
Storage: storage,
Network: convertNetworkConfig(j.manifest.Network),
EntryPoint: entrypoint,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ name: job
type: Scheduled Job

image:
labels:
com.amazonaws.ecs.copilot.description: Hello world!
location: alpine

# Number of CPU units for the task.
Expand Down Expand Up @@ -44,6 +46,15 @@ sidecars:
path: '/var/www'
variables:
NGINX_PORT: 8080
labels:
com.amazonaws.ecs.copilot.sidecars.nginx.description: tricky

environments:
test:
image:
labels:
com.amazonaws.ecs.copilot.coollabel: Synecdoche

# Optional fields for more advanced use-cases.
#
#variables: # Pass environment variables as key value pairs.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,9 @@ Resources:
- ContainerPath: /etc/mount2
ReadOnly: false
SourceVolume: managedEFSVolume
DockerLabels:
com.amazonaws.ecs.copilot.coollabel: Synecdoche
com.amazonaws.ecs.copilot.description: Hello world!
- Name: nginx
Image: 'public.ecr.aws/nginx/nginx'
LogConfiguration:
Expand All @@ -167,6 +170,8 @@ Resources:
Value: '8080'
- Name: COPILOT_MOUNT_POINTS
Value: '{"myEFSVolume":"/var/www"}'
DockerLabels:
com.amazonaws.ecs.copilot.sidecars.nginx.description: tricky

Volumes:
- Name: managedEFSVolume
Expand Down
19 changes: 10 additions & 9 deletions internal/pkg/deploy/cloudformation/stack/transformers.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,16 @@ func convertSidecar(s map[string]*manifest.SidecarConfig) ([]*template.SidecarOp
return nil, err
}
sidecars = append(sidecars, &template.SidecarOpts{
Name: aws.String(name),
Image: config.Image,
Essential: config.Essential,
Port: port,
Protocol: protocol,
CredsParam: config.CredsParam,
Secrets: config.Secrets,
Variables: config.Variables,
MountPoints: mp,
Name: aws.String(name),
Image: config.Image,
Essential: config.Essential,
Port: port,
Protocol: protocol,
CredsParam: config.CredsParam,
Secrets: config.Secrets,
Variables: config.Variables,
MountPoints: mp,
DockerLabels: config.DockerLabels,
})
}
return sidecars, nil
Expand Down
20 changes: 14 additions & 6 deletions internal/pkg/deploy/cloudformation/stack/transformers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func Test_convertSidecar(t *testing.T) {
testCases := map[string]struct {
inPort string
inEssential bool
inLabels map[string]string

wanted *template.SidecarOpts
wantedErr error
Expand Down Expand Up @@ -62,6 +63,9 @@ func Test_convertSidecar(t *testing.T) {
"specify essential as false": {
inPort: "2000",
inEssential: false,
inLabels: map[string]string{
"com.amazonaws.ecs.copilot.sidecar.description": "wow",
},

wanted: &template.SidecarOpts{
Name: aws.String("foo"),
Expand All @@ -71,19 +75,23 @@ func Test_convertSidecar(t *testing.T) {
Secrets: mockMap,
Variables: mockMap,
Essential: aws.Bool(false),
DockerLabels: map[string]string{
"com.amazonaws.ecs.copilot.sidecar.description": "wow",
},
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
sidecar := map[string]*manifest.SidecarConfig{
"foo": {
CredsParam: mockCredsParam,
Image: mockImage,
Secrets: mockMap,
Variables: mockMap,
Essential: aws.Bool(tc.inEssential),
Port: aws.String(tc.inPort),
CredsParam: mockCredsParam,
Image: mockImage,
Secrets: mockMap,
Variables: mockMap,
Essential: aws.Bool(tc.inEssential),
Port: aws.String(tc.inPort),
DockerLabels: tc.inLabels,
},
}
got, err := convertSidecar(sidecar)
Expand Down
20 changes: 20 additions & 0 deletions internal/pkg/manifest/backend_svc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,14 @@ func TestBackendSvc_ApplyEnv(t *testing.T) {
ImageConfig: imageWithPortAndHealthcheck{
ServiceImageWithPort: ServiceImageWithPort{
Port: aws.Uint16(80),
Image: Image{
DockerLabels: map[string]string{
"com.amazonaws.ecs.copilot.description": "Hello world!",
},
},
},
},

TaskConfig: TaskConfig{
CPU: aws.Int(256),
Memory: aws.Int(256),
Expand All @@ -263,6 +269,15 @@ func TestBackendSvc_ApplyEnv(t *testing.T) {
},
Environments: map[string]*BackendServiceConfig{
"test": {
ImageConfig: imageWithPortAndHealthcheck{
ServiceImageWithPort: ServiceImageWithPort{
Image: Image{
DockerLabels: map[string]string{
"com.amazonaws.ecs.copilot.description": "Overridden!",
},
},
},
},
TaskConfig: TaskConfig{
Count: Count{
Autoscaling: Autoscaling{
Expand Down Expand Up @@ -326,6 +341,11 @@ func TestBackendSvc_ApplyEnv(t *testing.T) {
ImageConfig: imageWithPortAndHealthcheck{
ServiceImageWithPort: ServiceImageWithPort{
Port: aws.Uint16(80),
Image: Image{
DockerLabels: map[string]string{
"com.amazonaws.ecs.copilot.description": "Overridden!",
},
},
},
},
TaskConfig: TaskConfig{
Expand Down
20 changes: 11 additions & 9 deletions internal/pkg/manifest/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ type Workload struct {

// Image represents the workload's container image.
type Image struct {
Build BuildArgsOrString `yaml:"build"` // Build an image from a Dockerfile.
Location *string `yaml:"location"` // Use an existing image instead.
Build BuildArgsOrString `yaml:"build"` // Build an image from a Dockerfile.
Location *string `yaml:"location"` // Use an existing image instead.
DockerLabels map[string]string `yaml:"labels,flow"` // Apply Docker labels to the container at runtime.
}

// GetLocation returns the location of the image.
Expand Down Expand Up @@ -356,13 +357,14 @@ func (lc *Logging) GetEnableMetadata() *string {

// SidecarConfig represents the configurable options for setting up a sidecar container.
type SidecarConfig struct {
Port *string `yaml:"port"`
Image *string `yaml:"image"`
Essential *bool `yaml:"essential"`
CredsParam *string `yaml:"credentialsParameter"`
Variables map[string]string `yaml:"variables"`
Secrets map[string]string `yaml:"secrets"`
MountPoints []SidecarMountPoint `yaml:"mount_points"`
Port *string `yaml:"port"`
Image *string `yaml:"image"`
Essential *bool `yaml:"essential"`
CredsParam *string `yaml:"credentialsParameter"`
Variables map[string]string `yaml:"variables"`
Secrets map[string]string `yaml:"secrets"`
MountPoints []SidecarMountPoint `yaml:"mount_points"`
DockerLabels map[string]string `yaml:"labels"`
}

// TaskConfig represents the resource boundaries and environment variables for the containers in the task.
Expand Down
21 changes: 12 additions & 9 deletions internal/pkg/template/workload.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ var (
"secrets",
"executionrole",
"taskrole",
"workload-container",
"fargate-taskdef-base-properties",
"service-base-properties",
"servicediscovery",
Expand Down Expand Up @@ -76,15 +77,16 @@ type WorkloadNestedStackOpts struct {

// SidecarOpts holds configuration that's needed if the service has sidecar containers.
type SidecarOpts struct {
Name *string
Image *string
Essential *bool
Port *string
Protocol *string
CredsParam *string
Variables map[string]string
Secrets map[string]string
MountPoints []*MountPoint
Name *string
Image *string
Essential *bool
Port *string
Protocol *string
CredsParam *string
Variables map[string]string
Secrets map[string]string
MountPoints []*MountPoint
DockerLabels map[string]string
}

// StorageOpts holds data structures for rendering Volumes and Mount Points
Expand Down Expand Up @@ -207,6 +209,7 @@ type WorkloadOpts struct {
EntryPoint []string
Command []string
DomainAlias string
DockerLabels map[string]string

// Additional options for service templates.
WorkloadType string
Expand Down
2 changes: 2 additions & 0 deletions internal/pkg/template/workload_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestTemplate_ParseSvc(t *testing.T) {
mockBox.AddString("workloads/partials/cf/secrets.yml", "secrets")
mockBox.AddString("workloads/partials/cf/executionrole.yml", "executionrole")
mockBox.AddString("workloads/partials/cf/taskrole.yml", "taskrole")
mockBox.AddString("workloads/partials/cf/workload-container.yml", "workload-container")
mockBox.AddString("workloads/partials/cf/fargate-taskdef-base-properties.yml", "fargate-taskdef-base-properties")
mockBox.AddString("workloads/partials/cf/service-base-properties.yml", "service-base-properties")
mockBox.AddString("workloads/partials/cf/servicediscovery.yml", "servicediscovery")
Expand All @@ -57,6 +58,7 @@ func TestTemplate_ParseSvc(t *testing.T) {
secrets
executionrole
taskrole
workload-container
fargate-taskdef-base-properties
service-base-properties
servicediscovery
Expand Down
4 changes: 4 additions & 0 deletions site/content/docs/developing/sidecars.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ sidecars:
path: {{ path }}
# Whether to allow the sidecar read-only access to the volume. (Default true)
read_only: {{ bool }}
# Optional Docker labels to apply to this container.
labels:
{{ label key }} : {{ label value }}

```

Below is an example of specifying the [nginx](https://www.nginx.com/) sidecar container in a load balanced web service manifest.
Expand Down
4 changes: 4 additions & 0 deletions site/content/docs/manifest/backend-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ How long to wait before considering the health check failed, in seconds. Default
<span class="parent-field">image.healthcheck.</span><a id="image-healthcheck-start-period" href="#image-healthcheck-start-period" class="field">`start_period`</a> <span class="type">Duration</span>
Grace period within which to provide containers time to bootstrap before failed health checks count towards the maximum number of retries. Default is 0s.

<span class="parent-field">image.</span><a id="image-labels" href="#image-labels" class="field">`labels`</a><span class="type">Map</span>
An optional key/value map of [Docker labels](https://docs.docker.com/config/labels-custom-metadata/) to add to the container.

<div class="separator"></div>

<a id="entrypoint" href="#entrypoint" class="field">`entrypoint`</a> <span class="type">String or Array of Strings</span>
Expand Down Expand Up @@ -257,3 +260,4 @@ Optional. Defaults to `""`. The ID of the EFS access point to connect to. If usi

<a id="environments" href="#environments" class="field">`environments`</a> <span class="type">Map</span>
The environment section lets you override any value in your manifest based on the environment you're in. In the example manifest above, we're overriding the count parameter so that we can run 2 copies of our service in our prod environment.

4 changes: 4 additions & 0 deletions site/content/docs/manifest/lb-web-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ The `location` field follows the same definition as the [`image` parameter](http
<span class="parent-field">image.</span><a id="image-port" href="#image-port" class="field">`port`</a> <span class="type">Integer</span>
The port exposed in your Dockerfile. Copilot should parse this value for you from your `EXPOSE` instruction.

<span class="parent-field">image.</span><a id="image-labels" href="#image-labels" class="field">`labels`</a><span class="type">Map</span>
An optional key/value map of [Docker labels](https://docs.docker.com/config/labels-custom-metadata/) to add to the container.

<div class="separator"></div>

<a id="entrypoint" href="#entrypoint" class="field">`entrypoint`</a> <span class="type">String or Array of Strings</span>
Expand Down Expand Up @@ -288,3 +291,4 @@ Optional. Defaults to `""`. The ID of the EFS access point to connect to. If usi

<a id="environments" href="#environments" class="field">`environments`</a> <span class="type">Map</span>
The environment section lets you override any value in your manifest based on the environment you're in. In the example manifest above, we're overriding the count parameter so that we can run 2 copies of our service in our prod environment.

3 changes: 3 additions & 0 deletions site/content/docs/manifest/scheduled-job.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ All paths are relative to your workspace root.
Instead of building a container from a Dockerfile, you can specify an existing image name. Mutually exclusive with [`image.build`](#image-build).
The `location` field follows the same definition as the [`image` parameter](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task_definition_parameters.html#container_definition_image) in the Amazon ECS task definition.

<span class="parent-field">image.</span><a id="image-labels" href="#image-labels" class="field">`labels`</a><span class="type">Map</span>
An optional key/value map of [Docker labels](https://docs.docker.com/config/labels-custom-metadata/) to add to the container.

<div class="separator"></div>

<a id="entrypoint" href="#entrypoint" class="field">`entrypoint`</a> <span class="type">String or Array of Strings</span>
Expand Down
10 changes: 1 addition & 9 deletions templates/workloads/jobs/scheduled-job/cf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,7 @@ Resources:
Properties:
{{include "fargate-taskdef-base-properties" . | indent 6}}
ContainerDefinitions:
- Name: !Ref WorkloadName
Image: !Ref ContainerImage
{{include "envvars" . | indent 10}}
{{include "secrets" . | indent 10}}
{{include "logconfig" . | indent 10}}
{{include "image-overrides" . | indent 10}}
{{- if .Storage -}}
{{include "mount-points" . | indent 10}}
{{- end -}}
{{include "workload-container" . | indent 8}}
{{include "sidecars" . | indent 8}}
{{- if .Storage -}}
{{include "volumes" . | indent 6}}
Expand Down
6 changes: 5 additions & 1 deletion templates/workloads/partials/cf/envvars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ Environment:
{{- if .Storage}}{{if .Storage.MountPoints}}
- Name: COPILOT_MOUNT_POINTS
Value: '{{jsonMountPoints .Storage.MountPoints}}'
{{- end}}{{end}}
{{- end}}{{end}}
{{- if eq .WorkloadType "Load Balanced Web Service"}}
- Name: COPILOT_LB_DNS
Value: !GetAtt EnvControllerAction.PublicLoadBalancerDNSName
{{- end}}
4 changes: 4 additions & 0 deletions templates/workloads/partials/cf/sidecars.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
awslogs-region: !Ref AWS::Region
awslogs-group: !Ref LogGroup
awslogs-stream-prefix: copilot
{{- if $sidecar.DockerLabels}}
DockerLabels:{{range $name, $value := $sidecar.DockerLabels}}
{{$name | printf "%q"}}: {{$value | printf "%q"}}{{end}}
{{- end -}}
{{- if $sidecar.CredsParam}}
RepositoryCredentials:
CredentialsParameter: {{$sidecar.CredsParam}}
Expand Down
28 changes: 28 additions & 0 deletions templates/workloads/partials/cf/workload-container.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
- Name: !Ref WorkloadName
Image: !Ref ContainerImage
{{include "secrets" . | indent 2}}
{{include "envvars" . | indent 2}}
{{include "logconfig" . | indent 2}}
{{include "image-overrides" . | indent 2}}
{{- if .Storage -}}
{{include "mount-points" . | indent 2}}
{{- end -}}
{{- if .DockerLabels}}
DockerLabels:{{range $name, $value := .DockerLabels}}
{{$name | printf "%q"}}: {{$value | printf "%q"}}{{end}}
{{- end}}
{{- if eq .WorkloadType "Load Balanced Web Service"}}
PortMappings:
- ContainerPort: !Ref ContainerPort
{{- end}}
{{- if eq .WorkloadType "Backend Service"}}
PortMappings: !If [ExposePort, [{ContainerPort: !Ref ContainerPort}], !Ref "AWS::NoValue"]
{{- end}}
{{- if .HealthCheck}}
HealthCheck:
Command: {{quoteSlice .HealthCheck.Command | fmtSlice}}
Interval: {{.HealthCheck.Interval}}
Retries: {{.HealthCheck.Retries}}
StartPeriod: {{.HealthCheck.StartPeriod}}
Timeout: {{.HealthCheck.Timeout}}
{{- end}}
Loading

0 comments on commit 4049bf0

Please sign in to comment.