Skip to content
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

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

5 changes: 3 additions & 2 deletions ecs-agent/metrics/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
SaveNetworkNamespaceMetricName = dbClientMetricNamespace + ".SaveNetworkNamespace"
GetNetworkNamespaceMetricName = dbClientMetricNamespace + ".GetNetworkNamespace"

networkBuilderNamespace = "NetworkBuilder"
BuildNetworkNamespaceMetricName = networkBuilderNamespace + ".BuildNetworkNamespace"
networkBuilderNamespace = "NetworkBuilder"
BuildNetworkNamespaceMetricName = networkBuilderNamespace + ".BuildNetworkNamespace"
DeleteNetworkNamespaceMetricName = networkBuilderNamespace + ".DeleteNetworkNamespace"
)
98 changes: 72 additions & 26 deletions ecs-agent/netlib/network_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,37 @@ func (nb *networkBuilder) Start(
}

func (nb *networkBuilder) Stop(ctx context.Context, mode string, taskID string, netNS *tasknetworkconfig.NetworkNamespace) error {
// TODO: To be implemented.
return nil
logFields := map[string]interface{}{
"NetworkMode": mode,
"NetNSName": netNS.Name,
"NetNSPath": netNS.Path,
"AppMeshEnabled": netNS.AppMeshConfig != nil,
"ServiceConnectEnabled": netNS.ServiceConnectConfig != nil,
}
metricEntry := nb.metricsFactory.New(metrics.DeleteNetworkNamespaceMetricName).WithFields(logFields)

netNS.Mutex.Lock()
defer netNS.Mutex.Unlock()

logger.Info("Deleting network namespace setup", logFields)

var err error
switch mode {
case ecs.NetworkModeAwsvpc:
err = nb.stopAWSVPC(ctx, netNS)
default:
err = errors.New("invalid network mode: " + mode)
}

metricEntry.Done(err)

return err
}

// startAWSVPC executes the required platform API methods in order to configure
// the task's network namespace running in AWSVPC mode.
func (nb *networkBuilder) startAWSVPC(ctx context.Context, taskID string, netNS *tasknetworkconfig.NetworkNamespace) error {
var err error
if netNS.DesiredState == status.NetworkDeleted {
return errors.New("invalid transition state encountered: " + netNS.DesiredState.String())
}
Expand All @@ -126,7 +150,7 @@ func (nb *networkBuilder) startAWSVPC(ctx context.Context, taskID string, netNS

logger.Debug("Creating netns: " + netNS.Path)
// Create network namespace on the host.
err := nb.platformAPI.CreateNetNS(netNS.Path)
err = nb.platformAPI.CreateNetNS(netNS.Path)
if err != nil {
return err
}
Expand All @@ -140,27 +164,10 @@ func (nb *networkBuilder) startAWSVPC(ctx context.Context, taskID string, netNS
}
}

// Execute CNI plugins to configure the interfaces in the namespace.
// Depending on the type of interfaces in the netns, there maybe operations
// to execute when the netns desired status is READY_PULL and READY.
for _, iface := range netNS.NetworkInterfaces {
logFields := logger.Fields{
"Interface": iface,
"NetNSName": netNS.Name,
}
if iface.KnownStatus == netNS.DesiredState {
logger.Debug("Interface already in desired state")
continue
}

logger.Debug("Configuring interface", logFields)
iface.DesiredStatus = netNS.DesiredState

err := nb.platformAPI.ConfigureInterface(ctx, netNS.Path, iface)
if err != nil {
return err
}
iface.KnownStatus = netNS.DesiredState
// Configure each interface inside the network namespace.
err = nb.configureNetNSInterfaces(ctx, netNS)
if err != nil {
return err
}

// Configure AppMesh and service connect rules in the netns.
Expand All @@ -171,7 +178,7 @@ func (nb *networkBuilder) startAWSVPC(ctx context.Context, taskID string, netNS
"AppMeshConfig": netNS.AppMeshConfig,
})

err := nb.platformAPI.ConfigureAppMesh(ctx, netNS.Path, netNS.AppMeshConfig)
err = nb.platformAPI.ConfigureAppMesh(ctx, netNS.Path, netNS.AppMeshConfig)
if err != nil {
return errors.Wrapf(err, "failed to configure AppMesh in netns %s", netNS.Name)
}
Expand All @@ -182,13 +189,52 @@ func (nb *networkBuilder) startAWSVPC(ctx context.Context, taskID string, netNS
"ServiceConnectConfig": netNS.ServiceConnectConfig,
})

