Skip to content

Commit

Permalink
Create support for managed instances in netlib
Browse files Browse the repository at this point in the history
  • Loading branch information
welladev committed Sep 10, 2024
1 parent 9ddccf9 commit 48304e7
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
4 changes: 4 additions & 0 deletions ecs-agent/netlib/network_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ func (nb *networkBuilder) Start(
switch mode {
case ecs.NetworkModeAwsvpc:
err = nb.startAWSVPC(ctx, taskID, netNS)
case ecs.NetworkModeHost:
err = nb.platformAPI.HandleHostMode()
default:
err = errors.New("invalid network mode: " + mode)
}
Expand Down Expand Up @@ -128,6 +130,8 @@ func (nb *networkBuilder) Stop(ctx context.Context, mode string, taskID string,
switch mode {
case ecs.NetworkModeAwsvpc:
err = nb.stopAWSVPC(ctx, netNS)
case ecs.NetworkModeHost:
err = nb.platformAPI.HandleHostMode()
default:
err = errors.New("invalid network mode: " + mode)
}
Expand Down
3 changes: 3 additions & 0 deletions ecs-agent/netlib/platform/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type API interface {
taskID string,
taskPayload *ecsacs.Task) (*tasknetworkconfig.TaskNetworkConfig, error)

// HandleHostMode returns error if host mode is not enabled for the platform
HandleHostMode() error

// CreateNetNS creates a network namespace with the specified path.
CreateNetNS(netNSPath string) error

Expand Down
7 changes: 7 additions & 0 deletions ecs-agent/netlib/platform/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package platform

import (
"context"
"errors"
"time"

"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/ecscni"
Expand All @@ -28,6 +29,7 @@ const (
FirecrackerDebugPlatform = "ec2-debug-firecracker"
WarmpoolPlatform = "warmpool"
FirecrackerPlatform = "firecracker"
ManagedPlatform = "managed-instance"
)

// executeCNIPlugin executes CNI plugins with the given network configs and a timeout context.
Expand Down Expand Up @@ -84,3 +86,8 @@ func (c *common) interfacesMACToName() (map[string]string, error) {

return macToName, nil
}

// HandleHostMode by default we do not want to support host mode
func (c *common) HandleHostMode() error {
return errors.New("invalid platform for host mode")
}
13 changes: 13 additions & 0 deletions ecs-agent/netlib/platform/common_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (

"github.com/aws/amazon-ecs-agent/ecs-agent/acs/model/ecsacs"
"github.com/aws/amazon-ecs-agent/ecs-agent/api/ecs/model/ecs"
"github.com/aws/amazon-ecs-agent/ecs-agent/credentials/instancecreds"
"github.com/aws/amazon-ecs-agent/ecs-agent/logger"
netlibdata "github.com/aws/amazon-ecs-agent/ecs-agent/netlib/data"
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/appmesh"
Expand All @@ -41,6 +42,8 @@ import (
"github.com/aws/amazon-ecs-agent/ecs-agent/volume"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/ec2metadata"
"github.com/aws/aws-sdk-go/aws/session"
cnitypes "github.com/containernetworking/cni/pkg/types/100"
cnins "github.com/containernetworking/plugins/pkg/ns"
"github.com/pkg/errors"
Expand All @@ -63,6 +66,8 @@ const (
// It is assigned 100 because it is an unrealistically high
// value for interface index.
indexHighValue = 100

metadataRetries = 5
)

// common will be embedded within every implementation of the platform API.
Expand Down Expand Up @@ -120,6 +125,14 @@ func NewPlatform(
common: commonPlatform,
},
}, nil
case ManagedPlatform:
config := aws.NewConfig().WithMaxRetries(metadataRetries)
config.Credentials = instancecreds.GetCredentials(false)
ec2Client := ec2metadata.New(session.New(), config)
return &managedLinux{
common: commonPlatform,
client: ec2Client,
}, nil
}
return nil, errors.New("invalid platform: " + platformString)
}
Expand Down
138 changes: 138 additions & 0 deletions ecs-agent/netlib/platform/managed_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package platform

import (
"context"
goErr "errors"
"fmt"

"github.com/aws/amazon-ecs-agent/ecs-agent/acs/model/ecsacs"
"github.com/aws/amazon-ecs-agent/ecs-agent/api/ecs/model/ecs"
"github.com/aws/amazon-ecs-agent/ecs-agent/ec2"
netlibdata "github.com/aws/amazon-ecs-agent/ecs-agent/netlib/data"
"github.com/aws/amazon-ecs-agent/ecs-agent/netlib/model/appmesh"
"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/amazon-ecs-agent/ecs-agent/netlib/model/tasknetworkconfig"

"github.com/aws/aws-sdk-go/aws"
"github.com/pkg/errors"
)

const (
MacResource = "mac"
SubNetCidrBlock = "network/interfaces/macs/%s/subnet-ipv4-cidr-block"
PrivateIPv4Resource = "local-ipv4"
InstanceIDResource = "instance-id"
)

type managedLinux struct {
common
client ec2.HttpClient
}

// BuildTaskNetworkConfiguration translates network data in task payload sent by ACS
// into the task network configuration data structure internal to the agent.
func (m *managedLinux) BuildTaskNetworkConfiguration(
taskID string,
taskPayload *ecsacs.Task,
) (*tasknetworkconfig.TaskNetworkConfig, error) {
mode := aws.StringValue(taskPayload.NetworkMode)
var netNSs []*tasknetworkconfig.NetworkNamespace
var err error
switch mode {
case ecs.NetworkModeAwsvpc:
netNSs, err = m.common.buildAWSVPCNetworkNamespaces(taskID, taskPayload, false, nil)
if err != nil {
return nil, errors.Wrap(err, "failed to translate network configuration")
}
case ecs.NetworkModeHost:
netNSs, err = m.buildDefaultNetworkNamespace(taskID)
if err != nil {
return nil, errors.Wrap(err, "failed to create network namespace with host eni")
}
default:
return nil, errors.New("invalid network mode: " + mode)
}
return &tasknetworkconfig.TaskNetworkConfig{
NetworkNamespaces: netNSs,
NetworkMode: mode,
}, nil
}

func (m *managedLinux) CreateDNSConfig(taskID string,
netNS *tasknetworkconfig.NetworkNamespace) error {
return m.common.createDNSConfig(taskID, false, netNS)
}

func (m *managedLinux) ConfigureInterface(
ctx context.Context,
netNSPath string,
iface *networkinterface.NetworkInterface,
netDAO netlibdata.NetworkDataClient,
) error {
return m.common.configureInterface(ctx, netNSPath, iface, netDAO)
}

func (m *managedLinux) ConfigureAppMesh(ctx context.Context,
netNSPath string,
cfg *appmesh.AppMesh) error {
return m.common.configureAppMesh(ctx, netNSPath, cfg)
}

func (m *managedLinux) ConfigureServiceConnect(
ctx context.Context,
netNSPath string,
primaryIf *networkinterface.NetworkInterface,
scConfig *serviceconnect.ServiceConnectConfig,
) error {
return m.common.configureServiceConnect(ctx, netNSPath, primaryIf, scConfig)
}

// buildDefaultNetworkNamespace generate a default network namespace for host mode where we do not need to create new namespaces
func (m *managedLinux) buildDefaultNetworkNamespace(taskID string) ([]*tasknetworkconfig.NetworkNamespace, error) {
privateIpv4, err1 := m.client.GetMetadata(PrivateIPv4Resource)
macAddress, err2 := m.client.GetMetadata(MacResource)
ec2ID, err3 := m.client.GetMetadata(InstanceIDResource)
subNet, err4 := m.client.GetMetadata(fmt.Sprintf(SubNetCidrBlock, macAddress))
macToNames, err5 := m.common.interfacesMACToName()
if err := goErr.Join(err1, err2, err3, err4, err5); err != nil {
return nil, err
}

hostENI := &ecsacs.ElasticNetworkInterface{
AttachmentArn: aws.String("arn"),
Ec2Id: aws.String(ec2ID),
Ipv4Addresses: []*ecsacs.IPv4AddressAssignment{
{
Primary: aws.Bool(true),
PrivateAddress: aws.String(privateIpv4),
},
},
SubnetGatewayIpv4Address: aws.String(subNet),
MacAddress: aws.String(macAddress),
DomainNameServers: []*string{},
DomainName: []*string{},
PrivateDnsName: aws.String("default"),
InterfaceAssociationProtocol: aws.String("default"),
Index: aws.Int64(64),
}

netNSName := networkinterface.NetNSName(taskID, "default")
netInt, _ := networkinterface.New(hostENI, "default", nil, macToNames)
netInt.Default = true
netInt.DesiredStatus = status.NetworkReady
netInt.KnownStatus = status.NetworkReady
defaultNameSpace, err := tasknetworkconfig.NewNetworkNamespace(netNSName, "", 0, nil, netInt)
if err != nil {
return nil, err
}
defaultNameSpace.KnownState = status.NetworkReady
defaultNameSpace.DesiredState = status.NetworkReady
return []*tasknetworkconfig.NetworkNamespace{defaultNameSpace}, nil
}

// HandleHostMode is a no op since we are allowing Host Mode for managed instances
func (m *managedLinux) HandleHostMode() error {
return nil
}
14 changes: 14 additions & 0 deletions ecs-agent/netlib/platform/mocks/platform_mocks.go

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

0 comments on commit 48304e7

Please sign in to comment.