Skip to content

Commit 88f3256

Browse files
authored
Merge pull request #4436 from JoseVillalta/branch-eni-config
Adds branch ENI config to non-firecracker platforms
2 parents c6101bc + 79443bb commit 88f3256

File tree

4 files changed

+113
-13
lines changed

4 files changed

+113
-13
lines changed

ecs-agent/netlib/platform/common_linux.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -695,22 +695,28 @@ func (c *common) configureBranchENI(ctx context.Context, netNSPath string, eni *
695695
"NetNSPath": netNSPath,
696696
})
697697

698-
var cniNetConf ecscni.PluginConfig
698+
// Set the path for the IPAM CNI local db to track assigned IPs.
699+
// Default path is /data but in some linux distros (i.e.Amazon BottleRocket) the root volume is read-only.
700+
c.os.Setenv(IPAMDataPathEnv, filepath.Join(c.stateDBDir, IPAMDataFileName))
701+
702+
var cniNetConf []ecscni.PluginConfig
699703
var err error
700704
add := true
701705

702706
// Generate CNI network configuration based on the ENI's desired state.
703707
switch eni.DesiredStatus {
704708
case status.NetworkReadyPull:
705-
cniNetConf = createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeVlan)
706-
case status.NetworkReady:
707-
cniNetConf = createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeTap)
709+
// Setup bridge to connect task network namespace to TMDS running in host's primary netns.
710+
if eni.IsPrimary() {
711+
cniNetConf = append(cniNetConf, createBridgePluginConfig(netNSPath))
712+
}
713+
cniNetConf = append(cniNetConf, createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeVlan))
708714
case status.NetworkDeleted:
709-
cniNetConf = createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeTap)
715+
cniNetConf = append(cniNetConf, createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeVlan))
710716
add = false
711717
}
712718

713-
_, err = c.executeCNIPlugin(ctx, add, cniNetConf)
719+
_, err = c.executeCNIPlugin(ctx, add, cniNetConf...)
714720
if err != nil {
715721
err = errors.Wrap(err, "failed to setup branch eni")
716722
}

ecs-agent/netlib/platform/common_linux_test.go

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -342,27 +342,35 @@ func testBranchENIConfiguration(t *testing.T) {
342342
defer ctrl.Finish()
343343

344344
ctx := context.TODO()
345+
osWrapper := mock_oswrapper.NewMockOS(ctrl)
345346
cniClient := mock_ecscni2.NewMockCNI(ctrl)
346347
commonPlatform := &common{
347-
cniClient: cniClient,
348+
os: osWrapper,
349+
cniClient: cniClient,
350+
stateDBDir: "dummy-db-dir",
348351
}
349352

350353
branchENI := getTestBranchENI()
351-
354+
branchENI.DesiredStatus = status.NetworkReadyPull
355+
bridgeConfig := createBridgePluginConfig(netNSPath)
352356
cniConfig := createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeVlan)
353-
cniClient.EXPECT().Add(gomock.Any(), cniConfig).Return(nil, nil).Times(1)
357+
gomock.InOrder(
358+
osWrapper.EXPECT().Setenv("IPAM_DB_PATH", filepath.Join(commonPlatform.stateDBDir, "eni-ipam.db")),
359+
cniClient.EXPECT().Add(gomock.Any(), bridgeConfig).Return(nil, nil).Times(1),
360+
cniClient.EXPECT().Add(gomock.Any(), cniConfig).Return(nil, nil).Times(1),
361+
)
354362
err := commonPlatform.configureInterface(ctx, netNSPath, branchENI, nil)
355363
require.NoError(t, err)
356364

365+
// Ready-Pull to Ready transition
357366
branchENI.DesiredStatus = status.NetworkReady
358-
cniConfig = createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeTap)
359-
cniClient.EXPECT().Add(gomock.Any(), cniConfig).Return(nil, nil).Times(1)
367+
osWrapper.EXPECT().Setenv("IPAM_DB_PATH", filepath.Join(commonPlatform.stateDBDir, "eni-ipam.db"))
360368
err = commonPlatform.configureInterface(ctx, netNSPath, branchENI, nil)
361369
require.NoError(t, err)
362370

363371
// Delete workflow.
364372
branchENI.DesiredStatus = status.NetworkDeleted
365-
cniConfig = createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeTap)
373+
osWrapper.EXPECT().Setenv("IPAM_DB_PATH", filepath.Join(commonPlatform.stateDBDir, "eni-ipam.db"))
366374
cniClient.EXPECT().Del(gomock.Any(), cniConfig).Return(nil).Times(1)
367375
err = commonPlatform.configureInterface(ctx, netNSPath, branchENI, nil)
368376
require.NoError(t, err)

