Skip to content

Commit

Permalink
Adding new error type for default network interface not found
Browse files Browse the repository at this point in the history
  • Loading branch information
mye956 committed Oct 2, 2024
1 parent 8431d7f commit 8f1c43b
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 14 deletions.
50 changes: 49 additions & 1 deletion agent/handlers/task_server_setup_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package handlers

import (
"errors"
"fmt"
"net"
"testing"

Expand All @@ -34,13 +36,20 @@ import (
"github.com/vishvananda/netlink"
)

const (
internalError = "internal error"
defaultNetworkInterfaceNameErrorMessage = "failed to obtain default network interface name: unable to obtain default network interface name on host from v3 endpoint ID: %s"
)

func TestV4GetTaskMetadataWithTaskNetworkConfig(t *testing.T) {

tcs := []struct {
name string
setStateExpectations func(state *mock_dockerstate.MockTaskEngineState)
setNetLinkExpectations func(netLink *mock_netlinkwrapper.MockNetLink)
expectedTaskNetworkConfig *v4.TaskNetworkConfig
shouldError bool
errorMessage string
}{
{
name: "happy case with awsvpc mode",
Expand Down Expand Up @@ -112,6 +121,38 @@ func TestV4GetTaskMetadataWithTaskNetworkConfig(t *testing.T) {
},
expectedTaskNetworkConfig: expectedV4TaskNetworkConfig(true, bridgeMode, "", ""),
},
{
name: "unhappy case with host mode",
setStateExpectations: func(state *mock_dockerstate.MockTaskEngineState) {
hostTask := standardHostTask()
hostTask.EnableFaultInjection = true
hostTask.NetworkNamespace = networkNamespace
hostTask.DefaultIfname = defaultIfname
gomock.InOrder(
state.EXPECT().TaskARNByV3EndpointID(v3EndpointID).Return(taskARN, true),
state.EXPECT().TaskByArn(taskARN).Return(hostTask, true).Times(2),
state.EXPECT().ContainerMapByArn(taskARN).Return(containerNameToDockerContainer, true),
state.EXPECT().ContainerByID(containerID).Return(nil, false).AnyTimes(),
state.EXPECT().PulledContainerMapByArn(taskARN).Return(nil, true),
state.EXPECT().ContainerByID(containerID).Return(nil, false).AnyTimes(),
)
},
setNetLinkExpectations: func(netLink *mock_netlinkwrapper.MockNetLink) {
routes := []netlink.Route{
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: nil,
LinkIndex: 0,
},
}
gomock.InOrder(
netLink.EXPECT().RouteList(nil, netlink.FAMILY_ALL).Return(routes, errors.New(internalError)).Times(1),
)
},
expectedTaskNetworkConfig: expectedV4TaskNetworkConfig(true, apitask.HostNetworkMode, hostNetworkNamespace, ""),
shouldError: true,
errorMessage: fmt.Sprintf(defaultNetworkInterfaceNameErrorMessage, v3EndpointID),
},
}

for _, tc := range tcs {
Expand All @@ -138,7 +179,14 @@ func TestV4GetTaskMetadataWithTaskNetworkConfig(t *testing.T) {

actualTaskResponse, err := tmdsAgentState.GetTaskMetadataWithTaskNetworkConfig(v3EndpointID, netConfigClient)

assert.NoError(t, err)
if tc.shouldError {
var errDefaultNetworkInterfaceName *v4.ErrorDefaultNetworkInterfaceName
assert.Error(t, err)
assert.ErrorAs(t, err, &errDefaultNetworkInterfaceName)
assert.Equal(t, tc.errorMessage, err.Error())
} else {
assert.NoError(t, err)
}
assert.Equal(t, tc.expectedTaskNetworkConfig, actualTaskResponse.TaskNetworkConfig)
})
}
Expand Down
13 changes: 10 additions & 3 deletions agent/handlers/v4/tmdsstate_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,28 @@
package v4

import (
"fmt"

"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/aws/amazon-ecs-agent/ecs-agent/tmds/utils/netconfig"
)

const (
defaultNetworkInterfaceNameNotFoundError = "unable to obtain default network interface name on host from v3 endpoint ID: %s"
)