err := nb.platformAPI.ConfigureServiceConnect(
err = nb.platformAPI.ConfigureServiceConnect(
ctx, netNS.Path, netNS.GetPrimaryInterface(), netNS.ServiceConnectConfig)
if err != nil {
return errors.Wrapf(err, "failed to configure ServiceConnect in netns %s", netNS.Name)
}
}
}

return err
}

// configureNetNSInterfaces executes the platform API to configure every interface inside a network namespace.
func (nb *networkBuilder) configureNetNSInterfaces(ctx context.Context, netNS *tasknetworkconfig.NetworkNamespace) error {
for _, iface := range netNS.NetworkInterfaces {
logFields := logger.Fields{
"Interface": iface,
"NetNSName": netNS.Name,
}
if iface.KnownStatus == netNS.DesiredState {
logger.Debug("Interface already in desired state", logFields)
continue
}

// The interface desired status is driven by the network namespace's desired state.
logger.Debug("Configuring interface", logFields)
iface.DesiredStatus = netNS.DesiredState

err := nb.platformAPI.ConfigureInterface(ctx, netNS.Path, iface)
if err != nil {
return err
}
iface.KnownStatus = netNS.DesiredState
}

return nil
}

func (nb *networkBuilder) stopAWSVPC(ctx context.Context, netNS *tasknetworkconfig.NetworkNamespace) error {
if netNS.DesiredState != status.NetworkDeleted {
return errors.New("invalid transition state encountered: " + netNS.DesiredState.String())
}

err := nb.configureNetNSInterfaces(ctx, netNS)
if err != nil {
return err
}

return nb.platformAPI.DeleteNetNS(netNS.Path)
}
66 changes: 66 additions & 0 deletions ecs-agent/netlib/network_builder_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ func TestNetworkBuilder_Start(t *testing.T) {
t.Run("awsvpc", testNetworkBuilder_StartAWSVPC)
}

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

// getTestFunc returns a test function that verifies the capability of the networkBuilder
// to translate a given input task payload into desired network data models.
func getTestFunc(dataGenF func(string) (input *ecsacs.Task, expected tasknetworkconfig.TaskNetworkConfig)) func(*testing.T) {
Expand Down Expand Up @@ -279,3 +284,64 @@ func getExpectedCalls_StartAWSVPC(

return calls
}

func testNetworkBuilder_StopAWSVPC(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,
}

// Single ENI use case without AppMesh and service connect configs.
_, taskNetConfig := getSingleNetNSMultiIfaceAWSVPCTestData(taskID)
netNS := taskNetConfig.GetPrimaryNetNS()
mockEntry = mock_metrics.NewMockEntry(ctrl)
t.Run("multi-eni", func(*testing.T) {
gomock.InOrder(
getExpectedCalls_StopAWSVPC(ctx, platformAPI, metricsFactory, mockEntry, netNS)...,
)
netBuilder.Stop(ctx, ecs.NetworkModeAwsvpc, taskID, netNS)
})
}

