Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #826 (2021-04-09 23:24:13)

Release 0.10.1
**Is this a bug fix or adding new feature?**
Bi-weekly csi driver release 
**What is this PR about? / Why do we need it?**
Bump minor version to v0.10.1 to include latest driver updates



Patch:
diff --git a/CHANGELOG-0.x.md b/CHANGELOG-0.x.md
index cbf59d99..9fb9eaf9 100644
--- a/CHANGELOG-0.x.md
+++ b/CHANGELOG-0.x.md
@@ -1,3 +1,10 @@
+# v0.10.1
+## Notable changes
+* support volume partition, users can specify partition in the pv and driver will mount the device on the specified partition ([#824](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/824), [@AndyXiangLi](https://github.com/AndyXiangLi))
+
+### Misc.
+* Warn users of migrating without draining ([#822](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/822), [@partcyborg](https://github.com/partcyborg))
+
 # v0.10.0
 
 ## Notable changes
diff --git a/Makefile b/Makefile
index ef30c8dd..7f59ef0b 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@
 
 PKG=github.com/kubernetes-sigs/aws-ebs-csi-driver
 IMAGE?=amazon/aws-ebs-csi-driver
-VERSION=v0.10.0
+VERSION=v0.10.1
 VERSION_AMAZONLINUX=$(VERSION)-amazonlinux
 GIT_COMMIT?=$(shell git rev-parse HEAD)
 BUILD_DATE?=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
diff --git a/charts/aws-ebs-csi-driver/Chart.yaml b/charts/aws-ebs-csi-driver/Chart.yaml
index 8547cf5b..f972def1 100644
--- a/charts/aws-ebs-csi-driver/Chart.yaml
+++ b/charts/aws-ebs-csi-driver/Chart.yaml
@@ -1,8 +1,8 @@
 apiVersion: v1
-appVersion: "0.10.0"
+appVersion: "0.10.1"
 name: aws-ebs-csi-driver
 description: A Helm chart for AWS EBS CSI Driver
-version: 0.10.0
+version: 0.10.1
 kubeVersion: ">=1.17.0-0"
 home: https://github.com/kubernetes-sigs/aws-ebs-csi-driver
 sources:
diff --git a/charts/aws-ebs-csi-driver/values.yaml b/charts/aws-ebs-csi-driver/values.yaml
index 7fcb2bb0..f25c4848 100644
--- a/charts/aws-ebs-csi-driver/values.yaml
+++ b/charts/aws-ebs-csi-driver/values.yaml
@@ -6,7 +6,7 @@ replicaCount: 2
 
 image:
   repository: k8s.gcr.io/provider-aws/aws-ebs-csi-driver
-  tag: "v0.10.0"
+  tag: "v0.10.1"
   pullPolicy: IfNotPresent
 
 sidecars:
diff --git a/deploy/kubernetes/base/controller.yaml b/deploy/kubernetes/base/controller.yaml
index e28c6b26..0cb4483b 100644
--- a/deploy/kubernetes/base/controller.yaml
+++ b/deploy/kubernetes/base/controller.yaml
@@ -32,7 +32,7 @@ spec:
           tolerationSeconds: 300
       containers:
         - name: ebs-plugin
-          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.10.0
+          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.10.1
           imagePullPolicy: IfNotPresent
           args:
             # - {all,controller,node} # specify the driver mode
diff --git a/deploy/kubernetes/base/node.yaml b/deploy/kubernetes/base/node.yaml
index 085a1228..d0a8c836 100644
--- a/deploy/kubernetes/base/node.yaml
+++ b/deploy/kubernetes/base/node.yaml
@@ -43,7 +43,7 @@ spec:
         - name: ebs-plugin
           securityContext:
             privileged: true
-          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.10.0
+          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.10.1
           args:
             - node
             - --endpoint=$(CSI_ENDPOINT)
diff --git a/deploy/kubernetes/overlays/stable/kustomization.yaml b/deploy/kubernetes/overlays/stable/kustomization.yaml
index debc97d0..6a971277 100644
--- a/deploy/kubernetes/overlays/stable/kustomization.yaml
+++ b/deploy/kubernetes/overlays/stable/kustomization.yaml
@@ -4,7 +4,7 @@ bases:
   - ../../base
 images:
   - name: k8s.gcr.io/provider-aws/aws-ebs-csi-driver
-    newTag: v0.10.0
+    newTag: v0.10.1
   - name: k8s.gcr.io/sig-storage/csi-provisioner
     newTag: v2.0.2
   - name: k8s.gcr.io/sig-storage/csi-attacher
diff --git a/docs/README.md b/docs/README.md
index 84e30bea..da6afdd4 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,7 +12,7 @@ The [Amazon Elastic Block Store](https://aws.amazon.com/ebs/) Container Storage
 | AWS EBS CSI Driver \ CSI Version       | v0.3.0| v1.0.0 | v1.1.0 |
 |----------------------------------------|-------|--------|--------|
 | master branch                          | no    | no     | yes    |
-| v0.10.0                                | no    | no     | yes    |
+| v0.10.x                                | no    | no     | yes    |
 | v0.9.x                                 | no    | no     | yes    |
 | v0.8.x                                 | no    | no     | yes    |
 | v0.7.1                                 | no    | no     | yes    |
@@ -53,7 +53,7 @@ Following sections are Kubernetes specific. If you are Kubernetes user, use foll
 | AWS EBS CSI Driver \ Kubernetes Version| v1.12 | v1.13 | v1.14 | v1.15 | v1.16 | v1.17 | v1.18+ |
 |----------------------------------------|-------|-------|-------|-------|-------|-------|-------|
 | master branch                          | no    | no+   | no    | no    | no    | yes   | yes   |
-| v0.10.0                                | no    | no+   | no    | no    | no    | yes   | yes   |
+| v0.10.x                                | no    | no+   | no    | no    | no    | yes   | yes   |
 | v0.9.x                                 | no    | no+   | no    | no    | no    | yes   | yes   |
 | v0.8.x                                 | no    | no+   | yes   | yes   | yes   | yes   | yes   |
 | v0.7.1                                 | no    | no+   | yes   | yes   | yes   | yes   | yes   |
@@ -70,6 +70,7 @@ Following sections are Kubernetes specific. If you are Kubernetes user, use foll
 |AWS EBS CSI Driver Version | Image                                            |
 |---------------------------|--------------------------------------------------|
 |master branch              |amazon/aws-ebs-csi-driver:latest                  |
+|v0.10.1                    |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.10.1|
 |v0.10.0                    |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.10.0|
 |v0.9.1                     |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.1 |
 |v0.9.0                     |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.0 |
@@ -161,12 +162,14 @@ Make sure you follow the [Prerequisites](README.md#Prerequisites) before the exa
 * [Volume Resizing](../examples/kubernetes/resizing)
 
 ## Migrating from in-tree EBS plugin
-Starting from Kubernetes 1.17, CSI migration is supported as beta feature (alpha since 1.14). If you have persistence volumes that are created with in-tree `kubernetes.io/aws-ebs` plugin, you could migrate to use EBS CSI driver. To turn on the migration, set `CSIMigration` and `CSIMigrationAWS` feature gates to `true` for `kube-controller-manager` and `kubelet`.
+Starting from Kubernetes 1.17, CSI migration is supported as beta feature (alpha since 1.14). If you have persistence volumes that are created with in-tree `kubernetes.io/aws-ebs` plugin, you could migrate to use EBS CSI driver. To turn on the migration, drain the node and set `CSIMigration` and `CSIMigrationAWS` feature gates to `true` for `kube-controller-manager` and `kubelet`.
 
 To make sure dynamically provisioned EBS volumes have all tags that the in-tree volume plugin used:
 * Run the external-provisioner sidecar with `--extra-create-metadata=true` cmdline option. External-provisioner v1.6 or newer is required.
 * Run the CSI driver with `--k8s-tag-cluster-id=<ID of the Kubernetes cluster>` command line option.
 
+**Warning**:
+* kubelet *must* be drained of all pods with mounted EBS volumes ***before*** changing its CSI migration feature flags.  Failure to do this will cause deleted pods to get stuck in `Terminating`, requiring a forced delete which can cause filesystem corruption. See [#679](../../../issues/679) for more details.
 
 ## Development
 Please go through [CSI Spec](https://github.com/container-storage-interface/spec/blob/master/spec.md) and [General CSI driver development guideline](https://kubernetes-csi.github.io/docs/developing.html) to get some basic understanding of CSI driver before you start.
diff --git a/pkg/driver/constants.go b/pkg/driver/constants.go
index 67a65f44..bc7e0b26 100644
--- a/pkg/driver/constants.go
+++ b/pkg/driver/constants.go
@@ -23,6 +23,19 @@ const (
 	DevicePathKey = "devicePath"
 )
 
+// constants of keys in VolumeContext
+const (
+	// VolumeAttributePartition represents key for partition config in VolumeContext
+	// this represents the partition number on a device used to mount
+	VolumeAttributePartition = "partition"
+)
+
+// constants of disk partition suffix
+const (
+	diskPartitionSuffix     = ""
+	nvmeDiskPartitionSuffix = "p"
+)
+
 // constants of keys in volume parameters
 const (
 	// VolumeTypeKey represents key for volume type
diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go
index 45d3b5c7..381187e3 100644
--- a/pkg/driver/controller.go
+++ b/pkg/driver/controller.go
@@ -450,6 +450,23 @@ func isValidVolumeCapabilities(volCaps []*csi.VolumeCapability) bool {
 	return foundAll
 }
 
+func isValidVolumeContext(volContext map[string]string) bool {
+	//There could be multiple volume attributes in the volumeContext map
+	//Validate here case by case
+	if partition, ok := volContext[VolumeAttributePartition]; ok {
+		partitionInt, err := strconv.ParseInt(partition, 10, 64)
+		if err != nil {
+			klog.Errorf("failed to parse partition %s as int", partition)
+			return false
+		}
+		if partitionInt < 0 {
+			klog.Errorf("invalid partition config, partition = %s", partition)
+			return false
+		}
+	}
+	return true
+}
+
 func (d *controllerService) CreateSnapshot(ctx context.Context, req *csi.CreateSnapshotRequest) (*csi.CreateSnapshotResponse, error) {
 	klog.V(4).Infof("CreateSnapshot: called with args %+v", req)
 	snapshotName := req.GetName()
diff --git a/pkg/driver/node.go b/pkg/driver/node.go
index 6ada6ee5..1ad3ad72 100644
--- a/pkg/driver/node.go
+++ b/pkg/driver/node.go
@@ -121,6 +121,10 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 	if !isValidVolumeCapabilities([]*csi.VolumeCapability{volCap}) {
 		return nil, status.Error(codes.InvalidArgument, "Volume capability not supported")
 	}
+	volumeContext := req.GetVolumeContext()
+	if isValidVolumeContext := isValidVolumeContext(volumeContext); !isValidVolumeContext {
+		return nil, status.Error(codes.InvalidArgument, "Volume Attribute is not valid")
+	}
 
 	// If the access type is block, do nothing for stage
 	switch volCap.GetAccessType().(type) {
@@ -158,7 +162,15 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 		return nil, status.Error(codes.InvalidArgument, "Device path not provided")
 	}
 
-	source, err := d.findDevicePath(devicePath, volumeID)
+	partition := ""
+	if part, ok := volumeContext[VolumeAttributePartition]; ok {
+		if part != "0" {
+			partition = part
+		} else {
+			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)
 	}
@@ -516,12 +528,26 @@ func (d *nodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoReque
 func (d *nodeService) nodePublishVolumeForBlock(req *csi.NodePublishVolumeRequest, mountOptions []string) error {
 	target := req.GetTargetPath()
 	volumeID := req.GetVolumeId()
+	volumeContext := req.GetVolumeContext()
 
 	devicePath, exists := req.PublishContext[DevicePathKey]
 	if !exists {
 		return status.Error(codes.InvalidArgument, "Device path not provided")
 	}
-	source, err := d.findDevicePath(devicePath, volumeID)
+	if isValidVolumeContext := isValidVolumeContext(volumeContext); !isValidVolumeContext {
+		return status.Error(codes.InvalidArgument, "Volume Attribute is invalid")
+	}
+
+	partition := ""
+	if part, ok := req.GetVolumeContext()[VolumeAttributePartition]; ok {
+		if part != "0" {
+			partition = part
+		} else {
+			klog.Warningf("NodePublishVolume: invalid partition config, will ignore. partition = %v", part)
+		}
+	}
+
+	source, err := d.findDevicePath(devicePath, volumeID, partition)
 	if err != nil {
 		return status.Errorf(codes.Internal, "Failed to find device path %s. %v", devicePath, err)
 	}
diff --git a/pkg/driver/node_linux.go b/pkg/driver/node_linux.go
index 5454db9b..71f2c53e 100644
--- a/pkg/driver/node_linux.go
+++ b/pkg/driver/node_linux.go
@@ -32,7 +32,7 @@ import (
 // findDevicePath finds path of device and verifies its existence
 // if the device is not nvme, return the path directly
 // if the device is nvme, finds and returns the nvme device path eg. /dev/nvme1n1
-func (d *nodeService) findDevicePath(devicePath, volumeID string) (string, error) {
+func (d *nodeService) findDevicePath(devicePath, volumeID string, partition string) (string, error) {
 	exists, err := d.mounter.PathExists(devicePath)
 	if err != nil {
 		return "", err
@@ -40,6 +40,9 @@ func (d *nodeService) findDevicePath(devicePath, volumeID string) (string, error
 
 	// If the path exists, assume it is not nvme device
 	if exists {
+		if partition != "" {
+			devicePath = devicePath + diskPartitionSuffix + partition
+		}
 		return devicePath, nil
 	}
 
@@ -49,7 +52,14 @@ func (d *nodeService) findDevicePath(devicePath, volumeID string) (string, error
 	// /dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol0fab1d5e3f72a5e23
 	nvmeName := "nvme-Amazon_Elastic_Block_Store_" + strings.Replace(volumeID, "-", "", -1)
 
-	return findNvmeVolume(nvmeName)
+	nvmeDevicePath, err := findNvmeVolume(nvmeName)
+	if err != nil {
+		return "", err
+	}
+	if partition != "" {
+		nvmeDevicePath = nvmeDevicePath + nvmeDiskPartitionSuffix + partition
+	}
+	return nvmeDevicePath, nil
 }
 
 // findNvmeVolume looks for the nvme volume with the specified name


Test patch:
diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go
index b2198786..1142eadc 100644
--- a/pkg/driver/node_test.go
+++ b/pkg/driver/node_test.go
@@ -48,6 +48,8 @@ func TestNodeStageVolume(t *testing.T) {
 				Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 			},
 		}
+		stdVolContext           = map[string]string{VolumeAttributePartition: "1"}
+		devicePathWithPartition = devicePath + "1"
 	)
 	testCases := []struct {
 		name         string
@@ -241,6 +243,17 @@ func TestNodeStageVolume(t *testing.T) {
 			},
 			expectedCode: codes.InvalidArgument,
 		},
+		{
+			name: "fail invalid volumeContext",
+			request: &csi.NodeStageVolumeRequest{
+				PublishContext:    map[string]string{DevicePathKey: devicePath},
+				StagingTargetPath: targetPath,
+				VolumeCapability:  stdVolCap,
+				VolumeContext:     map[string]string{VolumeAttributePartition: "partition1"},
+				VolumeId:          "vol-test",
+			},
+			expectedCode: codes.InvalidArgument,
+		},
 		{
 			name: "success device already mounted at target",
 			request: &csi.NodeStageVolumeRequest{
@@ -260,6 +273,44 @@ func TestNodeStageVolume(t *testing.T) {
 				mockMounter.EXPECT().FormatAndMount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(0)
 			},
 		},
+		{
+			name: "success with partition",
+			request: &csi.NodeStageVolumeRequest{
+				PublishContext:    map[string]string{DevicePathKey: devicePath},
+				StagingTargetPath: targetPath,
+				VolumeCapability:  stdVolCap,
+				VolumeContext:     stdVolContext,
+				VolumeId:          "vol-test",
+			},
+			expectMock: func(mockMounter mocks.MockMounter) {
+				gomock.InOrder(
+					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
+					mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(false, nil),
+				)
+				mockMounter.EXPECT().MakeDir(targetPath).Return(nil)
+				mockMounter.EXPECT().GetDeviceNameFromMount(targetPath).Return("", 1, nil)
+				mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePathWithPartition), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any())
+			},
+		},
+		{
+			name: "success with invalid partition config, will ignore partition",
+			request: &csi.NodeStageVolumeRequest{
+				PublishContext:    map[string]string{DevicePathKey: devicePath},
+				StagingTargetPath: targetPath,
+				VolumeCapability:  stdVolCap,
+				VolumeContext:     map[string]string{VolumeAttributePartition: "0"},
+				VolumeId:          "vol-test",
+			},
+			expectMock: func(mockMounter mocks.MockMounter) {
+				gomock.InOrder(
+					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
+					mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(false, nil),
+				)
+				mockMounter.EXPECT().MakeDir(targetPath).Return(nil)
+				mockMounter.EXPECT().GetDeviceNameFromMount(targetPath).Return("", 1, nil)
+				mockMounter.EXPECT().FormatAndMount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any())
+			},
+		},
 		{
 			name: "fail with in-flight request",
 			request: &csi.NodeStageVolumeRequest{
@@ -517,6 +568,8 @@ func TestNodePublishVolume(t *testing.T) {
 			Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 		},
 	}
+	stdVolContext := map[string]string{"partition": "1"}
+	devicePathWithPartition := devicePath + "1"
 	testCases := []struct {
 		name     string
 		testFunc func(t *testing.T)
@@ -716,6 +769,131 @@ func TestNodePublishVolume(t *testing.T) {
 				}
 			},
 		},
+		{
+			name: "success normal with partition [raw block]",
+			testFunc: func(t *testing.T) {
+				mockCtl := gomock.NewController(t)
+				defer mockCtl.Finish()
+
+				mockMetadata := mocks.NewMockMetadataService(mockCtl)
+				mockMounter := mocks.NewMockMounter(mockCtl)
+
+				awsDriver := &nodeService{
+					metadata: mockMetadata,
+					mounter:  mockMounter,
+					inFlight: internal.NewInFlight(),
+				}
+
+				gomock.InOrder(
+					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
+					mockMounter.EXPECT().PathExists(gomock.Eq("/test")).Return(false, nil),
+				)
+				mockMounter.EXPECT().MakeDir(gomock.Eq("/test")).Return(nil)
+				mockMounter.EXPECT().MakeFile(targetPath).Return(nil)
+				mockMounter.EXPECT().Mount(gomock.Eq(devicePathWithPartition), gomock.Eq(targetPath), gomock.Eq(""), gomock.Eq([]string{"bind"})).Return(nil)
+
+				req := &csi.NodePublishVolumeRequest{
+					PublishContext:    map[string]string{DevicePathKey: "/dev/fake"},
+					StagingTargetPath: stagingTargetPath,
+					TargetPath:        targetPath,
+					VolumeCapability: &csi.VolumeCapability{
+						AccessType: &csi.VolumeCapability_Block{
+							Block: &csi.VolumeCapability_BlockVolume{},
+						},
+						AccessMode: &csi.VolumeCapability_AccessMode{
+							Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+						},
+					},
+					VolumeContext: stdVolContext,
+					VolumeId:      "vol-test",
+				}
+
+				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
+				if err != nil {
+					t.Fatalf("Expect no error but got: %v", err)
+				}
+			},
+		},
+		{
+			name: "success normal with invalid partition config, will ignore the config [raw block]",
+			testFunc: func(t *testing.T) {
+				mockCtl := gomock.NewController(t)
+				defer mockCtl.Finish()
+
+				mockMetadata := mocks.NewMockMetadataService(mockCtl)
+				mockMounter := mocks.NewMockMounter(mockCtl)
+
+				awsDriver := &nodeService{
+					metadata: mockMetadata,
+					mounter:  mockMounter,
+					inFlight: internal.NewInFlight(),
+				}
+
+				gomock.InOrder(
+					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
+					mockMounter.EXPECT().PathExists(gomock.Eq("/test")).Return(false, nil),
+				)
+				mockMounter.EXPECT().MakeDir(gomock.Eq("/test")).Return(nil)
+				mockMounter.EXPECT().MakeFile(targetPath).Return(nil)
+				mockMounter.EXPECT().Mount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(""), gomock.Eq([]string{"bind"})).Return(nil)
+
+				req := &csi.NodePublishVolumeRequest{
+					PublishContext:    map[string]string{DevicePathKey: "/dev/fake"},
+					StagingTargetPath: stagingTargetPath,
+					TargetPath:        targetPath,
+					VolumeCapability: &csi.VolumeCapability{
+						AccessType: &csi.VolumeCapability_Block{
+							Block: &csi.VolumeCapability_BlockVolume{},
+						},
+						AccessMode: &csi.VolumeCapability_AccessMode{
+							Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+						},
+					},
+					VolumeContext: map[string]string{VolumeAttributePartition: "0"},
+					VolumeId:      "vol-test",
+				}
+
+				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
+				if err != nil {
+					t.Fatalf("Expect no error but got: %v", err)
+				}
+			},
+		},
+		{
+			name: "Fail invalid volumeContext config [raw block]",
+			testFunc: func(t *testing.T) {
+				mockCtl := gomock.NewController(t)
+				defer mockCtl.Finish()
+
+				mockMetadata := mocks.NewMockMetadataService(mockCtl)
+				mockMounter := mocks.NewMockMounter(mockCtl)
+
+				awsDriver := &nodeService{
+					metadata: mockMetadata,
+					mounter:  mockMounter,
+					inFlight: internal.NewInFlight(),
+				}
+
+				req := &csi.NodePublishVolumeRequest{
+					PublishContext:    map[string]string{DevicePathKey: "/dev/fake"},
+					StagingTargetPath: stagingTargetPath,
+					TargetPath:        targetPath,
+					VolumeCapability: &csi.VolumeCapability{
+						AccessType: &csi.VolumeCapability_Block{
+							Block: &csi.VolumeCapability_BlockVolume{},
+						},
+						AccessMode: &csi.VolumeCapability_AccessMode{
+							Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+						},
+					},
+					VolumeContext: map[string]string{VolumeAttributePartition: "partition1"},
+					VolumeId:      "vol-test",
+				}
+
+				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
+				expectErr(t, err, codes.InvalidArgument)
+			},
+		},
 		{
 			name: "fail no device path [raw block]",
 			testFunc: func(t *testing.T) {