From e3f3123ae3c7a4d42f82c6ffb9b2d10995a06942 Mon Sep 17 00:00:00 2001 From: Amogh Rathore Date: Wed, 17 May 2023 19:21:50 +0000 Subject: [PATCH 1/2] Add v2 metadata models to ecs-agent --- ecs-agent/tmds/handlers/v2/response.go | 95 ++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 ecs-agent/tmds/handlers/v2/response.go diff --git a/ecs-agent/tmds/handlers/v2/response.go b/ecs-agent/tmds/handlers/v2/response.go new file mode 100644 index 00000000000..798894b0c18 --- /dev/null +++ b/ecs-agent/tmds/handlers/v2/response.go @@ -0,0 +1,95 @@ +// Copyright 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 v2 + +import ( + "time" + + "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/response" +) + +// TaskResponse defines the schema for the task response JSON object +type TaskResponse struct { + Cluster string `json:"Cluster"` + TaskARN string `json:"TaskARN"` + Family string `json:"Family"` + Revision string `json:"Revision"` + DesiredStatus string `json:"DesiredStatus,omitempty"` + KnownStatus string `json:"KnownStatus"` + Containers []ContainerResponse `json:"Containers,omitempty"` + Limits *LimitsResponse `json:"Limits,omitempty"` + PullStartedAt *time.Time `json:"PullStartedAt,omitempty"` + PullStoppedAt *time.Time `json:"PullStoppedAt,omitempty"` + ExecutionStoppedAt *time.Time `json:"ExecutionStoppedAt,omitempty"` + AvailabilityZone string `json:"AvailabilityZone,omitempty"` + TaskTags map[string]string `json:"TaskTags,omitempty"` + ContainerInstanceTags map[string]string `json:"ContainerInstanceTags,omitempty"` + LaunchType string `json:"LaunchType,omitempty"` + Errors []ErrorResponse `json:"Errors,omitempty"` +} + +// ContainerResponse defines the schema for the container response +// JSON object +type ContainerResponse struct { + ID string `json:"DockerId"` + Name string `json:"Name"` + DockerName string `json:"DockerName"` + Image string `json:"Image"` + ImageID string `json:"ImageID"` + Ports []response.PortResponse `json:"Ports,omitempty"` + Labels map[string]string `json:"Labels,omitempty"` + DesiredStatus string `json:"DesiredStatus"` + KnownStatus string `json:"KnownStatus"` + ExitCode *int `json:"ExitCode,omitempty"` + Limits LimitsResponse `json:"Limits"` + CreatedAt *time.Time `json:"CreatedAt,omitempty"` + StartedAt *time.Time `json:"StartedAt,omitempty"` + FinishedAt *time.Time `json:"FinishedAt,omitempty"` + Type string `json:"Type"` + Networks []response.Network `json:"Networks,omitempty"` + Health *HealthStatus `json:"Health,omitempty"` + Volumes []response.VolumeResponse `json:"Volumes,omitempty"` + LogDriver string `json:"LogDriver,omitempty"` + LogOptions map[string]string `json:"LogOptions,omitempty"` + ContainerARN string `json:"ContainerARN,omitempty"` +} + +// Container health status +type HealthStatus struct { + // Status is the container health status + Status string `json:"status,omitempty"` + // Since is the timestamp when container health status changed + Since *time.Time `json:"statusSince,omitempty"` + // ExitCode is the exitcode of health check if failed + ExitCode int `json:"exitCode,omitempty"` + // Output is the output of health check + Output string `json:"output,omitempty"` +} + +// LimitsResponse defines the schema for task/cpu limits response +// JSON object +type LimitsResponse struct { + CPU *float64 `json:"CPU,omitempty"` + Memory *int64 `json:"Memory,omitempty"` +} + +// ErrorResponse defined the schema for error response +// JSON object +type ErrorResponse struct { + ErrorField string `json:"ErrorField,omitempty"` + ErrorCode string `json:"ErrorCode,omitempty"` + ErrorMessage string `json:"ErrorMessage,omitempty"` + StatusCode int `json:"StatusCode,omitempty"` + RequestId string `json:"RequestId,omitempty"` + ResourceARN string `json:"ResourceARN,omitempty"` +} From 27db7122250fe6f0227ffb96954301188be96a01 Mon Sep 17 00:00:00 2001 From: Amogh Rathore Date: Fri, 19 May 2023 00:29:31 +0000 Subject: [PATCH 2/2] Consume common TMDS models from ecs-agent --- agent/handlers/task_server_setup_test.go | 26 ++--- agent/handlers/v2/response.go | 107 +++++------------- agent/handlers/v2/response_test.go | 77 ++++++++++++- .../handlers/v3/container_metadata_handler.go | 3 +- agent/handlers/v3/task_metadata_handler.go | 3 +- agent/handlers/v4/response.go | 5 +- .../ecs-agent/tmds/handlers/v2/response.go | 95 ++++++++++++++++ 7 files changed, 217 insertions(+), 99 deletions(-) create mode 100644 agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2/response.go diff --git a/agent/handlers/task_server_setup_test.go b/agent/handlers/task_server_setup_test.go index ecda0e81dc8..4471d3bdc0c 100644 --- a/agent/handlers/task_server_setup_test.go +++ b/agent/handlers/task_server_setup_test.go @@ -38,7 +38,6 @@ import ( "github.com/aws/amazon-ecs-agent/agent/ecs_client/model/ecs" mock_dockerstate "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate/mocks" task_protection_v1 "github.com/aws/amazon-ecs-agent/agent/handlers/agentapi/taskprotection/v1/handlers" - v2 "github.com/aws/amazon-ecs-agent/agent/handlers/v2" v3 "github.com/aws/amazon-ecs-agent/agent/handlers/v3" v4 "github.com/aws/amazon-ecs-agent/agent/handlers/v4" "github.com/aws/amazon-ecs-agent/agent/stats" @@ -51,6 +50,7 @@ import ( tmdsresponse "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/response" "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/utils" tmdsv1 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v1" + v2 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2" "github.com/aws/aws-sdk-go/aws" "github.com/docker/docker/api/types" "github.com/golang/mock/gomock" @@ -839,21 +839,21 @@ func TestV2TaskWithTagsMetadata(t *testing.T) { state.EXPECT().TaskByArn(taskARN).Return(task, true), state.EXPECT().ContainerMapByArn(taskARN).Return(containerNameToDockerContainer, true), ecsClient.EXPECT().GetResourceTags(containerInstanceArn).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &contInstTag1Key, Value: &contInstTag1Val, }, - &ecs.Tag{ + { Key: &contInstTag2Key, Value: &contInstTag2Val, }, }, nil), ecsClient.EXPECT().GetResourceTags(taskARN).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &taskTag1Key, Value: &taskTag1Val, }, - &ecs.Tag{ + { Key: &taskTag2Key, Value: &taskTag2Val, }, @@ -1127,21 +1127,21 @@ func TestV3TaskMetadataWithTags(t *testing.T) { state.EXPECT().TaskByArn(taskARN).Return(task, true), state.EXPECT().ContainerMapByArn(taskARN).Return(containerNameToDockerContainer, true), ecsClient.EXPECT().GetResourceTags(containerInstanceArn).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &contInstTag1Key, Value: &contInstTag1Val, }, - &ecs.Tag{ + { Key: &contInstTag2Key, Value: &contInstTag2Val, }, }, nil), ecsClient.EXPECT().GetResourceTags(taskARN).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &taskTag1Key, Value: &taskTag1Val, }, - &ecs.Tag{ + { Key: &taskTag2Key, Value: &taskTag2Val, }, @@ -1469,21 +1469,21 @@ func TestV4TaskMetadataWithTags(t *testing.T) { state.EXPECT().TaskByArn(taskARN).Return(task, true).AnyTimes(), state.EXPECT().ContainerMapByArn(taskARN).Return(containerNameToDockerContainer, true), ecsClient.EXPECT().GetResourceTags(containerInstanceArn).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &contInstTag1Key, Value: &contInstTag1Val, }, - &ecs.Tag{ + { Key: &contInstTag2Key, Value: &contInstTag2Val, }, }, nil), ecsClient.EXPECT().GetResourceTags(taskARN).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &taskTag1Key, Value: &taskTag1Val, }, - &ecs.Tag{ + { Key: &taskTag2Key, Value: &taskTag2Val, }, diff --git a/agent/handlers/v2/response.go b/agent/handlers/v2/response.go index 8ac65c4cd77..d4c68d6fd0e 100644 --- a/agent/handlers/v2/response.go +++ b/agent/handlers/v2/response.go @@ -14,86 +14,22 @@ package v2 import ( - "time" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/aws/amazon-ecs-agent/agent/api" apicontainer "github.com/aws/amazon-ecs-agent/agent/api/container" + apicontainerstatus "github.com/aws/amazon-ecs-agent/agent/api/container/status" "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate" v1 "github.com/aws/amazon-ecs-agent/agent/handlers/v1" apieni "github.com/aws/amazon-ecs-agent/ecs-agent/api/eni" tmdsresponse "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/response" "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/utils" + tmdsv2 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2" "github.com/aws/aws-sdk-go/aws" "github.com/cihub/seelog" "github.com/pkg/errors" ) -// TaskResponse defines the schema for the task response JSON object -type TaskResponse struct { - Cluster string `json:"Cluster"` - TaskARN string `json:"TaskARN"` - Family string `json:"Family"` - Revision string `json:"Revision"` - DesiredStatus string `json:"DesiredStatus,omitempty"` - KnownStatus string `json:"KnownStatus"` - Containers []ContainerResponse `json:"Containers,omitempty"` - Limits *LimitsResponse `json:"Limits,omitempty"` - PullStartedAt *time.Time `json:"PullStartedAt,omitempty"` - PullStoppedAt *time.Time `json:"PullStoppedAt,omitempty"` - ExecutionStoppedAt *time.Time `json:"ExecutionStoppedAt,omitempty"` - AvailabilityZone string `json:"AvailabilityZone,omitempty"` - TaskTags map[string]string `json:"TaskTags,omitempty"` - ContainerInstanceTags map[string]string `json:"ContainerInstanceTags,omitempty"` - LaunchType string `json:"LaunchType,omitempty"` - Errors []ErrorResponse `json:"Errors,omitempty"` -} - -// ContainerResponse defines the schema for the container response -// JSON object -type ContainerResponse struct { - ID string `json:"DockerId"` - Name string `json:"Name"` - DockerName string `json:"DockerName"` - Image string `json:"Image"` - ImageID string `json:"ImageID"` - Ports []tmdsresponse.PortResponse `json:"Ports,omitempty"` - Labels map[string]string `json:"Labels,omitempty"` - DesiredStatus string `json:"DesiredStatus"` - KnownStatus string `json:"KnownStatus"` - ExitCode *int `json:"ExitCode,omitempty"` - Limits LimitsResponse `json:"Limits"` - CreatedAt *time.Time `json:"CreatedAt,omitempty"` - StartedAt *time.Time `json:"StartedAt,omitempty"` - FinishedAt *time.Time `json:"FinishedAt,omitempty"` - Type string `json:"Type"` - Networks []tmdsresponse.Network `json:"Networks,omitempty"` - Health *apicontainer.HealthStatus `json:"Health,omitempty"` - Volumes []tmdsresponse.VolumeResponse `json:"Volumes,omitempty"` - LogDriver string `json:"LogDriver,omitempty"` - LogOptions map[string]string `json:"LogOptions,omitempty"` - ContainerARN string `json:"ContainerARN,omitempty"` -} - -// LimitsResponse defines the schema for task/cpu limits response -// JSON object -type LimitsResponse struct { - CPU *float64 `json:"CPU,omitempty"` - Memory *int64 `json:"Memory,omitempty"` -} - -// ErrorResponse defined the schema for error response -// JSON object -type ErrorResponse struct { - ErrorField string `json:"ErrorField,omitempty"` - ErrorCode string `json:"ErrorCode,omitempty"` - ErrorMessage string `json:"ErrorMessage,omitempty"` - StatusCode int `json:"StatusCode,omitempty"` - RequestId string `json:"RequestId,omitempty"` - ResourceARN string `json:"ResourceARN,omitempty"` -} - // Agent versions >= 1.2.0: Null, zero, and CPU values of 1 // are passed to Docker as two CPU shares const minimumCPUUnit = 2 @@ -108,13 +44,13 @@ func NewTaskResponse( containerInstanceArn string, propagateTags bool, includeV4Metadata bool, -) (*TaskResponse, error) { +) (*tmdsv2.TaskResponse, error) { task, ok := state.TaskByArn(taskARN) if !ok { return nil, errors.Errorf("v2 task response: unable to find task '%s'", taskARN) } - resp := &TaskResponse{ + resp := &tmdsv2.TaskResponse{ Cluster: cluster, TaskARN: task.Arn, Family: task.Family, @@ -130,7 +66,7 @@ func NewTaskResponse( taskCPU := task.CPU taskMemory := task.Memory if taskCPU != 0 || taskMemory != 0 { - taskLimits := &LimitsResponse{} + taskLimits := &tmdsv2.LimitsResponse{} if taskCPU != 0 { taskLimits.CPU = &taskCPU } @@ -169,7 +105,7 @@ func NewTaskResponse( } // propagateTagsToMetadata retrieves container instance and task tags from ECS -func propagateTagsToMetadata(ecsClient api.ECSClient, containerInstanceARN, taskARN string, resp *TaskResponse, includeV4Metadata bool) { +func propagateTagsToMetadata(ecsClient api.ECSClient, containerInstanceARN, taskARN string, resp *tmdsv2.TaskResponse, includeV4Metadata bool) { containerInstanceTags, err := ecsClient.GetResourceTags(containerInstanceARN) if err == nil { @@ -198,7 +134,7 @@ func NewContainerResponseFromState( containerID string, state dockerstate.TaskEngineState, includeV4Metadata bool, -) (*ContainerResponse, error) { +) (*tmdsv2.ContainerResponse, error) { dockerContainer, ok := state.ContainerByID(containerID) if !ok { return nil, errors.Errorf( @@ -220,9 +156,9 @@ func NewContainerResponse( dockerContainer *apicontainer.DockerContainer, eni *apieni.ENI, includeV4Metadata bool, -) ContainerResponse { +) tmdsv2.ContainerResponse { container := dockerContainer.Container - resp := ContainerResponse{ + resp := tmdsv2.ContainerResponse{ ID: dockerContainer.DockerID, Name: container.Name, DockerName: dockerContainer.DockerName, @@ -230,7 +166,7 @@ func NewContainerResponse( ImageID: container.ImageID, DesiredStatus: container.GetDesiredStatus().String(), KnownStatus: container.GetKnownStatus().String(), - Limits: LimitsResponse{ + Limits: tmdsv2.LimitsResponse{ CPU: aws.Float64(float64(container.CPU)), Memory: aws.Int64(int64(container.Memory)), }, @@ -255,7 +191,7 @@ func NewContainerResponse( // Write the container health status inside the container if dockerContainer.Container.HealthStatusShouldBeReported() { health := dockerContainer.Container.GetHealthStatus() - resp.Health = &health + resp.Health = dockerContainerHealthToV2Health(health) } if createdAt := container.GetCreatedAt(); !createdAt.IsZero() { @@ -302,9 +238,24 @@ func NewContainerResponse( return resp } +// Converts apicontainer HealthStatus type to v2 Metadata HealthStatus type +func dockerContainerHealthToV2Health(health apicontainer.HealthStatus) *tmdsv2.HealthStatus { + status := health.Status.String() + if health.Status == apicontainerstatus.ContainerHealthUnknown { + // Skip sending status if it is unknown + status = "" + } + return &tmdsv2.HealthStatus{ + Status: status, + Since: health.Since, + ExitCode: health.ExitCode, + Output: health.Output, + } +} + // metadataErrorHandling writes an error to the logger, and append an error response // to V4 metadata endpoint task response -func metadataErrorHandling(resp *TaskResponse, err error, field, resourceARN string, includeV4Metadata bool) { +func metadataErrorHandling(resp *tmdsv2.TaskResponse, err error, field, resourceARN string, includeV4Metadata bool) { seelog.Errorf("Task Metadata error: unable to get '%s' for '%s': %s", field, resourceARN, err.Error()) if includeV4Metadata { errResp := newErrorResponse(err, field, resourceARN) @@ -313,8 +264,8 @@ func metadataErrorHandling(resp *TaskResponse, err error, field, resourceARN str } // newErrorResponse creates a new error response -func newErrorResponse(err error, field, resourceARN string) *ErrorResponse { - errResp := &ErrorResponse{ +func newErrorResponse(err error, field, resourceARN string) *tmdsv2.ErrorResponse { + errResp := &tmdsv2.ErrorResponse{ ErrorField: field, ErrorMessage: err.Error(), ResourceARN: resourceARN, diff --git a/agent/handlers/v2/response_test.go b/agent/handlers/v2/response_test.go index ae2b5c64111..44d725f4549 100644 --- a/agent/handlers/v2/response_test.go +++ b/agent/handlers/v2/response_test.go @@ -30,12 +30,14 @@ import ( "github.com/aws/amazon-ecs-agent/agent/ecs_client/model/ecs" mock_dockerstate "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate/mocks" apieni "github.com/aws/amazon-ecs-agent/ecs-agent/api/eni" + tmdsv2 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/awserr" "github.com/docker/docker/api/types" "github.com/golang/mock/gomock" "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -310,6 +312,10 @@ func TestContainerResponse(t *testing.T) { }, }, } + expectedHealthStatus := tmdsv2.HealthStatus{ + Status: "HEALTHY", + Since: container.Health.Since, + } gomock.InOrder( state.EXPECT().ContainerByID(containerID).Return(dockerContainer, true), state.EXPECT().TaskByID(containerID).Return(task, true), @@ -318,6 +324,9 @@ func TestContainerResponse(t *testing.T) { containerResponse, err := NewContainerResponseFromState(containerID, state, false) assert.NoError(t, err) assert.Equal(t, containerResponse.Health == nil, tc.result) + if !tc.result { + assert.Equal(t, *containerResponse.Health, expectedHealthStatus) + } _, err = json.Marshal(containerResponse) assert.NoError(t, err) }) @@ -328,6 +337,9 @@ func TestTaskResponseMarshal(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() + nowStr := "2023-05-17T22:55:55.745786125Z" + now, err := time.Parse("2006-01-02T15:04:05.999999999Z", nowStr) + require.NoError(t, err) expectedTaskResponseMap := map[string]interface{}{ "Cluster": cluster, "TaskARN": taskARN, @@ -365,6 +377,11 @@ func TestTaskResponseMarshal(t *testing.T) { "NetworkMode": "awsvpc", }, }, + "Health": map[string]interface{}{ + "status": "HEALTHY", + "statusSince": nowStr, + "output": "health check output", + }, }, }, "ContainerInstanceTags": map[string]interface{}{ @@ -408,6 +425,12 @@ func TestTaskResponseMarshal(t *testing.T) { Protocol: apicontainer.TransportProtocolTCP, }, }, + HealthCheckType: apicontainer.DockerHealthCheckType, + Health: apicontainer.HealthStatus{ + Status: apicontainerstatus.ContainerHealthy, + Since: &now, + Output: "health check output", + }, } containerNameToDockerContainer := map[string]*apicontainer.DockerContainer{ @@ -431,21 +454,21 @@ func TestTaskResponseMarshal(t *testing.T) { state.EXPECT().TaskByArn(taskARN).Return(task, true), state.EXPECT().ContainerMapByArn(taskARN).Return(containerNameToDockerContainer, true), ecsClient.EXPECT().GetResourceTags(containerInstanceArn).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &contInstTag1Key, Value: &contInstTag1Val, }, - &ecs.Tag{ + { Key: &contInstTag2Key, Value: &contInstTag2Val, }, }, nil), ecsClient.EXPECT().GetResourceTags(taskARN).Return([]*ecs.Tag{ - &ecs.Tag{ + { Key: &taskTag1Key, Value: &taskTag1Val, }, - &ecs.Tag{ + { Key: &taskTag2Key, Value: &taskTag2Val, }, @@ -688,3 +711,49 @@ func TestTaskResponseWithV4TagsError(t *testing.T) { assert.Equal(t, taskWithTagsResponse.Errors[1].RequestId, taskTagsRequestId) assert.Equal(t, taskWithTagsResponse.Errors[1].ResourceARN, taskARN) } + +// Tests that TMDS Health Status created by metadata endpoint v2 is marshaled to the same JSON +// as the source container health status. This test makes sure that the change in TMDS response +// model from using container health status directly to using a new TMDS Health Status type +// is transparent to customers. +func TestDockerContainerHealthToV2HealthJSON(t *testing.T) { + tcs := []struct { + name string + status apicontainerstatus.ContainerHealthStatus + }{ + { + name: "container healthy", + status: apicontainerstatus.ContainerHealthy, + }, + { + name: "container unhealthy", + status: apicontainerstatus.ContainerUnhealthy, + }, + { + name: "container health unknown", + status: apicontainerstatus.ContainerHealthUnknown, + }, + { + name: "container health invalid", + status: 2345, + }, + } + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + now := time.Now() + apiHealth := &apicontainer.HealthStatus{ + Status: tc.status, + Since: &now, + ExitCode: 5, + Output: "some output", + } + apiHealthJSON, err := json.Marshal(apiHealth) + require.NoError(t, err) + + tmdsHealthJSON, err := json.Marshal(dockerContainerHealthToV2Health(*apiHealth)) + require.NoError(t, err) + + assert.Equal(t, apiHealthJSON, tmdsHealthJSON) + }) + } +} diff --git a/agent/handlers/v3/container_metadata_handler.go b/agent/handlers/v3/container_metadata_handler.go index a7c90281780..dbb5ebe1c05 100644 --- a/agent/handlers/v3/container_metadata_handler.go +++ b/agent/handlers/v3/container_metadata_handler.go @@ -22,6 +22,7 @@ import ( v2 "github.com/aws/amazon-ecs-agent/agent/handlers/v2" tmdsresponse "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/response" "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/utils" + tmdsv2 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2" "github.com/cihub/seelog" "github.com/pkg/errors" ) @@ -62,7 +63,7 @@ func ContainerMetadataHandler(state dockerstate.TaskEngineState) func(http.Respo } // GetContainerResponse gets container response for v3 metadata -func GetContainerResponse(containerID string, state dockerstate.TaskEngineState) (*v2.ContainerResponse, error) { +func GetContainerResponse(containerID string, state dockerstate.TaskEngineState) (*tmdsv2.ContainerResponse, error) { containerResponse, err := v2.NewContainerResponseFromState(containerID, state, false) if err != nil { seelog.Errorf("Unable to get container metadata for container '%s'", containerID) diff --git a/agent/handlers/v3/task_metadata_handler.go b/agent/handlers/v3/task_metadata_handler.go index 8d3d328a800..61ebb608ca7 100644 --- a/agent/handlers/v3/task_metadata_handler.go +++ b/agent/handlers/v3/task_metadata_handler.go @@ -22,6 +22,7 @@ import ( "github.com/aws/amazon-ecs-agent/agent/engine/dockerstate" v2 "github.com/aws/amazon-ecs-agent/agent/handlers/v2" "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/utils" + tmdsv2 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2" "github.com/cihub/seelog" ) @@ -64,7 +65,7 @@ func TaskMetadataHandler(state dockerstate.TaskEngineState, ecsClient api.ECSCli task, _ := state.TaskByArn(taskARN) if !task.IsNetworkModeAWSVPC() { // fill in non-awsvpc network details for container responses here - responses := make([]v2.ContainerResponse, 0) + responses := make([]tmdsv2.ContainerResponse, 0) for _, containerResponse := range taskResponse.Containers { networks, err := GetContainerNetworkMetadata(containerResponse.ID, state) if err != nil { diff --git a/agent/handlers/v4/response.go b/agent/handlers/v4/response.go index 043e9aa445b..3e2c3af1279 100644 --- a/agent/handlers/v4/response.go +++ b/agent/handlers/v4/response.go @@ -22,13 +22,14 @@ import ( apieni "github.com/aws/amazon-ecs-agent/ecs-agent/api/eni" tmdsresponse "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/response" "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/utils" + tmdsv2 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2" "github.com/pkg/errors" ) // TaskResponse is the v4 Task response. It augments the v4 Container response // with the v2 task response object. type TaskResponse struct { - *v2.TaskResponse + *tmdsv2.TaskResponse Containers []ContainerResponse `json:"Containers,omitempty"` VPCID string `json:"VPCID,omitempty"` ServiceName string `json:"ServiceName,omitempty"` @@ -37,7 +38,7 @@ type TaskResponse struct { // ContainerResponse is the v4 Container response. It augments the v4 Network response // with the v2 container response object. type ContainerResponse struct { - *v2.ContainerResponse + *tmdsv2.ContainerResponse Networks []Network `json:"Networks,omitempty"` } diff --git a/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2/response.go b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2/response.go new file mode 100644 index 00000000000..798894b0c18 --- /dev/null +++ b/agent/vendor/github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v2/response.go @@ -0,0 +1,95 @@ +// Copyright 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 v2 + +import ( + "time" + + "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/response" +) + +// TaskResponse defines the schema for the task response JSON object +type TaskResponse struct { + Cluster string `json:"Cluster"` + TaskARN string `json:"TaskARN"` + Family string `json:"Family"` + Revision string `json:"Revision"` + DesiredStatus string `json:"DesiredStatus,omitempty"` + KnownStatus string `json:"KnownStatus"` + Containers []ContainerResponse `json:"Containers,omitempty"` + Limits *LimitsResponse `json:"Limits,omitempty"` + PullStartedAt *time.Time `json:"PullStartedAt,omitempty"` + PullStoppedAt *time.Time `json:"PullStoppedAt,omitempty"` + ExecutionStoppedAt *time.Time `json:"ExecutionStoppedAt,omitempty"` + AvailabilityZone string `json:"AvailabilityZone,omitempty"` + TaskTags map[string]string `json:"TaskTags,omitempty"` + ContainerInstanceTags map[string]string `json:"ContainerInstanceTags,omitempty"` + LaunchType string `json:"LaunchType,omitempty"` + Errors []ErrorResponse `json:"Errors,omitempty"` +} + +// ContainerResponse defines the schema for the container response +// JSON object +type ContainerResponse struct { + ID string `json:"DockerId"` + Name string `json:"Name"` + DockerName string `json:"DockerName"` + Image string `json:"Image"` + ImageID string `json:"ImageID"` + Ports []response.PortResponse `json:"Ports,omitempty"` + Labels map[string]string `json:"Labels,omitempty"` + DesiredStatus string `json:"DesiredStatus"` + KnownStatus string `json:"KnownStatus"` + ExitCode *int `json:"ExitCode,omitempty"` + Limits LimitsResponse `json:"Limits"` + CreatedAt *time.Time `json:"CreatedAt,omitempty"` + StartedAt *time.Time `json:"StartedAt,omitempty"` + FinishedAt *time.Time `json:"FinishedAt,omitempty"` + Type string `json:"Type"` + Networks []response.Network `json:"Networks,omitempty"` + Health *HealthStatus `json:"Health,omitempty"` + Volumes []response.VolumeResponse `json:"Volumes,omitempty"` + LogDriver string `json:"LogDriver,omitempty"` + LogOptions map[string]string `json:"LogOptions,omitempty"` + ContainerARN string `json:"ContainerARN,omitempty"` +} + +// Container health status +type HealthStatus struct { + // Status is the container health status + Status string `json:"status,omitempty"` + // Since is the timestamp when container health status changed + Since *time.Time `json:"statusSince,omitempty"` + // ExitCode is the exitcode of health check if failed + ExitCode int `json:"exitCode,omitempty"` + // Output is the output of health check + Output string `json:"output,omitempty"` +} + +// LimitsResponse defines the schema for task/cpu limits response +// JSON object +type LimitsResponse struct { + CPU *float64 `json:"CPU,omitempty"` + Memory *int64 `json:"Memory,omitempty"` +} + +// ErrorResponse defined the schema for error response +// JSON object +type ErrorResponse struct { + ErrorField string `json:"ErrorField,omitempty"` + ErrorCode string `json:"ErrorCode,omitempty"` + ErrorMessage string `json:"ErrorMessage,omitempty"` + StatusCode int `json:"StatusCode,omitempty"` + RequestId string `json:"RequestId,omitempty"` + ResourceARN string `json:"ResourceARN,omitempty"` +}