Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ Additionally, the following environment variable(s) can be used to configure the
| `ECS_ALLOW_OFFHOST_INTROSPECTION_ACCESS` | <true | false> | By default, the ecs-init service adds an iptable rule to block access to ECS Agent's introspection port from off-host (or containers in awsvpc network mode), and removes the rule upon stop. If `ECS_ALLOW_OFFHOST_INTROSPECTION_ACCESS` is set to true, this rule will not be added/removed. | false |
| `ECS_OFFHOST_INTROSPECTION_INTERFACE_NAME` | `eth0` | Primary network interface name to be used for blocking offhost agent introspection port access. By default, this value is `eth0` | `eth0` |
| `ECS_AGENT_LABELS` | `{"test.label.1":"value1","test.label.2":"value2"}` | The labels to add to the ECS Agent container. | |
| `ECS_PAUSE_LABELS` | `{"test.pause.label.1":"value1","test.pause.label.2":"value2"}` | The labels to add to the pause container. | |
| `ECS_AGENT_APPARMOR_PROFILE` | `unconfined` | Specifies the name of the AppArmor profile to run the ecs-agent container under. This only applies to AppArmor-enabled systems, such as Ubuntu, Debian, and SUSE. If unset, defaults to the profile written out by ecs-init (ecs-agent-default). | `ecs-agent-default` |
| `ECS_AGENT_PID_NAMESPACE_HOST` | <true | false> | By default, the ECS agent container runs with its own PID namespace. If ECS_AGENT_PID_NAMESPACE_HOST is set to true, ecs-init will start the ECS agent container with the host's PID namespace. This is particularly useful when running on SELinux-enforcing hosts with Docker's SELinux option enabled. | false |

Expand Down
32 changes: 32 additions & 0 deletions agent/api/task/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"reflect"
"strconv"
Expand Down Expand Up @@ -55,6 +56,7 @@ import (
"github.com/aws/amazon-ecs-agent/ecs-agent/utils/arn"
"github.com/aws/amazon-ecs-agent/ecs-agent/utils/ttime"
ecstypes "github.com/aws/aws-sdk-go-v2/service/ecs/types"
log "github.com/cihub/seelog"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/docker/docker/api/types"
Expand Down Expand Up @@ -163,6 +165,8 @@ const (
serviceConnectAttachmentType = "serviceconnectdetail"

ipv6LoopbackAddress = "::1"

PAUSE_LABELS_ENV_VAR = "ECS_PAUSE_LABELS"
)

// TaskOverrides are the overrides applied to a task
Expand Down Expand Up @@ -1811,6 +1815,14 @@ func (task *Task) dockerConfig(container *apicontainer.Container, apiVersion doc
containerConfig.Labels = make(map[string]string)
}

switch container.Type {
case apicontainer.ContainerCNIPause, apicontainer.ContainerNamespacePause:
if pauseLabels := os.Getenv(PAUSE_LABELS_ENV_VAR); pauseLabels != "" {
// Set labels to pause container if it's provieded as env var.
setLabelsFromJsonString(containerConfig, pauseLabels)
}
}

if container.Type == apicontainer.ContainerCNIPause && task.IsNetworkModeAWSVPC() {
// apply hostname to pause container's docker config
return task.applyENIHostname(containerConfig), nil
Expand All @@ -1819,6 +1831,26 @@ func (task *Task) dockerConfig(container *apicontainer.Container, apiVersion doc
return containerConfig, nil
}

// Parse label string and set them to the given container configuration.
func setLabelsFromJsonString(config *dockercontainer.Config, labelsString string) {
if len(labelsString) > 0 {
labels, err := toLabelMap(labelsString)
if err != nil {
log.Errorf("Skipped setting labels because of failed to decode. Error: %s", err)
return
}
if len(labels) > 0 {
config.Labels = labels
}
}
}

func toLabelMap(jsonBlock string) (map[string]string, error) {
out := map[string]string{}
err := json.Unmarshal([]byte(jsonBlock), &out)
return out, err
}

// dockerExposedPorts returns the container ports that need to be exposed for a container
// 1. For bridge-mode ServiceConnect-enabled tasks:
// 1a. Pause containers need to expose the port(s) for their associated task container. In particular, SC pause container
Expand Down
117 changes: 117 additions & 0 deletions agent/api/task/task_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5924,3 +5924,120 @@ func TestGenerateENIExtraHosts(t *testing.T) {
})
}
}

