From 2b56001f722c5d02c301dd0efa2fb7ca19b1b262 Mon Sep 17 00:00:00 2001 From: Rahul Raj Date: Fri, 6 Mar 2026 10:47:55 +0000 Subject: [PATCH] Fix EFA interface count for instances where MaximumNetworkCards exceeds EFA limit --- pkg/cfn/builder/network_interfaces.go | 18 +++++-- pkg/cfn/builder/network_interfaces_test.go | 62 ++++++++++++++++++++-- 2 files changed, 74 insertions(+), 6 deletions(-) diff --git a/pkg/cfn/builder/network_interfaces.go b/pkg/cfn/builder/network_interfaces.go index 21805511da..4bd3f456a3 100644 --- a/pkg/cfn/builder/network_interfaces.go +++ b/pkg/cfn/builder/network_interfaces.go @@ -49,19 +49,31 @@ func buildNetworkInterfaces( } var numEFAs = math.MaxFloat64 + var efaStartCard int for _, it := range info.InstanceTypes { networkInfo := it.NetworkInfo - numEFAs = math.Min(float64(aws.ToInt32(networkInfo.MaximumNetworkCards)), numEFAs) if !aws.ToBool(networkInfo.EfaSupported) { return fmt.Errorf("instance type %s does not support EFA", it.InstanceType) } + if networkInfo.EfaInfo != nil && networkInfo.EfaInfo.MaximumEfaInterfaces != nil { + maxEfa := aws.ToInt32(networkInfo.EfaInfo.MaximumEfaInterfaces) + numEFAs = math.Min(float64(maxEfa), numEFAs) + // If EFA interfaces < network cards, network card 0 is ENA-only + if maxEfa < aws.ToInt32(networkInfo.MaximumNetworkCards) { + efaStartCard = 1 + } + } else { + numEFAs = math.Min(float64(aws.ToInt32(networkInfo.MaximumNetworkCards)), numEFAs) + } } - firstNI.InterfaceType = gfnt.NewString("efa") + if efaStartCard == 0 { + firstNI.InterfaceType = gfnt.NewString("efa") + } nis := []gfnec2.LaunchTemplate_NetworkInterface{firstNI} // The primary network interface (device index 0) must be assigned to network card index 0 // device index 1 for additional cards - for i := 1; i < int(numEFAs); i++ { + for i := 1; i < int(numEFAs)+efaStartCard; i++ { ni := defaultNetworkInterface(securityGroups, 1, i) ni.InterfaceType = gfnt.NewString("efa") nis = append(nis, ni) diff --git a/pkg/cfn/builder/network_interfaces_test.go b/pkg/cfn/builder/network_interfaces_test.go index fa2a791c25..58996ba10b 100644 --- a/pkg/cfn/builder/network_interfaces_test.go +++ b/pkg/cfn/builder/network_interfaces_test.go @@ -24,6 +24,7 @@ func TestBuildNetworkInterfaces(t *testing.T) { mockInstanceTypes []ec2types.InstanceTypeInfo expectedNetworkInterfaces int expectedInterfaceType string + expectedFirstNIEfa bool // whether first NI should be EFA expectedError string }{ { @@ -56,6 +57,7 @@ func TestBuildNetworkInterfaces(t *testing.T) { }, expectedNetworkInterfaces: 1, expectedInterfaceType: "efa", + expectedFirstNIEfa: true, }, { name: "EFA nodegroup with multiple network cards", @@ -76,6 +78,7 @@ func TestBuildNetworkInterfaces(t *testing.T) { }, expectedNetworkInterfaces: 4, expectedInterfaceType: "efa", + expectedFirstNIEfa: true, }, { name: "EFA nodegroup with mixed instance types", @@ -103,6 +106,7 @@ func TestBuildNetworkInterfaces(t *testing.T) { }, expectedNetworkInterfaces: 1, // Should use minimum across instance types expectedInterfaceType: "efa", + expectedFirstNIEfa: true, }, { name: "EFA nodegroup with non-EFA instance type", @@ -141,6 +145,53 @@ func TestBuildNetworkInterfaces(t *testing.T) { }, expectedNetworkInterfaces: 4, expectedInterfaceType: "efa", + expectedFirstNIEfa: true, + }, + { + name: "EFA nodegroup with MaximumEfaInterfaces less than MaximumNetworkCards", + instanceTypes: []string{"p6-b300.48xlarge"}, + efaEnabled: true, + securityGroups: []*gfnt.Value{ + gfnt.NewString("sg-12345"), + }, + mockInstanceTypes: []ec2types.InstanceTypeInfo{ + { + InstanceType: "p6-b300.48xlarge", + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkCards: aws.Int32(17), + EfaSupported: aws.Bool(true), + EfaInfo: &ec2types.EfaInfo{ + MaximumEfaInterfaces: aws.Int32(16), + }, + }, + }, + }, + expectedNetworkInterfaces: 17, // 1 ENA (card 0) + 16 EFA (cards 1-16) + expectedInterfaceType: "efa", + expectedFirstNIEfa: false, // Network card 0 is ENA-only + }, + { + name: "EFA nodegroup with EfaInfo present and matching MaximumNetworkCards", + instanceTypes: []string{"p5.48xlarge"}, + efaEnabled: true, + securityGroups: []*gfnt.Value{ + gfnt.NewString("sg-12345"), + }, + mockInstanceTypes: []ec2types.InstanceTypeInfo{ + { + InstanceType: "p5.48xlarge", + NetworkInfo: &ec2types.NetworkInfo{ + MaximumNetworkCards: aws.Int32(32), + EfaSupported: aws.Bool(true), + EfaInfo: &ec2types.EfaInfo{ + MaximumEfaInterfaces: aws.Int32(32), + }, + }, + }, + }, + expectedNetworkInterfaces: 32, + expectedInterfaceType: "efa", + expectedFirstNIEfa: true, }, { name: "EFA nodegroup with custom EFA security groups (1.32 scenario)", @@ -162,6 +213,7 @@ func TestBuildNetworkInterfaces(t *testing.T) { }, expectedNetworkInterfaces: 4, expectedInterfaceType: "efa", + expectedFirstNIEfa: true, }, } @@ -235,9 +287,13 @@ func TestBuildNetworkInterfaces(t *testing.T) { } if tt.efaEnabled && tt.expectedError == "" { - // Verify EFA interface type - require.NotNil(t, firstNI.InterfaceType) - assert.Equal(t, gfnt.String(tt.expectedInterfaceType), firstNI.InterfaceType.Raw()) + // Verify first NI EFA type + if tt.expectedFirstNIEfa { + require.NotNil(t, firstNI.InterfaceType) + assert.Equal(t, gfnt.String(tt.expectedInterfaceType), firstNI.InterfaceType.Raw()) + } else { + assert.Nil(t, firstNI.InterfaceType) + } // Verify additional network interfaces for multi-card instances for i := 1; i < tt.expectedNetworkInterfaces; i++ {