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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions agent/handlers/v4/tmdsstate.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,18 @@ func (s *TMDSAgentState) GetTaskStats(v3EndpointID string) (map[string]*tmdsv4.S
return taskStatsResponse, nil
}

func (s *TMDSAgentState) GetTasksMetadata(endpointContainerID string) ([]tmdsv4.TaskResponse, error) {
return nil, tmdsv4.NewErrorMetadataFetchFailure("tasks metadata endpoint not supported")
}

func (s *TMDSAgentState) GetTasksMetadataWithTags(endpointContainerID string) ([]tmdsv4.TaskResponse, error) {
return nil, tmdsv4.NewErrorMetadataFetchFailure("tasks metadata with tags endpoint not supported")
}

func (s *TMDSAgentState) GetTasksStats(endpointContainerID string) ([]map[string]*tmdsv4.StatsResponse, error) {
return nil, tmdsv4.NewErrorStatsFetchFailure("tasks stats endpoint not supported", nil)
}

// sortContainersCNIPauseFirst sorts containers so that CNI_PAUSE containers appear first.
// Other containers maintain their relative order.
func sortContainersCNIPauseFirst(containers []tmdsv4.ContainerResponse) {
Expand Down
28 changes: 28 additions & 0 deletions agent/handlers/v4/tmdsstate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package v4

import (
"errors"
"testing"

apicontainer "github.com/aws/amazon-ecs-agent/agent/api/container"
Expand Down Expand Up @@ -179,3 +180,30 @@ func TestGetTaskMetadataWithTags(t *testing.T) {
})
}
}

func TestGetTasksMetadata_NotSupported(t *testing.T) {
state := &TMDSAgentState{}
_, err := state.GetTasksMetadata("test-container-id")

var metadataErr *tmdsv4.ErrorMetadataFetchFailure
assert.True(t, errors.As(err, &metadataErr))
assert.Contains(t, err.Error(), "not supported")
}

func TestGetTasksMetadataWithTags_NotSupported(t *testing.T) {
state := &TMDSAgentState{}
_, err := state.GetTasksMetadataWithTags("test-container-id")

var metadataErr *tmdsv4.ErrorMetadataFetchFailure
assert.True(t, errors.As(err, &metadataErr))
assert.Contains(t, err.Error(), "not supported")
}

func TestGetTasksStats_NotSupported(t *testing.T) {
state := &TMDSAgentState{}
_, err := state.GetTasksStats("test")

var statsErr *tmdsv4.ErrorStatsFetchFailure
assert.True(t, errors.As(err, &statsErr))
assert.Contains(t, err.Error(), "not supported")
}

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.

6 changes: 6 additions & 0 deletions ecs-agent/tmds/handlers/utils/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,18 @@ const (
// RequestTypeTaskMetadata specifies the task metadata request type of TaskContainerMetadataHandler.
RequestTypeTaskMetadata = "task metadata"

// RequestTypeTasksMetadata specifies the tasks metadata request type of TaskContainerMetadataHandler.
RequestTypeTasksMetadata = "tasks metadata"

// RequestTypeContainerMetadata specifies the container metadata request type of TaskContainerMetadataHandler.
RequestTypeContainerMetadata = "container metadata"

// RequestTypeTaskStats specifies the task stats request type of StatsHandler.
RequestTypeTaskStats = "task stats"

// RequestTypeTasksStats specifies the tasks stats request type of StatsHandler.
RequestTypeTasksStats = "tasks stats"

// RequestTypeContainerStats specifies the container stats request type of StatsHandler.
RequestTypeContainerStats = "container stats"

Expand Down
95 changes: 95 additions & 0 deletions ecs-agent/tmds/handlers/v4/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ func TaskMetadataPath() string {
utils.ConstructMuxVar(EndpointContainerIDMuxName, utils.AnythingButSlashRegEx))
}

// Returns the standard URI path for tasks metadata endpoint.
func TasksMetadataPath() string {
return fmt.Sprintf(
"/v4/%s/tasks",
utils.ConstructMuxVar(EndpointContainerIDMuxName, utils.AnythingButSlashRegEx))
}

