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
22 changes: 13 additions & 9 deletions ecs-agent/netlib/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,10 @@ func getSingleNetNSAWSVPCTestData(testTaskID string) (*ecsacs.Task, tasknetworkc
NetworkMode: types.NetworkModeAwsvpc,
NetworkNamespaces: []*tasknetworkconfig.NetworkNamespace{
{
Name: netNSName,
Path: netNSPath,
Index: 0,
Name: netNSName,
Path: netNSPath,
Index: 0,
NetworkMode: types.NetworkModeAwsvpc,
NetworkInterfaces: []*networkinterface.NetworkInterface{
&netIfs[0],
},
Expand Down Expand Up @@ -156,19 +157,21 @@ func getMultiNetNSMultiIfaceAWSVPCTestData(testTaskID string) (*ecsacs.Task, tas
NetworkMode: types.NetworkModeAwsvpc,
NetworkNamespaces: []*tasknetworkconfig.NetworkNamespace{
{
Name: primaryNetNSName,
Path: primaryNetNSPath,
Index: 0,
Name: primaryNetNSName,
Path: primaryNetNSPath,
Index: 0,
NetworkMode: types.NetworkModeAwsvpc,
NetworkInterfaces: []*networkinterface.NetworkInterface{
&netIfs[0],
},
KnownState: status.NetworkNone,
DesiredState: status.NetworkReadyPull,
},
{
Name: secondaryNetNSName,
Path: secondaryNetNSPath,
Index: 1,
Name: secondaryNetNSName,
Path: secondaryNetNSPath,
Index: 1,
NetworkMode: types.NetworkModeAwsvpc,
NetworkInterfaces: []*networkinterface.NetworkInterface{
&netIfs[1],
},
Expand Down Expand Up @@ -323,6 +326,7 @@ func getV2NTestData(testTaskID string) (*ecsacs.Task, tasknetworkconfig.TaskNetw
Name: netNSName,
Path: netNSPath,
Index: 0,
NetworkMode: types.NetworkModeAwsvpc,
NetworkInterfaces: netIfs,
KnownState: status.NetworkNone,
DesiredState: status.NetworkReadyPull,
Expand Down
12 changes: 12 additions & 0 deletions ecs-agent/netlib/model/tasknetworkconfig/network_namespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/networkinterface"
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/serviceconnect"
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/status"
"github.com/aws/aws-sdk-go-v2/service/ecs/types"
)

// NetworkNamespace is model representing each network namespace.
Expand All @@ -30,6 +31,10 @@ type NetworkNamespace struct {
Path string
Index int

// NetworkMode represents the network mode for this namespace.
// Supported values: awsvpc (default), host(managed-instances only), daemon-bridge (managed-instances only).
NetworkMode types.NetworkMode

// NetworkInterfaces represents ENIs or any kind of network interface associated the particular netns.
NetworkInterfaces []*networkinterface.NetworkInterface

Expand Down Expand Up @@ -58,6 +63,7 @@ func NewNetworkNamespace(
NetworkInterfaces: networkInterfaces,
KnownState: status.NetworkNone,
DesiredState: status.NetworkReadyPull,
NetworkMode: types.NetworkModeAwsvpc,
}

// Sort interfaces as per their index values in ascending order.
Expand Down Expand Up @@ -104,3 +110,9 @@ func (ns *NetworkNamespace) GetInterfaceByIndex(idx int64) *networkinterface.Net

return nil
}

// WithNetworkMode sets the NetworkMode field
func (ns *NetworkNamespace) WithNetworkMode(mode types.NetworkMode) *NetworkNamespace {
ns.NetworkMode = mode
return ns
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package tasknetworkconfig
import (
"testing"

"github.com/aws/aws-sdk-go-v2/service/ecs/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -47,6 +48,7 @@ func TestNewNetworkNamespace(t *testing.T) {
assert.Equal(t, primaryNetNSName, netns.Name)
assert.Equal(t, primaryNetNSPath, netns.Path)
assert.Equal(t, 0, netns.Index)
assert.Equal(t, types.NetworkModeAwsvpc, netns.NetworkMode)
assert.Empty(t, netns.AppMeshConfig)
assert.Equal(t, *netIFs[0], *netns.NetworkInterfaces[0])
assert.Equal(t, *netIFs[1], *netns.NetworkInterfaces[1])
Expand Down Expand Up @@ -78,3 +80,41 @@ func TestNetworkNamespace_IsPrimary(t *testing.T) {
require.Equal(t, tc.isPrimary, tc.netNS.IsPrimary())
}
}

func TestNetworkNamespace_WithNetworkMode(t *testing.T) {
testCases := []struct {
name string
networkMode types.NetworkMode
}{
{
name: "awsvpc mode",
networkMode: types.NetworkModeAwsvpc,
},
{
name: "host mode",
networkMode: types.NetworkModeHost,
},
{
name: "bridge mode",
networkMode: types.NetworkModeBridge,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
netns := &NetworkNamespace{
Name: "test-netns",
Path: "/test/path",
Index: 0,
NetworkMode: types.NetworkModeAwsvpc, // default
}

result := netns.WithNetworkMode(tc.networkMode)

// Verify the method returns the same instance
assert.Same(t, netns, result)
// Verify the NetworkMode was updated
assert.Equal(t, tc.networkMode, netns.NetworkMode)
})
}
}
4 changes: 4 additions & 0 deletions ecs-agent/netlib/network_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,8 @@ func (nb *networkBuilder) Start(
err = nb.startAWSVPC(ctx, taskID, netNS)
case types.NetworkModeHost:
err = nb.platformAPI.HandleHostMode()
case "daemon-bridge":
err = nb.platformAPI.ConfigureDaemonNetNS(netNS)
default:
err = errors.New("invalid network mode: " + string(mode))
}
Expand Down Expand Up @@ -132,6 +134,8 @@ func (nb *networkBuilder) Stop(ctx context.Context, mode types.NetworkMode, task
err = nb.stopAWSVPC(ctx, netNS)
case types.NetworkModeHost:
err = nb.platformAPI.HandleHostMode()
case "daemon-bridge":
err = nb.platformAPI.StopDaemonNetNS(ctx, netNS)
default:
err = errors.New("invalid network mode: " + string(mode))
}
Expand Down
64 changes: 64 additions & 0 deletions ecs-agent/netlib/network_builder_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,13 @@ func TestNetworkBuilder_BuildTaskNetworkConfiguration(t *testing.T) {

func TestNetworkBuilder_Start(t *testing.T) {
t.Run("awsvpc", testNetworkBuilder_StartAWSVPC)
t.Run("daemon-bridge", testNetworkBuilder_StartDaemonBridge)
}

// TestNetworkBuilder_Stop verifies stop workflow for AWSVPC mode.
func TestNetworkBuilder_Stop(t *testing.T) {
t.Run("awsvpc", testNetworkBuilder_StopAWSVPC)
t.Run("daemon-bridge", testNetworkBuilder_StopDaemonBridge)
}

// getTestFunc returns a test function that verifies the capability of the networkBuilder
Expand Down Expand Up @@ -380,3 +382,65 @@ func getExpectedCalls_StopAWSVPC(
platformAPI.EXPECT().DeleteDNSConfig(netNS.Name).Return(nil).Times(1),
platformAPI.EXPECT().DeleteNetNS(netNS.Path).Return(nil).Times(1))
}

// testNetworkBuilder_StartDaemonBridge verifies that the expected platform API calls
// are made by the network builder for daemon-bridge network mode.
func testNetworkBuilder_StartDaemonBridge(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

ctx := context.TODO()
platformAPI := mock_platform.NewMockAPI(ctrl)
metricsFactory := mock_metrics.NewMockEntryFactory(ctrl)
mockEntry := mock_metrics.NewMockEntry(ctrl)
netBuilder := &networkBuilder{
platformAPI: platformAPI,
metricsFactory: metricsFactory,
}

netNS := &tasknetworkconfig.NetworkNamespace{
Name: "daemon-test",
Path: "/var/run/netns/daemon-test",
}

gomock.InOrder(
metricsFactory.EXPECT().New(metrics.BuildNetworkNamespaceMetricName).Return(mockEntry).Times(1),
mockEntry.EXPECT().WithFields(gomock.Any()).Return(mockEntry).Times(1),
platformAPI.EXPECT().ConfigureDaemonNetNS(netNS).Return(nil).Times(1),
mockEntry.EXPECT().Done(nil).Times(1),
)

err := netBuilder.Start(ctx, "daemon-bridge", taskID, netNS)
require.NoError(t, err)
}

// testNetworkBuilder_StopDaemonBridge verifies that the expected platform API calls
// are made by the network builder for stopping daemon-bridge network mode.
func testNetworkBuilder_StopDaemonBridge(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

ctx := context.TODO()
platformAPI := mock_platform.NewMockAPI(ctrl)
metricsFactory := mock_metrics.NewMockEntryFactory(ctrl)
mockEntry := mock_metrics.NewMockEntry(ctrl)
netBuilder := &networkBuilder{
platformAPI: platformAPI,
metricsFactory: metricsFactory,
}

netNS := &tasknetworkconfig.NetworkNamespace{
Name: "daemon-test",
Path: "/var/run/netns/daemon-test",
}

gomock.InOrder(
metricsFactory.EXPECT().New(metrics.DeleteNetworkNamespaceMetricName).Return(mockEntry).Times(1),
mockEntry.EXPECT().WithFields(gomock.Any()).Return(mockEntry).Times(1),
platformAPI.EXPECT().StopDaemonNetNS(ctx, netNS).Return(nil).Times(1),
mockEntry.EXPECT().Done(nil).Times(1),
)

err := netBuilder.Stop(ctx, "daemon-bridge", taskID, netNS)
require.NoError(t, err)
}
8 changes: 8 additions & 0 deletions ecs-agent/netlib/platform/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ type API interface {
primaryIf *networkinterface.NetworkInterface,
scConfig *serviceconnect.ServiceConnectConfig,
) error

// ConfigureDaemonNetNS configures a network namespace for workloads running as daemons.
// This is an internal networking mode available in EMI (ECS Managed Instances) only.
ConfigureDaemonNetNS(netNS *tasknetworkconfig.NetworkNamespace) error

// StopDaemonNetNS stops and cleans up a daemon network namespace.
// This is an internal networking mode available in EMI (ECS Managed Instances) only.
StopDaemonNetNS(ctx context.Context, netNS *tasknetworkconfig.NetworkNamespace) error
}

// Config contains platform-specific data.
Expand Down
13 changes: 13 additions & 0 deletions ecs-agent/netlib/platform/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"time"

"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/ecscni"
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/tasknetworkconfig"

"github.com/containernetworking/cni/pkg/types"
)
Expand Down Expand Up @@ -92,3 +93,15 @@ func (c *common) interfacesMACToName() (map[string]string, error) {
func (c *common) HandleHostMode() error {
return errors.New("invalid platform for host mode")
}

// ConfigureDaemonNetNS configures a network namespace for workloads running as daemons.
// This is an internal networking mode available in EMI (ECS Managed Instances) only.
func (c *common) ConfigureDaemonNetNS(netNS *tasknetworkconfig.NetworkNamespace) error {
return errors.New("daemon network namespaces are not supported in this platform")
}

// StopDaemonNetNS stops and cleans up a daemon network namespace.
// This is an internal networking mode available in EMI (ECS Managed Instances) only.
func (c *common) StopDaemonNetNS(ctx context.Context, netNS *tasknetworkconfig.NetworkNamespace) error {
return errors.New("daemon network namespaces are not supported in this platform")
}
2 changes: 1 addition & 1 deletion ecs-agent/netlib/platform/common_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ func (c *common) buildAWSVPCNetworkNamespaces(
return netNSs, nil
}

// buildNetNS creates a single network namespace object using the input network config data.
// buildNetNS creates a single awsvpc network namespace object using the input network config data.
func (c *common) buildNetNS(
taskID string,
index int,
Expand Down
7 changes: 4 additions & 3 deletions ecs-agent/netlib/platform/containerd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ func (c *containerd) buildAWSVPCNetworkConfig(
}

netNS := &tasknetworkconfig.NetworkNamespace{
Name: netNSName,
Path: netNSPath,
Index: 0,
Name: netNSName,
Path: netNSPath,
Index: 0,
NetworkMode: ecstypes.NetworkModeAwsvpc,
NetworkInterfaces: []*networkinterface.NetworkInterface{
iface,
},
Expand Down
Loading
Loading