diff --git a/cmd/main.go b/cmd/main.go index 78cd00b4d9..5424c912eb 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -67,6 +67,7 @@ func main() { driver.WithExtraVolumeTags(options.ControllerOptions.ExtraVolumeTags), driver.WithMode(options.DriverMode), driver.WithVolumeAttachLimit(options.NodeOptions.VolumeAttachLimit), + driver.WithReservedVolumeAttachments(options.NodeOptions.ReservedVolumeAttachments), driver.WithKubernetesClusterID(options.ControllerOptions.KubernetesClusterID), driver.WithAwsSdkDebugLog(options.ControllerOptions.AwsSdkDebugLog), driver.WithWarnOnInvalidTag(options.ControllerOptions.WarnOnInvalidTag), diff --git a/cmd/options.go b/cmd/options.go index c10a4728ff..84cb5bf4ab 100644 --- a/cmd/options.go +++ b/cmd/options.go @@ -125,6 +125,14 @@ func GetOptions(fs *flag.FlagSet) *Options { klog.ErrorS(err, "failed to validate and apply logging configuration") } + if mode != driver.ControllerMode { + // nodeOptions must have been populated from the cmdline, validate them. + if err := nodeOptions.Validate(); err != nil { + klog.Error(err.Error()) + klog.FlushAndExit(klog.ExitFlushTimeout, 1) + } + } + if *version { versionInfo, err := driver.GetVersionJSON() if err != nil { diff --git a/cmd/options/node_options.go b/cmd/options/node_options.go index 1374016f1d..a66a2a9ab9 100644 --- a/cmd/options/node_options.go +++ b/cmd/options/node_options.go @@ -17,6 +17,8 @@ limitations under the License. package options import ( + "fmt" + flag "github.com/spf13/pflag" ) @@ -31,8 +33,23 @@ type NodeOptions struct { // itself (dynamically discovering the maximum number of attachable volume per EC2 machine type, see also // https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues/347). VolumeAttachLimit int64 + + // ReservedVolumeAttachments specifies number of volume attachments reserved for system use. + // Typically 1 for the root disk, but may be larger when more system disks are attached to nodes. + // This option is not used when --volume-attach-limit is specified. + // When -1, the amount of reserved attachments is loaded from instance metadata that captured state at node boot + // and may include not only system disks but also CSI volumes (and therefore it may be wrong). + ReservedVolumeAttachments int } func (o *NodeOptions) AddFlags(fs *flag.FlagSet) { - fs.Int64Var(&o.VolumeAttachLimit, "volume-attach-limit", -1, "Value for the maximum number of volumes attachable per node. If specified, the limit applies to all nodes. If not specified, the value is approximated from the instance type.") + fs.Int64Var(&o.VolumeAttachLimit, "volume-attach-limit", -1, "Value for the maximum number of volumes attachable per node. If specified, the limit applies to all nodes and overrides --reserved-volume-attachments. If not specified, the value is approximated from the instance type.") + fs.IntVar(&o.ReservedVolumeAttachments, "reserved-volume-attachments", -1, "Number of volume attachments reserved for system use. Not used when --volume-attach-limit is specified. The total amount of volume attachments for a node is computed as: - - . When -1, the amount of reserved attachments is loaded from instance metadata that captured state at node boot and may include not only system disks but also CSI volumes.") +} + +func (o *NodeOptions) Validate() error { + if o.VolumeAttachLimit != -1 && o.ReservedVolumeAttachments != -1 { + return fmt.Errorf("only one of --volume-attach-limit and --reserved-volume-attachments may be specified") + } + return nil } diff --git a/cmd/options/node_options_test.go b/cmd/options/node_options_test.go index 0415f5b7d0..44aa021836 100644 --- a/cmd/options/node_options_test.go +++ b/cmd/options/node_options_test.go @@ -55,3 +55,56 @@ func TestNodeOptions(t *testing.T) { }) } } + +func TestValidate(t *testing.T) { + testCases := []struct { + name string + options *NodeOptions + expectError bool + }{ + { + name: "valid VolumeAttachLimit", + options: &NodeOptions{ + VolumeAttachLimit: 42, + ReservedVolumeAttachments: -1, + }, + expectError: false, + }, + { + name: "valid ReservedVolumeAttachments", + options: &NodeOptions{ + VolumeAttachLimit: -1, + ReservedVolumeAttachments: 42, + }, + expectError: false, + }, + { + name: "default options", + options: &NodeOptions{ + VolumeAttachLimit: -1, + ReservedVolumeAttachments: -1, + }, + expectError: false, + }, + { + name: "both options set", + options: &NodeOptions{ + VolumeAttachLimit: 1, + ReservedVolumeAttachments: 1, + }, + expectError: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + err := tc.options.Validate() + if tc.expectError && err == nil { + t.Errorf("Expected error, got nil") + } + if !tc.expectError && err != nil { + t.Errorf("Unexpected error: %v", err) + } + }) + } +} diff --git a/cmd/options_test.go b/cmd/options_test.go index cf50237065..6ac9aa5f85 100644 --- a/cmd/options_test.go +++ b/cmd/options_test.go @@ -23,6 +23,7 @@ import ( "testing" flag "github.com/spf13/pflag" + "k8s.io/klog/v2" "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver" ) @@ -51,6 +52,9 @@ func TestGetOptions(t *testing.T) { awsSdkDebugFlagValue := true VolumeAttachLimitFlagName := "volume-attach-limit" var VolumeAttachLimit int64 = 42 + reservedVolumeAttachmentsFlagName := "reserved-volume-attachments" + reservedVolumeAttachments := -1 + userAgentExtraFlag := "user-agent-extra" userAgentExtraFlagValue := "test" otelTracingFlagName := "enable-otel-tracing" @@ -130,6 +134,13 @@ func TestGetOptions(t *testing.T) { if options.NodeOptions.VolumeAttachLimit != VolumeAttachLimit { t.Fatalf("expected VolumeAttachLimit to be %d but it is %d", VolumeAttachLimit, options.NodeOptions.VolumeAttachLimit) } + reservedVolumeAttachmentsFlag := flagSet.Lookup(reservedVolumeAttachmentsFlagName) + if reservedVolumeAttachmentsFlag == nil { + t.Fatalf("expected %q flag to be added but it is not", reservedVolumeAttachmentsFlagName) + } + if options.NodeOptions.ReservedVolumeAttachments != reservedVolumeAttachments { + t.Fatalf("expected reservedVolumeAttachmentsFlagName to be %d but it is %d", reservedVolumeAttachments, options.NodeOptions.ReservedVolumeAttachments) + } } return options @@ -211,6 +222,39 @@ func TestGetOptions(t *testing.T) { } }, }, + { + name: "both volume-attach-limit and reserved-volume-attachments specified", + testFunc: func(t *testing.T) { + oldOSExit := klog.OsExit + defer func() { klog.OsExit = oldOSExit }() + + var exitCode int + calledExit := false + testExit := func(code int) { + exitCode = code + calledExit = true + } + klog.OsExit = testExit + + oldArgs := os.Args + defer func() { os.Args = oldArgs }() + os.Args = []string{ + "aws-ebs-csi-driver", + "--volume-attach-limit=10", + "--reserved-volume-attachments=10", + } + + flagSet := flag.NewFlagSet("test-flagset", flag.ContinueOnError) + _ = GetOptions(flagSet) + + if exitCode != 1 { + t.Fatalf("expected exit code 1 but got %d", exitCode) + } + if !calledExit { + t.Fatalf("expect osExit to be called, but wasn't") + } + }, + }, } for _, tc := range testCases { diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go index fb7be4c907..626e31c259 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go @@ -69,6 +69,7 @@ type DriverOptions struct { extraTags map[string]string mode Mode volumeAttachLimit int64 + reservedVolumeAttachments int kubernetesClusterID string awsSdkDebugLog bool batching bool @@ -221,6 +222,12 @@ func WithVolumeAttachLimit(volumeAttachLimit int64) func(*DriverOptions) { } } +func WithReservedVolumeAttachments(reservedVolumeAttachments int) func(*DriverOptions) { + return func(o *DriverOptions) { + o.reservedVolumeAttachments = reservedVolumeAttachments + } +} + func WithBatching(enableBatching bool) func(*DriverOptions) { return func(o *DriverOptions) { o.batching = enableBatching diff --git a/pkg/driver/driver_test.go b/pkg/driver/driver_test.go index e0a692a9d2..3527968c40 100644 --- a/pkg/driver/driver_test.go +++ b/pkg/driver/driver_test.go @@ -78,6 +78,15 @@ func TestWithVolumeAttachLimit(t *testing.T) { } } +func TestWithVolumeAttachLimitFromMetadata(t *testing.T) { + value := 10 + options := &DriverOptions{} + WithReservedVolumeAttachments(value)(options) + if options.reservedVolumeAttachments != value { + t.Fatalf("expected reservedVolumeAttachments option got set to %d but is set to %d", value, options.reservedVolumeAttachments) + } +} + func TestWithClusterID(t *testing.T) { var id string = "test-cluster-id" options := &DriverOptions{} diff --git a/pkg/driver/node.go b/pkg/driver/node.go index e64c8ef59f..3a2415fa85 100644 --- a/pkg/driver/node.go +++ b/pkg/driver/node.go @@ -783,7 +783,12 @@ func (d *nodeService) getVolumesLimit() int64 { isNitro := cloud.IsNitroInstanceType(instanceType) availableAttachments := cloud.GetMaxAttachments(isNitro) - blockVolumes := d.metadata.GetNumBlockDeviceMappings() + + reservedVolumeAttachments := d.driverOptions.reservedVolumeAttachments + if reservedVolumeAttachments == -1 { + reservedVolumeAttachments = d.metadata.GetNumBlockDeviceMappings() + 1 // +1 for the root device + } + dedicatedLimit := cloud.GetDedicatedLimitForInstanceType(instanceType) maxEBSAttachments, ok := cloud.GetEBSLimitForInstanceType(instanceType) if ok { @@ -798,7 +803,7 @@ func (d *nodeService) getVolumesLimit() int64 { nvmeInstanceStoreVolumes := cloud.GetNVMeInstanceStoreVolumesForInstanceType(instanceType) availableAttachments = availableAttachments - enis - nvmeInstanceStoreVolumes } - availableAttachments = availableAttachments - blockVolumes - 1 // -1 for root device + availableAttachments = availableAttachments - reservedVolumeAttachments if availableAttachments <= 0 { availableAttachments = 1 } diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go index fe707a54d2..3a9dfb41a5 100644 --- a/pkg/driver/node_test.go +++ b/pkg/driver/node_test.go @@ -2020,204 +2020,261 @@ func TestNodeGetInfo(t *testing.T) { validOutpostArn, _ := arn.Parse(strings.ReplaceAll("arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0", "outpost/", "")) emptyOutpostArn := arn.ARN{} testCases := []struct { - name string - instanceID string - instanceType string - availabilityZone string - region string - attachedENIs int - blockDevices int - volumeAttachLimit int64 - expMaxVolumes int64 - outpostArn arn.ARN + name string + instanceID string + instanceType string + availabilityZone string + region string + attachedENIs int + blockDevices int + volumeAttachLimit int64 + reservedVolumeAttachments int + expMaxVolumes int64 + outpostArn arn.ARN }{ { - name: "non-nitro instance success normal", - instanceID: "i-123456789abcdef01", - instanceType: "t2.medium", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - expMaxVolumes: 38, - attachedENIs: 1, - outpostArn: emptyOutpostArn, - }, - { - name: "success normal with overwrite", - instanceID: "i-123456789abcdef01", - instanceType: "t2.medium", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: 42, - expMaxVolumes: 42, - outpostArn: emptyOutpostArn, - }, - { - name: "nitro instance success normal", - instanceID: "i-123456789abcdef01", - instanceType: "t3.xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 2, - expMaxVolumes: 25, // 28 (max) - 2 (enis) - 1 (root) - outpostArn: emptyOutpostArn, - }, - { - name: "nitro instance success normal with NVMe", - instanceID: "i-123456789abcdef01", - instanceType: "m5d.large", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 2, - expMaxVolumes: 24, - outpostArn: emptyOutpostArn, - }, - { - name: "success normal with NVMe and overwrite", - instanceID: "i-123456789abcdef01", - instanceType: "m5d.large", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: 30, - expMaxVolumes: 30, - outpostArn: emptyOutpostArn, - }, - { - name: "success normal outposts", - instanceID: "i-123456789abcdef01", - instanceType: "m5d.large", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: 30, - expMaxVolumes: 30, - outpostArn: validOutpostArn, - }, - { - name: "baremetal instances max EBS attachment limit", - instanceID: "i-123456789abcdef01", - instanceType: "c6i.metal", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - expMaxVolumes: 26, // 28 (max) - 1 (eni) - 1 (root) - outpostArn: emptyOutpostArn, - }, - { - name: "high memory baremetal instances max EBS attachment limit", - instanceID: "i-123456789abcdef01", - instanceType: "u-12tb1.metal", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - expMaxVolumes: 17, - outpostArn: emptyOutpostArn, - }, - { - name: "mac instances max EBS attachment limit", - instanceID: "i-123456789abcdef01", - instanceType: "mac1.metal", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - expMaxVolumes: 14, - outpostArn: emptyOutpostArn, - }, - { - name: "inf1.24xlarge instace max EBS attachment limit", - instanceID: "i-123456789abcdef01", - instanceType: "inf1.24xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - expMaxVolumes: 9, - outpostArn: emptyOutpostArn, - }, - { - name: "nitro instances already attached EBS volumes", - instanceID: "i-123456789abcdef01", - instanceType: "t3.xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - blockDevices: 2, - expMaxVolumes: 24, - outpostArn: emptyOutpostArn, - }, - { - name: "nitro instance already attached max EBS volumes", - instanceID: "i-123456789abcdef01", - instanceType: "t3.xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - blockDevices: 27, - expMaxVolumes: 1, - outpostArn: emptyOutpostArn, - }, - { - name: "non-nitro instance already attached max EBS volumes", - instanceID: "i-123456789abcdef01", - instanceType: "m5.xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - blockDevices: 39, - expMaxVolumes: 1, - outpostArn: emptyOutpostArn, - }, - { - name: "nitro instance already attached max ENIs", - instanceID: "i-123456789abcdef01", - instanceType: "t3.xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 27, - blockDevices: 1, - expMaxVolumes: 1, - outpostArn: emptyOutpostArn, - }, - { - name: "nitro instance with dedicated limit", - instanceID: "i-123456789abcdef01", - instanceType: "m7i.48xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 2, - expMaxVolumes: 127, // 128 (max) - 1 (root) - outpostArn: emptyOutpostArn, - }, - { - name: "d3.8xlarge instance max EBS attachment limit", - instanceID: "i-123456789abcdef01", - instanceType: "d3.8xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - expMaxVolumes: 1, - outpostArn: emptyOutpostArn, - }, - { - name: "d3en.12xlarge instance max EBS attachment limit", - instanceID: "i-123456789abcdef01", - instanceType: "d3en.12xlarge", - availabilityZone: "us-west-2b", - region: "us-west-2", - volumeAttachLimit: -1, - attachedENIs: 1, - expMaxVolumes: 1, - outpostArn: emptyOutpostArn, + name: "non-nitro instance success normal", + instanceID: "i-123456789abcdef01", + instanceType: "t2.medium", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + expMaxVolumes: 38, + attachedENIs: 1, + outpostArn: emptyOutpostArn, + }, + { + name: "success normal with overwrite", + instanceID: "i-123456789abcdef01", + instanceType: "t2.medium", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: 42, + reservedVolumeAttachments: -1, + expMaxVolumes: 42, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance success normal", + instanceID: "i-123456789abcdef01", + instanceType: "t3.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 2, + expMaxVolumes: 25, // 28 (max) - 2 (enis) - 1 (root) + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance success normal with NVMe", + instanceID: "i-123456789abcdef01", + instanceType: "m5d.large", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 2, + expMaxVolumes: 24, + outpostArn: emptyOutpostArn, + }, + { + name: "success normal with NVMe and overwrite", + instanceID: "i-123456789abcdef01", + instanceType: "m5d.large", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: 30, + reservedVolumeAttachments: -1, + expMaxVolumes: 30, + outpostArn: emptyOutpostArn, + }, + { + name: "success normal outposts", + instanceID: "i-123456789abcdef01", + instanceType: "m5d.large", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: 30, + reservedVolumeAttachments: -1, + expMaxVolumes: 30, + outpostArn: validOutpostArn, + }, + { + name: "baremetal instances max EBS attachment limit", + instanceID: "i-123456789abcdef01", + instanceType: "c6i.metal", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + expMaxVolumes: 26, // 28 (max) - 1 (eni) - 1 (root) + outpostArn: emptyOutpostArn, + }, + { + name: "high memory baremetal instances max EBS attachment limit", + instanceID: "i-123456789abcdef01", + instanceType: "u-12tb1.metal", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + expMaxVolumes: 17, + outpostArn: emptyOutpostArn, + }, + { + name: "mac instances max EBS attachment limit", + instanceID: "i-123456789abcdef01", + instanceType: "mac1.metal", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + expMaxVolumes: 14, + outpostArn: emptyOutpostArn, + }, + { + name: "inf1.24xlarge instace max EBS attachment limit", + instanceID: "i-123456789abcdef01", + instanceType: "inf1.24xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + expMaxVolumes: 9, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instances already attached EBS volumes", + instanceID: "i-123456789abcdef01", + instanceType: "t3.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + blockDevices: 2, + expMaxVolumes: 24, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance already attached max EBS volumes", + instanceID: "i-123456789abcdef01", + instanceType: "t3.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + blockDevices: 27, + expMaxVolumes: 1, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance already attached max EBS volumes ignoring mapped volumes in metadata", + instanceID: "i-123456789abcdef01", + instanceType: "t3.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: 1, + attachedENIs: 1, + blockDevices: 27, + expMaxVolumes: 26, + outpostArn: emptyOutpostArn, + }, + { + name: "non-nitro instance already attached max EBS volumes", + instanceID: "i-123456789abcdef01", + instanceType: "m5.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + blockDevices: 39, + expMaxVolumes: 1, + outpostArn: emptyOutpostArn, + }, + { + name: "non-nitro instance ignoring mapped volumes in metadata", + instanceID: "i-123456789abcdef01", + instanceType: "m5.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: 1, + attachedENIs: 10, + blockDevices: 39, + expMaxVolumes: 17, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance already attached max ENIs", + instanceID: "i-123456789abcdef01", + instanceType: "t3.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 27, + blockDevices: 1, + expMaxVolumes: 1, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance already attached max ENIs ignoring mapped volumes in metadata", + instanceID: "i-123456789abcdef01", + instanceType: "t3.xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: 1, + attachedENIs: 27, + blockDevices: 1, + expMaxVolumes: 1, + outpostArn: emptyOutpostArn, + }, + { + name: "nitro instance with dedicated limit", + instanceID: "i-123456789abcdef01", + instanceType: "m7i.48xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 2, + expMaxVolumes: 127, // 128 (max) - 1 (root) + outpostArn: emptyOutpostArn, + }, + { + name: "d3.8xlarge instance max EBS attachment limit", + instanceID: "i-123456789abcdef01", + instanceType: "d3.8xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + expMaxVolumes: 1, + outpostArn: emptyOutpostArn, + }, + { + name: "d3en.12xlarge instance max EBS attachment limit", + instanceID: "i-123456789abcdef01", + instanceType: "d3en.12xlarge", + availabilityZone: "us-west-2b", + region: "us-west-2", + volumeAttachLimit: -1, + reservedVolumeAttachments: -1, + attachedENIs: 1, + expMaxVolumes: 1, + outpostArn: emptyOutpostArn, }, } for _, tc := range testCases { @@ -2226,7 +2283,8 @@ func TestNodeGetInfo(t *testing.T) { defer mockCtl.Finish() driverOptions := &DriverOptions{ - volumeAttachLimit: tc.volumeAttachLimit, + volumeAttachLimit: tc.volumeAttachLimit, + reservedVolumeAttachments: tc.reservedVolumeAttachments, } mockMounter := NewMockMounter(mockCtl) @@ -2240,7 +2298,9 @@ func TestNodeGetInfo(t *testing.T) { if tc.volumeAttachLimit < 0 { mockMetadata.EXPECT().GetInstanceType().Return(tc.instanceType) - mockMetadata.EXPECT().GetNumBlockDeviceMappings().Return(tc.blockDevices) + if tc.reservedVolumeAttachments == -1 { + mockMetadata.EXPECT().GetNumBlockDeviceMappings().Return(tc.blockDevices) + } if cloud.IsNitroInstanceType(tc.instanceType) && cloud.GetDedicatedLimitForInstanceType(tc.instanceType) == 0 { mockMetadata.EXPECT().GetNumAttachedENIs().Return(tc.attachedENIs) }