// Returns the standard URI path for task metadata with tags endpoint.
func TaskMetadataWithTagsPath() string {
return fmt.Sprintf(
Expand All @@ -65,6 +72,12 @@ func TaskStatsPath() string {
utils.ConstructMuxVar(EndpointContainerIDMuxName, utils.AnythingButSlashRegEx))
}

// Returns a standard URI path for v4 tasks stats endpoint.
func TasksStatsPath() string {
return fmt.Sprintf("/v4/%s/tasks/stats",
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 @@ -123,6 +136,14 @@ func TaskMetadataHandler(
return taskMetadataHandler(agentState, metricsFactory, false)
}

// TasksMetadataHandler returns the HTTP handler function for handling tasks metadata requests.
func TasksMetadataHandler(
agentState state.AgentState,
metricsFactory metrics.EntryFactory,
) func(http.ResponseWriter, *http.Request) {
return tasksMetadataHandler(agentState, metricsFactory, false)
}

// TaskMetadataHandler returns the HTTP handler function for handling task metadata with tags requests.
func TaskMetadataWithTagsHandler(
agentState state.AgentState,
Expand All @@ -131,6 +152,14 @@ func TaskMetadataWithTagsHandler(
return taskMetadataHandler(agentState, metricsFactory, true)
}

// TasksMetadataHandler returns the HTTP handler function for handling tasks metadata with tags requests.
func TasksMetadataWithTagsHandler(
agentState state.AgentState,
metricsFactory metrics.EntryFactory,
) func(http.ResponseWriter, *http.Request) {
return tasksMetadataHandler(agentState, metricsFactory, true)
}

func taskMetadataHandler(
agentState state.AgentState,
metricsFactory metrics.EntryFactory,
Expand Down Expand Up @@ -169,6 +198,40 @@ func taskMetadataHandler(
}
}

func tasksMetadataHandler(
agentState state.AgentState,
metricsFactory metrics.EntryFactory,
includeTags bool,
) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
endpointContainerID := mux.Vars(r)[EndpointContainerIDMuxName]
var tasksMetadata []state.TaskResponse
var err error

if includeTags {
tasksMetadata, err = agentState.GetTasksMetadataWithTags(endpointContainerID)
} else {
tasksMetadata, err = agentState.GetTasksMetadata(endpointContainerID)
}

if err != nil {
logger.Error("Failed to get v4 tasks metadata", logger.Fields{
field.Error: err,
})

utils.WriteJSONResponse(w, http.StatusInternalServerError,
"failed to get tasks metadata", utils.RequestTypeTasksMetadata)
metricsFactory.New(metrics.InternalServerErrorMetricName).Done(err)
return
}

logger.Info("Writing response for v4 tasks metadata", logger.Fields{
"task_count": len(tasksMetadata),
})
utils.WriteJSONResponse(w, http.StatusOK, tasksMetadata, utils.RequestTypeTasksMetadata)
}
}

// 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
Expand Down Expand Up @@ -206,6 +269,38 @@ func TaskStatsHandler(
utils.RequestTypeTaskStats, taskStatsErrorPrefix)
}

// Returns an HTTP handler for v4 tasks stats endpoint
func TasksStatsHandler(
agentState state.AgentState,
metricsFactory metrics.EntryFactory,
) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
endpointContainerID := mux.Vars(r)[EndpointContainerIDMuxName]
// Get stats for all tasks
stats, err := agentState.GetTasksStats(endpointContainerID)
if err != nil {
logger.Error("Failed to get v4 tasks stats", logger.Fields{
field.Error: err,
})

responseCode, responseBody := getStatsErrorResponse("", err, "V4 tasks stats handler")
utils.WriteJSONResponse(w, responseCode, responseBody, utils.RequestTypeTasksStats)

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

return
}

// Write stats response
logger.Info("Writing response for v4 tasks stats", logger.Fields{
"task_count": len(stats),
})
utils.WriteJSONResponse(w, http.StatusOK, stats, utils.RequestTypeTasksStats)
}
}

// Generic function that returns an HTTP handler for container or task stats endpoint
// depending on the parameters.
func statsHandler[R state.StatsResponse | map[string]*state.StatsResponse](
Expand Down
Loading
Loading