diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go index 6e8a94422f..936c5a054d 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -51,6 +51,10 @@ const ( VolumeTypeSC1 = "sc1" // VolumeTypeST1 represents a throughput-optimized HDD type of volume. VolumeTypeST1 = "st1" + // VolumeTypeSBG1 represents a capacity-optimized HDD type of volume. Only for SBE devices. + VolumeTypeSBG1 = "sbg1" + // VolumeTypeSBP1 represents a performance-optimized SSD type of volume. Only for SBE devices. + VolumeTypeSBP1 = "sbp1" // VolumeTypeStandard represents a previous type of volume. VolumeTypeStandard = "standard" ) @@ -280,7 +284,7 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions * capacityGiB := util.BytesToGiB(diskOptions.CapacityBytes) switch diskOptions.VolumeType { - case VolumeTypeGP2, VolumeTypeSC1, VolumeTypeST1, VolumeTypeStandard: + case VolumeTypeGP2, VolumeTypeSC1, VolumeTypeST1, VolumeTypeSBG1, VolumeTypeSBP1, VolumeTypeStandard: createType = diskOptions.VolumeType case VolumeTypeIO1: createType = diskOptions.VolumeType @@ -331,12 +335,15 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions * clientToken := sha256.Sum256([]byte(volumeName)) requestInput := &ec2.CreateVolumeInput{ - AvailabilityZone: aws.String(zone), - ClientToken: aws.String(hex.EncodeToString(clientToken[:])), - Size: aws.Int64(capacityGiB), - VolumeType: aws.String(createType), - TagSpecifications: []*ec2.TagSpecification{&tagSpec}, - Encrypted: aws.Bool(diskOptions.Encrypted), + AvailabilityZone: aws.String(zone), + ClientToken: aws.String(hex.EncodeToString(clientToken[:])), + Size: aws.Int64(capacityGiB), + VolumeType: aws.String(createType), + Encrypted: aws.Bool(diskOptions.Encrypted), + } + + if !util.IsSBE(zone) { + requestInput.TagSpecifications = []*ec2.TagSpecification{&tagSpec} } // EBS doesn't handle empty outpost arn, so we have to include it only when it's non-empty @@ -392,7 +399,24 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions * } outpostArn := aws.StringValue(response.OutpostArn) - + var resources []*string + if util.IsSBE(zone) { + requestTagsInput := &ec2.CreateTagsInput{ + Resources: append(resources, &volumeID), + Tags: tags, + } + _, err := c.ec2.CreateTagsWithContext(ctx, requestTagsInput) + if err != nil { + // To avoid leaking volume, we should delete the volume just created + // TODO: Need to figure out how to handle DeleteDisk failed scenario instead of just log the error + if _, error := c.DeleteDisk(ctx, volumeID); error != nil { + klog.Errorf("%v failed to be deleted, this may cause volume leak", volumeID) + } else { + klog.V(5).Infof("[Debug] %v is deleted because there was an error while attaching the tags", volumeID) + } + return nil, fmt.Errorf("could not attach tags to volume: %v. %v", volumeID, err) + } + } return &Disk{CapacityGiB: size, VolumeID: volumeID, AvailabilityZone: zone, SnapshotID: snapshotID, OutpostArn: outpostArn}, nil } diff --git a/pkg/cloud/cloud_test.go b/pkg/cloud/cloud_test.go index 7436fd4550..db8b10c82d 100644 --- a/pkg/cloud/cloud_test.go +++ b/pkg/cloud/cloud_test.go @@ -36,6 +36,7 @@ import ( const ( defaultZone = "test-az" expZone = "us-west-2b" + snowZone = "snow" ) func TestCreateDisk(t *testing.T) { @@ -49,6 +50,7 @@ func TestCreateDisk(t *testing.T) { expErr error expCreateVolumeErr error expDescVolumeErr error + expCreateTagsErr error expCreateVolumeInput *ec2.CreateVolumeInput }{ { @@ -438,6 +440,41 @@ func TestCreateDisk(t *testing.T) { }, expErr: nil, }, + { + name: "success: create volume when zone is snow and add tags", + volumeName: "vol-test-name", + diskOptions: &DiskOptions{ + CapacityBytes: util.GiBToBytes(1), + Tags: map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"}, + AvailabilityZone: snowZone, + VolumeType: "sbp1", + }, + expCreateVolumeInput: &ec2.CreateVolumeInput{}, + expDisk: &Disk{ + VolumeID: "vol-test", + CapacityGiB: 1, + AvailabilityZone: snowZone, + }, + expErr: nil, + }, + { + name: "fail: zone is snow and add tags throws error", + volumeName: "vol-test-name", + diskOptions: &DiskOptions{ + CapacityBytes: util.GiBToBytes(1), + Tags: map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"}, + AvailabilityZone: snowZone, + VolumeType: "sbg1", + }, + expCreateVolumeInput: &ec2.CreateVolumeInput{}, + expCreateTagsErr: fmt.Errorf("CreateTags generic error"), + expDisk: &Disk{ + VolumeID: "vol-test", + CapacityGiB: 1, + AvailabilityZone: snowZone, + }, + expErr: fmt.Errorf("could not attach tags to volume: vol-test. CreateTags generic error"), + }, } for _, tc := range testCases { @@ -469,6 +506,10 @@ func TestCreateDisk(t *testing.T) { matcher := eqCreateVolume(tc.expCreateVolumeInput) mockEC2.EXPECT().CreateVolumeWithContext(gomock.Eq(ctx), matcher).Return(vol, tc.expCreateVolumeErr) mockEC2.EXPECT().DescribeVolumesWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeVolumesOutput{Volumes: []*ec2.Volume{vol}}, tc.expDescVolumeErr).AnyTimes() + if tc.diskOptions.AvailabilityZone == "snow" { + mockEC2.EXPECT().CreateTagsWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.CreateTagsOutput{}, tc.expCreateTagsErr) + mockEC2.EXPECT().DeleteVolumeWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DeleteVolumeOutput{}, nil).AnyTimes() + } if len(tc.diskOptions.SnapshotID) > 0 { mockEC2.EXPECT().DescribeSnapshotsWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeSnapshotsOutput{Snapshots: []*ec2.Snapshot{snapshot}}, nil).AnyTimes() } diff --git a/pkg/cloud/ec2_interface.go b/pkg/cloud/ec2_interface.go index 5da6b6983f..65a2fb3bbd 100644 --- a/pkg/cloud/ec2_interface.go +++ b/pkg/cloud/ec2_interface.go @@ -37,4 +37,5 @@ type EC2 interface { ModifyVolumeWithContext(ctx aws.Context, input *ec2.ModifyVolumeInput, opts ...request.Option) (*ec2.ModifyVolumeOutput, error) DescribeVolumesModificationsWithContext(ctx aws.Context, input *ec2.DescribeVolumesModificationsInput, opts ...request.Option) (*ec2.DescribeVolumesModificationsOutput, error) DescribeAvailabilityZonesWithContext(ctx aws.Context, input *ec2.DescribeAvailabilityZonesInput, opts ...request.Option) (*ec2.DescribeAvailabilityZonesOutput, error) + CreateTagsWithContext(ctx aws.Context, input *ec2.CreateTagsInput, opts ...request.Option) (*ec2.CreateTagsOutput, error) } diff --git a/pkg/cloud/metadata.go b/pkg/cloud/metadata.go index 2c3342805e..c52f5af42c 100644 --- a/pkg/cloud/metadata.go +++ b/pkg/cloud/metadata.go @@ -81,7 +81,7 @@ func (m *Metadata) GetOutpostArn() arn.ARN { return m.OutpostArn } -func NewMetadataService(ec2MetadataClient EC2MetadataClient, k8sAPIClient KubernetesAPIClient) (MetadataService, error) { +func NewMetadataService(ec2MetadataClient EC2MetadataClient, k8sAPIClient KubernetesAPIClient, region string) (MetadataService, error) { klog.Infof("retrieving instance data from ec2 metadata") svc, err := ec2MetadataClient() if !svc.Available() { @@ -90,7 +90,7 @@ func NewMetadataService(ec2MetadataClient EC2MetadataClient, k8sAPIClient Kubern klog.Warningf("error creating ec2 metadata client: %v", err) } else { klog.Infof("ec2 metadata is available") - return EC2MetadataInstanceInfo(svc) + return EC2MetadataInstanceInfo(svc, region) } klog.Infof("retrieving instance data from kubernetes api") diff --git a/pkg/cloud/metadata_ec2.go b/pkg/cloud/metadata_ec2.go index 77524bd8d8..f823815dfe 100644 --- a/pkg/cloud/metadata_ec2.go +++ b/pkg/cloud/metadata_ec2.go @@ -8,6 +8,7 @@ import ( "github.com/aws/aws-sdk-go/aws/arn" "github.com/aws/aws-sdk-go/aws/ec2metadata" "github.com/aws/aws-sdk-go/aws/session" + "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/util" "k8s.io/klog" ) @@ -19,8 +20,9 @@ var DefaultEC2MetadataClient = func() (EC2Metadata, error) { return svc, nil } -func EC2MetadataInstanceInfo(svc EC2Metadata) (*Metadata, error) { +func EC2MetadataInstanceInfo(svc EC2Metadata, regionFromSession string) (*Metadata, error) { doc, err := svc.GetInstanceIdentityDocument() + klog.Infof("regionFromSession %v", regionFromSession) if err != nil { return nil, fmt.Errorf("could not get EC2 instance identity metadata: %v", err) } @@ -34,11 +36,19 @@ func EC2MetadataInstanceInfo(svc EC2Metadata) (*Metadata, error) { } if len(doc.Region) == 0 { - return nil, fmt.Errorf("could not get valid EC2 region") + if len(regionFromSession) != 0 && util.IsSBE(regionFromSession) { + doc.Region = regionFromSession + } else { + return nil, fmt.Errorf("could not get valid EC2 region") + } } if len(doc.AvailabilityZone) == 0 { - return nil, fmt.Errorf("could not get valid EC2 availability zone") + if len(regionFromSession) != 0 && util.IsSBE(regionFromSession) { + doc.AvailabilityZone = regionFromSession + } else { + return nil, fmt.Errorf("could not get valid EC2 availability zone") + } } enis, err := svc.GetMetadata(enisEndpoint) @@ -52,12 +62,17 @@ func EC2MetadataInstanceInfo(svc EC2Metadata) (*Metadata, error) { attachedENIs := strings.Count(enis, "\n") + 1 - mappings, err := svc.GetMetadata(blockDevicesEndpoint) - if err != nil { - return nil, fmt.Errorf("could not get number of block device mappings: %v", err) + //As block device mapping contains 1 volume for the AMI. + blockDevMappings := 1 + + if !util.IsSBE(doc.Region) { + mappings, err := svc.GetMetadata(blockDevicesEndpoint) + // The output contains 1 volume for the AMI. Any other block device contributes to the attachment limit + blockDevMappings = strings.Count(mappings, "\n") + if err != nil { + return nil, fmt.Errorf("could not get number of block device mappings: %v", err) + } } - // The output contains 1 volume for the AMI. Any other block device contributes to the attachment limit - blockDevMappings := strings.Count(mappings, "\n") instanceInfo := Metadata{ InstanceID: doc.InstanceID, diff --git a/pkg/cloud/metadata_k8s.go b/pkg/cloud/metadata_k8s.go index 3e182a5ab2..92e3f4ff14 100644 --- a/pkg/cloud/metadata_k8s.go +++ b/pkg/cloud/metadata_k8s.go @@ -45,9 +45,9 @@ func KubernetesAPIInstanceInfo(clientset kubernetes.Interface) (*Metadata, error return nil, fmt.Errorf("node providerID empty, cannot parse") } - awsRegionRegex := "([a-z]{2}(-gov)?)-(central|(north|south)?(east|west)?)-[0-9]" - awsAvailabilityZoneRegex := "([a-z]{2}(-gov)?)-(central|(north|south)?(east|west)?)-[0-9][a-z]" - awsInstanceIDRegex := "i-[a-z0-9]+$" + awsRegionRegex := "(snow)|(([a-z]{2}(-gov)?)-(central|(north|south)?(east|west)?)-[0-9])" + awsAvailabilityZoneRegex := "(snow)|(([a-z]{2}(-gov)?)-(central|(north|south)?(east|west)?)-[0-9][a-z])" + awsInstanceIDRegex := "s\\.i-[a-z0-9]+|i-[a-z0-9]+$" re := regexp.MustCompile(awsRegionRegex) region := re.FindString(providerID) diff --git a/pkg/cloud/metadata_test.go b/pkg/cloud/metadata_test.go index 143ee78dcc..81a949a7d6 100644 --- a/pkg/cloud/metadata_test.go +++ b/pkg/cloud/metadata_test.go @@ -34,11 +34,13 @@ import ( ) const ( - nodeName = "ip-123-45-67-890.us-west-2.compute.internal" - stdInstanceID = "i-abcdefgh123456789" - stdInstanceType = "t2.medium" - stdRegion = "us-west-2" - stdAvailabilityZone = "us-west-2b" + nodeName = "ip-123-45-67-890.us-west-2.compute.internal" + stdInstanceID = "i-abcdefgh123456789" + stdInstanceType = "t2.medium" + stdRegion = "us-west-2" + stdAvailabilityZone = "us-west-2b" + snowRegion = "snow" + snowAvailabilityZone = "snow" ) func TestNewMetadataService(t *testing.T) { @@ -63,6 +65,7 @@ func TestNewMetadataService(t *testing.T) { expectedErr error node v1.Node nodeNameEnvVar string + regionFromSession string }{ { name: "success: normal", @@ -314,6 +317,20 @@ func TestNewMetadataService(t *testing.T) { imdsBlockDeviceOutput: "ami\nroot\nebs1\nebs2", expectedBlockDevices: 3, }, + { + name: "success: region from session is snow", + ec2metadataAvailable: true, + getInstanceIdentityDocumentValue: ec2metadata.EC2InstanceIdentityDocument{ + InstanceID: stdInstanceID, + InstanceType: stdInstanceType, + Region: "", + AvailabilityZone: "", + }, + imdsENIOutput: "00:00:00:00:00:00", + expectedENIs: 1, + regionFromSession: snowRegion, + expectedBlockDevices: 1, + }, } for _, tc := range testCases { @@ -339,7 +356,7 @@ func TestNewMetadataService(t *testing.T) { // output if tc.getInstanceIdentityDocumentError == nil && !tc.invalidInstanceIdentityDocument { mockEC2Metadata.EXPECT().GetMetadata(enisEndpoint).Return(tc.imdsENIOutput, nil) - mockEC2Metadata.EXPECT().GetMetadata(blockDevicesEndpoint).Return(tc.imdsBlockDeviceOutput, nil) + mockEC2Metadata.EXPECT().GetMetadata(blockDevicesEndpoint).Return(tc.imdsBlockDeviceOutput, nil).AnyTimes() if tc.getMetadataValue != "" || tc.getMetadataError != nil { mockEC2Metadata.EXPECT().GetMetadata(outpostArnEndpoint).Return(tc.getMetadataValue, tc.getMetadataError) @@ -358,8 +375,13 @@ func TestNewMetadataService(t *testing.T) { } os.Setenv("CSI_NODE_NAME", tc.nodeNameEnvVar) - - m, err := NewMetadataService(ec2MetadataClient, k8sAPIClient) + var m MetadataService + var err error + if tc.regionFromSession == snowRegion { + m, err = NewMetadataService(ec2MetadataClient, k8sAPIClient, snowRegion) + } else { + m, err = NewMetadataService(ec2MetadataClient, k8sAPIClient, stdRegion) + } if err != nil { if tc.expectedErr == nil { t.Errorf("got error %q, expected no error", err) @@ -376,10 +398,10 @@ func TestNewMetadataService(t *testing.T) { if m.GetInstanceType() != stdInstanceType { t.Errorf("GetInstanceType() failed: got wrong instance type %v, expected %v", m.GetInstanceType(), stdInstanceType) } - if m.GetRegion() != stdRegion { + if m.GetRegion() != stdRegion && m.GetRegion() != snowRegion { t.Errorf("NewMetadataService() failed: got wrong region %v, expected %v", m.GetRegion(), stdRegion) } - if m.GetAvailabilityZone() != stdAvailabilityZone { + if m.GetAvailabilityZone() != stdAvailabilityZone && m.GetAvailabilityZone() != snowAvailabilityZone { t.Errorf("NewMetadataService() failed: got wrong AZ %v, expected %v", m.GetAvailabilityZone(), stdAvailabilityZone) } if m.GetOutpostArn() != tc.expectedOutpostArn { diff --git a/pkg/cloud/mock_ec2.go b/pkg/cloud/mock_ec2.go index 0fbb9e4899..514437fb05 100644 --- a/pkg/cloud/mock_ec2.go +++ b/pkg/cloud/mock_ec2.go @@ -76,6 +76,26 @@ func (mr *MockEC2MockRecorder) CreateSnapshotWithContext(ctx, input interface{}, return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSnapshotWithContext", reflect.TypeOf((*MockEC2)(nil).CreateSnapshotWithContext), varargs...) } +// CreateTagsWithContext mocks base method. +func (m *MockEC2) CreateTagsWithContext(ctx aws.Context, input *ec2.CreateTagsInput, opts ...request.Option) (*ec2.CreateTagsOutput, error) { + m.ctrl.T.Helper() + varargs := []interface{}{ctx, input} + for _, a := range opts { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "CreateTagsWithContext", varargs...) + ret0, _ := ret[0].(*ec2.CreateTagsOutput) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateTagsWithContext indicates an expected call of CreateTagsWithContext. +func (mr *MockEC2MockRecorder) CreateTagsWithContext(ctx, input interface{}, opts ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx, input}, opts...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateTagsWithContext", reflect.TypeOf((*MockEC2)(nil).CreateTagsWithContext), varargs...) +} + // CreateVolumeWithContext mocks base method. func (m *MockEC2) CreateVolumeWithContext(ctx aws.Context, input *ec2.CreateVolumeInput, opts ...request.Option) (*ec2.Volume, error) { m.ctrl.T.Helper() diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go index c9560f2efe..15d1e2c17a 100644 --- a/pkg/driver/controller.go +++ b/pkg/driver/controller.go @@ -79,7 +79,7 @@ func newControllerService(driverOptions *DriverOptions) controllerService { region := os.Getenv("AWS_REGION") if region == "" { klog.V(5).Infof("[Debug] Retrieving region from metadata service") - metadata, err := NewMetadataFunc(cloud.DefaultEC2MetadataClient, cloud.DefaultKubernetesAPIClient) + metadata, err := NewMetadataFunc(cloud.DefaultEC2MetadataClient, cloud.DefaultKubernetesAPIClient, region) if err != nil { panic(err) } diff --git a/pkg/driver/controller_test.go b/pkg/driver/controller_test.go index ae46cb37bc..65cf21b19a 100644 --- a/pkg/driver/controller_test.go +++ b/pkg/driver/controller_test.go @@ -108,7 +108,7 @@ func TestNewControllerService(t *testing.T) { oldNewMetadataFunc := NewMetadataFunc defer func() { NewMetadataFunc = oldNewMetadataFunc }() - NewMetadataFunc = func(cloud.EC2MetadataClient, cloud.KubernetesAPIClient) (cloud.MetadataService, error) { + NewMetadataFunc = func(cloud.EC2MetadataClient, cloud.KubernetesAPIClient, string) (cloud.MetadataService, error) { if tc.newMetadataFuncErrors { return nil, testErr } diff --git a/pkg/driver/node.go b/pkg/driver/node.go index b23b92f5b4..08069b68d2 100644 --- a/pkg/driver/node.go +++ b/pkg/driver/node.go @@ -26,6 +26,7 @@ import ( csi "github.com/container-storage-interface/spec/lib/go/csi" "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud" "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/internal" + "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/util" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "k8s.io/klog" @@ -56,6 +57,9 @@ const ( // VolumeOperationAlreadyExists is message fmt returned to CO when there is another in-flight call on the given volumeID VolumeOperationAlreadyExists = "An operation with the given volume=%q is already in progress" + + //sbeDeviceVolumeAttachmentLimit refers to the maximum number of volumes that can be attached to an instance on snow. + sbeDeviceVolumeAttachmentLimit = 10 ) var ( @@ -90,7 +94,9 @@ type nodeService struct { // it panics if failed to create the service func newNodeService(driverOptions *DriverOptions) nodeService { klog.V(5).Infof("[Debug] Retrieving node info from metadata service") - metadata, err := cloud.NewMetadataService(cloud.DefaultEC2MetadataClient, cloud.DefaultKubernetesAPIClient) + region := os.Getenv("AWS_REGION") + klog.Infof("regionFromSession Node service %v", region) + metadata, err := cloud.NewMetadataService(cloud.DefaultEC2MetadataClient, cloud.DefaultKubernetesAPIClient, region) if err != nil { panic(err) } @@ -179,6 +185,7 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol klog.Warningf("NodeStageVolume: invalid partition config, will ignore. partition = %v", part) } } + source, err := d.findDevicePath(devicePath, volumeID, partition) if err != nil { return nil, status.Errorf(codes.Internal, "Failed to find device path %s. %v", devicePath, err) @@ -714,6 +721,10 @@ func (d *nodeService) getVolumesLimit() int64 { return d.driverOptions.volumeAttachLimit } + if util.IsSBE(d.metadata.GetRegion()) { + return sbeDeviceVolumeAttachmentLimit + } + instanceType := d.metadata.GetInstanceType() isNitro := cloud.IsNitroInstanceType(instanceType) diff --git a/pkg/driver/node_linux.go b/pkg/driver/node_linux.go index 9477eefeb4..51244e800b 100644 --- a/pkg/driver/node_linux.go +++ b/pkg/driver/node_linux.go @@ -26,6 +26,7 @@ import ( "strconv" "strings" + "github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/util" "golang.org/x/sys/unix" "k8s.io/klog" ) @@ -93,10 +94,16 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st if err == nil { klog.V(5).Infof("[Debug] successfully resolved nvmeName=%q to %q", nvmeName, nvmeDevicePath) canonicalDevicePath = nvmeDevicePath + return d.appendPartition(canonicalDevicePath, partition), nil } else { klog.V(5).Infof("[Debug] error searching for nvme path %q: %v", nvmeName, err) } + if util.IsSBE(d.metadata.GetRegion()) { + klog.V(5).Infof("[Debug] Falling back to snow volume lookup for: %q", devicePath) + canonicalDevicePath = "/dev/vd" + strings.TrimPrefix(devicePath, "/dev/xvdb") + } + if canonicalDevicePath == "" { return "", errNoDevicePathFound(devicePath, volumeID) } @@ -106,7 +113,7 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st } func errNoDevicePathFound(devicePath, volumeID string) error { - return fmt.Errorf("no device path for device %q volume %q found!", devicePath, volumeID) + return fmt.Errorf("no device path for device %q volume %q found", devicePath, volumeID) } // findNvmeVolume looks for the nvme volume with the specified name diff --git a/pkg/driver/node_linux_test.go b/pkg/driver/node_linux_test.go index a7a656ebae..969fafff32 100644 --- a/pkg/driver/node_linux_test.go +++ b/pkg/driver/node_linux_test.go @@ -32,8 +32,9 @@ import ( ) func TestFindDevicePath(t *testing.T) { - devicePath := "/dev/xvbda" + devicePath := "/dev/xvdba" nvmeDevicePath := "/dev/nvme1n1" + snowDevicePath := "/dev/vda" volumeID := "vol-test" nvmeName := "/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_voltest" deviceFileInfo := fs.FileInfo(&fakeFileInfo{devicePath, os.ModeDevice}) @@ -105,6 +106,20 @@ func TestFindDevicePath(t *testing.T) { }, expectError: errNoDevicePathFound(devicePath, volumeID).Error(), }, + { + name: "success: device path doesn't exist and snow path exists", + devicePath: devicePath, + volumeID: volumeID, + partition: "", + expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) { + gomock.InOrder( + mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(false, nil), + + mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(nvmeName)).Return(nil, os.ErrNotExist), + ) + }, + expectDevicePath: snowDevicePath, + }, } // The partition variant of each case should be the same except the partition // is expected to be appended to devicePath @@ -112,7 +127,7 @@ func TestFindDevicePath(t *testing.T) { for _, tc := range testCases { tc.name += " (with partition)" tc.partition = "1" - if tc.expectDevicePath == devicePath { + if tc.expectDevicePath == devicePath || tc.expectDevicePath == snowDevicePath { tc.expectDevicePath += tc.partition } else if tc.expectDevicePath == nvmeDevicePath { tc.expectDevicePath += "p" + tc.partition @@ -136,6 +151,16 @@ func TestFindDevicePath(t *testing.T) { driverOptions: &DriverOptions{}, } + if tc.expectDevicePath == snowDevicePath+tc.partition { + nodeDriver = nodeService{ + metadata: &cloud.Metadata{Region: "snow"}, + mounter: mockMounter, + deviceIdentifier: mockDeviceIdentifier, + inFlight: internal.NewInFlight(), + driverOptions: &DriverOptions{}, + } + } + if tc.expectMock != nil { tc.expectMock(*mockMounter, *mockDeviceIdentifier) } diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go index 956207ceb7..764a4f3737 100644 --- a/pkg/driver/node_test.go +++ b/pkg/driver/node_test.go @@ -1913,6 +1913,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID string instanceType string availabilityZone string + region string attachedENIs int blockDevices int volumeAttachLimit int64 @@ -1924,6 +1925,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "t2.medium", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, expMaxVolumes: 39, attachedENIs: 1, @@ -1934,6 +1936,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "t2.medium", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: 42, expMaxVolumes: 42, outpostArn: emptyOutpostArn, @@ -1943,6 +1946,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "t3.xlarge", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 2, expMaxVolumes: 26, // 28 (max) - 2 (enis) @@ -1953,6 +1957,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "m5d.large", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 2, expMaxVolumes: 25, @@ -1963,6 +1968,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "m5d.large", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: 30, expMaxVolumes: 30, outpostArn: emptyOutpostArn, @@ -1972,6 +1978,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "m5d.large", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: 30, expMaxVolumes: 30, outpostArn: validOutpostArn, @@ -1981,6 +1988,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "c6i.metal", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 1, expMaxVolumes: 31, @@ -1991,6 +1999,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "u-12tb1.metal", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 1, expMaxVolumes: 19, @@ -2001,6 +2010,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "mac1.metal", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 1, expMaxVolumes: 16, @@ -2011,6 +2021,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "inf1.24xlarge", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 1, expMaxVolumes: 11, @@ -2021,6 +2032,7 @@ func TestNodeGetInfo(t *testing.T) { instanceID: "i-123456789abcdef01", instanceType: "t3.xlarge", availabilityZone: "us-west-2b", + region: "us-west-2", volumeAttachLimit: -1, attachedENIs: 1, blockDevices: 2, @@ -2044,6 +2056,7 @@ func TestNodeGetInfo(t *testing.T) { mockMetadata.EXPECT().GetInstanceID().Return(tc.instanceID) mockMetadata.EXPECT().GetAvailabilityZone().Return(tc.availabilityZone) mockMetadata.EXPECT().GetOutpostArn().Return(tc.outpostArn) + mockMetadata.EXPECT().GetRegion().Return(tc.region).AnyTimes() if tc.volumeAttachLimit < 0 { mockMetadata.EXPECT().GetInstanceType().Return(tc.instanceType) diff --git a/pkg/util/util.go b/pkg/util/util.go index 6884dea4bb..4f68a5cc3f 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -89,3 +89,7 @@ func GetAccessModes(caps []*csi.VolumeCapability) *[]string { } return &modes } + +func IsSBE(region string) bool { + return region == "snow" +} diff --git a/tests/integration/setup_test.go b/tests/integration/setup_test.go index d1d3140ca3..d6d238cc46 100644 --- a/tests/integration/setup_test.go +++ b/tests/integration/setup_test.go @@ -111,7 +111,7 @@ func newMetadata() (cloud.MetadataService, error) { return nil, err } - return cloud.NewMetadataService(func() (cloud.EC2Metadata, error) { return ec2metadata.New(s), nil }, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(), nil }) + return cloud.NewMetadataService(func() (cloud.EC2Metadata, error) { return ec2metadata.New(s), nil }, func() (kubernetes.Interface, error) { return fake.NewSimpleClientset(), nil }, "") } func newEC2Client() (*ec2.EC2, error) {