// Returns task metadata including the task network configuration in v4 format for the
// task identified by the provided endpointContainerID.
func (s *TMDSAgentState) GetTaskMetadataWithTaskNetworkConfig(v3EndpointID string, networkConfigClient *netconfig.NetworkConfigClient) (tmdsv4.TaskResponse, error) {
taskResponse, err := s.getTaskMetadata(v3EndpointID, false, true)
if err == nil {
if taskResponse.TaskNetworkConfig != nil && taskResponse.TaskNetworkConfig.NetworkMode == "host" {
hostDeviceName, err := netconfig.DefaultNetInterfaceName(networkConfigClient.NetlinkClient)
if err != nil {
logger.Warn("Unable to obtain default network interface on host", logger.Fields{
hostDeviceName, netErr := netconfig.DefaultNetInterfaceName(networkConfigClient.NetlinkClient)
if netErr != nil {
err = tmdsv4.NewErrorDefaultNetworkInterfaceName(fmt.Sprintf(defaultNetworkInterfaceNameNotFoundError, v3EndpointID))
logger.Error("Unable to obtain default network interface on host", logger.Fields{
field.TaskARN: taskResponse.TaskARN,
field.Error: err,
})
Expand Down

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.

21 changes: 19 additions & 2 deletions ecs-agent/tmds/handlers/fault/v1/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,12 +1115,29 @@ func validateTaskMetadata(w http.ResponseWriter, agentState state.AgentState, re
func getTaskMetadataErrorResponse(endpointContainerID, requestType string, err error) (int, error) {
var errContainerLookupFailed *state.ErrorLookupFailure
if errors.As(err, &errContainerLookupFailed) {
return http.StatusNotFound, fmt.Errorf("unable to lookup container: %s", endpointContainerID)
logger.Error("Unable to lookup container", logger.Fields{
field.Error: errContainerLookupFailed.ExternalReason(),
field.TMDSEndpointContainerID: endpointContainerID,
})
return http.StatusNotFound, errors.New(errContainerLookupFailed.ExternalReason())
}

var errFailedToGetContainerMetadata *state.ErrorMetadataFetchFailure
if errors.As(err, &errFailedToGetContainerMetadata) {
return http.StatusInternalServerError, fmt.Errorf("unable to obtain container metadata for container: %s", endpointContainerID)
logger.Error("Unable to obtain container metadata for container", logger.Fields{
field.Error: errFailedToGetContainerMetadata.ExternalReason(),
field.TMDSEndpointContainerID: endpointContainerID,
})
return http.StatusInternalServerError, errors.New(errFailedToGetContainerMetadata.ExternalReason())
}

var errDefaultNetworkInterfaceName *state.ErrorDefaultNetworkInterfaceName
if errors.As(err, &errDefaultNetworkInterfaceName) {
logger.Error("Unable to obtain default network interface on host", logger.Fields{
field.Error: errDefaultNetworkInterfaceName.ExternalReason(),
field.TMDSEndpointContainerID: endpointContainerID,
})
return http.StatusInternalServerError, errors.New(errDefaultNetworkInterfaceName.ExternalReason())
}

logger.Error("Unknown error encountered when handling task metadata fetch failure", logger.Fields{
Expand Down
43 changes: 37 additions & 6 deletions ecs-agent/tmds/handlers/fault/v1/handlers/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ func generateCommonNetworkBlackHolePortTestCases(name string) []networkFaultInje
name: fmt.Sprintf("%s task lookup fail", name),
expectedStatusCode: 404,
requestBody: happyBlackHolePortReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse(fmt.Sprintf("unable to lookup container: %s", endpointId)),
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("task lookup failed"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).
Return(state.TaskResponse{}, state.NewErrorLookupFailure("task lookup failed")).
Expand All @@ -341,7 +341,7 @@ func generateCommonNetworkBlackHolePortTestCases(name string) []networkFaultInje
name: fmt.Sprintf("%s task metadata fetch fail", name),
expectedStatusCode: 500,
requestBody: happyBlackHolePortReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse(fmt.Sprintf("unable to obtain container metadata for container: %s", endpointId)),
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("Unable to generate metadata for task"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorMetadataFetchFailure(
"Unable to generate metadata for task")).
Expand Down Expand Up @@ -478,6 +478,17 @@ func generateCommonNetworkBlackHolePortTestCases(name string) []networkFaultInje
)
},
},
{
name: fmt.Sprintf("%s task metadata obtain default network interface name fail", name),
expectedStatusCode: 500,
requestBody: happyBlackHolePortReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("unable to obtain default network interface name on host"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorDefaultNetworkInterfaceName(
"unable to obtain default network interface name on host")).
Times(1)
},
},
}
return tcs
}
Expand Down Expand Up @@ -1085,7 +1096,7 @@ func generateCommonNetworkLatencyTestCases(name string) []networkFaultInjectionT
name: fmt.Sprintf("%s task lookup fail", name),
expectedStatusCode: 404,
requestBody: happyNetworkLatencyReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse(fmt.Sprintf("unable to lookup container: %s", endpointId)),
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("task lookup failed"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorLookupFailure("task lookup failed"))
},
Expand All @@ -1094,7 +1105,7 @@ func generateCommonNetworkLatencyTestCases(name string) []networkFaultInjectionT
name: fmt.Sprintf("%s task metadata fetch fail", name),
expectedStatusCode: 500,
requestBody: happyNetworkLatencyReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse(fmt.Sprintf("unable to obtain container metadata for container: %s", endpointId)),
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("Unable to generate metadata for task"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorMetadataFetchFailure(
"Unable to generate metadata for task"))
Expand Down Expand Up @@ -1193,6 +1204,16 @@ func generateCommonNetworkLatencyTestCases(name string) []networkFaultInjectionT
)
},
},
{
name: fmt.Sprintf("%s task metadata obtain default network interface name fail", name),
expectedStatusCode: 500,
requestBody: happyNetworkLatencyReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("unable to obtain default network interface name on host"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorDefaultNetworkInterfaceName(
"unable to obtain default network interface name on host"))
},
},
}
return tcs
}
Expand Down Expand Up @@ -1634,7 +1655,7 @@ func generateCommonNetworkPacketLossTestCases(name string) []networkFaultInjecti
name: fmt.Sprintf("%s task lookup fail", name),
expectedStatusCode: 404,
requestBody: happyNetworkPacketLossReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse(fmt.Sprintf("unable to lookup container: %s", endpointId)),
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("task lookup failed"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorLookupFailure("task lookup failed"))
},
Expand All @@ -1643,7 +1664,7 @@ func generateCommonNetworkPacketLossTestCases(name string) []networkFaultInjecti
name: fmt.Sprintf("%s task metadata fetch fail", name),
expectedStatusCode: 500,
requestBody: happyNetworkPacketLossReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse(fmt.Sprintf("unable to obtain container metadata for container: %s", endpointId)),
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("Unable to generate metadata for task"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorMetadataFetchFailure(
"Unable to generate metadata for task"))
Expand Down Expand Up @@ -1742,6 +1763,16 @@ func generateCommonNetworkPacketLossTestCases(name string) []networkFaultInjecti
)
},
},
{
name: fmt.Sprintf("%s task metadata obtain default network interface name fail", name),
expectedStatusCode: 500,
requestBody: happyNetworkPacketLossReqBody,
expectedResponseBody: types.NewNetworkFaultInjectionErrorResponse("unable to obtain default network interface name on host"),
setAgentStateExpectations: func(agentState *mock_state.MockAgentState, netConfigClient *netconfig.NetworkConfigClient) {
agentState.EXPECT().GetTaskMetadataWithTaskNetworkConfig(endpointId, netConfigClient).Return(state.TaskResponse{}, state.NewErrorDefaultNetworkInterfaceName(
"unable to obtain default network interface name on host"))
},
},
}
return tcs
}
Expand Down
17 changes: 17 additions & 0 deletions ecs-agent/tmds/handlers/v4/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,23 @@ func (e *ErrorStatsFetchFailure) Unwrap() error {
return e.cause
}

// Error to be returned when we're unable to obtain the default network interface name on the host namespace for a task
type ErrorDefaultNetworkInterfaceName struct {
externalReason string // Reason to be returned in TMDS response
}

func NewErrorDefaultNetworkInterfaceName(externalReason string) *ErrorDefaultNetworkInterfaceName {
return &ErrorDefaultNetworkInterfaceName{externalReason: externalReason}
}

func (e *ErrorDefaultNetworkInterfaceName) ExternalReason() string {
return e.externalReason
}

func (e *ErrorDefaultNetworkInterfaceName) Error() string {
return fmt.Sprintf("failed to obtain default network interface name: %s", e.externalReason)
}

// Interface for interacting with Agent State relevant to TMDS
type AgentState interface {
// Returns container metadata in v4 format for the container identified by the
Expand Down

0 comments on commit 8f1c43b

Please sign in to comment.