Skip to content

Commit

Permalink
Implement support for EFA interface type
Browse files Browse the repository at this point in the history
  • Loading branch information
RadekManak committed Jan 13, 2025
1 parent 3e480fb commit aa82a00
Show file tree
Hide file tree
Showing 12 changed files with 195 additions and 5 deletions.
1 change: 1 addition & 0 deletions api/v1beta1/awscluster_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ func (src *AWSCluster) ConvertTo(dstRaw conversion.Hub) error {
dst.Status.Bastion.PlacementGroupPartition = restored.Status.Bastion.PlacementGroupPartition
dst.Status.Bastion.PrivateDNSName = restored.Status.Bastion.PrivateDNSName
dst.Status.Bastion.PublicIPOnLaunch = restored.Status.Bastion.PublicIPOnLaunch
dst.Status.Bastion.NetworkInterfaceType = restored.Status.Bastion.NetworkInterfaceType
dst.Status.Bastion.CapacityReservationID = restored.Status.Bastion.CapacityReservationID
}
dst.Spec.Partition = restored.Spec.Partition
Expand Down
2 changes: 2 additions & 0 deletions api/v1beta1/awsmachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (src *AWSMachine) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.PrivateDNSName = restored.Spec.PrivateDNSName
dst.Spec.SecurityGroupOverrides = restored.Spec.SecurityGroupOverrides
dst.Spec.CapacityReservationID = restored.Spec.CapacityReservationID
dst.Spec.NetworkInterfaceType = restored.Spec.NetworkInterfaceType
if restored.Spec.ElasticIPPool != nil {
if dst.Spec.ElasticIPPool == nil {
dst.Spec.ElasticIPPool = &infrav1.ElasticIPPool{}
Expand Down Expand Up @@ -104,6 +105,7 @@ func (r *AWSMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Template.Spec.PrivateDNSName = restored.Spec.Template.Spec.PrivateDNSName
dst.Spec.Template.Spec.SecurityGroupOverrides = restored.Spec.Template.Spec.SecurityGroupOverrides
dst.Spec.Template.Spec.CapacityReservationID = restored.Spec.Template.Spec.CapacityReservationID
dst.Spec.Template.Spec.NetworkInterfaceType = restored.Spec.Template.Spec.NetworkInterfaceType
if restored.Spec.Template.Spec.ElasticIPPool != nil {
if dst.Spec.Template.Spec.ElasticIPPool == nil {
dst.Spec.Template.Spec.ElasticIPPool = &infrav1.ElasticIPPool{}
Expand Down
2 changes: 2 additions & 0 deletions api/v1beta1/zz_generated.conversion.go

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

15 changes: 15 additions & 0 deletions api/v1beta2/awsmachine_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,16 @@ const (
IgnitionStorageTypeOptionUnencryptedUserData = IgnitionStorageTypeOption("UnencryptedUserData")
)

// NetworkInterfaceType is the type of network interface.
type NetworkInterfaceType string

const (
// NetworkInterfaceTypeENI means the network interface type is Elastic Network Interface.
NetworkInterfaceTypeENI NetworkInterfaceType = NetworkInterfaceType("interface")
// NetworkInterfaceTypeEFAWithENAInterface means the network interface type is Elastic Fabric Adapter with Elastic Network Adapter.
NetworkInterfaceTypeEFAWithENAInterface NetworkInterfaceType = NetworkInterfaceType("efa")
)

// AWSMachineSpec defines the desired state of an Amazon EC2 instance.
type AWSMachineSpec struct {
// ProviderID is the unique identifier as specified by the cloud provider.
Expand Down Expand Up @@ -153,6 +163,11 @@ type AWSMachineSpec struct {
// +kubebuilder:validation:MaxItems=2
NetworkInterfaces []string `json:"networkInterfaces,omitempty"`

// NetworkInterfaceType is the interface type of the primary network Interface.
// +kubebuilder:validation:Enum=interface;efa
// +optional
NetworkInterfaceType *NetworkInterfaceType `json:"networkInterfaceType,omitempty"`

// UncompressedUserData specify whether the user data is gzip-compressed before it is sent to ec2 instance.
// cloud-init has built-in support for gzip-compressed user data
// user data stored in aws secret manager is always gzip-compressed.
Expand Down
3 changes: 3 additions & 0 deletions api/v1beta2/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ type Instance struct {
// Specifies ENIs attached to instance
NetworkInterfaces []string `json:"networkInterfaces,omitempty"`

// NetworkInterfaceType is the interface type of the primary network Interface.
NetworkInterfaceType *NetworkInterfaceType `json:"networkInterfaceType,omitempty"`

// The tags associated with the instance.
Tags map[string]string `json:"tags,omitempty"`

Expand Down
10 changes: 10 additions & 0 deletions api/v1beta2/zz_generated.deepcopy.go

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

Original file line number Diff line number Diff line change
Expand Up @@ -1210,6 +1210,10 @@ spec:
instanceState:
description: The current state of the instance.
type: string
networkInterfaceType:
description: NetworkInterfaceType is the interface type of the
primary network Interface.
type: string
networkInterfaces:
description: Specifies ENIs attached to instance
items:
Expand Down Expand Up @@ -3250,6 +3254,10 @@ spec:
instanceState:
description: The current state of the instance.
type: string
networkInterfaceType:
description: NetworkInterfaceType is the interface type of the
primary network Interface.
type: string
networkInterfaces:
description: Specifies ENIs attached to instance
items:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2177,6 +2177,10 @@ spec:
instanceState:
description: The current state of the instance.
type: string
networkInterfaceType:
description: NetworkInterfaceType is the interface type of the
primary network Interface.
type: string
networkInterfaces:
description: Specifies ENIs attached to instance
items:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,13 @@ spec:
m4.xlarge'
minLength: 2
type: string
networkInterfaceType:
description: NetworkInterfaceType is the interface type of the primary
network Interface.
enum:
- interface
- efa
type: string
networkInterfaces:
description: |-
NetworkInterfaces is a list of ENIs to associate with the instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,13 @@ spec:
Example: m4.xlarge'
minLength: 2
type: string
networkInterfaceType:
description: NetworkInterfaceType is the interface type of
the primary network Interface.
enum:
- interface
- efa
type: string
networkInterfaces:
description: |-
NetworkInterfaces is a list of ENIs to associate with the instance.
Expand Down
13 changes: 8 additions & 5 deletions pkg/cloud/services/ec2/instances.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,12 @@ func (s *Service) CreateInstance(scope *scope.MachineScope, userData []byte, use
s.scope.Debug("Creating an instance for a machine")

input := &infrav1.Instance{
Type: scope.AWSMachine.Spec.InstanceType,
IAMProfile: scope.AWSMachine.Spec.IAMInstanceProfile,
RootVolume: scope.AWSMachine.Spec.RootVolume.DeepCopy(),
NonRootVolumes: scope.AWSMachine.Spec.NonRootVolumes,
NetworkInterfaces: scope.AWSMachine.Spec.NetworkInterfaces,
Type: scope.AWSMachine.Spec.InstanceType,
IAMProfile: scope.AWSMachine.Spec.IAMInstanceProfile,
RootVolume: scope.AWSMachine.Spec.RootVolume.DeepCopy(),
NonRootVolumes: scope.AWSMachine.Spec.NonRootVolumes,
NetworkInterfaces: scope.AWSMachine.Spec.NetworkInterfaces,
NetworkInterfaceType: scope.AWSMachine.Spec.NetworkInterfaceType,
}

// Make sure to use the MachineScope here to get the merger of AWSCluster and AWSMachine tags
Expand Down Expand Up @@ -569,6 +570,7 @@ func (s *Service) runInstance(role string, i *infrav1.Instance) (*infrav1.Instan
})
}
netInterfaces[0].AssociatePublicIpAddress = i.PublicIPOnLaunch
netInterfaces[0].InterfaceType = (*string)(i.NetworkInterfaceType)

input.NetworkInterfaces = netInterfaces
} else {
Expand All @@ -578,6 +580,7 @@ func (s *Service) runInstance(role string, i *infrav1.Instance) (*infrav1.Instan
SubnetId: aws.String(i.SubnetID),
Groups: aws.StringSlice(i.SecurityGroupIDs),
AssociatePublicIpAddress: i.PublicIPOnLaunch,
InterfaceType: (*string)(i.NetworkInterfaceType),
},
}
}
Expand Down
128 changes: 128 additions & 0 deletions pkg/cloud/services/ec2/instances_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2450,6 +2450,134 @@ func TestCreateInstance(t *testing.T) {
}
},
},
{
name: "efa interface type",
machine: &clusterv1.Machine{
ObjectMeta: metav1.ObjectMeta{
Labels: map[string]string{"set": "node"},
},
Spec: clusterv1.MachineSpec{
Bootstrap: clusterv1.Bootstrap{
DataSecretName: ptr.To[string]("bootstrap-data"),
},
},
},
machineConfig: &infrav1.AWSMachineSpec{
AMI: infrav1.AMIReference{
ID: aws.String("abc"),
},
InstanceType: "m5.large",
NetworkInterfaceType: ptr.To(infrav1.NetworkInterfaceTypeEFAWithENAInterface),
},
awsCluster: &infrav1.AWSCluster{
ObjectMeta: metav1.ObjectMeta{Name: "test"},
Spec: infrav1.AWSClusterSpec{
NetworkSpec: infrav1.NetworkSpec{
Subnets: infrav1.Subnets{
infrav1.SubnetSpec{
ID: "subnet-1",
IsPublic: false,
},
infrav1.SubnetSpec{
IsPublic: false,
},
},
VPC: infrav1.VPCSpec{
ID: "vpc-test",
},
},
},
Status: infrav1.AWSClusterStatus{
Network: infrav1.NetworkStatus{
SecurityGroups: map[infrav1.SecurityGroupRole]infrav1.SecurityGroup{
infrav1.SecurityGroupControlPlane: {
ID: "1",
},
infrav1.SecurityGroupNode: {
ID: "2",
},
infrav1.SecurityGroupLB: {
ID: "3",
},
},
APIServerELB: infrav1.LoadBalancer{
DNSName: "test-apiserver.us-east-1.aws",
},
},
},
},
expect: func(m *mocks.MockEC2APIMockRecorder) {
m.
DescribeInstanceTypesWithContext(context.TODO(), gomock.Eq(&ec2.DescribeInstanceTypesInput{
InstanceTypes: []*string{
aws.String("m5.large"),
},
})).
Return(&ec2.DescribeInstanceTypesOutput{
InstanceTypes: []*ec2.InstanceTypeInfo{
{
ProcessorInfo: &ec2.ProcessorInfo{
SupportedArchitectures: []*string{
aws.String("x86_64"),
},
},
},
},
}, nil)
m.
RunInstancesWithContext(context.TODO(), gomock.Any()).
Do(func(_ context.Context, in *ec2.RunInstancesInput, _ ...request.Option) {
if len(in.NetworkInterfaces) == 0 {
t.Fatalf("expected a NetworkInterface to be defined")
}
if in.NetworkInterfaces[0].Groups == nil {
t.Fatalf("expected security groups to be set")
}
if interfaceType := aws.StringValue(in.NetworkInterfaces[0].InterfaceType); interfaceType != "efa" {
t.Fatalf("expected interface type to be \"efa\": got %q", interfaceType)
}
}).
Return(&ec2.Reservation{
Instances: []*ec2.Instance{
{
State: &ec2.InstanceState{
Name: aws.String(ec2.InstanceStateNamePending),
},
IamInstanceProfile: &ec2.IamInstanceProfile{
Arn: aws.String("arn:aws:iam::123456789012:instance-profile/foo"),
},
InstanceId: aws.String("two"),
InstanceType: aws.String("m5.large"),
SubnetId: aws.String("subnet-1"),
ImageId: aws.String("ami-1"),
RootDeviceName: aws.String("device-1"),
BlockDeviceMappings: []*ec2.InstanceBlockDeviceMapping{
{
DeviceName: aws.String("device-1"),
Ebs: &ec2.EbsInstanceBlockDevice{
VolumeId: aws.String("volume-1"),
},
},
},
Placement: &ec2.Placement{
AvailabilityZone: &az,
},
},
},
}, nil)
m.
DescribeNetworkInterfacesWithContext(context.TODO(), gomock.Any()).
Return(&ec2.DescribeNetworkInterfacesOutput{
NetworkInterfaces: []*ec2.NetworkInterface{},
NextToken: nil,
}, nil)
},
check: func(instance *infrav1.Instance, err error) {
if err != nil {
t.Fatalf("did not expect error: %v", err)
}
},
},
{
name: "public IP true and private subnet ID given",
machine: &clusterv1.Machine{
Expand Down

0 comments on commit aa82a00

Please sign in to comment.