// getExpectedCalls_StopAWSVPC returns a list of gomock calls that will be executed
// while test stop workflow for AWSVPC.
func getExpectedCalls_StopAWSVPC(
ctx context.Context,
platformAPI *mock_platform.MockAPI,
metricsFactory *mock_metrics.MockEntryFactory,
mockEntry *mock_metrics.MockEntry,
netNS *tasknetworkconfig.NetworkNamespace,
) []*gomock.Call {
var calls []*gomock.Call

calls = append(calls,
metricsFactory.EXPECT().New(metrics.DeleteNetworkNamespaceMetricName).Return(mockEntry).Times(1),
mockEntry.EXPECT().WithFields(gomock.Any()).Return(mockEntry).Times(1))

// Stop() should not be invoked when desired state = DELETED.
if netNS.DesiredState != status.NetworkDeleted {
calls = append(calls,
mockEntry.EXPECT().Done(gomock.Any()).Times(1))
return calls
}

// For each interface inside the netns, the network builder needs to invoke the
// `ConfigureInterface` platformAPI.
for _, iface := range netNS.NetworkInterfaces {
if iface.KnownStatus == netNS.DesiredState {
continue
}
calls = append(calls,
platformAPI.EXPECT().ConfigureInterface(ctx, netNS.Path, iface).Return(nil).Times(1))
}

return append(calls,
platformAPI.EXPECT().DeleteNetNS(netNS.Path).Return(nil).Times(1))
}
2 changes: 1 addition & 1 deletion ecs-agent/netlib/platform/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type API interface {
CreateNetNS(netNSPath string) error

// DeleteNetNS deletes the specified network namespace.
DeleteNetNS(netNSName string) error
DeleteNetNS(netNSPath string) error

// CreateDNSConfig creates the following DNS config files depending on the
// network namespace configuration:
Expand Down
18 changes: 18 additions & 0 deletions ecs-agent/netlib/platform/common_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,24 @@ func (c *common) CreateNetNS(netNSPath string) error {
return err
}

func (c *common) DeleteNetNS(netNSPath string) error {
nsExists, err := c.nsUtil.NSExists(netNSPath)
if err != nil {
return errors.Wrapf(err, "failed to check netns %s", netNSPath)
}

if !nsExists {
return nil
}

err = c.nsUtil.DelNetNS(netNSPath)
if err != nil {
return errors.Wrapf(err, "failed to delete netns %s", netNSPath)
}

return nil
}

// setUpLoFunc returns a method that sets the loop back interface inside a
// particular network namespace to the state "UP". This function is used to
// set up the loop back interface inside a task network namespace soon after
Expand Down
22 changes: 20 additions & 2 deletions ecs-agent/netlib/platform/common_linux_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,8 @@ func TestCommon_CreateDNSFiles(t *testing.T) {
}

func TestCommon_ConfigureInterface(t *testing.T) {
t.Run("create-regular-eni", testRegularENIConfiguration)
t.Run("create-branch-eni", testBranchENIConfiguration)
t.Run("configure-regular-eni", testRegularENIConfiguration)
t.Run("configure-branch-eni", testBranchENIConfiguration)
}

// TestInterfacesMACToName verifies interfacesMACToName behaves as expected.
Expand Down Expand Up @@ -282,6 +282,17 @@ func testRegularENIConfiguration(t *testing.T) {
)
err = commonPlatform.configureInterface(ctx, netNSPath, eni)
require.NoError(t, err)

// Delete workflow.
eni.Default = true
eni.DesiredStatus = status.NetworkDeleted
eniConfig = createENIPluginConfigs(netNSPath, eni)
gomock.InOrder(
osWrapper.EXPECT().Setenv("ECS_CNI_LOG_FILE", ecscni.PluginLogPath).Times(1),
osWrapper.EXPECT().Setenv("IPAM_DB_PATH", filepath.Join(commonPlatform.stateDBDir, "eni-ipam.db")),
)
err = commonPlatform.configureInterface(ctx, netNSPath, eni)
require.NoError(t, err)
}

func testBranchENIConfiguration(t *testing.T) {
Expand All @@ -306,4 +317,11 @@ func testBranchENIConfiguration(t *testing.T) {
cniClient.EXPECT().Add(gomock.Any(), cniConfig).Return(nil, nil).Times(1)
err = commonPlatform.configureInterface(ctx, netNSPath, branchENI)
require.NoError(t, err)

// Delete workflow.
branchENI.DesiredStatus = status.NetworkDeleted
cniConfig = createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeTap)
cniClient.EXPECT().Del(gomock.Any(), cniConfig).Return(nil).Times(1)
err = commonPlatform.configureInterface(ctx, netNSPath, branchENI)
require.NoError(t, err)
}
4 changes: 0 additions & 4 deletions ecs-agent/netlib/platform/containerd_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,6 @@ func (c *containerd) BuildTaskNetworkConfiguration(
return c.common.buildTaskNetworkConfiguration(taskID, taskPayload)
}

func (c *containerd) DeleteNetNS(netnsName string) error {
return nil
}

func (c *containerd) CreateDNSConfig(taskID string, netNS *tasknetworkconfig.NetworkNamespace) error {
return c.common.createDNSConfig(taskID, false, netNS)
}
Expand Down
2 changes: 1 addition & 1 deletion ecs-agent/netlib/platform/containerd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *common) CreateNetNS(netNSPath string) error {
return nil
}

func (c *common) DeleteNetNS(netNSName string) error {
func (c *common) DeleteNetNS(netNSPath string) error {
return nil
}

Expand Down