diff --git a/.gitmodules b/.gitmodules index 752bd66a628..f618d7e6c2e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "amazon-ecs-cni-plugins"] path = amazon-ecs-cni-plugins url = https://github.com/aws/amazon-ecs-cni-plugins.git +[submodule "amazon-vpc-cni-plugins"] + path = amazon-vpc-cni-plugins + url = https://github.com/aws/amazon-vpc-cni-plugins.git diff --git a/Makefile b/Makefile index ef24644fcbd..912e2f8666c 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ gobuild: # create output directories .out-stamp: - mkdir -p ./out/test-artifacts ./out/cni-plugins + mkdir -p ./out/test-artifacts ./out/cni-plugins ./out/amazon-ecs-cni-plugins ./out/amazon-vpc-cni-plugins touch .out-stamp # Basic go build @@ -237,21 +237,37 @@ ECS_CNI_REPOSITORY_REVISION=master # Variable to override cni repository location ECS_CNI_REPOSITORY_SRC_DIR=$(PWD)/amazon-ecs-cni-plugins +VPC_CNI_REPOSITORY_SRC_DIR=$(PWD)/amazon-vpc-cni-plugins get-cni-sources: - git submodule update --init --checkout + git submodule update --init --recursive --remote -cni-plugins: get-cni-sources .out-stamp - @docker build -f scripts/dockerfiles/Dockerfile.buildCNIPlugins -t "amazon/amazon-ecs-build-cniplugins:make" . +build-ecs-cni-plugins: + @docker build -f scripts/dockerfiles/Dockerfile.buildECSCNIPlugins -t "amazon/amazon-ecs-build-ecs-cni-plugins:make" . docker run --rm --net=none \ -e GIT_SHORT_HASH=$(shell cd $(ECS_CNI_REPOSITORY_SRC_DIR) && git rev-parse --short=8 HEAD) \ -e GIT_PORCELAIN=$(shell cd $(ECS_CNI_REPOSITORY_SRC_DIR) && git status --porcelain 2> /dev/null | wc -l | sed 's/^ *//') \ -u "$(USERID)" \ - -v "$(PWD)/out/cni-plugins:/go/src/github.com/aws/amazon-ecs-cni-plugins/bin/plugins" \ + -v "$(PWD)/out/amazon-ecs-cni-plugins:/go/src/github.com/aws/amazon-ecs-cni-plugins/bin/plugins" \ -v "$(ECS_CNI_REPOSITORY_SRC_DIR):/go/src/github.com/aws/amazon-ecs-cni-plugins" \ - "amazon/amazon-ecs-build-cniplugins:make" + "amazon/amazon-ecs-build-ecs-cni-plugins:make" @echo "Built amazon-ecs-cni-plugins successfully." +build-vpc-cni-plugins: + @docker build -f scripts/dockerfiles/Dockerfile.buildVPCCNIPlugins -t "amazon/amazon-ecs-build-vpc-cni-plugins:make" . + docker run --rm --net=none \ + -e GIT_SHORT_HASH=$(shell cd $(VPC_CNI_REPOSITORY_SRC_DIR) && git rev-parse --short=8 HEAD) \ + -u "$(USERID)" \ + -v "$(PWD)/out/amazon-vpc-cni-plugins:/go/src/github.com/aws/amazon-vpc-cni-plugins/build/linux_amd64" \ + -v "$(VPC_CNI_REPOSITORY_SRC_DIR):/go/src/github.com/aws/amazon-vpc-cni-plugins" \ + "amazon/amazon-ecs-build-vpc-cni-plugins:make" + @echo "Built amazon-vpc-cni-plugins successfully." + +cni-plugins: get-cni-sources .out-stamp build-ecs-cni-plugins build-vpc-cni-plugins + mv $(PWD)/out/amazon-ecs-cni-plugins/* $(PWD)/out/cni-plugins + mv $(PWD)/out/amazon-vpc-cni-plugins/* $(PWD)/out/cni-plugins + @echo "Built all cni plugins successfully." + ifeq (${BUILD_PLATFORM},aarch64) run-integ-tests: test-registry gremlin container-health-check-image run-sudo-tests . ./scripts/shared_env && go test -tags integration -timeout=20m -v ./agent/engine/... ./agent/stats/... ./agent/app/... diff --git a/agent/acs/handler/payload_handler.go b/agent/acs/handler/payload_handler.go index 1e29dad683b..7bf960b9223 100644 --- a/agent/acs/handler/payload_handler.go +++ b/agent/acs/handler/payload_handler.go @@ -19,6 +19,7 @@ import ( "github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs" "github.com/aws/amazon-ecs-agent/agent/api" + apiappmesh "github.com/aws/amazon-ecs-agent/agent/api/appmesh" apieni "github.com/aws/amazon-ecs-agent/agent/api/eni" apitask "github.com/aws/amazon-ecs-agent/agent/api/task" apitaskstatus "github.com/aws/amazon-ecs-agent/agent/api/task/status" @@ -222,6 +223,16 @@ func (payloadHandler *payloadRequestHandler) addPayloadTasks(payload *ecsacs.Pay apiTask.SetTaskENI(eni) } + // Add the app mesh information to task struct + if task.ProxyConfiguration != nil { + appmesh, err := apiappmesh.AppMeshFromACS(task.ProxyConfiguration) + if err != nil { + payloadHandler.handleUnrecognizedTask(task, err, payload) + allTasksOK = false + continue + } + apiTask.SetAppMesh(appmesh) + } if task.ExecutionRoleCredentials != nil { // The payload message contains execution credentials for the task. // Add the credentials to the credentials manager and set the diff --git a/agent/acs/handler/payload_handler_test.go b/agent/acs/handler/payload_handler_test.go index 004ac5600d4..eed8704f584 100644 --- a/agent/acs/handler/payload_handler_test.go +++ b/agent/acs/handler/payload_handler_test.go @@ -666,6 +666,78 @@ func TestPayloadHandlerAddedENIToTask(t *testing.T) { assert.Equal(t, aws.StringValue(expectedENI.Ipv6Addresses[0].Address), taskeni.IPV6Addresses[0].Address) } +func TestPayloadHandlerAddedAppMeshToTask(t *testing.T) { + appMeshType := "APPMESH" + mockEgressIgnoredIP1 := "128.0.0.1" + mockEgressIgnoredIP2 := "171.1.3.24" + mockAppPort1 := "8000" + mockAppPort2 := "8001" + mockEgressIgnoredPort1 := "13000" + mockEgressIgnoredPort2 := "13001" + mockIgnoredUID := "1337" + mockIgnoredGID := "2339" + mockProxyIngressPort := "9000" + mockProxyEgressPort := "9001" + mockAppPorts := mockAppPort1 + "," + mockAppPort2 + mockEgressIgnoredIPs := mockEgressIgnoredIP1 + "," + mockEgressIgnoredIP2 + mockEgressIgnoredPorts := mockEgressIgnoredPort1 + "," + mockEgressIgnoredPort2 + mockContainerName := "testEnvoyContainer" + taskMetadataEndpointIP := "169.254.170.2" + instanceMetadataEndpointIP := "169.254.169.254" + tester := setup(t) + defer tester.ctrl.Finish() + + var addedTask *apitask.Task + tester.mockTaskEngine.EXPECT().AddTask(gomock.Any()).Do( + func(task *apitask.Task) { + addedTask = task + }) + + payloadMessage := &ecsacs.PayloadMessage{ + Tasks: []*ecsacs.Task{ + { + Arn: aws.String("arn"), + ProxyConfiguration: &ecsacs.ProxyConfiguration{ + Type: aws.String(appMeshType), + Properties: map[string]*string{ + "IgnoredUID": aws.String(mockIgnoredUID), + "IgnoredGID": aws.String(mockIgnoredGID), + "ProxyIngressPort": aws.String(mockProxyIngressPort), + "ProxyEgressPort": aws.String(mockProxyEgressPort), + "AppPorts": aws.String(mockAppPorts), + "EgressIgnoredIPs": aws.String(mockEgressIgnoredIPs), + "EgressIgnoredPorts": aws.String(mockEgressIgnoredPorts), + }, + ContainerName: aws.String(mockContainerName), + }, + }, + }, + MessageId: aws.String(payloadMessageId), + } + + err := tester.payloadHandler.handleSingleMessage(payloadMessage) + assert.NoError(t, err) + + // Validate the added task has the eni information as expected + appMesh := addedTask.GetAppMesh() + assert.NotNil(t, appMesh) + assert.Equal(t, mockIgnoredUID, appMesh.IgnoredUID) + assert.Equal(t, mockIgnoredGID, appMesh.IgnoredGID) + assert.Equal(t, mockProxyIngressPort, appMesh.ProxyIngressPort) + assert.Equal(t, mockProxyEgressPort, appMesh.ProxyEgressPort) + assert.Equal(t, 2, len(appMesh.AppPorts)) + assert.Equal(t, mockAppPort1, appMesh.AppPorts[0]) + assert.Equal(t, mockAppPort2, appMesh.AppPorts[1]) + assert.Equal(t, 4, len(appMesh.EgressIgnoredIPs)) + assert.Equal(t, mockEgressIgnoredIP1, appMesh.EgressIgnoredIPs[0]) + assert.Equal(t, mockEgressIgnoredIP2, appMesh.EgressIgnoredIPs[1]) + assert.Equal(t, taskMetadataEndpointIP, appMesh.EgressIgnoredIPs[2]) + assert.Equal(t, instanceMetadataEndpointIP, appMesh.EgressIgnoredIPs[3]) + assert.Equal(t, 2, len(appMesh.EgressIgnoredPorts)) + assert.Equal(t, mockEgressIgnoredPort1, appMesh.EgressIgnoredPorts[0]) + assert.Equal(t, mockEgressIgnoredPort2, appMesh.EgressIgnoredPorts[1]) +} + func TestPayloadHandlerAddedECRAuthData(t *testing.T) { tester := setup(t) defer tester.ctrl.Finish() diff --git a/agent/acs/model/api/api-2.json b/agent/acs/model/api/api-2.json index a5004f3da42..bb65ae751b9 100644 --- a/agent/acs/model/api/api-2.json +++ b/agent/acs/model/api/api-2.json @@ -138,8 +138,8 @@ "AssociationType":{ "type":"string", "enum":[ - "gpu", - "elastic-inference" + "elastic-inference", + "gpu" ] }, "Associations":{ @@ -432,6 +432,18 @@ "type":"list", "member":{"shape":"PortMapping"} }, + "ProxyConfiguration":{ + "type":"structure", + "members":{ + "type":{"shape":"ProxyConfigurationType"}, + "containerName":{"shape":"String"}, + "properties":{"shape":"StringMap"} + } + }, + "ProxyConfigurationType":{ + "type":"string", + "enum":["APPMESH"] + }, "RegistryAuthenticationData":{ "type":"structure", "members":{ @@ -531,7 +543,8 @@ "memory":{"shape":"Integer"}, "associations":{"shape":"Associations"}, "pidMode":{"shape":"String"}, - "ipcMode":{"shape":"String"} + "ipcMode":{"shape":"String"}, + "proxyConfiguration":{"shape":"ProxyConfiguration"} } }, "TaskList":{ @@ -592,4 +605,4 @@ ] } } -} \ No newline at end of file +} diff --git a/agent/acs/model/ecsacs/api.go b/agent/acs/model/ecsacs/api.go index 5d1b3cfcdee..f6995dc9305 100644 --- a/agent/acs/model/ecsacs/api.go +++ b/agent/acs/model/ecsacs/api.go @@ -885,6 +885,26 @@ func (s PortMapping) GoString() string { return s.String() } +type ProxyConfiguration struct { + _ struct{} `type:"structure"` + + ContainerName *string `locationName:"containerName" type:"string"` + + Properties map[string]*string `locationName:"properties" type:"map"` + + Type *string `locationName:"type" type:"string" enum:"ProxyConfigurationType"` +} + +// String returns the string representation +func (s ProxyConfiguration) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ProxyConfiguration) GoString() string { + return s.String() +} + type RefreshTaskIAMRoleCredentialsInput struct { _ struct{} `type:"structure"` @@ -1080,6 +1100,8 @@ type Task struct { PidMode *string `locationName:"pidMode" type:"string"` + ProxyConfiguration *ProxyConfiguration `locationName:"proxyConfiguration" type:"structure"` + RoleCredentials *IAMRoleCredentials `locationName:"roleCredentials" type:"structure"` TaskDefinitionAccountId *string `locationName:"taskDefinitionAccountId" type:"string"` diff --git a/agent/api/appmesh/appmesh.go b/agent/api/appmesh/appmesh.go new file mode 100644 index 00000000000..cae3a347d5a --- /dev/null +++ b/agent/api/appmesh/appmesh.go @@ -0,0 +1,123 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. +package appmesh + +import ( + "fmt" + "strings" + + "github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs" + "github.com/aws/aws-sdk-go/aws" +) + +const ( + appMesh = "APPMESH" + splitter = "," + ignoredUID = "IgnoredUID" + ignoredGID = "IgnoredGID" + proxyIngressPort = "ProxyIngressPort" + proxyEgressPort = "ProxyEgressPort" + appPorts = "AppPorts" + egressIgnoredIPs = "EgressIgnoredIPs" + egressIgnoredPorts = "EgressIgnoredPorts" + taskMetadataEndpointIP = "169.254.170.2" + instanceMetadataEndpointIP = "169.254.169.254" +) + +// AppMesh contains information of app mesh config +type AppMesh struct { + // IgnoredUID is egress traffic from the processes owned by the UID will be ignored + IgnoredUID string + // IgnoredGID specifies egress traffic from the processes owned by the GID will be ignored + IgnoredGID string + // ProxyIngressPort is the ingress port number that proxy is listening on + ProxyIngressPort string + // ProxyEgressPort is the egress port number that proxy is listening on + ProxyEgressPort string + // AppPorts is the port number that application is listening on + AppPorts []string + // EgressIgnoredIPs is the list of ports for which egress traffic will be ignored + EgressIgnoredIPs []string + // EgressIgnoredPorts is the list of IPs for which egress traffic will be ignored + EgressIgnoredPorts []string +} + +// AppMeshFromACS validates proxy config if it is app mesh type and creates AppMesh object +func AppMeshFromACS(proxyConfig *ecsacs.ProxyConfiguration) (*AppMesh, error) { + + if *proxyConfig.Type != appMesh { + return nil, fmt.Errorf("agent does not support proxy type other than app mesh") + } + + return &AppMesh{ + IgnoredUID: aws.StringValue(proxyConfig.Properties[ignoredUID]), + IgnoredGID: aws.StringValue(proxyConfig.Properties[ignoredGID]), + ProxyIngressPort: aws.StringValue(proxyConfig.Properties[proxyIngressPort]), + ProxyEgressPort: aws.StringValue(proxyConfig.Properties[proxyEgressPort]), + AppPorts: buildAppPorts(proxyConfig), + EgressIgnoredIPs: buildEgressIgnoredIPs(proxyConfig), + EgressIgnoredPorts: buildEgressIgnoredPorts(proxyConfig), + }, nil +} + +// buildAppPorts creates app ports from proxy config +func buildAppPorts(proxyConfig *ecsacs.ProxyConfiguration) []string { + var inputAppPorts []string + if proxyConfig.Properties[appPorts] != nil { + inputAppPorts = strings.Split(*proxyConfig.Properties[appPorts], splitter) + } + return inputAppPorts +} + +// buildEgressIgnoredIPs creates egress ignored IPs from proxy config +func buildEgressIgnoredIPs(proxyConfig *ecsacs.ProxyConfiguration) []string { + var inputEgressIgnoredIPs []string + if proxyConfig.Properties[egressIgnoredIPs] != nil { + inputEgressIgnoredIPs = strings.Split(*proxyConfig.Properties[egressIgnoredIPs], splitter) + } + // append agent default egress ignored IPs + return appendDefaultEgressIgnoredIPs(inputEgressIgnoredIPs) +} + +// buildEgressIgnoredPorts creates egress ignored ports from proxy config +func buildEgressIgnoredPorts(proxyConfig *ecsacs.ProxyConfiguration) []string { + var inputEgressIgnoredPorts []string + if proxyConfig.Properties[egressIgnoredPorts] != nil { + inputEgressIgnoredPorts = strings.Split(*proxyConfig.Properties[egressIgnoredPorts], splitter) + } + return inputEgressIgnoredPorts +} + +// appendDefaultEgressIgnoredIPs append task metadata endpoint ip and +// instance metadata ip address to egress ignored IPs if does not exist +func appendDefaultEgressIgnoredIPs(egressIgnoredIPs []string) []string { + hasTaskMetadataEndpointIP := false + hasInstanceMetadataEndpointIP := false + for _, egressIgnoredIP := range egressIgnoredIPs { + if strings.TrimSpace(egressIgnoredIP) == taskMetadataEndpointIP { + hasTaskMetadataEndpointIP = true + } + if strings.TrimSpace(egressIgnoredIP) == instanceMetadataEndpointIP { + hasInstanceMetadataEndpointIP = true + } + } + + if !hasTaskMetadataEndpointIP { + egressIgnoredIPs = append(egressIgnoredIPs, taskMetadataEndpointIP) + } + if !hasInstanceMetadataEndpointIP { + egressIgnoredIPs = append(egressIgnoredIPs, instanceMetadataEndpointIP) + } + + return egressIgnoredIPs +} diff --git a/agent/api/appmesh/appmesh_test.go b/agent/api/appmesh/appmesh_test.go new file mode 100644 index 00000000000..2fe4ea40627 --- /dev/null +++ b/agent/api/appmesh/appmesh_test.go @@ -0,0 +1,112 @@ +// +build unit + +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"). You may +// not use this file except in compliance with the License. A copy of the +// License is located at +// +// http://aws.amazon.com/apache2.0/ +// +// or in the "license" file accompanying this file. This file is distributed +// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +// express or implied. See the License for the specific language governing +// permissions and limitations under the License. + +package appmesh + +import ( + "testing" + + "github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs" + "github.com/aws/aws-sdk-go/aws" + "github.com/stretchr/testify/assert" +) + +const ( + mockEgressIgnoredIP1 = "128.0.0.1" + mockEgressIgnoredIP2 = "171.1.3.24" + mockAppPort1 = "8000" + mockAppPort2 = "8001" + mockEgressIgnoredPort1 = "13000" + mockEgressIgnoredPort2 = "13001" + mockIgnoredUID = "1337" + mockIgnoredGID = "2339" + mockProxyIngressPort = "9000" + mockProxyEgressPort = "9001" + mockAppPorts = mockAppPort1 + splitter + mockAppPort2 + mockEgressIgnoredIPs = mockEgressIgnoredIP1 + splitter + mockEgressIgnoredIP2 + mockEgressIgnoredPorts = mockEgressIgnoredPort1 + splitter + mockEgressIgnoredPort2 + mockContainerName = "testEnvoyContainer" +) + +func TestAppMeshFromACS(t *testing.T) { + testProxyConfig := prepareProxyConfig() + + appMesh, err := AppMeshFromACS(&testProxyConfig) + + assert.NoError(t, err) + assert.NotNil(t, appMesh) + assert.Equal(t, mockIgnoredUID, appMesh.IgnoredUID) + assert.Equal(t, mockIgnoredGID, appMesh.IgnoredGID) + assert.Equal(t, mockProxyEgressPort, appMesh.ProxyEgressPort) + assert.Equal(t, mockProxyIngressPort, appMesh.ProxyIngressPort) + assert.Equal(t, mockAppPort1, appMesh.AppPorts[0]) + assert.Equal(t, mockAppPort2, appMesh.AppPorts[1]) + assert.Equal(t, mockEgressIgnoredIP1, appMesh.EgressIgnoredIPs[0]) + assert.Equal(t, mockEgressIgnoredIP2, appMesh.EgressIgnoredIPs[1]) + assert.Equal(t, taskMetadataEndpointIP, appMesh.EgressIgnoredIPs[2]) + assert.Equal(t, instanceMetadataEndpointIP, appMesh.EgressIgnoredIPs[3]) + assert.Equal(t, mockEgressIgnoredPort1, appMesh.EgressIgnoredPorts[0]) + assert.Equal(t, mockEgressIgnoredPort2, appMesh.EgressIgnoredPorts[1]) +} + +func TestAppMeshFromACSContainsDefaultEgressIgnoredIP(t *testing.T) { + testProxyConfig := prepareProxyConfig() + egressIgnoredIPs := mockEgressIgnoredIPs + splitter + taskMetadataEndpointIP + splitter + instanceMetadataEndpointIP + testProxyConfig.Properties[egressIgnoredIPs] = aws.String(egressIgnoredIPs) + + appMesh, err := AppMeshFromACS(&testProxyConfig) + + assert.NoError(t, err) + assert.NotNil(t, appMesh) + assert.Equal(t, mockIgnoredUID, appMesh.IgnoredUID) + assert.Equal(t, mockIgnoredGID, appMesh.IgnoredGID) + assert.Equal(t, mockProxyEgressPort, appMesh.ProxyEgressPort) + assert.Equal(t, mockProxyIngressPort, appMesh.ProxyIngressPort) + assert.Equal(t, mockAppPort1, appMesh.AppPorts[0]) + assert.Equal(t, mockAppPort2, appMesh.AppPorts[1]) + assert.Equal(t, mockEgressIgnoredIP1, appMesh.EgressIgnoredIPs[0]) + assert.Equal(t, mockEgressIgnoredIP2, appMesh.EgressIgnoredIPs[1]) + assert.Equal(t, taskMetadataEndpointIP, appMesh.EgressIgnoredIPs[2]) + assert.Equal(t, instanceMetadataEndpointIP, appMesh.EgressIgnoredIPs[3]) + assert.Equal(t, mockEgressIgnoredPort1, appMesh.EgressIgnoredPorts[0]) + assert.Equal(t, mockEgressIgnoredPort2, appMesh.EgressIgnoredPorts[1]) +} + +func TestAppMeshFromACSNonAppMeshProxyInput(t *testing.T) { + someOtherProxyType := "fooProxy" + testProxyConfig := prepareProxyConfig() + testProxyConfig.Type = &someOtherProxyType + + _, err := AppMeshFromACS(&testProxyConfig) + + assert.Error(t, err) +} + +func prepareProxyConfig() ecsacs.ProxyConfiguration { + + return ecsacs.ProxyConfiguration{ + Type: aws.String(appMesh), + Properties: map[string]*string{ + ignoredUID: aws.String(mockIgnoredUID), + ignoredGID: aws.String(mockIgnoredGID), + proxyIngressPort: aws.String(mockProxyIngressPort), + proxyEgressPort: aws.String(mockProxyEgressPort), + appPorts: aws.String(mockAppPorts), + egressIgnoredIPs: aws.String(mockEgressIgnoredIPs), + egressIgnoredPorts: aws.String(mockEgressIgnoredPorts), + }, + ContainerName: aws.String(mockContainerName), + } +} diff --git a/agent/api/task/task.go b/agent/api/task/task.go index ff8120ea248..09724d9f7fd 100644 --- a/agent/api/task/task.go +++ b/agent/api/task/task.go @@ -25,6 +25,7 @@ import ( "time" "github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs" + apiappmesh "github.com/aws/amazon-ecs-agent/agent/api/appmesh" apicontainer "github.com/aws/amazon-ecs-agent/agent/api/container" apicontainerstatus "github.com/aws/amazon-ecs-agent/agent/api/container/status" apieni "github.com/aws/amazon-ecs-agent/agent/api/eni" @@ -176,6 +177,9 @@ type Task struct { // ENI is the elastic network interface specified by this task ENI *apieni.ENI + // AppMesh is the service mesh specified by the task + AppMesh *apiappmesh.AppMesh + // MemoryCPULimitsEnabled to determine if task supports CPU, memory limits MemoryCPULimitsEnabled bool `json:"MemoryCPULimitsEnabled,omitempty"` @@ -693,8 +697,16 @@ func (task *Task) BuildCNIConfig() (*ecscni.Config, error) { } cfg := &ecscni.Config{} - eni := task.GetTaskENI() + convertENIToCNIConfig(task.GetTaskENI(), cfg) + if task.GetAppMesh() != nil { + convertAppMeshToCNIConfig(task.GetAppMesh(), cfg) + } + + return cfg, nil +} +// convertENIToCNIConfig converts input eni config into cni config +func convertENIToCNIConfig(eni *apieni.ENI, cfg *ecscni.Config) { cfg.ENIID = eni.ID cfg.ID = eni.MacAddress cfg.ENIMACAddress = eni.MacAddress @@ -705,13 +717,23 @@ func (task *Task) BuildCNIConfig() (*ecscni.Config, error) { break } } - // If there is ipv6 assigned to eni then set it if len(eni.IPV6Addresses) > 0 { cfg.ENIIPV6Address = eni.IPV6Addresses[0].Address } +} + +// convertAppMeshToCNIConfig converts input app mesh config into cni config +func convertAppMeshToCNIConfig(appMesh *apiappmesh.AppMesh, cfg *ecscni.Config) { + cfg.AppMeshCNIEnabled = true + cfg.IgnoredUID = appMesh.IgnoredUID + cfg.IgnoredGID = appMesh.IgnoredGID + cfg.ProxyIngressPort = appMesh.ProxyIngressPort + cfg.ProxyEgressPort = appMesh.ProxyEgressPort + cfg.AppPorts = appMesh.AppPorts + cfg.EgressIgnoredIPs = appMesh.EgressIgnoredIPs + cfg.EgressIgnoredPorts = appMesh.EgressIgnoredPorts - return cfg, nil } // isNetworkModeVPC checks if the task is configured to use task-networking feature @@ -1522,6 +1544,22 @@ func (task *Task) GetTaskENI() *apieni.ENI { return task.ENI } +// SetAppMesh sets the app mesh config of the task +func (task *Task) SetAppMesh(appMesh *apiappmesh.AppMesh) { + task.lock.Lock() + defer task.lock.Unlock() + + task.AppMesh = appMesh +} + +// GetAppMesh returns the app mesh config of the task +func (task *Task) GetAppMesh() *apiappmesh.AppMesh { + task.lock.RLock() + defer task.lock.RUnlock() + + return task.AppMesh +} + // GetStopSequenceNumber returns the stop sequence number of a task func (task *Task) GetStopSequenceNumber() int64 { task.lock.RLock() diff --git a/agent/app/agent_capability.go b/agent/app/agent_capability.go index 9fcd3324d0b..30bc8fed192 100644 --- a/agent/app/agent_capability.go +++ b/agent/app/agent_capability.go @@ -32,6 +32,7 @@ const ( capabilityTaskIAMRoleNetHost = "task-iam-role-network-host" taskENIAttributeSuffix = "task-eni" taskENIBlockInstanceMetadataAttributeSuffix = "task-eni-block-instance-metadata" + appMeshAttributeSuffix = "aws-appmesh" cniPluginVersionSuffix = "cni-plugin-version" capabilityTaskCPUMemLimit = "task-cpu-mem-limit" capabilityDockerPluginInfix = "docker-plugin." @@ -75,6 +76,7 @@ const ( // ecs.capability.pid-ipc-namespace-sharing // ecs.capability.ecr-endpoint // ecs.capability.secrets.asm.environment-variables +// ecs.capability.aws-appmesh // ecs.capability.task-eia func (agent *ecsAgent) capabilities() ([]*ecs.Attribute, error) { var capabilities []*ecs.Attribute @@ -138,6 +140,9 @@ func (agent *ecsAgent) capabilities() ([]*ecs.Attribute, error) { // ecs agent version 1.23.0 supports ecs secrets integrating with aws secrets manager capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+capabilitySecretEnvASM) + // ecs agent version 1.26.0 supports aws-appmesh cni plugin + capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+appMeshAttributeSuffix) + // support elastic inference in agent capabilities = appendNameOnlyAttribute(capabilities, attributePrefix+taskEIAAttributeSuffix) @@ -216,7 +221,7 @@ func (agent *ecsAgent) appendTaskCPUMemLimitCapabilities(capabilities []*ecs.Att func (agent *ecsAgent) appendTaskENICapabilities(capabilities []*ecs.Attribute) []*ecs.Attribute { if agent.cfg.TaskENIEnabled { - // The assumption here is that all of the dependecies for supporting the + // The assumption here is that all of the dependencies for supporting the // Task ENI in the Agent have already been validated prior to the invocation of // the `agent.capabilities()` call capabilities = append(capabilities, &ecs.Attribute{ diff --git a/agent/app/agent_capability_test.go b/agent/app/agent_capability_test.go index dc5ce1d1b8e..f8b2ef1525f 100644 --- a/agent/app/agent_capability_test.go +++ b/agent/app/agent_capability_test.go @@ -123,6 +123,9 @@ func TestCapabilities(t *testing.T) { { Name: aws.String(attributePrefix + capabilitySecretEnvASM), }, + { + Name: aws.String(attributePrefix + appMeshAttributeSuffix), + }, { Name: aws.String(attributePrefix + taskEIAAttributeSuffix), }, diff --git a/agent/app/agent_unix.go b/agent/app/agent_unix.go index 10b1ee49d5a..bae4ecf39c3 100644 --- a/agent/app/agent_unix.go +++ b/agent/app/agent_unix.go @@ -48,6 +48,7 @@ const initPID = 1 var awsVPCCNIPlugins = []string{ecscni.ECSENIPluginName, ecscni.ECSBridgePluginName, ecscni.ECSIPAMPluginName, + ecscni.ECSAppMeshPluginName, } // startWindowsService is not supported on Linux @@ -148,6 +149,7 @@ func isInstanceLaunchedInVPC(err error) bool { // a. ecs-eni // b. ecs-bridge // c. ecs-ipam +// d. aws-appmesh func (agent *ecsAgent) verifyCNIPluginsCapabilities() error { // Check if we can get capabilities from each plugin for _, plugin := range awsVPCCNIPlugins { @@ -155,6 +157,10 @@ func (agent *ecsAgent) verifyCNIPluginsCapabilities() error { if err != nil { return err } + // appmesh plugin is not needed for awsvpc networking capability + if plugin == ecscni.ECSAppMeshPluginName { + continue + } if !contains(capabilities, ecscni.CapabilityAWSVPCNetworkingMode) { return errors.Errorf("plugin '%s' doesn't support the capability: %s", plugin, ecscni.CapabilityAWSVPCNetworkingMode) diff --git a/agent/app/agent_unix_test.go b/agent/app/agent_unix_test.go index 1ba6e1f1a22..193f801ca83 100644 --- a/agent/app/agent_unix_test.go +++ b/agent/app/agent_unix_test.go @@ -170,6 +170,7 @@ func TestDoStartTaskENIHappyPath(t *testing.T) { cniClient.EXPECT().Capabilities(ecscni.ECSENIPluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSBridgePluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSIPAMPluginName).Return(cniCapabilities, nil), + cniClient.EXPECT().Capabilities(ecscni.ECSAppMeshPluginName).Return(cniCapabilities, nil), mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil), state.EXPECT().ENIByMac(gomock.Any()).Return(nil, false).AnyTimes(), mockCredentialsProvider.EXPECT().Retrieve().Return(credentials.Value{}, nil), @@ -320,6 +321,7 @@ func TestQueryCNIPluginsCapabilitiesHappyPath(t *testing.T) { cniClient.EXPECT().Capabilities(ecscni.ECSENIPluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSBridgePluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSIPAMPluginName).Return(cniCapabilities, nil), + cniClient.EXPECT().Capabilities(ecscni.ECSAppMeshPluginName).Return(cniCapabilities, nil), ) agent := &ecsAgent{ cniClient: cniClient, @@ -341,6 +343,26 @@ func TestQueryCNIPluginsCapabilitiesEmptyCapabilityListFromPlugin(t *testing.T) assert.Error(t, agent.verifyCNIPluginsCapabilities()) } +func TestQueryCNIPluginsCapabilitiesMissAppMesh(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + cniCapabilities := []string{ecscni.CapabilityAWSVPCNetworkingMode} + cniClient := mock_ecscni.NewMockCNIClient(ctrl) + gomock.InOrder( + cniClient.EXPECT().Capabilities(ecscni.ECSENIPluginName).Return(cniCapabilities, nil), + cniClient.EXPECT().Capabilities(ecscni.ECSBridgePluginName).Return(cniCapabilities, nil), + cniClient.EXPECT().Capabilities(ecscni.ECSIPAMPluginName).Return(cniCapabilities, nil), + cniClient.EXPECT().Capabilities(ecscni.ECSAppMeshPluginName).Return(nil, errors.New("error")), + ) + cfg := getTestConfig() + agent := &ecsAgent{ + cniClient: cniClient, + cfg: &cfg, + } + assert.Error(t, agent.verifyCNIPluginsCapabilities()) +} + func TestQueryCNIPluginsCapabilitiesErrorGettingCapabilitiesFromPlugin(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -446,6 +468,7 @@ func TestInitializeTaskENIDependenciesPauseLoaderError(t *testing.T) { cniClient.EXPECT().Capabilities(ecscni.ECSENIPluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSBridgePluginName).Return(cniCapabilities, nil), cniClient.EXPECT().Capabilities(ecscni.ECSIPAMPluginName).Return(cniCapabilities, nil), + cniClient.EXPECT().Capabilities(ecscni.ECSAppMeshPluginName).Return(cniCapabilities, nil), mockPauseLoader.EXPECT().LoadImage(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, loadErr), ) cfg := getTestConfig() diff --git a/agent/ecs_client/model/api/api-2.json b/agent/ecs_client/model/api/api-2.json index 22b24344c14..82200c1cece 100644 --- a/agent/ecs_client/model/api/api-2.json +++ b/agent/ecs_client/model/api/api-2.json @@ -6,7 +6,7 @@ "jsonVersion":"1.1", "protocol":"json", "serviceAbbreviation":"Amazon ECS", - "serviceFullName":"Amazon EC2 Container Service", + "serviceFullName":"Amazon Elastic Container Service", "serviceId":"ECS", "signatureVersion":"v4", "targetPrefix":"AmazonEC2ContainerServiceV20141113", @@ -1553,6 +1553,23 @@ "type":"list", "member":{"shape":"PortMapping"} }, + "ProxyConfiguration":{ + "type":"structure", + "required":["containerName"], + "members":{ + "type":{"shape":"ProxyConfigurationType"}, + "containerName":{"shape":"String"}, + "properties":{"shape":"ProxyConfigurationProperties"} + } + }, + "ProxyConfigurationProperties":{ + "type":"list", + "member":{"shape":"KeyValuePair"} + }, + "ProxyConfigurationType":{ + "type":"string", + "enum":["APPMESH"] + }, "PutAccountSettingRequest":{ "type":"structure", "required":[ @@ -1599,7 +1616,7 @@ "clientToken":{ "shape":"String" }, - "platformDevices":{"shape":"PlatformDevices"} + "platformDevices":{"shape":"PlatformDevices"} } }, "RegisterContainerInstanceResponse":{ @@ -1628,7 +1645,10 @@ "pidMode":{"shape":"PidMode"}, "ipcMode":{"shape":"IpcMode"}, "tags":{"shape":"Tags"}, - "inferenceAccelerators":{"shape":"InferenceAccelerators"} + "inferenceAccelerators":{"shape":"InferenceAccelerators"}, + "proxyConfiguration":{ + "shape":"ProxyConfiguration" + } } }, "RegisterTaskDefinitionResponse":{ @@ -1932,8 +1952,8 @@ "SystemControl":{ "type":"structure", "members":{ - "namespace":{"shape":"String"}, - "value":{"shape":"String"} + "namespace":{"shape":"String"}, + "value":{"shape":"String"} } }, "SystemControls":{ @@ -2028,7 +2048,10 @@ "memory":{"shape":"String"}, "pidMode":{"shape":"PidMode"}, "ipcMode":{"shape":"IpcMode"}, - "inferenceAccelerators":{"shape":"InferenceAccelerators"} + "inferenceAccelerators":{"shape":"InferenceAccelerators"}, + "proxyConfiguration":{ + "shape":"ProxyConfiguration" + } } }, "TaskDefinitionFamilyStatus":{ diff --git a/agent/ecs_client/model/ecs/api.go b/agent/ecs_client/model/ecs/api.go index d4e2465a1c6..8abdba7c40e 100644 --- a/agent/ecs_client/model/ecs/api.go +++ b/agent/ecs_client/model/ecs/api.go @@ -62,7 +62,7 @@ func (c *ECS) CreateClusterRequest(input *CreateClusterInput) (req *request.Requ return } -// CreateCluster API operation for Amazon EC2 Container Service. +// CreateCluster API operation for Amazon Elastic Container Service. // // Creates a new Amazon ECS cluster. By default, your account receives a default // cluster when you launch your first container instance. However, you can create @@ -80,7 +80,7 @@ func (c *ECS) CreateClusterRequest(input *CreateClusterInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation CreateCluster for usage and error information. // // Returned Error Codes: @@ -157,7 +157,7 @@ func (c *ECS) CreateServiceRequest(input *CreateServiceInput) (req *request.Requ return } -// CreateService API operation for Amazon EC2 Container Service. +// CreateService API operation for Amazon Elastic Container Service. // // Runs and maintains a desired number of tasks from a specified task definition. // If the number of tasks running in a service drops below desiredCount, Amazon @@ -227,7 +227,7 @@ func (c *ECS) CreateServiceRequest(input *CreateServiceInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation CreateService for usage and error information. // // Returned Error Codes: @@ -321,13 +321,13 @@ func (c *ECS) DeleteAccountSettingRequest(input *DeleteAccountSettingInput) (req return } -// DeleteAccountSetting API operation for Amazon EC2 Container Service. +// DeleteAccountSetting API operation for Amazon Elastic Container Service. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DeleteAccountSetting for usage and error information. // // Returned Error Codes: @@ -404,7 +404,7 @@ func (c *ECS) DeleteAttributesRequest(input *DeleteAttributesInput) (req *reques return } -// DeleteAttributes API operation for Amazon EC2 Container Service. +// DeleteAttributes API operation for Amazon Elastic Container Service. // // Deletes one or more custom attributes from an Amazon ECS resource. // @@ -412,7 +412,7 @@ func (c *ECS) DeleteAttributesRequest(input *DeleteAttributesInput) (req *reques // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DeleteAttributes for usage and error information. // // Returned Error Codes: @@ -490,7 +490,7 @@ func (c *ECS) DeleteClusterRequest(input *DeleteClusterInput) (req *request.Requ return } -// DeleteCluster API operation for Amazon EC2 Container Service. +// DeleteCluster API operation for Amazon Elastic Container Service. // // Deletes the specified cluster. You must deregister all container instances // from this cluster before you may delete it. You can list the container instances @@ -500,7 +500,7 @@ func (c *ECS) DeleteClusterRequest(input *DeleteClusterInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DeleteCluster for usage and error information. // // Returned Error Codes: @@ -594,7 +594,7 @@ func (c *ECS) DeleteServiceRequest(input *DeleteServiceInput) (req *request.Requ return } -// DeleteService API operation for Amazon EC2 Container Service. +// DeleteService API operation for Amazon Elastic Container Service. // // Deletes a specified service within a cluster. You can delete a service if // you have no running tasks in it and the desired task count is zero. If the @@ -615,7 +615,7 @@ func (c *ECS) DeleteServiceRequest(input *DeleteServiceInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DeleteService for usage and error information. // // Returned Error Codes: @@ -700,7 +700,7 @@ func (c *ECS) DeregisterContainerInstanceRequest(input *DeregisterContainerInsta return } -// DeregisterContainerInstance API operation for Amazon EC2 Container Service. +// DeregisterContainerInstance API operation for Amazon Elastic Container Service. // // Deregisters an Amazon ECS container instance from the specified cluster. // This instance is no longer available to run tasks. @@ -722,7 +722,7 @@ func (c *ECS) DeregisterContainerInstanceRequest(input *DeregisterContainerInsta // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DeregisterContainerInstance for usage and error information. // // Returned Error Codes: @@ -803,7 +803,7 @@ func (c *ECS) DeregisterTaskDefinitionRequest(input *DeregisterTaskDefinitionInp return } -// DeregisterTaskDefinition API operation for Amazon EC2 Container Service. +// DeregisterTaskDefinition API operation for Amazon Elastic Container Service. // // Deregisters the specified task definition by family and revision. Upon deregistration, // the task definition is marked as INACTIVE. Existing tasks and services that @@ -825,7 +825,7 @@ func (c *ECS) DeregisterTaskDefinitionRequest(input *DeregisterTaskDefinitionInp // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DeregisterTaskDefinition for usage and error information. // // Returned Error Codes: @@ -902,7 +902,7 @@ func (c *ECS) DescribeClustersRequest(input *DescribeClustersInput) (req *reques return } -// DescribeClusters API operation for Amazon EC2 Container Service. +// DescribeClusters API operation for Amazon Elastic Container Service. // // Describes one or more of your clusters. // @@ -910,7 +910,7 @@ func (c *ECS) DescribeClustersRequest(input *DescribeClustersInput) (req *reques // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DescribeClusters for usage and error information. // // Returned Error Codes: @@ -987,7 +987,7 @@ func (c *ECS) DescribeContainerInstancesRequest(input *DescribeContainerInstance return } -// DescribeContainerInstances API operation for Amazon EC2 Container Service. +// DescribeContainerInstances API operation for Amazon Elastic Container Service. // // Describes Amazon Elastic Container Service container instances. Returns metadata // about registered and remaining resources on each container instance requested. @@ -996,7 +996,7 @@ func (c *ECS) DescribeContainerInstancesRequest(input *DescribeContainerInstance // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DescribeContainerInstances for usage and error information. // // Returned Error Codes: @@ -1077,7 +1077,7 @@ func (c *ECS) DescribeServicesRequest(input *DescribeServicesInput) (req *reques return } -// DescribeServices API operation for Amazon EC2 Container Service. +// DescribeServices API operation for Amazon Elastic Container Service. // // Describes the specified services running in your cluster. // @@ -1085,7 +1085,7 @@ func (c *ECS) DescribeServicesRequest(input *DescribeServicesInput) (req *reques // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DescribeServices for usage and error information. // // Returned Error Codes: @@ -1166,7 +1166,7 @@ func (c *ECS) DescribeTaskDefinitionRequest(input *DescribeTaskDefinitionInput) return } -// DescribeTaskDefinition API operation for Amazon EC2 Container Service. +// DescribeTaskDefinition API operation for Amazon Elastic Container Service. // // Describes a task definition. You can specify a family and revision to find // information about a specific task definition, or you can simply specify the @@ -1179,7 +1179,7 @@ func (c *ECS) DescribeTaskDefinitionRequest(input *DescribeTaskDefinitionInput) // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DescribeTaskDefinition for usage and error information. // // Returned Error Codes: @@ -1256,7 +1256,7 @@ func (c *ECS) DescribeTasksRequest(input *DescribeTasksInput) (req *request.Requ return } -// DescribeTasks API operation for Amazon EC2 Container Service. +// DescribeTasks API operation for Amazon Elastic Container Service. // // Describes a specified task or tasks. // @@ -1264,7 +1264,7 @@ func (c *ECS) DescribeTasksRequest(input *DescribeTasksInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DescribeTasks for usage and error information. // // Returned Error Codes: @@ -1345,7 +1345,7 @@ func (c *ECS) DiscoverPollEndpointRequest(input *DiscoverPollEndpointInput) (req return } -// DiscoverPollEndpoint API operation for Amazon EC2 Container Service. +// DiscoverPollEndpoint API operation for Amazon Elastic Container Service. // // This action is only used by the Amazon ECS agent, and it is not intended // for use outside of the agent. @@ -1356,7 +1356,7 @@ func (c *ECS) DiscoverPollEndpointRequest(input *DiscoverPollEndpointInput) (req // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation DiscoverPollEndpoint for usage and error information. // // Returned Error Codes: @@ -1429,7 +1429,7 @@ func (c *ECS) ListAttributesRequest(input *ListAttributesInput) (req *request.Re return } -// ListAttributes API operation for Amazon EC2 Container Service. +// ListAttributes API operation for Amazon Elastic Container Service. // // Lists the attributes for Amazon ECS resources within a specified target type // and cluster. When you specify a target type and cluster, ListAttributes returns @@ -1443,7 +1443,7 @@ func (c *ECS) ListAttributesRequest(input *ListAttributesInput) (req *request.Re // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListAttributes for usage and error information. // // Returned Error Codes: @@ -1522,7 +1522,7 @@ func (c *ECS) ListClustersRequest(input *ListClustersInput) (req *request.Reques return } -// ListClusters API operation for Amazon EC2 Container Service. +// ListClusters API operation for Amazon Elastic Container Service. // // Returns a list of existing clusters. // @@ -1530,7 +1530,7 @@ func (c *ECS) ListClustersRequest(input *ListClustersInput) (req *request.Reques // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListClusters for usage and error information. // // Returned Error Codes: @@ -1663,7 +1663,7 @@ func (c *ECS) ListContainerInstancesRequest(input *ListContainerInstancesInput) return } -// ListContainerInstances API operation for Amazon EC2 Container Service. +// ListContainerInstances API operation for Amazon Elastic Container Service. // // Returns a list of container instances in a specified cluster. You can filter // the results of a ListContainerInstances operation with cluster query language @@ -1675,7 +1675,7 @@ func (c *ECS) ListContainerInstancesRequest(input *ListContainerInstancesInput) // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListContainerInstances for usage and error information. // // Returned Error Codes: @@ -1812,7 +1812,7 @@ func (c *ECS) ListServicesRequest(input *ListServicesInput) (req *request.Reques return } -// ListServices API operation for Amazon EC2 Container Service. +// ListServices API operation for Amazon Elastic Container Service. // // Lists the services that are running in a specified cluster. // @@ -1820,7 +1820,7 @@ func (c *ECS) ListServicesRequest(input *ListServicesInput) (req *request.Reques // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListServices for usage and error information. // // Returned Error Codes: @@ -1951,13 +1951,13 @@ func (c *ECS) ListTagsForResourceRequest(input *ListTagsForResourceInput) (req * return } -// ListTagsForResource API operation for Amazon EC2 Container Service. +// ListTagsForResource API operation for Amazon Elastic Container Service. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListTagsForResource for usage and error information. // // Returned Error Codes: @@ -2044,7 +2044,7 @@ func (c *ECS) ListTaskDefinitionFamiliesRequest(input *ListTaskDefinitionFamilie return } -// ListTaskDefinitionFamilies API operation for Amazon EC2 Container Service. +// ListTaskDefinitionFamilies API operation for Amazon Elastic Container Service. // // Returns a list of task definition families that are registered to your account // (which may include task definition families that no longer have any ACTIVE @@ -2058,7 +2058,7 @@ func (c *ECS) ListTaskDefinitionFamiliesRequest(input *ListTaskDefinitionFamilie // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListTaskDefinitionFamilies for usage and error information. // // Returned Error Codes: @@ -2191,7 +2191,7 @@ func (c *ECS) ListTaskDefinitionsRequest(input *ListTaskDefinitionsInput) (req * return } -// ListTaskDefinitions API operation for Amazon EC2 Container Service. +// ListTaskDefinitions API operation for Amazon Elastic Container Service. // // Returns a list of task definitions that are registered to your account. You // can filter the results by family name with the familyPrefix parameter or @@ -2201,7 +2201,7 @@ func (c *ECS) ListTaskDefinitionsRequest(input *ListTaskDefinitionsInput) (req * // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListTaskDefinitions for usage and error information. // // Returned Error Codes: @@ -2334,7 +2334,7 @@ func (c *ECS) ListTasksRequest(input *ListTasksInput) (req *request.Request, out return } -// ListTasks API operation for Amazon EC2 Container Service. +// ListTasks API operation for Amazon Elastic Container Service. // // Returns a list of tasks for a specified cluster. You can filter the results // by family name, by a particular container instance, or by the desired status @@ -2347,7 +2347,7 @@ func (c *ECS) ListTasksRequest(input *ListTasksInput) (req *request.Request, out // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation ListTasks for usage and error information. // // Returned Error Codes: @@ -2482,13 +2482,13 @@ func (c *ECS) PutAccountSettingRequest(input *PutAccountSettingInput) (req *requ return } -// PutAccountSetting API operation for Amazon EC2 Container Service. +// PutAccountSetting API operation for Amazon Elastic Container Service. // // Returns awserr.Error for service API and SDK errors. Use runtime type assertions // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation PutAccountSetting for usage and error information. // // Returned Error Codes: @@ -2565,7 +2565,7 @@ func (c *ECS) PutAttributesRequest(input *PutAttributesInput) (req *request.Requ return } -// PutAttributes API operation for Amazon EC2 Container Service. +// PutAttributes API operation for Amazon Elastic Container Service. // // Create or update an attribute on an Amazon ECS resource. If the attribute // does not exist, it is created. If the attribute exists, its value is replaced @@ -2577,7 +2577,7 @@ func (c *ECS) PutAttributesRequest(input *PutAttributesInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation PutAttributes for usage and error information. // // Returned Error Codes: @@ -2660,7 +2660,7 @@ func (c *ECS) RegisterContainerInstanceRequest(input *RegisterContainerInstanceI return } -// RegisterContainerInstance API operation for Amazon EC2 Container Service. +// RegisterContainerInstance API operation for Amazon Elastic Container Service. // // This action is only used by the Amazon ECS agent, and it is not intended // for use outside of the agent. @@ -2672,7 +2672,7 @@ func (c *ECS) RegisterContainerInstanceRequest(input *RegisterContainerInstanceI // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation RegisterContainerInstance for usage and error information. // // Returned Error Codes: @@ -2749,7 +2749,7 @@ func (c *ECS) RegisterTaskDefinitionRequest(input *RegisterTaskDefinitionInput) return } -// RegisterTaskDefinition API operation for Amazon EC2 Container Service. +// RegisterTaskDefinition API operation for Amazon Elastic Container Service. // // Registers a new task definition from the supplied family and containerDefinitions. // Optionally, you can add data volumes to your containers with the volumes @@ -2777,7 +2777,7 @@ func (c *ECS) RegisterTaskDefinitionRequest(input *RegisterTaskDefinitionInput) // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation RegisterTaskDefinition for usage and error information. // // Returned Error Codes: @@ -2854,7 +2854,7 @@ func (c *ECS) RunTaskRequest(input *RunTaskInput) (req *request.Request, output return } -// RunTask API operation for Amazon EC2 Container Service. +// RunTask API operation for Amazon Elastic Container Service. // // Starts a new task using the specified task definition. // @@ -2891,7 +2891,7 @@ func (c *ECS) RunTaskRequest(input *RunTaskInput) (req *request.Request, output // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation RunTask for usage and error information. // // Returned Error Codes: @@ -2989,7 +2989,7 @@ func (c *ECS) StartTaskRequest(input *StartTaskInput) (req *request.Request, out return } -// StartTask API operation for Amazon EC2 Container Service. +// StartTask API operation for Amazon Elastic Container Service. // // Starts a new task from the specified task definition on the specified container // instance or instances. @@ -3002,7 +3002,7 @@ func (c *ECS) StartTaskRequest(input *StartTaskInput) (req *request.Request, out // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation StartTask for usage and error information. // // Returned Error Codes: @@ -3083,7 +3083,7 @@ func (c *ECS) StopTaskRequest(input *StopTaskInput) (req *request.Request, outpu return } -// StopTask API operation for Amazon EC2 Container Service. +// StopTask API operation for Amazon Elastic Container Service. // // Stops a running task. // @@ -3102,7 +3102,7 @@ func (c *ECS) StopTaskRequest(input *StopTaskInput) (req *request.Request, outpu // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation StopTask for usage and error information. // // Returned Error Codes: @@ -3183,7 +3183,7 @@ func (c *ECS) SubmitContainerStateChangeRequest(input *SubmitContainerStateChang return } -// SubmitContainerStateChange API operation for Amazon EC2 Container Service. +// SubmitContainerStateChange API operation for Amazon Elastic Container Service. // // This action is only used by the Amazon ECS agent, and it is not intended // for use outside of the agent. @@ -3194,7 +3194,7 @@ func (c *ECS) SubmitContainerStateChangeRequest(input *SubmitContainerStateChang // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation SubmitContainerStateChange for usage and error information. // // Returned Error Codes: @@ -3270,7 +3270,7 @@ func (c *ECS) SubmitTaskStateChangeRequest(input *SubmitTaskStateChangeInput) (r return } -// SubmitTaskStateChange API operation for Amazon EC2 Container Service. +// SubmitTaskStateChange API operation for Amazon Elastic Container Service. // // This action is only used by the Amazon ECS agent, and it is not intended // for use outside of the agent. @@ -3281,7 +3281,7 @@ func (c *ECS) SubmitTaskStateChangeRequest(input *SubmitTaskStateChangeInput) (r // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation SubmitTaskStateChange for usage and error information. // // Returned Error Codes: @@ -3357,7 +3357,7 @@ func (c *ECS) UpdateContainerAgentRequest(input *UpdateContainerAgentInput) (req return } -// UpdateContainerAgent API operation for Amazon EC2 Container Service. +// UpdateContainerAgent API operation for Amazon Elastic Container Service. // // Updates the Amazon ECS container agent on a specified container instance. // Updating the Amazon ECS container agent does not interrupt running tasks @@ -3375,7 +3375,7 @@ func (c *ECS) UpdateContainerAgentRequest(input *UpdateContainerAgentInput) (req // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation UpdateContainerAgent for usage and error information. // // Returned Error Codes: @@ -3474,7 +3474,7 @@ func (c *ECS) UpdateContainerInstancesStateRequest(input *UpdateContainerInstanc return } -// UpdateContainerInstancesState API operation for Amazon EC2 Container Service. +// UpdateContainerInstancesState API operation for Amazon Elastic Container Service. // // Modifies the status of an Amazon ECS container instance. // @@ -3525,7 +3525,7 @@ func (c *ECS) UpdateContainerInstancesStateRequest(input *UpdateContainerInstanc // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation UpdateContainerInstancesState for usage and error information. // // Returned Error Codes: @@ -3606,7 +3606,7 @@ func (c *ECS) UpdateServiceRequest(input *UpdateServiceInput) (req *request.Requ return } -// UpdateService API operation for Amazon EC2 Container Service. +// UpdateService API operation for Amazon Elastic Container Service. // // Modifies the desired count, deployment configuration, network configuration, // or task definition used in a service. @@ -3689,7 +3689,7 @@ func (c *ECS) UpdateServiceRequest(input *UpdateServiceInput) (req *request.Requ // with awserr.Error's Code and Message methods to get detailed information about // the error. // -// See the AWS API reference guide for Amazon EC2 Container Service's +// See the AWS API reference guide for Amazon Elastic Container Service's // API operation UpdateService for usage and error information. // // Returned Error Codes: @@ -8655,6 +8655,58 @@ func (s *PortMapping) SetProtocol(v string) *PortMapping { return s } +type ProxyConfiguration struct { + _ struct{} `type:"structure"` + + // ContainerName is a required field + ContainerName *string `locationName:"containerName" type:"string" required:"true"` + + Properties []*KeyValuePair `locationName:"properties" type:"list"` + + Type *string `locationName:"type" type:"string" enum:"ProxyConfigurationType"` +} + +// String returns the string representation +func (s ProxyConfiguration) String() string { + return awsutil.Prettify(s) +} + +// GoString returns the string representation +func (s ProxyConfiguration) GoString() string { + return s.String() +} + +// Validate inspects the fields of the type to determine if they are valid. +func (s *ProxyConfiguration) Validate() error { + invalidParams := request.ErrInvalidParams{Context: "ProxyConfiguration"} + if s.ContainerName == nil { + invalidParams.Add(request.NewErrParamRequired("ContainerName")) + } + + if invalidParams.Len() > 0 { + return invalidParams + } + return nil +} + +// SetContainerName sets the ContainerName field's value. +func (s *ProxyConfiguration) SetContainerName(v string) *ProxyConfiguration { + s.ContainerName = &v + return s +} + +// SetProperties sets the Properties field's value. +func (s *ProxyConfiguration) SetProperties(v []*KeyValuePair) *ProxyConfiguration { + s.Properties = v + return s +} + +// SetType sets the Type field's value. +func (s *ProxyConfiguration) SetType(v string) *ProxyConfiguration { + s.Type = &v + return s +} + type PutAccountSettingInput struct { _ struct{} `type:"structure"` @@ -9102,6 +9154,8 @@ type RegisterTaskDefinitionInput struct { // the task definition and those specified at run time). PlacementConstraints []*TaskDefinitionPlacementConstraint `locationName:"placementConstraints" type:"list"` + ProxyConfiguration *ProxyConfiguration `locationName:"proxyConfiguration" type:"structure"` + // The launch type required by the task. If no value is specified, it defaults // to EC2. RequiresCompatibilities []*string `locationName:"requiresCompatibilities" type:"list"` @@ -9159,6 +9213,11 @@ func (s *RegisterTaskDefinitionInput) Validate() error { } } } + if s.ProxyConfiguration != nil { + if err := s.ProxyConfiguration.Validate(); err != nil { + invalidParams.AddNested("ProxyConfiguration", err.(request.ErrInvalidParams)) + } + } if s.Tags != nil { for i, v := range s.Tags { if v == nil { @@ -9236,6 +9295,12 @@ func (s *RegisterTaskDefinitionInput) SetPlacementConstraints(v []*TaskDefinitio return s } +// SetProxyConfiguration sets the ProxyConfiguration field's value. +func (s *RegisterTaskDefinitionInput) SetProxyConfiguration(v *ProxyConfiguration) *RegisterTaskDefinitionInput { + s.ProxyConfiguration = v + return s +} + // SetRequiresCompatibilities sets the RequiresCompatibilities field's value. func (s *RegisterTaskDefinitionInput) SetRequiresCompatibilities(v []*string) *RegisterTaskDefinitionInput { s.RequiresCompatibilities = v @@ -11080,6 +11145,8 @@ type TaskDefinition struct { // not valid if using the Fargate launch type for your task. PlacementConstraints []*TaskDefinitionPlacementConstraint `locationName:"placementConstraints" type:"list"` + ProxyConfiguration *ProxyConfiguration `locationName:"proxyConfiguration" type:"structure"` + // The container instance attributes required by your task. This field is not // valid if using the Fargate launch type for your task. RequiresAttributes []*Attribute `locationName:"requiresAttributes" type:"list"` @@ -11197,6 +11264,12 @@ func (s *TaskDefinition) SetPlacementConstraints(v []*TaskDefinitionPlacementCon return s } +// SetProxyConfiguration sets the ProxyConfiguration field's value. +func (s *TaskDefinition) SetProxyConfiguration(v *ProxyConfiguration) *TaskDefinition { + s.ProxyConfiguration = v + return s +} + // SetRequiresAttributes sets the RequiresAttributes field's value. func (s *TaskDefinition) SetRequiresAttributes(v []*Attribute) *TaskDefinition { s.RequiresAttributes = v @@ -12107,6 +12180,11 @@ const ( PlatformDeviceTypeGpu = "GPU" ) +const ( + // ProxyConfigurationTypeAppmesh is a ProxyConfigurationType enum value + ProxyConfigurationTypeAppmesh = "APPMESH" +) + const ( // ResourceTypeGpu is a ResourceType enum value ResourceTypeGpu = "GPU" diff --git a/agent/ecs_client/model/ecs/service.go b/agent/ecs_client/model/ecs/service.go index ad6cb78ce13..0477c2ee7fc 100644 --- a/agent/ecs_client/model/ecs/service.go +++ b/agent/ecs_client/model/ecs/service.go @@ -18,12 +18,12 @@ import ( "github.com/aws/aws-sdk-go/aws/client" "github.com/aws/aws-sdk-go/aws/client/metadata" "github.com/aws/aws-sdk-go/aws/request" - "github.com/aws/aws-sdk-go/aws/signer/v4" + v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/private/protocol/jsonrpc" ) // ECS provides the API operation methods for making requests to -// Amazon EC2 Container Service. See this package's package overview docs +// Amazon Elastic Container Service. See this package's package overview docs // for details on the service. // // ECS methods are safe to use concurrently. It is not safe to diff --git a/agent/ecscni/plugin.go b/agent/ecscni/plugin.go index 5bad65eba5c..ba0d9a73fe1 100644 --- a/agent/ecscni/plugin.go +++ b/agent/ecscni/plugin.go @@ -33,9 +33,10 @@ import ( const ( currentCNISpec = "0.3.1" - // CNIVersion and CNIGitHash needs to be updated every time CNI plugin is updated - currentCNIVersion = "2018.10.0" - currentCNIGitHash = "93f4377604504bff92e7555da73b0cba732a4fbb" + // ECSCNIVersion, ECSCNIGitHash, VPCCNIGitHash needs to be updated every time CNI plugin is updated + currentECSCNIVersion = "2018.10.0" + currentECSCNIGitHash = "93f4377604504bff92e7555da73b0cba732a4fbb" + currentVPCCNIGitHash = "8c97331a89b561a5224d28d0dfce40008641efb0" ) // CNIClient defines the method of setting/cleaning up container namespace @@ -125,6 +126,15 @@ func (client *cniClient) setupNS(cfg *Config) (*current.Result, error) { if err != nil { return nil, errors.Wrap(err, "cni setup: invoke bridge plugin failed") } + if cfg.AppMeshCNIEnabled { + // Invoke app mesh plugin ADD command + seelog.Debug("[APPMESH] Starting aws-appmesh setup") + _, err = client.add(runtimeConfig, cfg, client.createAppMeshConfig) + if err != nil { + return nil, errors.Wrap(err, "cni setup: invoke app mesh plugin failed") + } + seelog.Debug("[APPMESH] Set up aws-appmesh done") + } seelog.Debugf("[ECSCNI] Set up container namespace done: %s", result.String()) if _, err = result.GetAsVersion(currentCNISpec); err != nil { seelog.Warnf("[ECSCNI] Unable to convert result to spec version %s; error: %v; result is of version: %s", @@ -180,11 +190,20 @@ func (client *cniClient) cleanupNS(cfg *Config) error { return errors.Wrap(err, "cni cleanup: invoke bridge plugin failed") } seelog.Debugf("[ECSCNI] bridge cleanup done: %s", cfg.ContainerID) - + // clean up eni network namespace err = client.del(runtimeConfig, cfg, client.createENINetworkConfig) if err != nil { return errors.Wrap(err, "cni cleanup: invoke eni plugin failed") } + if cfg.AppMeshCNIEnabled { + // clean up app mesh network namespace + seelog.Debug("[APPMESH] Starting clean up aws-appmesh namespace") + err = client.del(runtimeConfig, cfg, client.createAppMeshConfig) + if err != nil { + return errors.Wrap(err, "cni cleanup: invoke app mesh plugin failed") + } + seelog.Debug("[APPMESH] Clean up aws-appmesh namespace done") + } seelog.Debugf("[ECSCNI] container namespace cleanup done: %s", cfg.ContainerID) return nil } @@ -311,6 +330,25 @@ func (client *cniClient) createENINetworkConfig(cfg *Config) (string, *libcni.Ne return defaultENIName, networkConfig, nil } +func (client *cniClient) createAppMeshConfig(cfg *Config) (string, *libcni.NetworkConfig, error) { + appMeshConfig := AppMeshConfig{ + Type: ECSAppMeshPluginName, + CNIVersion: client.cniVersion, + IgnoredUID: cfg.IgnoredUID, + IgnoredGID: cfg.IgnoredGID, + ProxyIngressPort: cfg.ProxyIngressPort, + ProxyEgressPort: cfg.ProxyEgressPort, + AppPorts: cfg.AppPorts, + EgressIgnoredPorts: cfg.EgressIgnoredPorts, + EgressIgnoredIPs: cfg.EgressIgnoredIPs, + } + networkConfig, err := client.constructNetworkConfig(appMeshConfig, ECSAppMeshPluginName) + if err != nil { + return "", nil, errors.Wrap(err, "createAppMeshNetworkConfig: construct the app mesh network configuration failed") + } + return defaultAppMeshIfName, networkConfig, nil +} + // createIPAMNetworkConfig constructs the ipam configuration accepted by libcni func (client *cniClient) createIPAMNetworkConfig(cfg *Config) (string, *libcni.NetworkConfig, error) { ipamConfig, err := client.createIPAMConfig(cfg) diff --git a/agent/ecscni/plugin_test.go b/agent/ecscni/plugin_test.go index cef9064f465..8249096dcb3 100644 --- a/agent/ecscni/plugin_test.go +++ b/agent/ecscni/plugin_test.go @@ -1,6 +1,6 @@ // +build linux,unit -// Copyright 2014-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright 2014-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). You may // not use this file except in compliance with the License. A copy of the @@ -52,12 +52,12 @@ func TestSetupNS(t *testing.T) { // ENI plugin was called first libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).Do( func(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) { - assert.Equal(t, ECSENIPluginName, net.Network.Type, "second plugin should be eni") + assert.Equal(t, ECSENIPluginName, net.Network.Type, "first plugin should be eni") }), - // Bridge plugin was called last + // Bridge plugin was called second libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).Do( func(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) { - assert.Equal(t, ECSBridgePluginName, net.Network.Type, "first plugin should be bridge") + assert.Equal(t, ECSBridgePluginName, net.Network.Type, "second plugin should be bridge") var bridgeConfig BridgeConfig err := json.Unmarshal(net.Bytes, &bridgeConfig) assert.NoError(t, err, "unmarshal BridgeConfig") @@ -69,6 +69,45 @@ func TestSetupNS(t *testing.T) { assert.NoError(t, err) } +func TestSetupNSAppMeshEnabled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ecscniClient := NewClient(&Config{}) + libcniClient := mock_libcni.NewMockCNI(ctrl) + ecscniClient.(*cniClient).libcni = libcniClient + + additionalRoutesJson := `["169.254.172.1/32", "10.11.12.13/32"]` + var additionalRoutes []cnitypes.IPNet + err := json.Unmarshal([]byte(additionalRoutesJson), &additionalRoutes) + assert.NoError(t, err) + + gomock.InOrder( + // ENI plugin was called first + libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).Do( + func(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) { + assert.Equal(t, ECSENIPluginName, net.Network.Type, "first plugin should be eni") + }), + // Bridge plugin was called second + libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).Do( + func(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) { + assert.Equal(t, ECSBridgePluginName, net.Network.Type, "second plugin should be bridge") + var bridgeConfig BridgeConfig + err := json.Unmarshal(net.Bytes, &bridgeConfig) + assert.NoError(t, err, "unmarshal BridgeConfig") + assert.Len(t, bridgeConfig.IPAM.IPV4Routes, 3, "default route plus two extra routes") + }), + // AppMesh plugin was called third + libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).Do( + func(net *libcni.NetworkConfig, rt *libcni.RuntimeConf) { + assert.Equal(t, ECSAppMeshPluginName, net.Network.Type, "third plugin should be app mesh") + }), + ) + + _, err = ecscniClient.SetupNS(context.TODO(), &Config{AdditionalLocalRoutes: additionalRoutes, AppMeshCNIEnabled: true}, time.Second) + assert.NoError(t, err) +} + func TestSetupNSTimeout(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -87,6 +126,7 @@ func TestSetupNSTimeout(t *testing.T) { wg.Wait() }).MaxTimes(1), libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).MaxTimes(1), + libcniClient.EXPECT().AddNetwork(gomock.Any(), gomock.Any()).Return(¤t.Result{}, nil).MaxTimes(1), ) _, err := ecscniClient.SetupNS(context.TODO(), &Config{}, time.Millisecond) @@ -113,6 +153,25 @@ func TestCleanupNS(t *testing.T) { assert.NoError(t, err) } +func TestCleanupNSAppMeshEnabled(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ecscniClient := NewClient(&Config{}) + libcniClient := mock_libcni.NewMockCNI(ctrl) + ecscniClient.(*cniClient).libcni = libcniClient + + // This will be called for both bridge and eni plugin + libcniClient.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Return(nil).Times(3) + + additionalRoutesJson := `["169.254.172.1/32", "10.11.12.13/32"]` + var additionalRoutes []cnitypes.IPNet + err := json.Unmarshal([]byte(additionalRoutesJson), &additionalRoutes) + assert.NoError(t, err) + err = ecscniClient.CleanupNS(context.TODO(), &Config{AdditionalLocalRoutes: additionalRoutes, AppMeshCNIEnabled: true}, time.Second) + assert.NoError(t, err) +} + func TestCleanupNSTimeout(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -128,7 +187,7 @@ func TestCleanupNSTimeout(t *testing.T) { libcniClient.EXPECT().DelNetwork(gomock.Any(), gomock.Any()).Do( func(x interface{}, y interface{}) { wg.Wait() - }).Return(nil).MaxTimes(2) + }).Return(nil).MaxTimes(3) additionalRoutesJson := `["169.254.172.1/32", "10.11.12.13/32"]` var additionalRoutes []cnitypes.IPNet @@ -205,6 +264,45 @@ func TestConstructBridgeNetworkConfigWithoutIPAM(t *testing.T) { assert.Equal(t, IPAMConfig{}, bridgeConfig.IPAM) } +// TestConstructAppMeshNetworkConfig tests createAppMeshConfig creates the correct +// configuration for app mesh plugin +func TestConstructAppMeshNetworkConfig(t *testing.T) { + ecscniClient := NewClient(&Config{}) + + config := &Config{ + AppMeshCNIEnabled: true, + IgnoredUID: "1337", + IgnoredGID: "1448", + ProxyIngressPort: "15000", + ProxyEgressPort: "15001", + AppPorts: []string{ + "9000", + }, + EgressIgnoredPorts: []string{ + "9001", + }, + EgressIgnoredIPs: []string{ + "169.254.169.254", + }, + } + + _, appMeshNetworkConfig, err := ecscniClient.(*cniClient).createAppMeshConfig(config) + + assert.NoError(t, err, "construct eni network config failed") + appMeshConfig := &AppMeshConfig{} + err = json.Unmarshal(appMeshNetworkConfig.Bytes, appMeshConfig) + assert.NoError(t, err, "unmarshal config from bytes failed") + + assert.Equal(t, config.IgnoredUID, appMeshConfig.IgnoredUID) + assert.Equal(t, config.IgnoredGID, appMeshConfig.IgnoredGID) + assert.Equal(t, config.ProxyIngressPort, appMeshConfig.ProxyIngressPort) + assert.Equal(t, config.ProxyEgressPort, appMeshConfig.ProxyEgressPort) + assert.Equal(t, len(config.ProxyEgressPort), len(appMeshConfig.ProxyEgressPort)) + assert.Equal(t, config.ProxyEgressPort[0], appMeshConfig.ProxyEgressPort[0]) + assert.Equal(t, len(config.EgressIgnoredIPs), len(appMeshConfig.EgressIgnoredIPs)) + assert.Equal(t, config.EgressIgnoredIPs[0], appMeshConfig.EgressIgnoredIPs[0]) +} + // TestConstructBridgeNetworkConfigWithIPAM tests createBridgeNetworkConfigWithIPAM // creates the correct configuration for bridge and ipam plugin func TestConstructNetworkConfig(t *testing.T) { @@ -267,7 +365,7 @@ func TestCNIPluginVersion(t *testing.T) { // Asserts that CNI plugin version matches the expected version func TestCNIPluginVersionNumber(t *testing.T) { versionStr := getCNIVersionString(t) - assert.Equal(t, currentCNIVersion, versionStr) + assert.Equal(t, currentECSCNIVersion, versionStr) } // Asserts that CNI plugin version is upgraded when new commits are made to CNI plugin submodule @@ -276,10 +374,13 @@ func TestCNIPluginVersionUpgrade(t *testing.T) { cmd := exec.Command("git", "submodule") versionInfo, err := cmd.Output() assert.NoError(t, err, "Error running the command: git submodule") - versionInfoStr := string(versionInfo) + versionInfoStrList := strings.Split(string(versionInfo), "\n") // If a new commit is added, version should be upgraded - if currentCNIGitHash != strings.Split(versionInfoStr, " ")[1] { - assert.NotEqual(t, currentCNIVersion, versionStr) + if currentECSCNIGitHash != strings.Split(versionInfoStrList[0], " ")[1] { + assert.NotEqual(t, currentECSCNIVersion, versionStr) + } + if currentVPCCNIGitHash != strings.Split(versionInfoStrList[1], " ")[1] { + assert.NotEqual(t, currentVPCCNIGitHash, versionStr) } } diff --git a/agent/ecscni/types.go b/agent/ecscni/types.go index 56306a3c105..3a23887508b 100644 --- a/agent/ecscni/types.go +++ b/agent/ecscni/types.go @@ -30,6 +30,9 @@ const ( // defaultBridgeName is the default name of bridge created for container to // communicate with ecs-agent defaultBridgeName = "ecs-bridge" + // defaultAppMeshIfName is the default name of app mesh to setup iptable rules + // for app mesh container. IfName is mandatory field to invoke CNI plugin. + defaultAppMeshIfName = "aws-appmesh" // netnsFormat is used to construct the path to cotainer network namespace netnsFormat = "/host/proc/%s/ns/net" // ecsSubnet is the available ip addresses to use for task networking @@ -41,6 +44,8 @@ const ( ECSBridgePluginName = "ecs-bridge" // ECSENIPluginName is the binary of the eni plugin ECSENIPluginName = "ecs-eni" + // ECSAppMeshPluginName is the binary of aws-appmesh plugin + ECSAppMeshPluginName = "aws-appmesh" // TaskIAMRoleEndpoint is the endpoint of ecs-agent exposes credentials for // task IAM role TaskIAMRoleEndpoint = "169.254.170.2/32" @@ -124,6 +129,28 @@ type ENIConfig struct { SubnetGatewayIPV4Address string `json:"subnetgateway-ipv4-address"` } +// AppMeshConfig contains all the information needed to invoke the app mesh plugin +type AppMeshConfig struct { + // Type is the cni plugin name + Type string `json:"type,omitempty"` + // CNIVersion is the cni spec version to use + CNIVersion string `json:"cniVersion,omitempty"` + // IgnoredUID specifies egress traffic from the processes owned by the UID will be ignored + IgnoredUID string `json:"ignoredUID,omitempty"` + // IgnoredGID specifies egress traffic from the processes owned by the GID will be ignored + IgnoredGID string `json:"ignoredGID,omitempty"` + // ProxyIngressPort is the ingress port number that proxy is listening on + ProxyIngressPort string `json:"proxyIngressPort"` + // ProxyEgressPort is the egress port number that proxy is listening on + ProxyEgressPort string `json:"proxyEgressPort"` + // AppPorts specifies port numbers that application is listening on + AppPorts []string `json:"appPorts"` + // EgressIgnoredPorts is the list of ports for which egress traffic will be ignored + EgressIgnoredPorts []string `json:"egressIgnoredPorts,omitempty"` + // EgressIgnoredIPs is the list of IPs for which egress traffic will be ignored + EgressIgnoredIPs []string `json:"egressIgnoredIPs,omitempty"` +} + // Config contains all the information to set up the container namespace using // the plugins type Config struct { @@ -156,4 +183,23 @@ type Config struct { AdditionalLocalRoutes []cnitypes.IPNet // SubnetGatewayIPV4Address is the address to the subnet gate for the eni SubnetGatewayIPV4Address string + // AppMeshCNIEnabled specifies if app mesh cni plugin is enabled + AppMeshCNIEnabled bool + // IgnoredUID specifies egress traffic from the processes owned + // by the UID will be ignored + IgnoredUID string + // IgnoredGID specifies egress traffic from the processes owned + // by the GID will be ignored + IgnoredGID string + // ProxyIngressPort is the ingress port number that proxy is listening on + ProxyIngressPort string + // ProxyEgressPort is the egress port number that proxy is listening on + ProxyEgressPort string + // AppPorts specifies port numbers that application is listening on + AppPorts []string + // EgressIgnoredPorts is the list of ports for which egress traffic + // will be ignored + EgressIgnoredPorts []string + // EgressIgnoredIPs is the list of IPs for which egress traffic will be ignored + EgressIgnoredIPs []string } diff --git a/agent/engine/docker_task_engine_test.go b/agent/engine/docker_task_engine_test.go index eccffc202f3..c4eb8c23a92 100644 --- a/agent/engine/docker_task_engine_test.go +++ b/agent/engine/docker_task_engine_test.go @@ -28,6 +28,7 @@ import ( "time" "github.com/aws/amazon-ecs-agent/agent/api" + "github.com/aws/amazon-ecs-agent/agent/api/appmesh" apicontainer "github.com/aws/amazon-ecs-agent/agent/api/container" apicontainerstatus "github.com/aws/amazon-ecs-agent/agent/api/container/status" apieni "github.com/aws/amazon-ecs-agent/agent/api/eni" @@ -87,6 +88,11 @@ const ( region = "us-west-2" username = "irene" password = "sher" + ignoredUID = "1337" + proxyIngressPort = "15000" + proxyEgressPort = "15001" + appPort = "9000" + egressIgnoredIP = "169.254.169.254" ) var ( @@ -338,6 +344,17 @@ func TestTaskWithSteadyStateResourcesProvisioned(t *testing.T) { }, }, }) + sleepTask.SetAppMesh(&appmesh.AppMesh{ + IgnoredUID: ignoredUID, + ProxyIngressPort: proxyIngressPort, + ProxyEgressPort: proxyEgressPort, + AppPorts: []string{ + appPort, + }, + EgressIgnoredIPs: []string{ + egressIgnoredIP, + }, + }) assert.Equal(t, "none", string(hostConfig.NetworkMode)) assert.True(t, strings.Contains(containerName, pauseContainer.Name)) containerEventsWG.Add(1) @@ -1056,6 +1073,18 @@ func TestPauseContainerHappyPath(t *testing.T) { }, }) + sleepTask.SetAppMesh(&appmesh.AppMesh{ + IgnoredUID: ignoredUID, + ProxyIngressPort: proxyIngressPort, + ProxyEgressPort: proxyEgressPort, + AppPorts: []string{ + appPort, + }, + EgressIgnoredIPs: []string{ + egressIgnoredIP, + }, + }) + dockerClient.EXPECT().ContainerEvents(gomock.Any()).Return(eventStream, nil) pauseContainerID := "pauseContainerID" @@ -1173,6 +1202,17 @@ func TestBuildCNIConfigFromTaskContainer(t *testing.T) { }, }, }) + testTask.SetAppMesh(&appmesh.AppMesh{ + IgnoredUID: ignoredUID, + ProxyIngressPort: proxyIngressPort, + ProxyEgressPort: proxyEgressPort, + AppPorts: []string{ + appPort, + }, + EgressIgnoredIPs: []string{ + egressIgnoredIP, + }, + }) container := &apicontainer.Container{ Name: "container", } @@ -1196,6 +1236,11 @@ func TestBuildCNIConfigFromTaskContainer(t *testing.T) { assert.Equal(t, mac, cniConfig.ENIMACAddress) assert.Equal(t, ipv4, cniConfig.ENIIPV4Address) assert.Equal(t, ipv6, cniConfig.ENIIPV6Address) + assert.Equal(t, ignoredUID, cniConfig.IgnoredUID) + assert.Equal(t, proxyIngressPort, cniConfig.ProxyIngressPort) + assert.Equal(t, proxyEgressPort, cniConfig.ProxyEgressPort) + assert.Equal(t, appPort, cniConfig.AppPorts[0]) + assert.Equal(t, egressIgnoredIP, cniConfig.EgressIgnoredIPs[0]) assert.Equal(t, blockIMDS, cniConfig.BlockInstanceMetdata) }) } @@ -1209,6 +1254,7 @@ func TestBuildCNIConfigFromTaskContainerInspectError(t *testing.T) { testTask := testdata.LoadTask("sleep5") testTask.SetTaskENI(&apieni.ENI{}) + testTask.SetAppMesh(&appmesh.AppMesh{}) container := &apicontainer.Container{ Name: "container", } @@ -1254,6 +1300,17 @@ func TestStopPauseContainerCleanupCalled(t *testing.T) { }, }, }) + testTask.SetAppMesh(&appmesh.AppMesh{ + IgnoredUID: ignoredUID, + ProxyIngressPort: proxyIngressPort, + ProxyEgressPort: proxyEgressPort, + AppPorts: []string{ + appPort, + }, + EgressIgnoredIPs: []string{ + egressIgnoredIP, + }, + }) taskEngine.(*DockerTaskEngine).State().AddTask(testTask) taskEngine.(*DockerTaskEngine).State().AddContainer(&apicontainer.DockerContainer{ DockerID: containerID, diff --git a/amazon-vpc-cni-plugins b/amazon-vpc-cni-plugins new file mode 160000 index 00000000000..8c97331a89b --- /dev/null +++ b/amazon-vpc-cni-plugins @@ -0,0 +1 @@ +Subproject commit 8c97331a89b561a5224d28d0dfce40008641efb0 diff --git a/scripts/dockerfiles/Dockerfile.buildCNIPlugins b/scripts/dockerfiles/Dockerfile.buildECSCNIPlugins similarity index 89% rename from scripts/dockerfiles/Dockerfile.buildCNIPlugins rename to scripts/dockerfiles/Dockerfile.buildECSCNIPlugins index ea77146bc0b..1ede471e3c1 100644 --- a/scripts/dockerfiles/Dockerfile.buildCNIPlugins +++ b/scripts/dockerfiles/Dockerfile.buildECSCNIPlugins @@ -1,4 +1,4 @@ -# Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# Copyright 2017-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"). You may # not use this file except in compliance with the License. A copy of the diff --git a/scripts/dockerfiles/Dockerfile.buildVPCCNIPlugins b/scripts/dockerfiles/Dockerfile.buildVPCCNIPlugins new file mode 100644 index 00000000000..93ee996adc3 --- /dev/null +++ b/scripts/dockerfiles/Dockerfile.buildVPCCNIPlugins @@ -0,0 +1,21 @@ +# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You may +# not use this file except in compliance with the License. A copy of the +# License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is distributed +# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either +# express or implied. See the License for the specific language governing +# permissions and limitations under the License. + +FROM golang:1.10 +MAINTAINER Amazon Web Services, Inc. + +RUN mkdir -p /go/src/github.com/aws/ + +WORKDIR /go/src/github.com/aws/amazon-vpc-cni-plugins + +CMD ["make", "aws-appmesh"]