ecs-agent/netlib/platform/firecracker_linux.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ import (
2020
netlibdata "github.com/aws/amazon-ecs-agent/ecs-agent/netlib/data"
2121

2222
"github.com/aws/amazon-ecs-agent/ecs-agent/acs/model/ecsacs"
23+
"github.com/aws/amazon-ecs-agent/ecs-agent/logger"
2324
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/appmesh"
25+
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/ecscni"
2426
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/networkinterface"
2527
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/serviceconnect"
28+
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/status"
2629
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/tasknetworkconfig"
2730

2831
"github.com/aws/aws-sdk-go/aws"
@@ -59,13 +62,29 @@ func (f *firecraker) CreateDNSConfig(taskID string, netNS *tasknetworkconfig.Net
5962
return f.configureSecondaryDNSConfig(taskID, netNS)
6063
}
6164

65+
// ConfigureInterface is a firecracker-specific method that adds network interfaces to tasks running on
66+
// Firecracker microVMs. It calls a FC-specific method that configures and connect Branch ENIs to a TAP interface.
6267
func (f *firecraker) ConfigureInterface(
6368
ctx context.Context,
6469
netNSPath string,
6570
iface *networkinterface.NetworkInterface,
6671
netDAO netlibdata.NetworkDataClient,
6772
) error {
68-
return f.common.configureInterface(ctx, netNSPath, iface, netDAO)
73+
var err error
74+
switch iface.InterfaceAssociationProtocol {
75+
case networkinterface.DefaultInterfaceAssociationProtocol:
76+
err = f.common.configureRegularENI(ctx, netNSPath, iface)
77+
case networkinterface.VLANInterfaceAssociationProtocol:
78+
err = f.configureBranchENI(ctx, netNSPath, iface)
79+
case networkinterface.V2NInterfaceAssociationProtocol:
80+
err = f.common.configureGENEVEInterface(ctx, netNSPath, iface, netDAO)
81+
case networkinterface.VETHInterfaceAssociationProtocol:
82+
// Do nothing. Virtual Ethernet Interfaces do not need to be configured by the Linux Kernel.
83+
return nil
84+
default:
85+
err = errors.New("invalid interface association protocol " + iface.InterfaceAssociationProtocol)
86+
}
87+
return err
6988
}
7089

7190
func (f *firecraker) ConfigureAppMesh(ctx context.Context, netNSPath string, cfg *appmesh.AppMesh) error {
@@ -171,3 +190,33 @@ func assignInterfacesToNamespaces(taskPayload *ecsacs.Task) (map[string]string,
171190

172191
return i2n, nil
173192
}
193+
194+
// configureBranchENI configures a network interface for a branch ENI.
195+
func (f *firecraker) configureBranchENI(ctx context.Context, netNSPath string, eni *networkinterface.NetworkInterface) error {
196+
logger.Info("Configuring branch ENI", map[string]interface{}{
197+
"ENIName": eni.Name,
198+
"NetNSPath": netNSPath,
199+
})
200+
201+
var cniNetConf ecscni.PluginConfig
202+
var err error
203+
add := true
204+
205+
// Generate CNI network configuration based on the ENI's desired state.
206+
switch eni.DesiredStatus {
207+
case status.NetworkReadyPull:
208+
cniNetConf = createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeVlan)
209+
case status.NetworkReady:
210+
cniNetConf = createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeTap)
211+
case status.NetworkDeleted:
212+
cniNetConf = createBranchENIConfig(netNSPath, eni, VPCBranchENIInterfaceTypeTap)
213+
add = false
214+
}
215+
216+
_, err = f.common.executeCNIPlugin(ctx, add, cniNetConf)
217+
if err != nil {
218+
err = errors.Wrap(err, "failed to setup branch eni")
219+
}
220+
221+
return err
222+
}

ecs-agent/netlib/platform/firecracker_linux_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,16 @@
1717
package platform
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"io/fs"
2223
"os"
2324
"testing"
2425

26+
mock_ecscni2 "github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/ecscni/mocks_ecscni"
2527
mock_ecscni "github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/ecscni/mocks_nsutil"
2628
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/networkinterface"
29+
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/status"
2730
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/tasknetworkconfig"
2831
mock_ioutilwrapper "github.com/aws/amazon-ecs-agent/ecs-agent/utils/ioutilwrapper/mocks"
2932
mock_oswrapper "github.com/aws/amazon-ecs-agent/ecs-agent/utils/oswrapper/mocks"
@@ -142,3 +145,37 @@ func TestFirecracker_CreateDNSConfig(t *testing.T) {
142145
err := fc.CreateDNSConfig(taskID, netns)
143146
require.NoError(t, err)
144147
}
148+
149+
func TestFirecracker_BranchENIConfiguration(t *testing.T) {
150+
ctrl := gomock.NewController(t)
151+
defer ctrl.Finish()
152+
153+
ctx := context.TODO()
154+
cniClient := mock_ecscni2.NewMockCNI(ctrl)
155+
commonPlatform := common{
156+
cniClient: cniClient,
157+
}
158+
fc := &firecraker{
159+
common: commonPlatform,
160+
}
161+
162+
branchENI := getTestBranchENI()
163+
164+
cniConfig := createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeVlan)
165+
cniClient.EXPECT().Add(gomock.Any(), cniConfig).Return(nil, nil).Times(1)
166+
err := fc.ConfigureInterface(ctx, netNSPath, branchENI, nil)
167+
require.NoError(t, err)
168+
169+
branchENI.DesiredStatus = status.NetworkReady
170+
cniConfig = createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeTap)
171+
cniClient.EXPECT().Add(gomock.Any(), cniConfig).Return(nil, nil).Times(1)
172+
err = fc.ConfigureInterface(ctx, netNSPath, branchENI, nil)
173+
require.NoError(t, err)
174+
175+
// Delete workflow.
176+
branchENI.DesiredStatus = status.NetworkDeleted
177+
cniConfig = createBranchENIConfig(netNSPath, branchENI, VPCBranchENIInterfaceTypeTap)
178+
cniClient.EXPECT().Del(gomock.Any(), cniConfig).Return(nil).Times(1)
179+
err = fc.ConfigureInterface(ctx, netNSPath, branchENI, nil)
180+
require.NoError(t, err)
181+
}

0 commit comments

Comments
 (0)