diff --git a/agent/api/container.go b/agent/api/container.go index ff190c0b3a0..780f5bc237a 100644 --- a/agent/api/container.go +++ b/agent/api/container.go @@ -493,6 +493,14 @@ func (c *Container) GetLabels() map[string]string { return c.labels } +// GetPorts gets the ports for a container +func (c *Container) GetPorts() []PortBinding { + c.lock.RLock() + defer c.lock.RUnlock() + + return c.Ports +} + // HealthStatusShouldBeReported returns true if the health check is defined in // the task definition func (c *Container) HealthStatusShouldBeReported() bool { diff --git a/agent/handlers/types/v1/response.go b/agent/handlers/types/v1/response.go index c59e5d69a32..81761278fa9 100644 --- a/agent/handlers/types/v1/response.go +++ b/agent/handlers/types/v1/response.go @@ -13,6 +13,11 @@ package v1 +import ( + "github.com/aws/amazon-ecs-agent/agent/containermetadata" + "github.com/aws/amazon-ecs-agent/agent/handlers/types/v2" +) + // MetadataResponse is the schema for the metadata response JSON object type MetadataResponse struct { Cluster string @@ -40,4 +45,6 @@ type ContainerResponse struct { DockerId string DockerName string Name string + Ports []v2.PortResponse + Networks []containermetadata.Network } diff --git a/agent/handlers/v1_handlers.go b/agent/handlers/v1_handlers.go index 20859c7dd1e..2643cd69d5c 100644 --- a/agent/handlers/v1_handlers.go +++ b/agent/handlers/v1_handlers.go @@ -23,9 +23,11 @@ import ( "github.com/aws/amazon-ecs-agent/agent/api" "github.com/aws/amazon-ecs-agent/agent/config" + "github.com/aws/amazon-ecs-agent/agent/containermetadata" "github.com/aws/amazon-ecs-agent/agent/engine" "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate" "github.com/aws/amazon-ecs-agent/agent/handlers/types/v1" + "github.com/aws/amazon-ecs-agent/agent/handlers/types/v2" "github.com/aws/amazon-ecs-agent/agent/logger" "github.com/aws/amazon-ecs-agent/agent/utils" "github.com/aws/amazon-ecs-agent/agent/version" @@ -66,11 +68,12 @@ func metadataV1RequestHandlerMaker(containerInstanceArn *string, cfg *config.Con func newTaskResponse(task *api.Task, containerMap map[string]*api.DockerContainer) *v1.TaskResponse { containers := []v1.ContainerResponse{} - for containerName, container := range containerMap { + for _, container := range containerMap { if container.Container.IsInternal() { continue } - containers = append(containers, v1.ContainerResponse{DockerId: container.DockerID, DockerName: container.DockerName, Name: containerName}) + containerResponse := newContainerResponse(container, task.GetTaskENI()) + containers = append(containers, containerResponse) } knownStatus := task.GetKnownStatus() @@ -92,6 +95,41 @@ func newTaskResponse(task *api.Task, containerMap map[string]*api.DockerContaine } } +func newContainerResponse(dockerContainer *api.DockerContainer, eni *api.ENI) v1.ContainerResponse { + container := dockerContainer.Container + resp := v1.ContainerResponse{ + Name: container.Name, + DockerId: dockerContainer.DockerID, + DockerName: dockerContainer.DockerName, + } + + for _, binding := range container.GetPorts() { + port := v2.PortResponse{ + ContainerPort: binding.ContainerPort, + Protocol: binding.Protocol.String(), + } + + if eni == nil { + port.HostPort = binding.HostPort + } else { + port.HostPort = port.ContainerPort + } + + resp.Ports = append(resp.Ports, port) + } + + if eni != nil { + resp.Networks = []containermetadata.Network{ + { + NetworkMode: "awsvpc", + IPv4Addresses: eni.GetIPV4Addresses(), + IPv6Addresses: eni.GetIPV6Addresses(), + }, + } + } + return resp +} + func newTasksResponse(state dockerstate.TaskEngineState) *v1.TasksResponse { allTasks := state.AllTasks() taskResponses := make([]*v1.TaskResponse, len(allTasks)) diff --git a/agent/handlers/v1_handlers_test.go b/agent/handlers/v1_handlers_test.go index 76262b961a5..a9bab30025c 100644 --- a/agent/handlers/v1_handlers_test.go +++ b/agent/handlers/v1_handlers_test.go @@ -36,6 +36,7 @@ import ( const testContainerInstanceArn = "test_container_instance_arn" const testClusterArn = "test_cluster_arn" +const eniIPV4Address = "10.0.0.2" func TestMetadataHandler(t *testing.T) { metadataHandler := metadataV1RequestHandlerMaker(utils.Strptr(testContainerInstanceArn), &config.Config{Cluster: testClusterArn}) @@ -125,6 +126,24 @@ func TestGetTaskByTaskArn(t *testing.T) { taskDiffHelper(t, []*api.Task{testTasks[0]}, v1.TasksResponse{Tasks: []*v1.TaskResponse{&taskResponse}}) } +func TestGetAWSVPCTaskByTaskArn(t *testing.T) { + recorder := performMockRequest(t, "/v1/tasks?taskarn=awsvpcTask") + + var taskResponse v1.TaskResponse + err := json.Unmarshal(recorder.Body.Bytes(), &taskResponse) + if err != nil { + t.Fatal(err) + } + + resp := v1.TasksResponse{Tasks: []*v1.TaskResponse{&taskResponse}} + + assert.Equal(t, eniIPV4Address, resp.Tasks[0].Containers[0].Networks[0].IPv4Addresses[0]) + assert.Equal(t, uint16(80), resp.Tasks[0].Containers[0].Ports[0].ContainerPort) + assert.Equal(t, "tcp", resp.Tasks[0].Containers[0].Ports[0].Protocol) + + taskDiffHelper(t, []*api.Task{testTasks[3]}, resp) +} + func TestGetTaskByTaskArnNotFound(t *testing.T) { recorder := performMockRequest(t, "/v1/tasks?taskarn=doesnotexist") @@ -303,6 +322,31 @@ var testTasks = []*api.Task{ }, }, }, + { + Arn: "awsvpcTask", + DesiredStatusUnsafe: api.TaskRunning, + KnownStatusUnsafe: api.TaskRunning, + Family: "test", + Version: "1", + Containers: []*api.Container{ + { + Name: "awsvpc", + Ports: []api.PortBinding{ + { + ContainerPort: 80, + Protocol: api.TransportProtocolTCP, + }, + }, + }, + }, + ENI: &api.ENI{ + IPV4Addresses: []*api.ENIIPV4Address{ + { + Address: eniIPV4Address, + }, + }, + }, + }, } func stateSetupHelper(state dockerstate.TaskEngineState, tasks []*api.Task) {