Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move v4 task metadata handler to ecs-agent module #3733

Merged
merged 8 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions agent/handlers/task_server_setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,10 @@ func v4HandlersSetup(muxRouter *mux.Router,
vpcID string,
containerInstanceArn string,
) {
tmdsAgentState := v4.NewTMDSAgentState(state)
tmdsAgentState := v4.NewTMDSAgentState(state, ecsClient, cluster, availabilityZone, vpcID, containerInstanceArn)
metricsFactory := metrics.NewNopEntryFactory()
muxRouter.HandleFunc(tmdsv4.ContainerMetadataPath(), tmdsv4.ContainerMetadataHandler(tmdsAgentState, metricsFactory))
muxRouter.HandleFunc(v4.TaskMetadataPath, v4.TaskMetadataHandler(state, ecsClient, cluster, availabilityZone, vpcID, containerInstanceArn, false))
muxRouter.HandleFunc(tmdsv4.TaskMetadataPath(), tmdsv4.TaskMetadataHandler(tmdsAgentState, metricsFactory))
muxRouter.HandleFunc(v4.TaskWithTagsMetadataPath, v4.TaskMetadataHandler(state, ecsClient, cluster, availabilityZone, vpcID, containerInstanceArn, true))
muxRouter.HandleFunc(v4.ContainerStatsPath, v4.ContainerStatsHandler(state, statsEngine))
muxRouter.HandleFunc(v4.TaskStatsPath, v4.TaskStatsHandler(state, statsEngine))
Expand Down
91 changes: 85 additions & 6 deletions agent/handlers/v4/tmdsstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,39 @@ package v4
import (
"fmt"

"github.com/aws/amazon-ecs-agent/agent/api"
"github.com/aws/amazon-ecs-agent/agent/engine/dockerstate"
"github.com/aws/amazon-ecs-agent/ecs-agent/logger"
"github.com/aws/amazon-ecs-agent/ecs-agent/logger/field"
tmdsv4 "github.com/aws/amazon-ecs-agent/ecs-agent/tmds/handlers/v4/state"

"github.com/cihub/seelog"
)

// Implements AgentState interface for TMDS v4.
type TMDSAgentState struct {
state dockerstate.TaskEngineState
state dockerstate.TaskEngineState
ecsClient api.ECSClient
cluster string
availabilityZone string
vpcID string
containerInstanceARN string
}

func NewTMDSAgentState(state dockerstate.TaskEngineState) *TMDSAgentState {
return &TMDSAgentState{state: state}
func NewTMDSAgentState(
state dockerstate.TaskEngineState,
ecsClient api.ECSClient,
cluster string,
availabilityZone string,
vpcID string,
containerInstanceARN string,
) *TMDSAgentState {
return &TMDSAgentState{
state: state,
ecsClient: ecsClient,
cluster: cluster,
availabilityZone: availabilityZone,
vpcID: vpcID,
containerInstanceARN: containerInstanceARN,
}
}

// Returns container metadata in v4 format for the container identified by the provided
Expand All @@ -43,7 +63,10 @@ func (s *TMDSAgentState) GetContainerMetadata(v3EndpointID string) (tmdsv4.Conta

containerResponse, err := NewContainerResponse(containerID, s.state)
if err != nil {
seelog.Errorf("Unable to get container metadata for container '%s'", containerID)
logger.Error("Failed to get container metadata", logger.Fields{
field.Container: containerID,
field.Error: err,
})
return tmdsv4.ContainerResponse{}, tmdsv4.NewErrorMetadataFetchFailure(fmt.Sprintf(
"unable to generate metadata for container '%s'", containerID))
}
Expand All @@ -57,3 +80,59 @@ func (s *TMDSAgentState) GetContainerMetadata(v3EndpointID string) (tmdsv4.Conta

return *containerResponse, nil
}

// Returns task metadata in v4 format for the task identified by the provided endpointContainerID.
func (s *TMDSAgentState) GetTaskMetadata(v3EndpointID string) (tmdsv4.TaskResponse, error) {
taskARN, ok := s.state.TaskARNByV3EndpointID(v3EndpointID)
if !ok {
return tmdsv4.TaskResponse{}, tmdsv4.NewErrorLookupFailure(fmt.Sprintf(
"unable to get task arn from request: unable to get task Arn from v3 endpoint ID: %s",
v3EndpointID))
}

task, ok := s.state.TaskByArn(taskARN)
if !ok {
logger.Error("Task not found in state", logger.Fields{field.TaskARN: taskARN})
return tmdsv4.TaskResponse{}, tmdsv4.NewErrorMetadataFetchFailure(fmt.Sprintf(
"Unable to generate metadata for v4 task: '%s'", taskARN))
}

taskResponse, err := NewTaskResponse(taskARN, s.state, s.ecsClient, s.cluster,
s.availabilityZone, s.vpcID, s.containerInstanceARN, task.ServiceName, false)
if err != nil {
logger.Error("Failed to get task metadata", logger.Fields{
field.TaskARN: taskARN,
field.Error: err,
})
return tmdsv4.TaskResponse{}, tmdsv4.NewErrorMetadataFetchFailure(fmt.Sprintf(
"Unable to generate metadata for v4 task: '%s'", taskARN))
}

// for non-awsvpc task mode
if !task.IsNetworkModeAWSVPC() {
// fill in non-awsvpc network details for container responses here
responses := make([]tmdsv4.ContainerResponse, 0)
for _, containerResponse := range taskResponse.Containers {
networks, err := GetContainerNetworkMetadata(containerResponse.ID, s.state)
if err != nil {
logger.Warn("Error retrieving network metadata", logger.Fields{
field.Container: containerResponse.ID,
field.Error: err,
})
}
containerResponse.Networks = networks
responses = append(responses, containerResponse)
}
taskResponse.Containers = responses
}

pulledContainers, _ := s.state.PulledContainerMapByArn(task.Arn)
// Convert each pulled container into v4 container response
// and append pulled containers to taskResponse.Containers
for _, dockerContainer := range pulledContainers {
taskResponse.Containers = append(taskResponse.Containers,
NewPulledContainerResponse(dockerContainer, task.GetPrimaryENI()))
}

return *taskResponse, nil
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 57 additions & 0 deletions ecs-agent/tmds/handlers/v4/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ func ContainerMetadataPath() string {
return "/v4/" + utils.ConstructMuxVar(EndpointContainerIDMuxName, utils.AnythingButSlashRegEx)
}

func TaskMetadataPath() string {
return fmt.Sprintf(
"/v4/%s/task",
utils.ConstructMuxVar(EndpointContainerIDMuxName, utils.AnythingButSlashRegEx))
}

// ContainerMetadataHandler returns the HTTP handler function for handling container metadata requests.
func ContainerMetadataHandler(
agentState state.AgentState,
Expand Down Expand Up @@ -86,3 +92,54 @@ func getContainerErrorResponse(endpointContainerID string, err error) (int, stri
logger.Fields{field.Error: err})
return http.StatusInternalServerError, "failed to get container metadata"
}

// TaskMetadataHandler returns the HTTP handler function for handling task metadata requests.
func TaskMetadataHandler(
agentState state.AgentState,
metricsFactory metrics.EntryFactory,
) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
endpointContainerID := mux.Vars(r)[EndpointContainerIDMuxName]
taskMetadata, err := agentState.GetTaskMetadata(endpointContainerID)
if err != nil {
logger.Error("Failed to get v4 task metadata", logger.Fields{
field.TMDSEndpointContainerID: endpointContainerID,
field.Error: err,
})

responseCode, responseBody := getTaskErrorResponse(endpointContainerID, err)
utils.WriteJSONResponse(w, responseCode, responseBody, utils.RequestTypeTaskMetadata)

if utils.Is5XXStatus(responseCode) {
metricsFactory.New(metrics.InternalServerErrorMetricName).Done(err)()
}

return
}

logger.Info("Writing response for v4 task metadata", logger.Fields{
field.TMDSEndpointContainerID: endpointContainerID,
field.TaskARN: taskMetadata.TaskARN,
})
utils.WriteJSONResponse(w, http.StatusOK, taskMetadata, utils.RequestTypeTaskMetadata)
}
}

// Returns an appropriate HTTP response status code and body for the task metadata error.
func getTaskErrorResponse(endpointContainerID string, err error) (int, string) {
var errContainerLookupFailed *state.ErrorLookupFailure
if errors.As(err, &errContainerLookupFailed) {
return http.StatusNotFound, fmt.Sprintf("V4 task metadata handler: %s",
errContainerLookupFailed.ExternalReason())
}

var errFailedToGetContainerMetadata *state.ErrorMetadataFetchFailure
if errors.As(err, &errFailedToGetContainerMetadata) {
return http.StatusInternalServerError, errFailedToGetContainerMetadata.ExternalReason()
}

logger.Error("Unknown error encountered when handling task metadata fetch failure", logger.Fields{
field.Error: err,
})
return http.StatusInternalServerError, "failed to get task metadata"
}
Loading