func TestDockerConfigPauseContainerLabelsWithoutEnvVar_ECS_PAUSE_LABELS(t *testing.T) {
testTask := &Task{
Arn: "arn:aws:ecs:us-east-1:012345678910:task/c09f0188-7f87-4b0f-bfc3-16296622b6fe",
Family: "myFamily",
Version: "1",
Containers: []*apicontainer.Container{
{
Name: "pause",
Type: apicontainer.ContainerCNIPause,
},
},
}

config, configErr := testTask.DockerConfig(testTask.Containers[0], defaultDockerClientAPIVersion)
if configErr != nil {
t.Fatal(configErr)
}

assert.Equal(t, 0, len(config.Labels))
}

func TestDockerConfigPauseContainerLabelsWithEnvVar_ECS_PAUSE_LABELS(t *testing.T) {
testTask := &Task{
Arn: "arn:aws:ecs:us-east-1:012345678910:task/c09f0188-7f87-4b0f-bfc3-16296622b6fe",
Family: "myFamily",
Version: "1",
Containers: []*apicontainer.Container{
{
Name: "pause",
Type: apicontainer.ContainerCNIPause,
},
},
}
labelsString := "{\"test.label.1\":\"test_a\",\"test.label.2\":\"test_b\"}"
t.Setenv("ECS_PAUSE_LABELS", labelsString)

config, configErr := testTask.DockerConfig(testTask.Containers[0], defaultDockerClientAPIVersion)
if configErr != nil {
t.Fatal(configErr)
}

assert.Equal(t, 2, len(config.Labels))
assert.Equal(t, "test_a", config.Labels["test.label.1"])
assert.Equal(t, "test_b", config.Labels["test.label.2"])
}

func TestDockerConfigNamespacePauseContainerLabelsWithEnvVar_ECS_PAUSE_LABELS(t *testing.T) {
testTask := &Task{
Arn: "arn:aws:ecs:us-east-1:012345678910:task/c09f0188-7f87-4b0f-bfc3-16296622b6fe",
Family: "myFamily",
Version: "1",
Containers: []*apicontainer.Container{
{
Name: "pause",
Type: apicontainer.ContainerNamespacePause,
},
},
}
labelsString := "{\"test.label.1\":\"test_a\",\"test.label.2\":\"test_b\"}"
t.Setenv("ECS_PAUSE_LABELS", labelsString)

config, configErr := testTask.DockerConfig(testTask.Containers[0], defaultDockerClientAPIVersion)
if configErr != nil {
t.Fatal(configErr)
}

assert.Equal(t, 2, len(config.Labels))
assert.Equal(t, "test_a", config.Labels["test.label.1"])
assert.Equal(t, "test_b", config.Labels["test.label.2"])
}

func TestDockerConfigPauseContainerLabelsWithInvalidEnvVar_ECS_PAUSE_LABELS(t *testing.T) {
testTask := &Task{
Arn: "arn:aws:ecs:us-east-1:012345678910:task/c09f0188-7f87-4b0f-bfc3-16296622b6fe",
Family: "myFamily",
Version: "1",
Containers: []*apicontainer.Container{
{
Name: "pause",
Type: apicontainer.ContainerCNIPause,
},
},
}
// Invalid format.
labelsString := "{\"test.label\":\"test\""
t.Setenv("ECS_PAUSE_LABELS", labelsString)

config, configErr := testTask.DockerConfig(testTask.Containers[0], defaultDockerClientAPIVersion)
if configErr != nil {
t.Fatal(configErr)
}

assert.Equal(t, 0, len(config.Labels))
}

func TestDockerConfigContainerLabelsWithEnvVar_ECS_PAUSE_LABELS(t *testing.T) {
testTask := &Task{
Arn: "arn:aws:ecs:us-east-1:012345678910:task/c09f0188-7f87-4b0f-bfc3-16296622b6fe",
Family: "myFamily",
Version: "1",
Containers: []*apicontainer.Container{
{
Name: "c1",
CPU: uint(10),
Memory: uint(256),
},
},
}

config, configErr := testTask.DockerConfig(testTask.Containers[0], defaultDockerClientAPIVersion)
if configErr != nil {
t.Fatal(configErr)
}

assert.Equal(t, 0, len(config.Labels))
}
Loading