Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #1997 (2024-04-05 15:45:39)

Fix relationship between node service and mounter interface
**What is this PR about? / Why do we need it?**

This patch is 2/2, designed to improve the architecture of the code base with the goal of promoting a more cohesive and understandable design that makes it easier to reason about the driver and make future modifications or extensions more intuitive.

More specifically, this PR fixes the relationship between the node service and mounter interface, allowing for a platform agnostic node service.

**Summary of Changes**

- Integrate `resizeFs` functionality into mounter package
   - Updated the `NodeStageVolume` and `NodeExpandVolume` RPCs to use the `Resize` method directly from the `Mounter` interface, instead of creating a new `ResizeFs` instance.
 - Remove `DeviceIdentifier` interface
   - Replaced the usage of` deviceIdentifier` with direct calls to `os.Lstat` and `filepath.EvalSymlinks`.
 - Refactor node service to be platform agnostic
   - Moved platform-specific code from `node_linux.go` and `node_windows.go` to `mount_linux.go` and `mount_windows.go`.
 - Refactor node service initialization and dependency injection
   - Improved modularity and testability by injecting dependencies (`metadata.MetadataService`, `Mounter`, `metadata.KubernetesAPIClient`) into the `NewDriver()` and `NewNodeService()` functions.
   

**What testing is done?**

- manual
- `make test && make verify`
- CI


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by:
**Once this PR has been reviewed and has the lgtm label**, please ask for approval from [torredil](https://github.com/torredil). For more information see [the Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Faws-ebs-csi-driver).

<details open>
Needs approval from an approver in each of these files:

- **[OWNERS](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["torredil"]} -->
github-actions[bot]: <!-- pr-coverage -->
## Code Coverage Diff
File | Old Coverage | New Coverage | Delta
---- |:------------:|:------------:|:-----:
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata/ec2.go | Does not exist | 94.4% | 
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata/k8s.go | Does not exist | 80.0% | 
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata/metadata.go | Does not exist | 96.7% | 
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/controller.go | 83.6% | 82.8% | -0.8
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/driver.go | 36.5% | 0.0% | -36.5
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/mount.go | 42.9% | 75.0% | 32.1
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/mount_linux.go | 68.4% | 27.7% | -40.8
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/node.go | 80.6% | 22.2% | -58.4
github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/options.go | Does not exist | 0.0% |
k8s-ci-robot: @torredil: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-verify | 4046e86c92592236145fd35887f130fe1349ce87 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-verify/1778080705549438976) | true | `/test pull-aws-ebs-csi-driver-verify`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778109172210995200) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778109172210995200) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778109172571705344) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778109172210995200) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778109172571705344) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778109172466847744) | true | `/test pull-aws-ebs-csi-driver-external-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
torredil: /retest
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778109172210995200) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778109172571705344) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778109172466847744) | true | `/test pull-aws-ebs-csi-driver-external-test`
pull-aws-ebs-csi-driver-external-test-eks | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778109172521373696) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778109172571705344) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778109172466847744) | true | `/test pull-aws-ebs-csi-driver-external-test`
pull-aws-ebs-csi-driver-external-test-eks | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778109172521373696) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778115357744041984) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778109172571705344) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test-eks | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778109172521373696) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778115357744041984) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`
pull-aws-ebs-csi-driver-external-test | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778115357802762240) | true | `/test pull-aws-ebs-csi-driver-external-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-external-test-eks | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778109172521373696) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778115357744041984) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`
pull-aws-ebs-csi-driver-external-test | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778115357802762240) | true | `/test pull-aws-ebs-csi-driver-external-test`
pull-aws-ebs-csi-driver-test-e2e-external-eks-windows | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-e2e-external-eks-windows/1778109172160663552) | true | `/test pull-aws-ebs-csi-driver-test-e2e-external-eks-windows`
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778115357857288192) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-external-test-eks | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778109172521373696) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`
pull-aws-ebs-csi-driver-test-helm-chart | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-helm-chart/1778115357744041984) | true | `/test pull-aws-ebs-csi-driver-test-helm-chart`
pull-aws-ebs-csi-driver-test-e2e-external-eks-windows | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-e2e-external-eks-windows/1778109172160663552) | true | `/test pull-aws-ebs-csi-driver-test-e2e-external-eks-windows`
pull-aws-ebs-csi-driver-external-test-arm64 | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778115357857288192) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778132534652047360) | true | `/test pull-aws-ebs-csi-driver-external-test`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-external-test-eks | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778109172521373696) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`
pull-aws-ebs-csi-driver-test-e2e-external-eks-windows | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-e2e-external-eks-windows/1778109172160663552) | true | `/test pull-aws-ebs-csi-driver-test-e2e-external-eks-windows`
pull-aws-ebs-csi-driver-external-test | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778132534652047360) | true | `/test pull-aws-ebs-csi-driver-external-test`
pull-aws-ebs-csi-driver-external-test-arm64 | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778132534765293568) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-test-e2e-external-eks-windows | b090dc261a914d174de15ef72ba94c2510c11eca | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-e2e-external-eks-windows/1778109172160663552) | true | `/test pull-aws-ebs-csi-driver-test-e2e-external-eks-windows`
pull-aws-ebs-csi-driver-external-test | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778132534652047360) | true | `/test pull-aws-ebs-csi-driver-external-test`
pull-aws-ebs-csi-driver-external-test-arm64 | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778132534765293568) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test-eks | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778132534710767616) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
k8s-ci-robot: @torredil: The following tests **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-external-test | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test/1778132534652047360) | true | `/test pull-aws-ebs-csi-driver-external-test`
pull-aws-ebs-csi-driver-external-test-arm64 | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-arm64/1778132534765293568) | false | `/test pull-aws-ebs-csi-driver-external-test-arm64`
pull-aws-ebs-csi-driver-external-test-eks | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-external-test-eks/1778132534710767616) | true | `/test pull-aws-ebs-csi-driver-external-test-eks`
pull-aws-ebs-csi-driver-test-e2e-external-eks-windows | bd4b87d7c217391c15f41dd0f713b326b6642a80 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-test-e2e-external-eks-windows/1778132534324891648) | true | `/test pull-aws-ebs-csi-driver-test-e2e-external-eks-windows`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
torredil: /retest
k8s-ci-robot: PR needs rebase.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
k8s-ci-robot: @torredil: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | 88e5cd64386f88c2cf86186dde33bc8e0912f6e7 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/1997/pull-aws-ebs-csi-driver-e2e-single-az/1780281551360102400) | true | `/test pull-aws-ebs-csi-driver-e2e-single-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=1997). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atorredil). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues?q=is:issue+is:open) when you hit one in your PR.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->

Patch:
diff --git a/cmd/main.go b/cmd/main.go
index a25b1433..c1c7fbd2 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -28,6 +28,7 @@ import (
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/metrics"
+	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
 	flag "github.com/spf13/pflag"
 	"k8s.io/component-base/featuregate"
 	logsapi "k8s.io/component-base/logs/api/v1"
@@ -132,19 +133,33 @@ func main() {
 		r.InitializeMetricsHandler(options.HttpEndpoint, "/metrics")
 	}
 
+	cfg := metadata.MetadataServiceConfig{
+		EC2MetadataClient: metadata.DefaultEC2MetadataClient,
+		K8sAPIClient:      metadata.DefaultKubernetesAPIClient,
+	}
+
 	region := os.Getenv("AWS_REGION")
+	var md metadata.MetadataService
+	var metadataErr error
+
 	if region == "" {
 		klog.V(5).InfoS("[Debug] Retrieving region from metadata service")
-		cfg := metadata.MetadataServiceConfig{
-			EC2MetadataClient: metadata.DefaultEC2MetadataClient,
-			K8sAPIClient:      metadata.DefaultKubernetesAPIClient,
-		}
-		metadata, metadataErr := metadata.NewMetadataService(cfg, region)
+		md, metadataErr = metadata.NewMetadataService(cfg, region)
 		if metadataErr != nil {
 			klog.ErrorS(metadataErr, "Could not determine region from any metadata service. The region can be manually supplied via the AWS_REGION environment variable.")
-			panic(err)
+			panic(metadataErr)
+		}
+		region = md.GetRegion()
+	}
+
+	if md == nil {
+		if options.Mode == driver.NodeMode || options.Mode == driver.AllMode {
+			md, metadataErr = metadata.NewMetadataService(cfg, region)
+			if metadataErr != nil {
+				klog.ErrorS(metadataErr, "failed to initialize metadata service")
+				klog.FlushAndExit(klog.ExitFlushTimeout, 1)
+			}
 		}
-		region = metadata.GetRegion()
 	}
 
 	cloud, err := cloud.NewCloud(region, options.AwsSdkDebugLog, options.UserAgentExtra, options.Batching)
@@ -153,7 +168,17 @@ func main() {
 		klog.FlushAndExit(klog.ExitFlushTimeout, 1)
 	}
 
-	drv, err := driver.NewDriver(cloud, &options)
+	m, err := mounter.NewNodeMounter()
+	if err != nil {
+		panic(err)
+	}
+
+	k8sClient, err := cfg.K8sAPIClient()
+	if err != nil {
+		klog.V(2).InfoS("Failed to setup k8s client")
+	}
+
+	drv, err := driver.NewDriver(cloud, &options, m, md, k8sClient)
 	if err != nil {
 		klog.ErrorS(err, "failed to create driver")
 		klog.FlushAndExit(klog.ExitFlushTimeout, 1)
diff --git a/hack/update-mockgen.sh b/hack/update-mockgen.sh
index 0e35f5b2..10cd3ed6 100755
--- a/hack/update-mockgen.sh
+++ b/hack/update-mockgen.sh
@@ -21,7 +21,7 @@ BIN="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/../bin"
 # Source-based mocking for internal interfaces
 "${BIN}/mockgen" -package cloud -destination=./pkg/cloud/mock_cloud.go -source pkg/cloud/interface.go
 "${BIN}/mockgen" -package metadata -destination=./pkg/cloud/metadata/mock_metadata.go -source pkg/cloud/metadata/interface.go
-"${BIN}/mockgen" -package driver -destination=./pkg/driver/mock_mount.go -source pkg/driver/mount.go
+"${BIN}/mockgen" -package mounter -destination=./pkg/mounter/mock_mount.go -source pkg/mounter/mount.go
 "${BIN}/mockgen" -package mounter -destination=./pkg/mounter/mock_mount_windows.go -source pkg/mounter/safe_mounter_windows.go
 "${BIN}/mockgen" -package cloud -destination=./pkg/cloud/mock_ec2.go -source pkg/cloud/ec2_interface.go EC2API
 
@@ -30,8 +30,3 @@ BIN="$(dirname "$(realpath "${BASH_SOURCE[0]}")")/../bin"
 "${BIN}/mockgen" -package driver -destination=./pkg/driver/mock_k8s_corev1.go k8s.io/client-go/kubernetes/typed/core/v1 CoreV1Interface,NodeInterface
 "${BIN}/mockgen" -package driver -destination=./pkg/driver/mock_k8s_storagev1.go k8s.io/client-go/kubernetes/typed/storage/v1 VolumeAttachmentInterface,StorageV1Interface
 "${BIN}/mockgen" -package driver -destination=./pkg/driver/mock_k8s_storagev1_csinode.go k8s.io/client-go/kubernetes/typed/storage/v1 CSINodeInterface
-
-# Fixes "Mounter Type cannot implement 'Mounter' as it has a non-exported method and is defined in a different package"
-# See https://github.com/kubernetes/mount-utils/commit/a20fcfb15a701977d086330b47b7efad51eb608e for context.
-sed -i '/type MockMounter struct {/a \\tmount_utils.Interface' pkg/driver/mock_mount.go
-sed -i '/type MockProxyMounter struct {/a \\tmount.Interface' pkg/mounter/mock_mount_windows.go
diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go
index ac6984bc..5b3c2854 100644
--- a/pkg/cloud/cloud.go
+++ b/pkg/cloud/cloud.go
@@ -613,7 +613,14 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
 	}
 
 	if err := c.waitForVolume(ctx, volumeID); err != nil {
-		return nil, fmt.Errorf("timed out waiting for volume to create: %w", err)
+		// 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.ErrorS(error, "failed to be deleted, this may cause volume leak", "volumeID", volumeID)
+		} else {
+			klog.V(5).InfoS("[Debug] volume is deleted because it is not in desired state within retry limit", "volumeID", volumeID)
+		}
+		return nil, fmt.Errorf("failed to get an available volume in EC2: %w", err)
 	}
 
 	outpostArn := aws.ToString(response.OutpostArn)
diff --git a/pkg/driver/constants.go b/pkg/driver/constants.go
index f34ccbc7..7775bbbd 100644
--- a/pkg/driver/constants.go
+++ b/pkg/driver/constants.go
@@ -32,12 +32,6 @@ const (
 	VolumeAttributePartition = "partition"
 )
 
-// constants of disk partition suffix
-const (
-	diskPartitionSuffix     = ""
-	nvmeDiskPartitionSuffix = "p"
-)
-
 // constants of keys in volume parameters
 const (
 	// VolumeTypeKey represents key for volume type
@@ -162,12 +156,6 @@ const (
 	DefaultModifyVolumeRequestHandlerTimeout = 2 * time.Second
 )
 
-// constants for disk block size
-const (
-	//DefaultBlockSize represents the default block size (4KB)
-	DefaultBlockSize = 4096
-)
-
 // constants for fstypes
 const (
 	// FSTypeExt2 represents the ext2 filesystem type
diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go
index 544f8e3f..63471429 100644
--- a/pkg/driver/driver.go
+++ b/pkg/driver/driver.go
@@ -24,9 +24,12 @@ import (
 	"github.com/awslabs/volume-modifier-for-k8s/pkg/rpc"
 	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/cloud/metadata"
+	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/util"
 	"go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
 	"google.golang.org/grpc"
+	"k8s.io/client-go/kubernetes"
 	"k8s.io/klog/v2"
 )
 
@@ -56,12 +59,12 @@ const (
 
 type Driver struct {
 	controller *ControllerService
-	nodeService
-	srv     *grpc.Server
-	options *Options
+	node       *NodeService
+	srv        *grpc.Server
+	options    *Options
 }
 
-func NewDriver(c cloud.Cloud, o *Options) (*Driver, error) {
+func NewDriver(c cloud.Cloud, o *Options, m mounter.Mounter, md metadata.MetadataService, k kubernetes.Interface) (*Driver, error) {
 	klog.InfoS("Driver Information", "Driver", DriverName, "Version", driverVersion)
 
 	if err := ValidateDriverOptions(o); err != nil {
@@ -76,10 +79,10 @@ func NewDriver(c cloud.Cloud, o *Options) (*Driver, error) {
 	case ControllerMode:
 		driver.controller = NewControllerService(c, o)
 	case NodeMode:
-		driver.nodeService = newNodeService(o)
+		driver.node = NewNodeService(o, md, m, k)
 	case AllMode:
 		driver.controller = NewControllerService(c, o)
-		driver.nodeService = newNodeService(o)
+		driver.node = NewNodeService(o, md, m, k)
 	default:
 		return nil, fmt.Errorf("unknown mode: %s", o.Mode)
 	}
@@ -122,10 +125,10 @@ func (d *Driver) Run() error {
 		csi.RegisterControllerServer(d.srv, d.controller)
 		rpc.RegisterModifyServer(d.srv, d.controller)
 	case NodeMode:
-		csi.RegisterNodeServer(d.srv, d)
+		csi.RegisterNodeServer(d.srv, d.node)
 	case AllMode:
 		csi.RegisterControllerServer(d.srv, d.controller)
-		csi.RegisterNodeServer(d.srv, d)
+		csi.RegisterNodeServer(d.srv, d.node)
 		rpc.RegisterModifyServer(d.srv, d.controller)
 	default:
 		return fmt.Errorf("unknown mode: %s", d.options.Mode)
diff --git a/pkg/driver/mount_linux.go b/pkg/driver/mount_linux.go
deleted file mode 100644
index 62b49465..00000000
--- a/pkg/driver/mount_linux.go
+++ /dev/null
@@ -1,160 +0,0 @@
-//go:build linux
-// +build linux
-
-/*
-Copyright 2019 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package driver
-
-import (
-	"fmt"
-	"os"
-	"strconv"
-	"strings"
-
-	mountutils "k8s.io/mount-utils"
-)
-
-// GetDeviceNameFromMount returns the volume ID for a mount path.
-func (m NodeMounter) GetDeviceNameFromMount(mountPath string) (string, int, error) {
-	return mountutils.GetDeviceNameFromMount(m, mountPath)
-}
-
-// IsCorruptedMnt return true if err is about corrupted mount point
-func (m NodeMounter) IsCorruptedMnt(err error) bool {
-	return mountutils.IsCorruptedMnt(err)
-}
-
-// This function is mirrored in ./sanity_test.go to make sure sanity test covered this block of code
-// Please mirror the change to func MakeFile in ./sanity_test.go
-func (m *NodeMounter) MakeFile(path string) error {
-	f, err := os.OpenFile(path, os.O_CREATE, os.FileMode(0644))
-	if err != nil {
-		if !os.IsExist(err) {
-			return err
-		}
-	}
-	if err = f.Close(); err != nil {
-		return err
-	}
-	return nil
-}
-
-// This function is mirrored in ./sanity_test.go to make sure sanity test covered this block of code
-// Please mirror the change to func MakeFile in ./sanity_test.go
-func (m *NodeMounter) MakeDir(path string) error {
-	err := os.MkdirAll(path, os.FileMode(0755))
-	if err != nil {
-		if !os.IsExist(err) {
-			return err
-		}
-	}
-	return nil
-}
-
-// This function is mirrored in ./sanity_test.go to make sure sanity test covered this block of code
-// Please mirror the change to func MakeFile in ./sanity_test.go
-func (m *NodeMounter) PathExists(path string) (bool, error) {
-	return mountutils.PathExists(path)
-}
-
-func (m *NodeMounter) NeedResize(devicePath string, deviceMountPath string) (bool, error) {
-	return mountutils.NewResizeFs(m.Exec).NeedResize(devicePath, deviceMountPath)
-}
-
-func (m *NodeMounter) getExtSize(devicePath string) (uint64, uint64, error) {
-	output, err := m.SafeFormatAndMount.Exec.Command("dumpe2fs", "-h", devicePath).CombinedOutput()
-	if err != nil {
-		return 0, 0, fmt.Errorf("failed to read size of filesystem on %s: %w: %s", devicePath, err, string(output))
-	}
-
-	blockSize, blockCount, _ := m.parseFsInfoOutput(string(output), ":", "block size", "block count")
-
-	if blockSize == 0 {
-		return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath)
-	}
-	if blockCount == 0 {
-		return 0, 0, fmt.Errorf("could not find block count of device %s", devicePath)
-	}
-	return blockSize, blockSize * blockCount, nil
-}
-
-func (m *NodeMounter) getXFSSize(devicePath string) (uint64, uint64, error) {
-	output, err := m.SafeFormatAndMount.Exec.Command("xfs_io", "-c", "statfs", devicePath).CombinedOutput()
-	if err != nil {
-		return 0, 0, fmt.Errorf("failed to read size of filesystem on %s: %w: %s", devicePath, err, string(output))
-	}
-
-	blockSize, blockCount, _ := m.parseFsInfoOutput(string(output), "=", "geom.bsize", "geom.datablocks")
-
-	if blockSize == 0 {
-		return 0, 0, fmt.Errorf("could not find block size of device %s", devicePath)
-	}
-	if blockCount == 0 {
-		return 0, 0, fmt.Errorf("could not find block count of device %s", devicePath)
-	}
-	return blockSize, blockSize * blockCount, nil
-}
-
-func (m *NodeMounter) parseFsInfoOutput(cmdOutput string, spliter string, blockSizeKey string, blockCountKey string) (uint64, uint64, error) {
-	lines := strings.Split(cmdOutput, "\n")
-	var blockSize, blockCount uint64
-	var err error
-
-	for _, line := range lines {
-		tokens := strings.Split(line, spliter)
-		if len(tokens) != 2 {
-			continue
-		}
-		key, value := strings.ToLower(strings.TrimSpace(tokens[0])), strings.ToLower(strings.TrimSpace(tokens[1]))
-		if key == blockSizeKey {
-			blockSize, err = strconv.ParseUint(value, 10, 64)
-			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse block size %s: %w", value, err)
-			}
-		}
-		if key == blockCountKey {
-			blockCount, err = strconv.ParseUint(value, 10, 64)
-			if err != nil {
-				return 0, 0, fmt.Errorf("failed to parse block count %s: %w", value, err)
-			}
-		}
-	}
-	return blockSize, blockCount, err
-}
-
-func (m *NodeMounter) Unpublish(path string) error {
-	// On linux, unpublish and unstage both perform an unmount
-	return m.Unstage(path)
-}
-
-func (m *NodeMounter) Unstage(path string) error {
-	err := mountutils.CleanupMountPoint(path, m, false)
-	// Ignore the error when it contains "not mounted", because that indicates the
-	// world is already in the desired state
-	//
-	// mount-utils attempts to detect this on its own but fails when running on
-	// a read-only root filesystem, which our manifests use by default
-	if err == nil || strings.Contains(fmt.Sprint(err), "not mounted") {
-		return nil
-	} else {
-		return err
-	}
-}
-
-func (m *NodeMounter) NewResizeFs() (Resizefs, error) {
-	return mountutils.NewResizeFs(m.Exec), nil
-}
diff --git a/pkg/driver/node.go b/pkg/driver/node.go
index ff12ef3e..d9a3680f 100644
--- a/pkg/driver/node.go
+++ b/pkg/driver/node.go
@@ -30,6 +30,7 @@ import (
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/internal"
+	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/util"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
@@ -81,53 +82,35 @@ var (
 	}
 )
 
-// nodeService represents the node service of CSI driver
-type nodeService struct {
-	metadata         metadata.MetadataService
-	mounter          Mounter
-	deviceIdentifier DeviceIdentifier
-	inFlight         *internal.InFlight
-	options          *Options
+// NodeService represents the node service of CSI driver
+type NodeService struct {
+	metadata metadata.MetadataService
+	mounter  mounter.Mounter
+	inFlight *internal.InFlight
+	options  *Options
 }
 
-// newNodeService creates a new node service
-// it panics if failed to create the service
-func newNodeService(o *Options) nodeService {
+// NewNodeService creates a new node service
+func NewNodeService(o *Options, md metadata.MetadataService, m mounter.Mounter, k kubernetes.Interface) *NodeService {
 	klog.V(5).InfoS("[Debug] Retrieving node info from metadata service")
 	region := os.Getenv("AWS_REGION")
 	klog.InfoS("regionFromSession Node service", "region", region)
 
-	cfg := metadata.MetadataServiceConfig{
-		EC2MetadataClient: metadata.DefaultEC2MetadataClient,
-		K8sAPIClient:      metadata.DefaultKubernetesAPIClient,
-	}
-
-	metadata, err := metadata.NewMetadataService(cfg, region)
-	if err != nil {
-		panic(err)
-	}
-
-	nodeMounter, err := newNodeMounter()
-	if err != nil {
-		panic(err)
-	}
-
 	// Remove taint from node to indicate driver startup success
 	// This is done at the last possible moment to prevent race conditions or false positive removals
 	time.AfterFunc(taintRemovalInitialDelay, func() {
-		removeTaintInBackground(cfg.K8sAPIClient, removeNotReadyTaint)
+		removeTaintInBackground(k, taintRemovalBackoff, removeNotReadyTaint)
 	})
 
-	return nodeService{
-		metadata:         metadata,
-		mounter:          nodeMounter,
-		deviceIdentifier: newNodeDeviceIdentifier(),
-		inFlight:         internal.NewInFlight(),
-		options:          o,
+	return &NodeService{
+		metadata: md,
+		mounter:  m,
+		inFlight: internal.NewInFlight(),
+		options:  o,
 	}
 }
 
-func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
+func (d *NodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVolumeRequest) (*csi.NodeStageVolumeResponse, error) {
 	klog.V(4).InfoS("NodeStageVolume: called", "args", *req)
 
 	volumeID := req.GetVolumeId()
@@ -225,7 +208,7 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 		}
 	}
 
-	source, err := d.findDevicePath(devicePath, volumeID, partition)
+	source, err := d.mounter.FindDevicePath(devicePath, volumeID, partition, d.metadata.GetRegion())
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "Failed to find device path %s. %v", devicePath, err)
 	}
@@ -304,12 +287,8 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 	}
 
 	if needResize {
-		r, err := d.mounter.NewResizeFs()
-		if err != nil {
-			return nil, status.Errorf(codes.Internal, "Error attempting to create new ResizeFs:  %v", err)
-		}
 		klog.V(2).InfoS("Volume needs resizing", "source", source)
-		if _, err := r.Resize(source, target); err != nil {
+		if _, err := d.mounter.Resize(source, target); err != nil {
 			return nil, status.Errorf(codes.Internal, "Could not resize volume %q (%q):  %v", volumeID, source, err)
 		}
 	}
@@ -317,7 +296,7 @@ func (d *nodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 	return &csi.NodeStageVolumeResponse{}, nil
 }
 
-func (d *nodeService) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
+func (d *NodeService) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstageVolumeRequest) (*csi.NodeUnstageVolumeResponse, error) {
 	klog.V(4).InfoS("NodeUnstageVolume: called", "args", *req)
 	volumeID := req.GetVolumeId()
 	if len(volumeID) == 0 {
@@ -367,7 +346,7 @@ func (d *nodeService) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag
 	return &csi.NodeUnstageVolumeResponse{}, nil
 }
 
-func (d *nodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) {
+func (d *NodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) {
 	klog.V(4).InfoS("NodeExpandVolume: called", "args", *req)
 	volumeID := req.GetVolumeId()
 	if len(volumeID) == 0 {
@@ -394,13 +373,13 @@ func (d *nodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV
 	} else {
 		// TODO use util.GenericResizeFS
 		// VolumeCapability is nil, check if volumePath point to a block device
-		isBlock, err := d.IsBlockDevice(volumePath)
+		isBlock, err := d.mounter.IsBlockDevice(volumePath)
 		if err != nil {
 			return nil, status.Errorf(codes.Internal, "failed to determine if volumePath [%v] is a block device: %v", volumePath, err)
 		}
 		if isBlock {
 			// Skip resizing for Block NodeExpandVolume
-			bcap, err := d.getBlockSizeBytes(volumePath)
+			bcap, err := d.mounter.GetBlockSizeBytes(volumePath)
 			if err != nil {
 				return nil, status.Errorf(codes.Internal, "failed to get block capacity on path %s: %v", req.GetVolumePath(), err)
 			}
@@ -414,29 +393,24 @@ func (d *nodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV
 		return nil, status.Errorf(codes.Internal, "failed to get device name from mount %s: %v", volumePath, err)
 	}
 
-	devicePath, err := d.findDevicePath(deviceName, volumeID, "")
+	devicePath, err := d.mounter.FindDevicePath(deviceName, volumeID, "", d.metadata.GetRegion())
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "failed to find device path for device name %s for mount %s: %v", deviceName, req.GetVolumePath(), err)
 	}
 
-	r, err := d.mounter.NewResizeFs()
-	if err != nil {
-		return nil, status.Errorf(codes.Internal, "Error attempting to create new ResizeFs:  %v", err)
-	}
-
 	// TODO: lock per volume ID to have some idempotency
-	if _, err = r.Resize(devicePath, volumePath); err != nil {
-		return nil, status.Errorf(codes.Internal, "Could not resize volume %q (%q):  %v", volumeID, devicePath, err)
+	if _, err = d.mounter.Resize(devicePath, volumePath); err != nil {
+		return nil, status.Errorf(codes.Internal, "Could not resize volume %q (%q): %v", volumeID, devicePath, err)
 	}
 
-	bcap, err := d.getBlockSizeBytes(devicePath)
+	bcap, err := d.mounter.GetBlockSizeBytes(devicePath)
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "failed to get block capacity on path %s: %v", req.GetVolumePath(), err)
 	}
 	return &csi.NodeExpandVolumeResponse{CapacityBytes: bcap}, nil
 }
 
-func (d *nodeService) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
+func (d *NodeService) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolumeRequest) (*csi.NodePublishVolumeResponse, error) {
 	klog.V(4).InfoS("NodePublishVolume: called", "args", *req)
 	volumeID := req.GetVolumeId()
 	if len(volumeID) == 0 {
@@ -489,7 +463,7 @@ func (d *nodeService) NodePublishVolume(ctx context.Context, req *csi.NodePublis
 	return &csi.NodePublishVolumeResponse{}, nil
 }
 
-func (d *nodeService) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
+func (d *NodeService) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpublishVolumeRequest) (*csi.NodeUnpublishVolumeResponse, error) {
 	klog.V(4).InfoS("NodeUnpublishVolume: called", "args", *req)
 	volumeID := req.GetVolumeId()
 	if len(volumeID) == 0 {
@@ -500,9 +474,11 @@ func (d *nodeService) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu
 	if len(target) == 0 {
 		return nil, status.Error(codes.InvalidArgument, "Target path not provided")
 	}
+
 	if ok := d.inFlight.Insert(volumeID); !ok {
 		return nil, status.Errorf(codes.Aborted, VolumeOperationAlreadyExists, volumeID)
 	}
+
 	defer func() {
 		klog.V(4).InfoS("NodeUnPublishVolume: volume operation finished", "volumeId", volumeID)
 		d.inFlight.Delete(volumeID)
@@ -517,7 +493,7 @@ func (d *nodeService) NodeUnpublishVolume(ctx context.Context, req *csi.NodeUnpu
 	return &csi.NodeUnpublishVolumeResponse{}, nil
 }
 
-func (d *nodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
+func (d *NodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVolumeStatsRequest) (*csi.NodeGetVolumeStatsResponse, error) {
 	klog.V(4).InfoS("NodeGetVolumeStats: called", "args", *req)
 	if len(req.GetVolumeId()) == 0 {
 		return nil, status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty")
@@ -534,15 +510,15 @@ func (d *nodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVo
 		return nil, status.Errorf(codes.NotFound, "path %s does not exist", req.GetVolumePath())
 	}
 
-	isBlock, err := d.IsBlockDevice(req.GetVolumePath())
+	isBlock, err := d.mounter.IsBlockDevice(req.GetVolumePath())
 
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "failed to determine whether %s is block device: %v", req.GetVolumePath(), err)
 	}
 	if isBlock {
-		bcap, blockErr := d.getBlockSizeBytes(req.GetVolumePath())
+		bcap, blockErr := d.mounter.GetBlockSizeBytes(req.GetVolumePath())
 		if blockErr != nil {
-			return nil, status.Errorf(codes.Internal, "failed to get block capacity on path %s: %v", req.GetVolumePath(), err)
+			return nil, status.Errorf(codes.Internal, "failed to get block capacity on path %s: %v", req.GetVolumePath(), blockErr)
 		}
 		return &csi.NodeGetVolumeStatsResponse{
 			Usage: []*csi.VolumeUsage{
@@ -580,7 +556,7 @@ func (d *nodeService) NodeGetVolumeStats(ctx context.Context, req *csi.NodeGetVo
 
 }
 
-func (d *nodeService) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
+func (d *NodeService) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetCapabilitiesRequest) (*csi.NodeGetCapabilitiesResponse, error) {
 	klog.V(4).InfoS("NodeGetCapabilities: called", "args", *req)
 	var caps []*csi.NodeServiceCapability
 	for _, cap := range nodeCaps {
@@ -596,7 +572,7 @@ func (d *nodeService) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC
 	return &csi.NodeGetCapabilitiesResponse{Capabilities: caps}, nil
 }
 
-func (d *nodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
+func (d *NodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
 	klog.V(4).InfoS("NodeGetInfo: called", "args", *req)
 
 	zone := d.metadata.GetAvailabilityZone()
@@ -627,7 +603,7 @@ func (d *nodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoReque
 	}, nil
 }
 
-func (d *nodeService) nodePublishVolumeForBlock(req *csi.NodePublishVolumeRequest, mountOptions []string) error {
+func (d *NodeService) nodePublishVolumeForBlock(req *csi.NodePublishVolumeRequest, mountOptions []string) error {
 	target := req.GetTargetPath()
 	volumeID := req.GetVolumeId()
 	volumeContext := req.GetVolumeContext()
@@ -649,7 +625,7 @@ func (d *nodeService) nodePublishVolumeForBlock(req *csi.NodePublishVolumeReques
 		}
 	}
 
-	source, err := d.findDevicePath(devicePath, volumeID, partition)
+	source, err := d.mounter.FindDevicePath(devicePath, volumeID, partition, d.metadata.GetRegion())
 	if err != nil {
 		return status.Errorf(codes.Internal, "Failed to find device path %s. %v", devicePath, err)
 	}
@@ -703,7 +679,7 @@ func (d *nodeService) nodePublishVolumeForBlock(req *csi.NodePublishVolumeReques
 
 // isMounted checks if target is mounted. It does NOT return an error if target
 // doesn't exist.
-func (d *nodeService) isMounted(_ string, target string) (bool, error) {
+func (d *NodeService) isMounted(_ string, target string) (bool, error) {
 	/*
 		Checking if it's a mount point using IsLikelyNotMountPoint. There are three different return values,
 		1. true, err when the directory does not exist or corrupted.
@@ -743,7 +719,7 @@ func (d *nodeService) isMounted(_ string, target string) (bool, error) {
 	return !notMnt, nil
 }
 
-func (d *nodeService) nodePublishVolumeForFileSystem(req *csi.NodePublishVolumeRequest, mountOptions []string, mode *csi.VolumeCapability_Mount) error {
+func (d *NodeService) nodePublishVolumeForFileSystem(req *csi.NodePublishVolumeRequest, mountOptions []string, mode *csi.VolumeCapability_Mount) error {
 	target := req.GetTargetPath()
 	source := req.GetStagingTargetPath()
 	if m := mode.Mount; m != nil {
@@ -754,7 +730,7 @@ func (d *nodeService) nodePublishVolumeForFileSystem(req *csi.NodePublishVolumeR
 		}
 	}
 
-	if err := d.preparePublishTarget(target); err != nil {
+	if err := d.mounter.PreparePublishTarget(target); err != nil {
 		return status.Errorf(codes.Internal, err.Error())
 	}
 
@@ -786,11 +762,11 @@ func (d *nodeService) nodePublishVolumeForFileSystem(req *csi.NodePublishVolumeR
 }
 
 // getVolumesLimit returns the limit of volumes that the node supports
-func (d *nodeService) getVolumesLimit() int64 {
+func (d *NodeService) getVolumesLimit() int64 {
+
 	if d.options.VolumeAttachLimit >= 0 {
 		return d.options.VolumeAttachLimit
 	}
-
 	if util.IsSBE(d.metadata.GetRegion()) {
 		return sbeDeviceVolumeAttachmentLimit
 	}
@@ -875,8 +851,8 @@ type JSONPatch struct {
 }
 
 // removeTaintInBackground is a goroutine that retries removeNotReadyTaint with exponential backoff
-func removeTaintInBackground(k8sClient metadata.KubernetesAPIClient, removalFunc func(metadata.KubernetesAPIClient) error) {
-	backoffErr := wait.ExponentialBackoff(taintRemovalBackoff, func() (bool, error) {
+func removeTaintInBackground(k8sClient kubernetes.Interface, backoff wait.Backoff, removalFunc func(kubernetes.Interface) error) {
+	backoffErr := wait.ExponentialBackoff(backoff, func() (bool, error) {
 		err := removalFunc(k8sClient)
 		if err != nil {
 			klog.ErrorS(err, "Unexpected failure when attempting to remove node taint(s)")
@@ -893,19 +869,13 @@ func removeTaintInBackground(k8sClient metadata.KubernetesAPIClient, removalFunc
 // removeNotReadyTaint removes the taint ebs.csi.aws.com/agent-not-ready from the local node
 // This taint can be optionally applied by users to prevent startup race conditions such as
 // https://github.com/kubernetes/kubernetes/issues/95911
-func removeNotReadyTaint(k8sClient metadata.KubernetesAPIClient) error {
+func removeNotReadyTaint(clientset kubernetes.Interface) error {
 	nodeName := os.Getenv("CSI_NODE_NAME")
 	if nodeName == "" {
 		klog.V(4).InfoS("CSI_NODE_NAME missing, skipping taint removal")
 		return nil
 	}
 
-	clientset, err := k8sClient()
-	if err != nil {
-		klog.V(4).InfoS("Failed to setup k8s client")
-		return nil //lint:ignore nilerr If there are no k8s credentials, treat that as a soft failure
-	}
-
 	node, err := clientset.CoreV1().Nodes().Get(context.Background(), nodeName, metav1.GetOptions{})
 	if err != nil {
 		return err
@@ -980,7 +950,7 @@ func recheckFormattingOptionParameter(context map[string]string, key string, fsC
 	if ok {
 		// This check is already performed on the controller side
 		// However, because it is potentially security-sensitive, we redo it here to be safe
-		if isAlphanumeric := util.StringIsAlphanumeric(value); !isAlphanumeric {
+		if isAlphanumeric := util.StringIsAlphanumeric(v); !isAlphanumeric {
 			return "", status.Errorf(codes.InvalidArgument, "Invalid %s (aborting!): %v", key, err)
 		}
 
diff --git a/pkg/driver/node_windows.go b/pkg/driver/node_windows.go
deleted file mode 100644
index 9e31a8c6..00000000
--- a/pkg/driver/node_windows.go
+++ /dev/null
@@ -1,109 +0,0 @@
-//go:build windows
-// +build windows
-
-/*
-Copyright 2019 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package driver
-
-import (
-	"context"
-	"fmt"
-	"strconv"
-	"strings"
-
-	diskapi "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1"
-	diskclient "github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
-	"k8s.io/klog/v2"
-)
-
-// findDevicePath finds disk number of device
-// https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/ec2-windows-volumes.html#list-nvme-powershell
-func (d *nodeService) findDevicePath(devicePath, volumeID, _ string) (string, error) {
-	diskClient, err := diskclient.NewClient()
-	if err != nil {
-		return "", fmt.Errorf("error creating csi-proxy disk client: %q", err)
-	}
-	defer diskClient.Close()
-
-	response, err := diskClient.ListDiskIDs(context.TODO(), &diskapi.ListDiskIDsRequest{})
-	if err != nil {
-		return "", fmt.Errorf("error listing disk ids: %q", err)
-	}
-
-	diskIDs := response.GetDiskIDs()
-
-	foundDiskNumber := ""
-	for diskNumber, diskID := range diskIDs {
-		serialNumber := diskID.GetSerialNumber()
-		cleanVolumeID := strings.ReplaceAll(volumeID, "-", "")
-		if strings.Contains(serialNumber, cleanVolumeID) {
-			foundDiskNumber = strconv.Itoa(int(diskNumber))
-			break
-		}
-	}
-
-	if foundDiskNumber == "" {
-		return "", fmt.Errorf("disk number for device path %q volume id %q not found", devicePath, volumeID)
-	}
-
-	return foundDiskNumber, nil
-}
-
-func (d *nodeService) preparePublishTarget(target string) error {
-	// On Windows, Mount will create the parent of target and mklink (create a symbolic link) at target later, so don't create a
-	// directory at target now. Otherwise mklink will error: "Cannot create a file when that file already exists".
-	// Instead, delete the target if it already exists (like if it was created by kubelet <1.20)
-	// https://github.com/kubernetes/kubernetes/pull/88759
-	klog.V(4).InfoS("NodePublishVolume: removing dir", "target", target)
-	exists, err := d.mounter.PathExists(target)
-	if err != nil {
-		return fmt.Errorf("error checking path %q exists: %v", target, err)
-	}
-
-	proxyMounter, ok := (d.mounter.(*NodeMounter)).SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
-	if !ok {
-		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
-	}
-
-	if exists {
-		if err := proxyMounter.Rmdir(target); err != nil {
-			return fmt.Errorf("error Rmdir target %q: %v", target, err)
-		}
-	}
-	return nil
-}
-
-// IsBlockDevice checks if the given path is a block device
-func (d *nodeService) IsBlockDevice(fullPath string) (bool, error) {
-	return false, nil
-}
-
-// getBlockSizeBytes gets the size of the disk in bytes
-func (d *nodeService) getBlockSizeBytes(devicePath string) (int64, error) {
-	proxyMounter, ok := (d.mounter.(*NodeMounter)).SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
-	if !ok {
-		return -1, fmt.Errorf("failed to cast mounter to csi proxy mounter")
-	}
-
-	sizeInBytes, err := proxyMounter.GetDeviceSize(devicePath)
-	if err != nil {
-		return -1, err
-	}
-
-	return sizeInBytes, nil
-}
diff --git a/pkg/driver/mock_mount.go b/pkg/mounter/mock_mount.go
similarity index 79%
rename from pkg/driver/mock_mount.go
rename to pkg/mounter/mock_mount.go
index 22d62274..3048cd1d 100644
--- a/pkg/driver/mock_mount.go
+++ b/pkg/mounter/mock_mount.go
@@ -1,20 +1,18 @@
 // Code generated by MockGen. DO NOT EDIT.
-// Source: pkg/driver/mount.go
+// Source: pkg/mounter/mount.go
 
-// Package driver is a generated GoMock package.
-package driver
+// Package mounter is a generated GoMock package.
+package mounter
 
 import (
-	os "os"
 	reflect "reflect"
 
 	gomock "github.com/golang/mock/gomock"
-	mount_utils "k8s.io/mount-utils"
+	mount "k8s.io/mount-utils"
 )
 
 // MockMounter is a mock of Mounter interface.
 type MockMounter struct {
-	mount_utils.Interface
 	ctrl     *gomock.Controller
 	recorder *MockMounterMockRecorder
 }
@@ -50,6 +48,21 @@ func (mr *MockMounterMockRecorder) CanSafelySkipMountPointCheck() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CanSafelySkipMountPointCheck", reflect.TypeOf((*MockMounter)(nil).CanSafelySkipMountPointCheck))
 }
 
+// FindDevicePath mocks base method.
+func (m *MockMounter) FindDevicePath(devicePath, volumeID, partition, region string) (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "FindDevicePath", devicePath, volumeID, partition, region)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// FindDevicePath indicates an expected call of FindDevicePath.
+func (mr *MockMounterMockRecorder) FindDevicePath(devicePath, volumeID, partition, region interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FindDevicePath", reflect.TypeOf((*MockMounter)(nil).FindDevicePath), devicePath, volumeID, partition, region)
+}
+
 // FormatAndMountSensitiveWithFormatOptions mocks base method.
 func (m *MockMounter) FormatAndMountSensitiveWithFormatOptions(source, target, fstype string, options, sensitiveOptions, formatOptions []string) error {
 	m.ctrl.T.Helper()
@@ -64,6 +77,21 @@ func (mr *MockMounterMockRecorder) FormatAndMountSensitiveWithFormatOptions(sour
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FormatAndMountSensitiveWithFormatOptions", reflect.TypeOf((*MockMounter)(nil).FormatAndMountSensitiveWithFormatOptions), source, target, fstype, options, sensitiveOptions, formatOptions)
 }
 
+// GetBlockSizeBytes mocks base method.
+func (m *MockMounter) GetBlockSizeBytes(devicePath string) (int64, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetBlockSizeBytes", devicePath)
+	ret0, _ := ret[0].(int64)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetBlockSizeBytes indicates an expected call of GetBlockSizeBytes.
+func (mr *MockMounterMockRecorder) GetBlockSizeBytes(devicePath interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBlockSizeBytes", reflect.TypeOf((*MockMounter)(nil).GetBlockSizeBytes), devicePath)
+}
+
 // GetDeviceNameFromMount mocks base method.
 func (m *MockMounter) GetDeviceNameFromMount(mountPath string) (string, int, error) {
 	m.ctrl.T.Helper()
@@ -95,6 +123,21 @@ func (mr *MockMounterMockRecorder) GetMountRefs(pathname interface{}) *gomock.Ca
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMountRefs", reflect.TypeOf((*MockMounter)(nil).GetMountRefs), pathname)
 }
 
+// IsBlockDevice mocks base method.
+func (m *MockMounter) IsBlockDevice(fullPath string) (bool, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "IsBlockDevice", fullPath)
+	ret0, _ := ret[0].(bool)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// IsBlockDevice indicates an expected call of IsBlockDevice.
+func (mr *MockMounterMockRecorder) IsBlockDevice(fullPath interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsBlockDevice", reflect.TypeOf((*MockMounter)(nil).IsBlockDevice), fullPath)
+}
+
 // IsCorruptedMnt mocks base method.
 func (m *MockMounter) IsCorruptedMnt(err error) bool {
 	m.ctrl.T.Helper()
@@ -140,10 +183,10 @@ func (mr *MockMounterMockRecorder) IsMountPoint(file interface{}) *gomock.Call {
 }
 
 // List mocks base method.
-func (m *MockMounter) List() ([]mount_utils.MountPoint, error) {
+func (m *MockMounter) List() ([]mount.MountPoint, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "List")
-	ret0, _ := ret[0].([]mount_utils.MountPoint)
+	ret0, _ := ret[0].([]mount.MountPoint)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
@@ -253,34 +296,48 @@ func (mr *MockMounterMockRecorder) NeedResize(devicePath, deviceMountPath interf
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NeedResize", reflect.TypeOf((*MockMounter)(nil).NeedResize), devicePath, deviceMountPath)
 }
 
-// NewResizeFs mocks base method.
-func (m *MockMounter) NewResizeFs() (Resizefs, error) {
+// PathExists mocks base method.
+func (m *MockMounter) PathExists(path string) (bool, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "NewResizeFs")
-	ret0, _ := ret[0].(Resizefs)
+	ret := m.ctrl.Call(m, "PathExists", path)
+	ret0, _ := ret[0].(bool)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// NewResizeFs indicates an expected call of NewResizeFs.
-func (mr *MockMounterMockRecorder) NewResizeFs() *gomock.Call {
+// PathExists indicates an expected call of PathExists.
+func (mr *MockMounterMockRecorder) PathExists(path interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewResizeFs", reflect.TypeOf((*MockMounter)(nil).NewResizeFs))
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PathExists", reflect.TypeOf((*MockMounter)(nil).PathExists), path)
 }
 
-// PathExists mocks base method.
-func (m *MockMounter) PathExists(path string) (bool, error) {
+// PreparePublishTarget mocks base method.
+func (m *MockMounter) PreparePublishTarget(target string) error {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "PathExists", path)
+	ret := m.ctrl.Call(m, "PreparePublishTarget", target)
+	ret0, _ := ret[0].(error)
+	return ret0
+}
+
+// PreparePublishTarget indicates an expected call of PreparePublishTarget.
+func (mr *MockMounterMockRecorder) PreparePublishTarget(target interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PreparePublishTarget", reflect.TypeOf((*MockMounter)(nil).PreparePublishTarget), target)
+}
+
+// Resize mocks base method.
+func (m *MockMounter) Resize(devicePath, deviceMountPath string) (bool, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Resize", devicePath, deviceMountPath)
 	ret0, _ := ret[0].(bool)
 	ret1, _ := ret[1].(error)
 	return ret0, ret1
 }
 
-// PathExists indicates an expected call of PathExists.
-func (mr *MockMounterMockRecorder) PathExists(path interface{}) *gomock.Call {
+// Resize indicates an expected call of Resize.
+func (mr *MockMounterMockRecorder) Resize(devicePath, deviceMountPath interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PathExists", reflect.TypeOf((*MockMounter)(nil).PathExists), path)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resize", reflect.TypeOf((*MockMounter)(nil).Resize), devicePath, deviceMountPath)
 }
 
 // Unmount mocks base method.
@@ -324,94 +381,3 @@ func (mr *MockMounterMockRecorder) Unstage(path interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Unstage", reflect.TypeOf((*MockMounter)(nil).Unstage), path)
 }
-
-// MockResizefs is a mock of Resizefs interface.
-type MockResizefs struct {
-	ctrl     *gomock.Controller
-	recorder *MockResizefsMockRecorder
-}
-
-// MockResizefsMockRecorder is the mock recorder for MockResizefs.
-type MockResizefsMockRecorder struct {
-	mock *MockResizefs
-}
-
-// NewMockResizefs creates a new mock instance.
-func NewMockResizefs(ctrl *gomock.Controller) *MockResizefs {
-	mock := &MockResizefs{ctrl: ctrl}
-	mock.recorder = &MockResizefsMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockResizefs) EXPECT() *MockResizefsMockRecorder {
-	return m.recorder
-}
-
-// Resize mocks base method.
-func (m *MockResizefs) Resize(devicePath, deviceMountPath string) (bool, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Resize", devicePath, deviceMountPath)
-	ret0, _ := ret[0].(bool)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Resize indicates an expected call of Resize.
-func (mr *MockResizefsMockRecorder) Resize(devicePath, deviceMountPath interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Resize", reflect.TypeOf((*MockResizefs)(nil).Resize), devicePath, deviceMountPath)
-}
-
-// MockDeviceIdentifier is a mock of DeviceIdentifier interface.
-type MockDeviceIdentifier struct {
-	ctrl     *gomock.Controller
-	recorder *MockDeviceIdentifierMockRecorder
-}
-
-// MockDeviceIdentifierMockRecorder is the mock recorder for MockDeviceIdentifier.
-type MockDeviceIdentifierMockRecorder struct {
-	mock *MockDeviceIdentifier
-}
-
-// NewMockDeviceIdentifier creates a new mock instance.
-func NewMockDeviceIdentifier(ctrl *gomock.Controller) *MockDeviceIdentifier {
-	mock := &MockDeviceIdentifier{ctrl: ctrl}
-	mock.recorder = &MockDeviceIdentifierMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockDeviceIdentifier) EXPECT() *MockDeviceIdentifierMockRecorder {
-	return m.recorder
-}
-
-// EvalSymlinks mocks base method.
-func (m *MockDeviceIdentifier) EvalSymlinks(path string) (string, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "EvalSymlinks", path)
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// EvalSymlinks indicates an expected call of EvalSymlinks.
-func (mr *MockDeviceIdentifierMockRecorder) EvalSymlinks(path interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EvalSymlinks", reflect.TypeOf((*MockDeviceIdentifier)(nil).EvalSymlinks), path)
-}
-
-// Lstat mocks base method.
-func (m *MockDeviceIdentifier) Lstat(name string) (os.FileInfo, error) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "Lstat", name)
-	ret0, _ := ret[0].(os.FileInfo)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// Lstat indicates an expected call of Lstat.
-func (mr *MockDeviceIdentifierMockRecorder) Lstat(name interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Lstat", reflect.TypeOf((*MockDeviceIdentifier)(nil).Lstat), name)
-}
diff --git a/pkg/mounter/mock_mount_windows.go b/pkg/mounter/mock_mount_windows.go
index ec70a250..0c5ffe98 100644
--- a/pkg/mounter/mock_mount_windows.go
+++ b/pkg/mounter/mock_mount_windows.go
@@ -13,7 +13,6 @@ import (
 
 // MockProxyMounter is a mock of ProxyMounter interface.
 type MockProxyMounter struct {
-	mount.Interface
 	ctrl     *gomock.Controller
 	recorder *MockProxyMounterMockRecorder
 }
diff --git a/pkg/driver/mount.go b/pkg/mounter/mount.go
similarity index 62%
rename from pkg/driver/mount.go
rename to pkg/mounter/mount.go
index 5c44db5c..e4a69fd7 100644
--- a/pkg/driver/mount.go
+++ b/pkg/mounter/mount.go
@@ -14,13 +14,12 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package driver
+// Package mounter implements OS-specific functionality for interacting with mounts.
+//
+// The package should any implementation of mount related functionality that is not portable across platforms.
+package mounter
 
 import (
-	"os"
-	"path/filepath"
-
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
 	mountutils "k8s.io/mount-utils"
 )
 
@@ -41,11 +40,11 @@ type Mounter interface {
 	NeedResize(devicePath string, deviceMountPath string) (bool, error)
 	Unpublish(path string) error
 	Unstage(path string) error
-	NewResizeFs() (Resizefs, error)
-}
-
-type Resizefs interface {
 	Resize(devicePath, deviceMountPath string) (bool, error)
+	FindDevicePath(devicePath, volumeID, partition, region string) (string, error)
+	PreparePublishTarget(target string) error
+	IsBlockDevice(fullPath string) (bool, error)
+	GetBlockSizeBytes(devicePath string) (int64, error)
 }
 
 // NodeMounter implements Mounter.
@@ -54,35 +53,12 @@ type NodeMounter struct {
 	*mountutils.SafeFormatAndMount
 }
 
-func newNodeMounter() (Mounter, error) {
+// NewNodeMounter returns a new intsance of NodeMounter.
+func NewNodeMounter() (Mounter, error) {
 	// mounter.NewSafeMounter returns a SafeFormatAndMount
-	safeMounter, err := mounter.NewSafeMounter()
+	safeMounter, err := NewSafeMounter()
 	if err != nil {
 		return nil, err
 	}
 	return &NodeMounter{safeMounter}, nil
 }
-
-// DeviceIdentifier is for mocking os io functions used for the driver to
-// identify an EBS volume's corresponding device (in Linux, the path under
-// /dev; in Windows, the volume number) so that it can mount it. For volumes
-// already mounted, see GetDeviceNameFromMount.
-// https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/nvme-ebs-volumes.html#identify-nvme-ebs-device
-type DeviceIdentifier interface {
-	Lstat(name string) (os.FileInfo, error)
-	EvalSymlinks(path string) (string, error)
-}
-
-type nodeDeviceIdentifier struct{}
-
-func newNodeDeviceIdentifier() DeviceIdentifier {
-	return &nodeDeviceIdentifier{}
-}
-
-func (i *nodeDeviceIdentifier) Lstat(name string) (os.FileInfo, error) {
-	return os.Lstat(name)
-}
-
-func (i *nodeDeviceIdentifier) EvalSymlinks(path string) (string, error) {
-	return filepath.EvalSymlinks(path)
-}
diff --git a/pkg/driver/node_linux.go b/pkg/mounter/mount_linux.go
similarity index 61%
rename from pkg/driver/node_linux.go
rename to pkg/mounter/mount_linux.go
index 5d0981ee..e2b6c6b0 100644
--- a/pkg/driver/node_linux.go
+++ b/pkg/mounter/mount_linux.go
@@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package driver
+package mounter
 
 import (
 	"fmt"
@@ -31,24 +31,18 @@ import (
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/util"
 	"golang.org/x/sys/unix"
 	"k8s.io/klog/v2"
+	mountutils "k8s.io/mount-utils"
 )
 
-func (d *nodeService) appendPartition(devicePath, partition string) string {
-	if partition == "" {
-		return devicePath
-	}
-
-	if strings.HasPrefix(devicePath, "/dev/nvme") {
-		return devicePath + nvmeDiskPartitionSuffix + partition
-	}
-
-	return devicePath + diskPartitionSuffix + partition
-}
+const (
+	nvmeDiskPartitionSuffix = "p"
+	diskPartitionSuffix     = ""
+)
 
-// findDevicePath finds path of device and verifies its existence
+// 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, partition string) (string, error) {
+func (m *NodeMounter) FindDevicePath(devicePath, volumeID, partition, region string) (string, error) {
 	strippedVolumeName := strings.Replace(volumeID, "-", "", -1)
 	canonicalDevicePath := ""
 
@@ -58,19 +52,19 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st
 	// | File: ‘/dev/xvdba’ -> ‘nvme1n1’
 	// Since these are maybes, not guarantees, the search for the nvme device
 	// path below must happen and must rely on volume ID
-	exists, err := d.mounter.PathExists(devicePath)
+	exists, err := m.PathExists(devicePath)
 	if err != nil {
 		return "", fmt.Errorf("failed to check if path %q exists: %w", devicePath, err)
 	}
 
 	if exists {
-		stat, lstatErr := d.deviceIdentifier.Lstat(devicePath)
+		stat, lstatErr := os.Lstat(devicePath)
 		if lstatErr != nil {
 			return "", fmt.Errorf("failed to lstat %q: %w", devicePath, err)
 		}
 
 		if stat.Mode()&os.ModeSymlink == os.ModeSymlink {
-			canonicalDevicePath, err = d.deviceIdentifier.EvalSymlinks(devicePath)
+			canonicalDevicePath, err = filepath.EvalSymlinks(devicePath)
 			if err != nil {
 				return "", fmt.Errorf("failed to evaluate symlink %q: %w", devicePath, err)
 			}
@@ -82,7 +76,7 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st
 		if err = verifyVolumeSerialMatch(canonicalDevicePath, strippedVolumeName, execRunner); err != nil {
 			return "", err
 		}
-		return d.appendPartition(canonicalDevicePath, partition), nil
+		return m.appendPartition(canonicalDevicePath, partition), nil
 	}
 
 	klog.V(5).InfoS("[Debug] Falling back to nvme volume ID lookup", "devicePath", devicePath)
@@ -94,8 +88,7 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st
 	// vol-0fab1d5e3f72a5e23 creates a symlink at
 	// /dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol0fab1d5e3f72a5e23
 	nvmeName := "nvme-Amazon_Elastic_Block_Store_" + strippedVolumeName
-
-	nvmeDevicePath, err := findNvmeVolume(d.deviceIdentifier, nvmeName)
+	nvmeDevicePath, err := findNvmeVolume(nvmeName)
 
 	if err == nil {
 		klog.V(5).InfoS("[Debug] successfully resolved", "nvmeName", nvmeName, "nvmeDevicePath", nvmeDevicePath)
@@ -103,12 +96,12 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st
 		if err = verifyVolumeSerialMatch(canonicalDevicePath, strippedVolumeName, execRunner); err != nil {
 			return "", err
 		}
-		return d.appendPartition(canonicalDevicePath, partition), nil
+		return m.appendPartition(canonicalDevicePath, partition), nil
 	} else {
 		klog.V(5).InfoS("[Debug] error searching for nvme path", "nvmeName", nvmeName, "err", err)
 	}
 
-	if util.IsSBE(d.metadata.GetRegion()) {
+	if util.IsSBE(region) {
 		klog.V(5).InfoS("[Debug] Falling back to snow volume lookup", "devicePath", devicePath)
 		// Snow completely ignores the requested device path and mounts volumes starting at /dev/vda .. /dev/vdb .. etc
 		// Morph the device path to the snow form by chopping off the last letter and prefixing with /dev/vd
@@ -118,19 +111,51 @@ func (d *nodeService) findDevicePath(devicePath, volumeID, partition string) (st
 	}
 
 	if canonicalDevicePath == "" {
-		return "", errNoDevicePathFound(devicePath, volumeID)
+		return "", fmt.Errorf("no device path for device %q volume %q found", devicePath, volumeID)
 	}
 
-	canonicalDevicePath = d.appendPartition(canonicalDevicePath, partition)
+	canonicalDevicePath = m.appendPartition(canonicalDevicePath, partition)
 	return canonicalDevicePath, nil
 }
 
-// Helper to inject exec.Comamnd().CombinedOutput() for verifyVolumeSerialMatch
+// findNvmeVolume looks for the nvme volume with the specified name
+// It follows the symlink (if it exists) and returns the absolute path to the device
+func findNvmeVolume(findName string) (device string, err error) {
+	p := filepath.Join("/dev/disk/by-id/", findName)
+	stat, err := os.Lstat(p)
+	if err != nil {
+		if os.IsNotExist(err) {
+			klog.V(5).InfoS("[Debug] nvme path not found", "path", p)
+			return "", fmt.Errorf("nvme path %q not found", p)
+		}
+		return "", fmt.Errorf("error getting stat of %q: %w", p, err)
+	}
+
+	if stat.Mode()&os.ModeSymlink != os.ModeSymlink {
+		klog.InfoS("nvme file found, but was not a symlink", "path", p)
+		return "", fmt.Errorf("nvme file %q found, but was not a symlink", p)
+	}
+	// Find the target, resolving to an absolute path
+	// For example, /dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol0fab1d5e3f72a5e23 -> ../../nvme2n1
+	resolved, err := filepath.EvalSymlinks(p)
+	if err != nil {
+		return "", fmt.Errorf("error reading target of symlink %q: %w", p, err)
+	}
+
+	if !strings.HasPrefix(resolved, "/dev") {
+		return "", fmt.Errorf("resolved symlink for %q was unexpected: %q", p, resolved)
+	}
+
+	return resolved, nil
+}
+
+// execRunner is a helper to inject exec.Comamnd().CombinedOutput() for verifyVolumeSerialMatch
 // Tests use a mocked version that does not actually execute any binaries
 func execRunner(name string, arg ...string) ([]byte, error) {
 	return exec.Command(name, arg...).CombinedOutput()
 }
 
+// verifyVolumeSerialMatch checks the volume serial of the device against the expected volume
 func verifyVolumeSerialMatch(canonicalDevicePath string, strippedVolumeName string, execRunner func(string, ...string) ([]byte, error)) error {
 	// In some rare cases, a race condition can lead to the /dev/disk/by-id/ symlink becoming out of date
 	// See https://github.com/kubernetes-sigs/aws-ebs-csi-driver/issues/1224 for more info
@@ -145,7 +170,7 @@ func verifyVolumeSerialMatch(canonicalDevicePath string, strippedVolumeName stri
 		for _, volume := range volumeRegex.FindAllString(string(output), -1) {
 			klog.V(6).InfoS("Comparing volume serial", "canonicalDevicePath", canonicalDevicePath, "expected", strippedVolumeName, "actual", volume)
 			if volume != strippedVolumeName {
-				return fmt.Errorf("Refusing to mount %s because it claims to be %s but should be %s", canonicalDevicePath, volume, strippedVolumeName)
+				return fmt.Errorf("refusing to mount %s because it claims to be %s but should be %s", canonicalDevicePath, volume, strippedVolumeName)
 			}
 		}
 	} else {
@@ -156,51 +181,17 @@ func verifyVolumeSerialMatch(canonicalDevicePath string, strippedVolumeName stri
 	return nil
 }
 
-func errNoDevicePathFound(devicePath, volumeID string) error {
-	return fmt.Errorf("no device path for device %q volume %q found", devicePath, volumeID)
-}
-
-// findNvmeVolume looks for the nvme volume with the specified name
-// It follows the symlink (if it exists) and returns the absolute path to the device
-func findNvmeVolume(deviceIdentifier DeviceIdentifier, findName string) (device string, err error) {
-	p := filepath.Join("/dev/disk/by-id/", findName)
-	stat, err := deviceIdentifier.Lstat(p)
-	if err != nil {
-		if os.IsNotExist(err) {
-			klog.V(5).InfoS("[Debug] nvme path not found", "path", p)
-			return "", fmt.Errorf("nvme path %q not found", p)
-		}
-		return "", fmt.Errorf("error getting stat of %q: %w", p, err)
-	}
-
-	if stat.Mode()&os.ModeSymlink != os.ModeSymlink {
-		klog.InfoS("nvme file found, but was not a symlink", "path", p)
-		return "", fmt.Errorf("nvme file %q found, but was not a symlink", p)
-	}
-	// Find the target, resolving to an absolute path
-	// For example, /dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_vol0fab1d5e3f72a5e23 -> ../../nvme2n1
-	resolved, err := deviceIdentifier.EvalSymlinks(p)
-	if err != nil {
-		return "", fmt.Errorf("error reading target of symlink %q: %w", p, err)
-	}
-
-	if !strings.HasPrefix(resolved, "/dev") {
-		return "", fmt.Errorf("resolved symlink for %q was unexpected: %q", p, resolved)
-	}
-
-	return resolved, nil
-}
-
-func (d *nodeService) preparePublishTarget(target string) error {
+// PreparePublishTarget creates the target directory for the volume to be mounted
+func (m *NodeMounter) PreparePublishTarget(target string) error {
 	klog.V(4).InfoS("NodePublishVolume: creating dir", "target", target)
-	if err := d.mounter.MakeDir(target); err != nil {
-		return fmt.Errorf("Could not create dir %q: %w", target, err)
+	if err := m.MakeDir(target); err != nil {
+		return fmt.Errorf("could not create dir %q: %w", target, err)
 	}
 	return nil
 }
 
-// IsBlock checks if the given path is a block device
-func (d *nodeService) IsBlockDevice(fullPath string) (bool, error) {
+// IsBlockDevice checks if the given path is a block device
+func (m *NodeMounter) IsBlockDevice(fullPath string) (bool, error) {
 	var st unix.Stat_t
 	err := unix.Stat(fullPath, &st)
 	if err != nil {
@@ -210,9 +201,9 @@ func (d *nodeService) IsBlockDevice(fullPath string) (bool, error) {
 	return (st.Mode & unix.S_IFMT) == unix.S_IFBLK, nil
 }
 
-func (d *nodeService) getBlockSizeBytes(devicePath string) (int64, error) {
-	cmd := d.mounter.(*NodeMounter).Exec.Command("blockdev", "--getsize64", devicePath)
-	output, err := cmd.Output()
+// GetBlockSizeBytes gets the size of the disk in bytes
+func (m *NodeMounter) GetBlockSizeBytes(devicePath string) (int64, error) {
+	output, err := m.Exec.Command("blockdev", "--getsize64", devicePath).Output()
 	if err != nil {
 		return -1, fmt.Errorf("error when getting size of block volume at path %s: output: %s, err: %w", devicePath, string(output), err)
 	}
@@ -223,3 +214,90 @@ func (d *nodeService) getBlockSizeBytes(devicePath string) (int64, error) {
 	}
 	return gotSizeBytes, nil
 }
+
+// appendPartition appends the partition to the device path
+func (m *NodeMounter) appendPartition(devicePath, partition string) string {
+	if partition == "" {
+		return devicePath
+	}
+
+	if strings.HasPrefix(devicePath, "/dev/nvme") {
+		return devicePath + nvmeDiskPartitionSuffix + partition
+	}
+
+	return devicePath + diskPartitionSuffix + partition
+}
+
+// GetDeviceNameFromMount returns the volume ID for a mount path.
+func (m NodeMounter) GetDeviceNameFromMount(mountPath string) (string, int, error) {
+	return mountutils.GetDeviceNameFromMount(m, mountPath)
+}
+
+// IsCorruptedMnt return true if err is about corrupted mount point
+func (m NodeMounter) IsCorruptedMnt(err error) bool {
+	return mountutils.IsCorruptedMnt(err)
+}
+
+// This function is mirrored in ./sanity_test.go to make sure sanity test covered this block of code
+// Please mirror the change to func MakeFile in ./sanity_test.go
+func (m *NodeMounter) MakeFile(path string) error {
+	f, err := os.OpenFile(path, os.O_CREATE, os.FileMode(0644))
+	if err != nil {
+		if !os.IsExist(err) {
+			return err
+		}
+	}
+	if err = f.Close(); err != nil {
+		return err
+	}
+	return nil
+}
+
+// This function is mirrored in ./sanity_test.go to make sure sanity test covered this block of code
+// Please mirror the change to func MakeFile in ./sanity_test.go
+func (m *NodeMounter) MakeDir(path string) error {
+	err := os.MkdirAll(path, os.FileMode(0755))
+	if err != nil {
+		if !os.IsExist(err) {
+			return err
+		}
+	}
+	return nil
+}
+
+// This function is mirrored in ./sanity_test.go to make sure sanity test covered this block of code
+// Please mirror the change to func MakeFile in ./sanity_test.go
+func (m *NodeMounter) PathExists(path string) (bool, error) {
+	return mountutils.PathExists(path)
+}
+
+// Resize resizes the filesystem of the given devicePath
+func (m *NodeMounter) Resize(devicePath, deviceMountPath string) (bool, error) {
+	return mountutils.NewResizeFs(m.Exec).Resize(devicePath, deviceMountPath)
+}
+
+// NeedResize checks if the filesystem of the given devicePath needs to be resized
+func (m *NodeMounter) NeedResize(devicePath string, deviceMountPath string) (bool, error) {
+	return mountutils.NewResizeFs(m.Exec).NeedResize(devicePath, deviceMountPath)
+}
+
+// Unpublish unmounts the given path
+func (m *NodeMounter) Unpublish(path string) error {
+	// On linux, unpublish and unstage both perform an unmount
+	return m.Unstage(path)
+}
+
+// Unstage unmounts the given path
+func (m *NodeMounter) Unstage(path string) error {
+	err := mountutils.CleanupMountPoint(path, m, false)
+	// Ignore the error when it contains "not mounted", because that indicates the
+	// world is already in the desired state
+	//
+	// mount-utils attempts to detect this on its own but fails when running on
+	// a read-only root filesystem, which our manifests use by default
+	if err == nil || strings.Contains(fmt.Sprint(err), "not mounted") {
+		return nil
+	} else {
+		return err
+	}
+}
diff --git a/pkg/driver/mount_windows.go b/pkg/mounter/mount_windows.go
similarity index 56%
rename from pkg/driver/mount_windows.go
rename to pkg/mounter/mount_windows.go
index 8824f3db..15ceb1f6 100644
--- a/pkg/driver/mount_windows.go
+++ b/pkg/mounter/mount_windows.go
@@ -17,18 +17,102 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package driver
+package mounter
 
 import (
+	"context"
 	"fmt"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/resizefs"
-	mountutils "k8s.io/mount-utils"
 	"regexp"
+	"strconv"
+	"strings"
+
+	diskapi "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1"
+	diskclient "github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1"
+	"k8s.io/klog/v2"
+	mountutils "k8s.io/mount-utils"
 )
 
+const (
+	DefaultBlockSize = 4096
+)
+
+func (m NodeMounter) FindDevicePath(devicePath, volumeID, _, _ string) (string, error) {
+	diskClient, err := diskclient.NewClient()
+	if err != nil {
+		return "", fmt.Errorf("error creating csi-proxy disk client: %q", err)
+	}
+	defer diskClient.Close()
+
+	response, err := diskClient.ListDiskIDs(context.TODO(), &diskapi.ListDiskIDsRequest{})
+	if err != nil {
+		return "", fmt.Errorf("error listing disk ids: %q", err)
+	}
+
+	diskIDs := response.GetDiskIDs()
+
+	foundDiskNumber := ""
+	for diskNumber, diskID := range diskIDs {
+		serialNumber := diskID.GetSerialNumber()
+		cleanVolumeID := strings.ReplaceAll(volumeID, "-", "")
+		if strings.Contains(serialNumber, cleanVolumeID) {
+			foundDiskNumber = strconv.Itoa(int(diskNumber))
+			break
+		}
+	}
+
+	if foundDiskNumber == "" {
+		return "", fmt.Errorf("disk number for device path %q volume id %q not found", devicePath, volumeID)
+	}
+
+	return foundDiskNumber, nil
+}
+
+func (m NodeMounter) PreparePublishTarget(target string) error {
+	// On Windows, Mount will create the parent of target and mklink (create a symbolic link) at target later, so don't create a
+	// directory at target now. Otherwise mklink will error: "Cannot create a file when that file already exists".
+	// Instead, delete the target if it already exists (like if it was created by kubelet <1.20)
+	// https://github.com/kubernetes/kubernetes/pull/88759
+	klog.V(4).InfoS("NodePublishVolume: removing dir", "target", target)
+	exists, err := m.PathExists(target)
+	if err != nil {
+		return fmt.Errorf("error checking path %q exists: %v", target, err)
+	}
+
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
+	if !ok {
+		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
+	}
+
+	if exists {
+		if err := proxyMounter.Rmdir(target); err != nil {
+			return fmt.Errorf("error Rmdir target %q: %v", target, err)
+		}
+	}
+	return nil
+}
+
+// IsBlockDevice checks if the given path is a block device
+func (m NodeMounter) IsBlockDevice(fullPath string) (bool, error) {
+	return false, nil
+}
+
+// getBlockSizeBytes gets the size of the disk in bytes
+func (m NodeMounter) GetBlockSizeBytes(devicePath string) (int64, error) {
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
+	if !ok {
+		return -1, fmt.Errorf("failed to cast mounter to csi proxy mounter")
+	}
+
+	sizeInBytes, err := proxyMounter.GetDeviceSize(devicePath)
+	if err != nil {
+		return -1, err
+	}
+
+	return sizeInBytes, nil
+}
+
 func (m NodeMounter) FormatAndMountSensitiveWithFormatOptions(source string, target string, fstype string, options []string, sensitiveOptions []string, formatOptions []string) error {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -44,7 +128,7 @@ func (m NodeMounter) FormatAndMountSensitiveWithFormatOptions(source string, tar
 // Command to determine ref count would be something like:
 // Get-Volume -UniqueId "\\?\Volume{7c3da0c1-0000-0000-0000-010000000000}\" | Get-Partition | Select AccessPaths
 func (m NodeMounter) GetDeviceNameFromMount(mountPath string) (string, int, error) {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return "", 0, fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -76,7 +160,7 @@ func (m NodeMounter) IsCorruptedMnt(err error) bool {
 }
 
 func (m *NodeMounter) MakeFile(path string) error {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -84,7 +168,7 @@ func (m *NodeMounter) MakeFile(path string) error {
 }
 
 func (m *NodeMounter) MakeDir(path string) error {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -92,16 +176,24 @@ func (m *NodeMounter) MakeDir(path string) error {
 }
 
 func (m *NodeMounter) PathExists(path string) (bool, error) {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return false, fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
 	return proxyMounter.ExistsPath(path)
 }
 
+func (m *NodeMounter) Resize(devicePath, deviceMountPath string) (bool, error) {
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
+	if !ok {
+		return false, fmt.Errorf("failed to cast mounter to csi proxy mounter")
+	}
+	return proxyMounter.ResizeVolume(deviceMountPath)
+}
+
 // NeedResize called at NodeStage to ensure file system is the correct size
 func (m *NodeMounter) NeedResize(devicePath, deviceMountPath string) (bool, error) {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return false, fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -124,7 +216,7 @@ func (m *NodeMounter) NeedResize(devicePath, deviceMountPath string) (bool, erro
 
 // Unmount volume from target path
 func (m *NodeMounter) Unpublish(target string) error {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -139,7 +231,7 @@ func (m *NodeMounter) Unpublish(target string) error {
 // Unmount volume from staging path
 // usually this staging path is a global directory on the node
 func (m *NodeMounter) Unstage(target string) error {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
+	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*CSIProxyMounter)
 	if !ok {
 		return fmt.Errorf("failed to cast mounter to csi proxy mounter")
 	}
@@ -150,11 +242,3 @@ func (m *NodeMounter) Unstage(target string) error {
 	}
 	return nil
 }
-
-func (m *NodeMounter) NewResizeFs() (Resizefs, error) {
-	proxyMounter, ok := m.SafeFormatAndMount.Interface.(*mounter.CSIProxyMounter)
-	if !ok {
-		return nil, fmt.Errorf("failed to cast mounter to csi proxy mounter")
-	}
-	return resizefs.NewResizeFs(proxyMounter), nil
-}
diff --git a/pkg/mounter/safe_mounter_unix.go b/pkg/mounter/safe_mounter.go
similarity index 100%
rename from pkg/mounter/safe_mounter_unix.go
rename to pkg/mounter/safe_mounter.go
diff --git a/pkg/mounter/safe_mounter_unix_test.go b/pkg/mounter/safe_mounter_test.go
similarity index 100%
rename from pkg/mounter/safe_mounter_unix_test.go
rename to pkg/mounter/safe_mounter_test.go
diff --git a/pkg/resizefs/resizefs_windows.go b/pkg/resizefs/resizefs_windows.go
deleted file mode 100644
index 4a8c0638..00000000
--- a/pkg/resizefs/resizefs_windows.go
+++ /dev/null
@@ -1,25 +0,0 @@
-//go:build windows
-// +build windows
-
-package resizefs
-
-import (
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
-	"k8s.io/klog/v2"
-)
-
-// resizeFs provides support for resizing file systems
-type resizeFs struct {
-	proxy mounter.ProxyMounter
-}
-
-// NewResizeFs returns an instance of resizeFs
-func NewResizeFs(p mounter.ProxyMounter) *resizeFs {
-	return &resizeFs{proxy: p}
-}
-
-// Resize performs resize of file system
-func (r *resizeFs) Resize(_, deviceMountPath string) (bool, error) {
-	klog.V(3).InfoS("Resize - Expanding mounted volume", "deviceMountPath", deviceMountPath)
-	return r.proxy.ResizeVolume(deviceMountPath)
-}


Test patch:
diff --git a/pkg/cloud/cloud_test.go b/pkg/cloud/cloud_test.go
index 48f11f42..125bd31a 100644
--- a/pkg/cloud/cloud_test.go
+++ b/pkg/cloud/cloud_test.go
@@ -667,6 +667,7 @@ func TestCreateDisk(t *testing.T) {
 		volState             string
 		diskOptions          *DiskOptions
 		expDisk              *Disk
+		cleanUpFailedVolume  bool
 		expErr               error
 		expCreateVolumeErr   error
 		expDescVolumeErr     error
@@ -918,8 +919,9 @@ func TestCreateDisk(t *testing.T) {
 				AvailabilityZone: "",
 			},
 			expCreateVolumeInput: &ec2.CreateVolumeInput{},
-			expErr:               fmt.Errorf("timed out waiting for volume to create: DescribeVolumes generic error"),
+			expErr:               fmt.Errorf("failed to get an available volume in EC2: DescribeVolumes generic error"),
 			expDescVolumeErr:     fmt.Errorf("DescribeVolumes generic error"),
+			cleanUpFailedVolume:  true,
 		},
 		{
 			name:       "fail: Volume is not ready to use, volume stuck in creating status and controller context deadline exceeded",
@@ -930,8 +932,9 @@ func TestCreateDisk(t *testing.T) {
 				Tags:             map[string]string{VolumeNameTagKey: "vol-test", AwsEbsDriverTagKey: "true"},
 				AvailabilityZone: "",
 			},
+			cleanUpFailedVolume:  true,
 			expCreateVolumeInput: &ec2.CreateVolumeInput{},
-			expErr:               fmt.Errorf("timed out waiting for volume to create: context deadline exceeded"),
+			expErr:               fmt.Errorf("failed to get an available volume in EC2: context deadline exceeded"),
 		},
 		{
 			name:       "success: normal from snapshot",
@@ -1239,6 +1242,9 @@ func TestCreateDisk(t *testing.T) {
 				if len(tc.diskOptions.SnapshotID) > 0 {
 					mockEC2.EXPECT().DescribeSnapshots(gomock.Any(), gomock.Any()).Return(&ec2.DescribeSnapshotsOutput{Snapshots: []types.Snapshot{snapshot}}, nil).AnyTimes()
 				}
+				if tc.cleanUpFailedVolume == true {
+					mockEC2.EXPECT().DeleteVolume(gomock.Any(), gomock.Any()).Return(&ec2.DeleteVolumeOutput{}, nil)
+				}
 				if len(tc.diskOptions.AvailabilityZone) == 0 {
 					mockEC2.EXPECT().DescribeAvailabilityZones(gomock.Any(), gomock.Any()).Return(&ec2.DescribeAvailabilityZonesOutput{
 						AvailabilityZones: []types.AvailabilityZone{
diff --git a/pkg/driver/mount_test.go b/pkg/driver/mount_test.go
deleted file mode 100644
index 1dc5de8c..00000000
--- a/pkg/driver/mount_test.go
+++ /dev/null
@@ -1,420 +0,0 @@
-//go:build linux
-// +build linux
-
-/*
-Copyright 2020 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package driver
-
-import (
-	"os"
-	"path/filepath"
-	"testing"
-
-	"k8s.io/mount-utils"
-
-	utilexec "k8s.io/utils/exec"
-	fakeexec "k8s.io/utils/exec/testing"
-)
-
-func TestGetFileSystemSize(t *testing.T) {
-	cmdOutputSuccessXfs :=
-		`
-	statfs.f_bsize = 4096
-	statfs.f_blocks = 1832448
-	statfs.f_bavail = 1822366
-	statfs.f_files = 3670016
-	statfs.f_ffree = 3670012
-	statfs.f_flags = 0x1020
-	geom.bsize = 4096
-	geom.agcount = 4
-	geom.agblocks = 458752
-	geom.datablocks = 1835008
-	geom.rtblocks = 0
-	geom.rtextents = 0
-	geom.rtextsize = 1
-	geom.sunit = 0
-	geom.swidth = 0
-	counts.freedata = 1822372
-	counts.freertx = 0
-	counts.freeino = 61
-	counts.allocino = 64
-`
-	cmdOutputNoDataXfs :=
-		`
-	statfs.f_bsize = 4096
-	statfs.f_blocks = 1832448
-	statfs.f_bavail = 1822366
-	statfs.f_files = 3670016
-	statfs.f_ffree = 3670012
-	statfs.f_flags = 0x1020
-	geom.agcount = 4
-	geom.agblocks = 458752
-	geom.rtblocks = 0
-	geom.rtextents = 0
-	geom.rtextsize = 1
-	geom.sunit = 0
-	geom.swidth = 0
-	counts.freedata = 1822372
-	counts.freertx = 0
-	counts.freeino = 61
-	counts.allocino = 64
-`
-	cmdOutputSuccessExt4 :=
-		`
-Filesystem volume name:   cloudimg-rootfs
-Last mounted on:          /
-Filesystem UUID:          testUUID
-Filesystem magic number:  0xEF53
-Filesystem revision #:    1 (dynamic)
-Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit
-Default mount options:    user_xattr acl
-Filesystem state:         clean
-Errors behavior:          Continue
-Filesystem OS type:       Linux
-Inode count:              3840000
-Block count:              5242880
-Reserved block count:     0
-Free blocks:              5514413
-Free inodes:              3677492
-First block:              0
-Block size:               4096
-Fragment size:            4096
-Group descriptor size:    64
-Reserved GDT blocks:      252
-Blocks per group:         32768
-Fragments per group:      32768
-Inodes per group:         16000
-Inode blocks per group:   1000
-Flex block group size:    16
-Mount count:              2
-Maximum mount count:      -1
-Check interval:           0 (<none>)
-Lifetime writes:          180 GB
-Reserved blocks uid:      0 (user root)
-Reserved blocks gid:      0 (group root)
-First inode:              11
-Inode size:	              256
-Required extra isize:     32
-Desired extra isize:      32
-Journal inode:            8
-Default directory hash:   half_md4
-Directory Hash Seed:      Test Hashing
-Journal backup:           inode blocks
-Checksum type:            crc32c
-Checksum:                 0x57705f62
-Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3
-Journal size:             64M
-Journal length:           16384
-Journal sequence:         0x00037109
-Journal start:            1
-Journal checksum type:    crc32c
-Journal checksum:         0xb7df3c6e
-`
-	cmdOutputNoDataExt4 :=
-		`Filesystem volume name:   cloudimg-rootfs
-Last mounted on:          /
-Filesystem UUID:          testUUID
-Filesystem magic number:  0xEF53
-Filesystem revision #:    1 (dynamic)
-Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit
-Default mount options:    user_xattr acl
-Filesystem state:         clean
-Errors behavior:          Continue
-Filesystem OS type:       Linux
-Inode count:              3840000
-Reserved block count:     0
-Free blocks:              5514413
-Free inodes:              3677492
-First block:              0
-Fragment size:            4096
-Group descriptor size:    64
-Reserved GDT blocks:      252
-Blocks per group:         32768
-Fragments per group:      32768
-Inodes per group:         16000
-Inode blocks per group:   1000
-Flex block group size:    16
-Mount count:              2
-Maximum mount count:      -1
-Check interval:           0 (<none>)
-Lifetime writes:          180 GB
-Reserved blocks uid:      0 (user root)
-Reserved blocks gid:      0 (group root)
-First inode:              11
-Inode size:	              256
-Required extra isize:     32
-Desired extra isize:      32
-Journal inode:            8
-Default directory hash:   half_md4
-Directory Hash Seed:      Test Hashing
-Journal backup:           inode blocks
-Checksum type:            crc32c
-Checksum:                 0x57705f62
-Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3
-Journal size:             64M
-Journal length:           16384
-Journal sequence:         0x00037109
-Journal start:            1
-Journal checksum type:    crc32c
-Journal checksum:         0xb7df3c6e
-`
-	testcases := []struct {
-		name        string
-		devicePath  string
-		blocksize   uint64
-		blockCount  uint64
-		cmdOutput   string
-		expectError bool
-		fsType      string
-	}{
-		{
-			name:        "success parse xfs info",
-			devicePath:  "/dev/test1",
-			blocksize:   4096,
-			blockCount:  1835008,
-			cmdOutput:   cmdOutputSuccessXfs,
-			expectError: false,
-			fsType:      "xfs",
-		},
-		{
-			name:        "block size not present - xfs",
-			devicePath:  "/dev/test1",
-			blocksize:   0,
-			blockCount:  0,
-			cmdOutput:   cmdOutputNoDataXfs,
-			expectError: true,
-			fsType:      "xfs",
-		},
-		{
-			name:        "success parse ext info",
-			devicePath:  "/dev/test1",
-			blocksize:   4096,
-			blockCount:  5242880,
-			cmdOutput:   cmdOutputSuccessExt4,
-			expectError: false,
-			fsType:      "ext4",
-		},
-		{
-			name:        "block size not present - ext4",
-			devicePath:  "/dev/test1",
-			blocksize:   0,
-			blockCount:  0,
-			cmdOutput:   cmdOutputNoDataExt4,
-			expectError: true,
-			fsType:      "ext4",
-		},
-	}
-
-	for _, test := range testcases {
-		t.Run(test.name, func(t *testing.T) {
-			fcmd := fakeexec.FakeCmd{
-				CombinedOutputScript: []fakeexec.FakeAction{
-					func() ([]byte, []byte, error) { return []byte(test.cmdOutput), nil, nil },
-				},
-			}
-			fexec := fakeexec.FakeExec{
-				CommandScript: []fakeexec.FakeCommandAction{
-					func(cmd string, args ...string) utilexec.Cmd {
-						return fakeexec.InitFakeCmd(&fcmd, cmd, args...)
-					},
-				},
-			}
-			safe := mount.SafeFormatAndMount{
-				Interface: mount.New(""),
-				Exec:      &fexec,
-			}
-			fakeMounter := NodeMounter{&safe}
-
-			var blockSize uint64
-			var fsSize uint64
-			var err error
-			switch test.fsType {
-			case "xfs":
-				blockSize, fsSize, err = fakeMounter.getXFSSize(test.devicePath)
-			case "ext4":
-				blockSize, fsSize, err = fakeMounter.getExtSize(test.devicePath)
-			}
-
-			if blockSize != test.blocksize {
-				t.Fatalf("Parse wrong block size value, expect %d, but got %d", test.blocksize, blockSize)
-			}
-			if fsSize != test.blocksize*test.blockCount {
-				t.Fatalf("Parse wrong fs size value, expect %d, but got %d", test.blocksize*test.blockCount, fsSize)
-			}
-			if !test.expectError && err != nil {
-				t.Fatalf("Expect no error but got %v", err)
-			}
-		})
-	}
-}
-
-func TestNeedResize(t *testing.T) {
-	testcases := []struct {
-		name            string
-		devicePath      string
-		deviceMountPath string
-		deviceSize      string
-		cmdOutputFsType string
-		expectError     bool
-		expectResult    bool
-	}{
-		{
-			name:            "False - Unsupported fs type",
-			devicePath:      "/dev/test1",
-			deviceMountPath: "/mnt/test1",
-			deviceSize:      "2048",
-			cmdOutputFsType: "TYPE=ntfs",
-			expectError:     true,
-			expectResult:    false,
-		},
-	}
-
-	for _, test := range testcases {
-		t.Run(test.name, func(t *testing.T) {
-			fcmd := fakeexec.FakeCmd{
-				CombinedOutputScript: []fakeexec.FakeAction{
-					func() ([]byte, []byte, error) { return []byte(test.deviceSize), nil, nil },
-					func() ([]byte, []byte, error) { return []byte(test.cmdOutputFsType), nil, nil },
-				},
-			}
-			fexec := fakeexec.FakeExec{
-				CommandScript: []fakeexec.FakeCommandAction{
-					func(cmd string, args ...string) utilexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
-					func(cmd string, args ...string) utilexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
-				},
-			}
-			safe := mount.SafeFormatAndMount{
-				Interface: mount.New(""),
-				Exec:      &fexec,
-			}
-			fakeMounter := NodeMounter{&safe}
-
-			needResize, err := fakeMounter.NeedResize(test.devicePath, test.deviceMountPath)
-			if needResize != test.expectResult {
-				t.Fatalf("Expect result is %v but got %v", test.expectResult, needResize)
-			}
-			if !test.expectError && err != nil {
-				t.Fatalf("Expect no error but got %v", err)
-			}
-		})
-	}
-}
-
-func TestMakeDir(t *testing.T) {
-	// Setup the full driver and its environment
-	dir, err := os.MkdirTemp("", "mount-ebs-csi")
-	if err != nil {
-		t.Fatalf("error creating directory %v", err)
-	}
-	defer os.RemoveAll(dir)
-
-	targetPath := filepath.Join(dir, "targetdir")
-
-	mountObj, err := newNodeMounter()
-	if err != nil {
-		t.Fatalf("error creating mounter %v", err)
-	}
-
-	if mountObj.MakeDir(targetPath) != nil {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-	if mountObj.MakeDir(targetPath) != nil {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-	if exists, err := mountObj.PathExists(targetPath); !exists {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-}
-
-func TestMakeFile(t *testing.T) {
-	// Setup the full driver and its environment
-	dir, err := os.MkdirTemp("", "mount-ebs-csi")
-	if err != nil {
-		t.Fatalf("error creating directory %v", err)
-	}
-	defer os.RemoveAll(dir)
-
-	targetPath := filepath.Join(dir, "targetfile")
-
-	mountObj, err := newNodeMounter()
-	if err != nil {
-		t.Fatalf("error creating mounter %v", err)
-	}
-
-	if mountObj.MakeFile(targetPath) != nil {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-	if mountObj.MakeFile(targetPath) != nil {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-	if exists, err := mountObj.PathExists(targetPath); !exists {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-}
-
-func TestPathExists(t *testing.T) {
-	// Setup the full driver and its environment
-	dir, err := os.MkdirTemp("", "mount-ebs-csi")
-	if err != nil {
-		t.Fatalf("error creating directory %v", err)
-	}
-	defer os.RemoveAll(dir)
-
-	targetPath := filepath.Join(dir, "notafile")
-
-	mountObj, err := newNodeMounter()
-	if err != nil {
-		t.Fatalf("error creating mounter %v", err)
-	}
-
-	exists, err := mountObj.PathExists(targetPath)
-
-	if err != nil {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-	if exists {
-		t.Fatalf("Expected file %s to not exist", targetPath)
-	}
-
-}
-
-func TestGetDeviceName(t *testing.T) {
-	// Setup the full driver and its environment
-	dir, err := os.MkdirTemp("", "mount-ebs-csi")
-	if err != nil {
-		t.Fatalf("error creating directory %v", err)
-	}
-	defer os.RemoveAll(dir)
-
-	targetPath := filepath.Join(dir, "notafile")
-
-	mountObj, err := newNodeMounter()
-	if err != nil {
-		t.Fatalf("error creating mounter %v", err)
-	}
-
-	if _, _, err := mountObj.GetDeviceNameFromMount(targetPath); err != nil {
-		t.Fatalf("Expect no error but got: %v", err)
-	}
-
-}
diff --git a/pkg/driver/node_linux_test.go b/pkg/driver/node_linux_test.go
deleted file mode 100644
index 00c787ec..00000000
--- a/pkg/driver/node_linux_test.go
+++ /dev/null
@@ -1,285 +0,0 @@
-//go:build linux
-// +build linux
-
-/*
-Copyright 2019 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package driver
-
-import (
-	"fmt"
-	"io/fs"
-	"os"
-	"testing"
-	"time"
-
-	"github.com/golang/mock/gomock"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/internal"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestFindDevicePath(t *testing.T) {
-	devicePath := "/dev/xvdaa"
-	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})
-	symlinkFileInfo := fs.FileInfo(&fakeFileInfo{nvmeName, os.ModeSymlink})
-	nvmeDevicePathSymlinkFileInfo := fs.FileInfo(&fakeFileInfo{nvmeDevicePath, os.ModeSymlink})
-	type testCase struct {
-		name             string
-		devicePath       string
-		volumeID         string
-		partition        string
-		expectMock       func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier)
-		expectDevicePath string
-		expectError      string
-	}
-	testCases := []testCase{
-		{
-			name:       "11: device path exists and nvme device path exists",
-			devicePath: devicePath,
-			volumeID:   volumeID,
-			partition:  "",
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				gomock.InOrder(
-					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
-					mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(nvmeDevicePathSymlinkFileInfo, nil),
-					mockDeviceIdentifier.EXPECT().EvalSymlinks(gomock.Eq(devicePath)).Return(nvmeDevicePath, nil),
-				)
-			},
-			expectDevicePath: nvmeDevicePath,
-		},
-		{
-			name:       "10: device path exists and nvme device path doesn't exist",
-			devicePath: devicePath,
-			volumeID:   volumeID,
-			partition:  "",
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				gomock.InOrder(
-					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
-					mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, nil),
-				)
-			},
-			expectDevicePath: devicePath,
-		},
-		{
-			name:       "01: device path doesn't exist and nvme device 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(symlinkFileInfo, nil),
-					mockDeviceIdentifier.EXPECT().EvalSymlinks(gomock.Eq(symlinkFileInfo.Name())).Return(nvmeDevicePath, nil),
-				)
-			},
-			expectDevicePath: nvmeDevicePath,
-		},
-		{
-			name:       "00: device path doesn't exist and nvme device path doesn't exist",
-			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),
-				)
-			},
-			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,
-		},
-		{
-			name:       "success: non-standard snow device path",
-			devicePath: "/dev/sda",
-			volumeID:   volumeID,
-			partition:  "",
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				gomock.InOrder(
-					mockMounter.EXPECT().PathExists(gomock.Eq("/dev/sda")).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
-	generatedTestCases := []testCase{}
-	for _, tc := range testCases {
-		tc.name += " (with partition)"
-		tc.partition = "1"
-		if tc.expectDevicePath == devicePath || tc.expectDevicePath == snowDevicePath {
-			tc.expectDevicePath += tc.partition
-		} else if tc.expectDevicePath == nvmeDevicePath {
-			tc.expectDevicePath += "p" + tc.partition
-		}
-		generatedTestCases = append(generatedTestCases, tc)
-	}
-	testCases = append(testCases, generatedTestCases...)
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			mockCtl := gomock.NewController(t)
-			defer mockCtl.Finish()
-
-			mockMounter := NewMockMounter(mockCtl)
-			mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-			nodeDriver := nodeService{
-				metadata:         &metadata.Metadata{},
-				mounter:          mockMounter,
-				deviceIdentifier: mockDeviceIdentifier,
-				inFlight:         internal.NewInFlight(),
-				options:          &Options{},
-			}
-
-			if tc.expectDevicePath == snowDevicePath+tc.partition {
-				nodeDriver = nodeService{
-					metadata:         &metadata.Metadata{Region: "snow"},
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-					options:          &Options{},
-				}
-			}
-
-			if tc.expectMock != nil {
-				tc.expectMock(*mockMounter, *mockDeviceIdentifier)
-			}
-
-			devicePath, err := nodeDriver.findDevicePath(tc.devicePath, tc.volumeID, tc.partition)
-			if tc.expectError != "" {
-				assert.EqualError(t, err, tc.expectError)
-			} else {
-				assert.Equal(t, tc.expectDevicePath, devicePath)
-				require.NoError(t, err)
-			}
-		})
-	}
-}
-
-const fakeVolumeName = "vol11111111111111111"
-const fakeIncorrectVolumeName = "vol21111111111111111"
-
-func TestVerifyVolumeSerialMatch(t *testing.T) {
-	type testCase struct {
-		name        string
-		execOutput  string
-		execError   error
-		expectError bool
-	}
-	testCases := []testCase{
-		{
-			name:       "success: empty",
-			execOutput: "",
-		},
-		{
-			name:       "success: single",
-			execOutput: fakeVolumeName,
-		},
-		{
-			name:       "success: multiple",
-			execOutput: fakeVolumeName + "\n" + fakeVolumeName + "\n" + fakeVolumeName,
-		},
-		{
-			name:       "success: whitespace",
-			execOutput: "\t     " + fakeVolumeName + "         \n    \t    ",
-		},
-		{
-			name:       "success: extra output",
-			execOutput: "extra output without name in it\n" + fakeVolumeName,
-		},
-		{
-			name:      "success: failed command",
-			execError: fmt.Errorf("Exec failed"),
-		},
-		{
-			name:        "failure: wrong volume",
-			execOutput:  fakeIncorrectVolumeName,
-			expectError: true,
-		},
-		{
-			name:        "failure: mixed",
-			execOutput:  fakeVolumeName + "\n" + fakeIncorrectVolumeName,
-			expectError: true,
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			mockExecRunner := func(_ string, _ ...string) ([]byte, error) {
-				return []byte(tc.execOutput), tc.execError
-			}
-
-			result := verifyVolumeSerialMatch("path", fakeVolumeName, mockExecRunner)
-			if tc.expectError {
-				assert.Error(t, result)
-			} else {
-				require.NoError(t, result)
-			}
-		})
-	}
-}
-
-type fakeFileInfo struct {
-	name string
-	mode os.FileMode
-}
-
-func (fi *fakeFileInfo) Name() string {
-	return fi.name
-}
-
-func (fi *fakeFileInfo) Size() int64 {
-	return 0
-}
-
-func (fi *fakeFileInfo) Mode() os.FileMode {
-	return fi.mode
-}
-
-func (fi *fakeFileInfo) ModTime() time.Time {
-	return time.Now()
-}
-
-func (fi *fakeFileInfo) IsDir() bool {
-	return false
-}
-
-func (fi *fakeFileInfo) Sys() interface{} {
-	return nil
-}
diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go
index e336cb5b..1b4856e9 100644
--- a/pkg/driver/node_test.go
+++ b/pkg/driver/node_test.go
@@ -1,8 +1,5 @@
-//go:build linux
-// +build linux
-
 /*
-Copyright 2019 The Kubernetes Authors.
+Copyright 2024 The Kubernetes Authors.
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
@@ -23,1627 +20,1980 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"io/fs"
 	"os"
 	"reflect"
 	"runtime"
-	"strings"
 	"testing"
+	"time"
 
 	"github.com/aws/aws-sdk-go-v2/aws/arn"
-	"github.com/container-storage-interface/spec/lib/go/csi"
+	csi "github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/golang/mock/gomock"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/metadata"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/internal"
+	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
 	"github.com/stretchr/testify/assert"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
 	corev1 "k8s.io/api/core/v1"
 	v1 "k8s.io/api/storage/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/kubernetes"
 )
 
-var (
-	volumeID        = "voltest"
-	nvmeName        = "/dev/disk/by-id/nvme-Amazon_Elastic_Block_Store_voltest"
-	symlinkFileInfo = fs.FileInfo(&fakeFileInfo{nvmeName, os.ModeSymlink})
-)
+func TestNewNodeService(t *testing.T) {
+	ctrl := gomock.NewController(t)
+	defer ctrl.Finish()
 
-func TestNodeStageVolume(t *testing.T) {
+	mockMetadataService := metadata.NewMockMetadataService(ctrl)
+	mockMounter := mounter.NewMockMounter(ctrl)
+	mockKubernetesClient := NewMockKubernetesClient(ctrl)
 
-	var (
-		targetPath     = "/test/path"
-		devicePath     = "/dev/fake"
-		nvmeDevicePath = "/dev/nvmefake1n1"
-		deviceFileInfo = fs.FileInfo(&fakeFileInfo{devicePath, os.ModeDevice})
-		//deviceSymlinkFileInfo = fs.FileInfo(&fakeFileInfo{nvmeDevicePath, os.ModeSymlink})
-		stdVolCap = &csi.VolumeCapability{
-			AccessType: &csi.VolumeCapability_Mount{
-				Mount: &csi.VolumeCapability_MountVolume{
-					FsType: FSTypeExt4,
-				},
-			},
-			AccessMode: &csi.VolumeCapability_AccessMode{
-				Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
-			},
-		}
-		stdVolContext           = map[string]string{VolumeAttributePartition: "1"}
-		devicePathWithPartition = devicePath + "1"
-		// With few exceptions, all "success" non-block cases have roughly the same
-		// expected calls and only care about testing the FormatAndMountSensitiveWithFormatOptions call. The
-		// exceptions should not call this, instead they should define expectMock
-		// from scratch.
-		successExpectMock = func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-			mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(false, nil)
-			mockMounter.EXPECT().MakeDir(targetPath).Return(nil)
-			mockMounter.EXPECT().GetDeviceNameFromMount(targetPath).Return("", 1, nil)
-			mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil)
-			mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, nil)
-			mockMounter.EXPECT().NeedResize(gomock.Eq(devicePath), gomock.Eq(targetPath)).Return(false, nil)
-		}
-	)
+	os.Setenv("AWS_REGION", "us-west-2")
+	defer os.Unsetenv("AWS_REGION")
+
+	options := &Options{}
+
+	nodeService := NewNodeService(options, mockMetadataService, mockMounter, mockKubernetesClient)
+
+	if nodeService == nil {
+		t.Fatal("Expected NewNodeService to return a non-nil NodeService")
+	}
+
+	if nodeService.metadata != mockMetadataService {
+		t.Error("Expected NodeService.metadata to be set to the mock MetadataService")
+	}
+
+	if nodeService.mounter != mockMounter {
+		t.Error("Expected NodeService.mounter to be set to the mock Mounter")
+	}
+
+	if nodeService.inFlight == nil {
+		t.Error("Expected NodeService.inFlight to be initialized")
+	}
+
+	if nodeService.options != options {
+		t.Error("Expected NodeService.options to be set to the provided options")
+	}
+}
+
+func TestNodeStageVolume(t *testing.T) {
 	testCases := []struct {
 		name         string
-		request      *csi.NodeStageVolumeRequest
-		inFlightFunc func(*internal.InFlight) *internal.InFlight
-		expectMock   func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier)
-		expectedCode codes.Code
+		req          *csi.NodeStageVolumeRequest
+		mounterMock  func(ctrl *gomock.Controller) *mounter.MockMounter
+		metadataMock func(ctrl *gomock.Controller) *metadata.MockMetadataService
+		expectedErr  error
+		inflight     bool
 	}{
 		{
-			name: "success normal",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
+			name: "success",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{DevicePathKey: "/dev/xvdba"},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				m.EXPECT().NeedResize(gomock.Any(), gomock.Any()).Return(false, nil)
+				return m
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Len(0))
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
+			expectedErr: nil,
 		},
 		{
-			name: "success normal [raw block]",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "missing_volume_id",
+			req: &csi.NodeStageVolumeRequest{
+				StagingTargetPath: "/staging/path",
 				VolumeCapability: &csi.VolumeCapability{
-					AccessType: &csi.VolumeCapability_Block{
-						Block: &csi.VolumeCapability_BlockVolume{},
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
 					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
 						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 					},
 				},
-				VolumeId: volumeID,
-			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Nil(), gomock.Len(0)).Times(0)
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Error(codes.InvalidArgument, "Volume ID not provided"),
 		},
 		{
-			name: "success with mount options",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "missing_staging_target",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId: "vol-test",
 				VolumeCapability: &csi.VolumeCapability{
 					AccessType: &csi.VolumeCapability_Mount{
 						Mount: &csi.VolumeCapability_MountVolume{
-							MountFlags: []string{"dirsync", "noexec"},
+							FsType: "ext4",
 						},
 					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
 						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 					},
 				},
-				VolumeId: volumeID,
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeExt4), gomock.Eq([]string{"dirsync", "noexec"}), gomock.Nil(), gomock.Len(0))
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Error(codes.InvalidArgument, "Staging target not provided"),
+		},
+		{
+			name: "missing_volume_capability",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Error(codes.InvalidArgument, "Volume capability not provided"),
 		},
 		{
-			name: "success fsType ext3",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "invalid_volume_attribute",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
 				VolumeCapability: &csi.VolumeCapability{
 					AccessType: &csi.VolumeCapability_Mount{
 						Mount: &csi.VolumeCapability_MountVolume{
-							FsType: FSTypeExt3,
+							FsType: "ext4",
 						},
 					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
 						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 					},
 				},
-				VolumeId: volumeID,
-			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeExt3), gomock.Any(), gomock.Nil(), gomock.Len(0))
+				VolumeContext: map[string]string{
+					VolumeAttributePartition: "invalid-partition",
+				},
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Error(codes.InvalidArgument, "Volume Attribute is not valid"),
 		},
 		{
-			name: "success mount with default fsType ext4",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "unsupported_volume_capability",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
 				VolumeCapability: &csi.VolumeCapability{
 					AccessType: &csi.VolumeCapability_Mount{
 						Mount: &csi.VolumeCapability_MountVolume{
-							FsType: "",
+							FsType: "ext4",
 						},
 					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
-						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+						Mode: csi.VolumeCapability_AccessMode_UNKNOWN,
 					},
 				},
-				VolumeId: volumeID,
-			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeExt4), gomock.Any(), gomock.Nil(), gomock.Len(0))
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Error(codes.InvalidArgument, "Volume capability not supported"),
 		},
 		{
-			name: "success device already mounted at target",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(true, nil)
-				mockMounter.EXPECT().GetDeviceNameFromMount(targetPath).Return(devicePath, 1, nil)
-				mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, nil)
-
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Nil(), gomock.Len(0)).Times(0)
+			name: "block_volume",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  nil,
 		},
 		{
-			name: "success nvme device already mounted at target",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(true, nil)
-
-				// If the device is nvme GetDeviceNameFromMount should return the
-				// canonical device path
-				mockMounter.EXPECT().GetDeviceNameFromMount(targetPath).Return(nvmeDevicePath, 1, nil)
-
-				// The publish context device path may not exist but the driver should
-				// find the canonical device path (see TestFindDevicePath), compare it
-				// to the one returned by GetDeviceNameFromMount, and then skip
-				// FormatAndMountSensitiveWithFormatOptions
-				mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(false, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(nvmeName)).Return(symlinkFileInfo, nil)
-				mockDeviceIdentifier.EXPECT().EvalSymlinks(gomock.Eq(symlinkFileInfo.Name())).Return(nvmeDevicePath, nil)
-
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Nil(), gomock.Len(0)).Times(0)
+			name: "missing_mount_volume",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Error(codes.InvalidArgument, "NodeStageVolume: mount is nil within volume capability"),
 		},
 		{
-			name: "success with partition",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeContext:     stdVolContext,
-				VolumeId:          volumeID,
-			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(false, nil)
-				mockMounter.EXPECT().MakeDir(targetPath).Return(nil)
-				mockMounter.EXPECT().GetDeviceNameFromMount(targetPath).Return("", 1, nil)
-				mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, nil)
-
-				// The device path argument should be canonicalized to contain the
-				// partition
-				mockMounter.EXPECT().NeedResize(gomock.Eq(devicePathWithPartition), gomock.Eq(targetPath)).Return(false, nil)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePathWithPartition), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Len(0))
+			name: "default_fstype",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{DevicePathKey: "/dev/xvdba"},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(false, nil)
+				m.EXPECT().MakeDir(gomock.Any()).Return(nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 0, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), defaultFsType, gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				m.EXPECT().NeedResize(gomock.Any(), gomock.Any()).Return(false, nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: nil,
+		},
+		{
+			name: "invalid_fstype",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "invalid",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
 			},
+			mounterMock:  nil,
+			metadataMock: nil,
+			expectedErr:  status.Errorf(codes.InvalidArgument, "NodeStageVolume: invalid fstype invalid"),
 		},
 		{
-			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:          volumeID,
+			name: "invalid_block_size",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					BlockSizeKey: "-",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Len(0))
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
+			},
+			expectedErr: status.Error(codes.InvalidArgument, "Invalid blocksize (aborting!): <nil>"),
 		},
 		{
-			name: "success with block size",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-				VolumeContext:     map[string]string{BlockSizeKey: "1024"},
+			name: "invalid_inode_size",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					InodeSizeKey: "-",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-b", "1024"}))
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
+			},
+			expectedErr: status.Error(codes.InvalidArgument, "Invalid inodesize (aborting!): <nil>"),
 		},
 		{
-			name: "success with inode size in ext4",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-				VolumeContext:     map[string]string{InodeSizeKey: "256"},
+			name: "invalid_bytes_per_inode",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					BytesPerInodeKey: "-",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-I", "256"}))
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
+			},
+			expectedErr: status.Error(codes.InvalidArgument, "Invalid bytesperinode (aborting!): <nil>"),
 		},
 		{
-			name: "success with inode size in xfs",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "invalid_number_of_inodes",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
 				VolumeCapability: &csi.VolumeCapability{
 					AccessType: &csi.VolumeCapability_Mount{
 						Mount: &csi.VolumeCapability_MountVolume{
-							FsType: FSTypeXfs,
+							FsType: "ext4",
 						},
 					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
 						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 					},
 				},
-				VolumeId:      volumeID,
-				VolumeContext: map[string]string{InodeSizeKey: "256"},
+				VolumeContext: map[string]string{
+					NumberOfInodesKey: "-",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(FSTypeXfs), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-i", "size=256"}))
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Invalid numberofinodes (aborting!): <nil>"),
 		},
 		{
-			name: "success with bytes-per-inode",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-				VolumeContext:     map[string]string{BytesPerInodeKey: "8192"},
+			name: "invalid_ext4_bigalloc",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					Ext4BigAllocKey: "-",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-i", "8192"}))
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Invalid ext4bigalloc (aborting!): <nil>"),
 		},
 		{
-			name: "success with number-of-inodes",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-				VolumeContext:     map[string]string{NumberOfInodesKey: "13107200"},
+			name: "invalid_ext4_cluster_size",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					Ext4ClusterSizeKey: "-",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-N", "13107200"}))
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Invalid ext4clustersize (aborting!): <nil>"),
 		},
 		{
-			name: "success with bigalloc feature flag enabled in ext4",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-				VolumeContext:     map[string]string{Ext4BigAllocKey: "true"},
+			name: "device_path_not_provided",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					Ext4ClusterSizeKey: "51",
+				},
+				PublishContext: map[string]string{},
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-O", "bigalloc"}))
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
+			},
+			expectedErr: status.Error(codes.InvalidArgument, "Device path not provided"),
 		},
 		{
-			name: "success with custom cluster size and bigalloc feature flag enabled in ext4",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
-				VolumeContext:     map[string]string{Ext4BigAllocKey: "true", Ext4ClusterSizeKey: "16384"},
+			name: "volume_operation_already_exists",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectMock: func(mockMounter MockMounter, mockDeviceIdentifier MockDeviceIdentifier) {
-				successExpectMock(mockMounter, mockDeviceIdentifier)
-				mockMounter.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Any(), gomock.Nil(), gomock.Eq([]string{"-O", "bigalloc", "-C", "16384"}))
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				return m
 			},
-		},
-		{
-			name: "fail no VolumeId",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				return nil
 			},
-			expectedCode: codes.InvalidArgument,
+			expectedErr: status.Errorf(codes.Aborted, VolumeOperationAlreadyExists, "vol-test"),
+			inflight:    true,
 		},
 		{
-			name: "fail no mount",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "valid_partition",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
 				VolumeCapability: &csi.VolumeCapability{
-					AccessType: &csi.VolumeCapability_Mount{},
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
 						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 					},
 				},
+				VolumeContext: map[string]string{
+					VolumeAttributePartition: "1",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), "1", gomock.Any()).Return("/dev/xvdba1", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				m.EXPECT().NeedResize(gomock.Any(), gomock.Any()).Return(false, nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
-			expectedCode: codes.InvalidArgument,
+			expectedErr: nil,
 		},
 		{
-			name: "fail no StagingTargetPath",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:   map[string]string{DevicePathKey: devicePath},
-				VolumeCapability: stdVolCap,
-				VolumeId:         volumeID,
+			name: "invalid_partition",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					VolumeAttributePartition: "0",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), "", gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				m.EXPECT().NeedResize(gomock.Any(), gomock.Any()).Return(false, nil)
+				return m
 			},
-			expectedCode: codes.InvalidArgument,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: nil,
 		},
 		{
-			name: "fail no VolumeCapability",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeId:          volumeID,
+			name: "find_device_path_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Eq("/dev/xvdba"), gomock.Eq("vol-test"), gomock.Eq(""), gomock.Eq("us-west-2")).Return("", errors.New("find device path error"))
+				return m
 			},
-			expectedCode: codes.InvalidArgument,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Errorf(codes.Internal, "Failed to find device path %s. %v", "/dev/xvdba", errors.New("find device path error")),
 		},
 		{
-			name: "fail invalid VolumeCapability",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
+			name: "path_exists_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
 				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
 					AccessMode: &csi.VolumeCapability_AccessMode{
-						Mode: csi.VolumeCapability_AccessMode_UNKNOWN,
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
 					},
 				},
-				VolumeId: volumeID,
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectedCode: codes.InvalidArgument,
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(false, errors.New("path exists error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "failed to check if target \"/staging/path\" exists: path exists error"),
 		},
 		{
-			name: "fail no devicePath",
-			request: &csi.NodeStageVolumeRequest{
-				VolumeCapability: stdVolCap,
-				VolumeId:         volumeID,
+			name: "create_target_dir_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectedCode: codes.InvalidArgument,
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(false, nil)
+				m.EXPECT().MakeDir(gomock.Eq("/staging/path")).Return(errors.New("make dir error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "could not create target dir \"/staging/path\": make dir error"),
 		},
 		{
-			name: "fail invalid volumeContext",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeContext:     map[string]string{VolumeAttributePartition: "partition1"},
-				VolumeId:          volumeID,
+			name: "get_device_name_from_mount_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			expectedCode: codes.InvalidArgument,
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/staging/path")).Return("", 0, errors.New("get device name error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "failed to check if volume is already mounted: get device name error"),
 		},
 		{
-			name: "fail with in-flight request",
-			request: &csi.NodeStageVolumeRequest{
-				PublishContext:    map[string]string{DevicePathKey: devicePath},
-				StagingTargetPath: targetPath,
-				VolumeCapability:  stdVolCap,
-				VolumeId:          volumeID,
+			name: "volume_already_staged",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
-			inFlightFunc: func(inFlight *internal.InFlight) *internal.InFlight {
-				inFlight.Insert(volumeID)
-				return inFlight
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/staging/path")).Return("/dev/xvdba", 1, nil)
+				return m
 			},
-			expectedCode: codes.Aborted,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: nil,
 		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			mockCtl := gomock.NewController(t)
-			defer mockCtl.Finish()
-
-			mockMetadata := metadata.NewMockMetadataService(mockCtl)
-			mockMounter := NewMockMounter(mockCtl)
-			mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
+		{
+			name: "format_and_mount_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/staging/path")).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path"), gomock.Eq("ext4"), gomock.Any(), gomock.Any(), gomock.Any()).Return(errors.New("format and mount error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "could not format \"/dev/xvdba\" and mount it at \"/staging/path\": format and mount error"),
+		},
+		{
+			name: "need_resize_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				m.EXPECT().NeedResize(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path")).Return(false, errors.New("need resize error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "Could not determine if volume \"vol-test\" (\"/dev/xvdba\") need to be resized:  need resize error"),
+		},
+		{
+			name: "resize_error",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				m.EXPECT().NeedResize(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path")).Return(true, nil)
+				m.EXPECT().Resize(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path")).Return(false, errors.New("resize error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "Could not resize volume \"vol-test\" (\"/dev/xvdba\"):  resize error"),
+		},
+		{
+			name: "format_options_ext4",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "ext4",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					BlockSizeKey:       "4096",
+					InodeSizeKey:       "512",
+					BytesPerInodeKey:   "16384",
+					NumberOfInodesKey:  "1000000",
+					Ext4BigAllocKey:    "true",
+					Ext4ClusterSizeKey: "65536",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/staging/path")).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path"), gomock.Eq("ext4"), gomock.Any(), gomock.Any(), gomock.Eq([]string{"-b", "4096", "-I", "512", "-i", "16384", "-N", "1000000", "-O", "bigalloc", "-C", "65536"})).Return(nil)
+				m.EXPECT().NeedResize(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path")).Return(false, nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: nil,
+		},
+		{
+			name: "format_options_xfs",
+			req: &csi.NodeStageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{
+							FsType: "xfs",
+						},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				VolumeContext: map[string]string{
+					BlockSizeKey: "4096",
+					InodeSizeKey: "512",
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Eq("/staging/path")).Return(true, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/staging/path")).Return("", 1, nil)
+				m.EXPECT().FormatAndMountSensitiveWithFormatOptions(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path"), gomock.Eq("xfs"), gomock.Any(), gomock.Any(), gomock.Eq([]string{"-b", "size=4096", "-i", "size=512"})).Return(nil)
+				m.EXPECT().NeedResize(gomock.Eq("/dev/xvdba"), gomock.Eq("/staging/path")).Return(false, nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedErr: nil,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
+
+			var mounter *mounter.MockMounter
+			if tc.mounterMock != nil {
+				mounter = tc.mounterMock(ctrl)
+			}
 
-			inFlight := internal.NewInFlight()
-			if tc.inFlightFunc != nil {
-				tc.inFlightFunc(inFlight)
+			var metadata *metadata.MockMetadataService
+			if tc.metadataMock != nil {
+				metadata = tc.metadataMock(ctrl)
 			}
 
-			awsDriver := &nodeService{
-				metadata:         mockMetadata,
-				mounter:          mockMounter,
-				deviceIdentifier: mockDeviceIdentifier,
-				inFlight:         inFlight,
+			driver := &NodeService{
+				metadata: metadata,
+				mounter:  mounter,
+				inFlight: internal.NewInFlight(),
 			}
 
-			if tc.expectMock != nil {
-				tc.expectMock(*mockMounter, *mockDeviceIdentifier)
+			if tc.inflight {
+				driver.inFlight.Insert("vol-test")
 			}
 
-			_, err := awsDriver.NodeStageVolume(context.TODO(), tc.request)
-			if tc.expectedCode != codes.OK {
-				expectErr(t, err, tc.expectedCode)
-			} else if err != nil {
-				t.Fatalf("Expect no error but got: %v", err)
+			_, err := driver.NodeStageVolume(context.Background(), tc.req)
+			if !reflect.DeepEqual(err, tc.expectedErr) {
+				t.Fatalf("Expected error '%v' but got '%v'", tc.expectedErr, err)
 			}
 		})
 	}
 }
 
-func TestNodeUnstageVolume(t *testing.T) {
-	targetPath := "/test/path"
-	devicePath := "/dev/fake"
-
+func TestGetVolumesLimit(t *testing.T) {
 	testCases := []struct {
-		name     string
-		testFunc func(t *testing.T)
+		name         string
+		expectedErr  error
+		expectedVal  int64
+		options      *Options
+		metadataMock func(ctrl *gomock.Controller) *metadata.MockMetadataService
 	}{
 		{
-			name: "success normal",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().GetDeviceNameFromMount(gomock.Eq(targetPath)).Return(devicePath, 1, nil)
-				mockMounter.EXPECT().Unstage(gomock.Eq(targetPath)).Return(nil)
-
-				req := &csi.NodeUnstageVolumeRequest{
-					StagingTargetPath: targetPath,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "VolumeAttachLimit_specified",
+			options: &Options{
+				VolumeAttachLimit:         10,
+				ReservedVolumeAttachments: -1,
 			},
+			expectedVal: 10,
 		},
 		{
-			name: "success no device mounted at target",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().GetDeviceNameFromMount(gomock.Eq(targetPath)).Return(devicePath, 0, nil)
-
-				req := &csi.NodeUnstageVolumeRequest{
-					StagingTargetPath: targetPath,
-					VolumeId:          volumeID,
-				}
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "sbeDeviceVolumeAttachmentLimit",
+			options: &Options{
+				VolumeAttachLimit: -1,
+			},
+			expectedVal: sbeDeviceVolumeAttachmentLimit,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("snow")
+				return m
 			},
 		},
 		{
-			name: "success device mounted at multiple targets",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().GetDeviceNameFromMount(gomock.Eq(targetPath)).Return(devicePath, 2, nil)
-				mockMounter.EXPECT().Unstage(gomock.Eq(targetPath)).Return(nil)
-
-				req := &csi.NodeUnstageVolumeRequest{
-					StagingTargetPath: targetPath,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "t2.medium_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 38,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				m.EXPECT().GetInstanceType().Return("t2.medium")
+				return m
 			},
 		},
 		{
-			name: "fail no VolumeId",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnstageVolumeRequest{
-					StagingTargetPath: targetPath,
-				}
-
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+			name: "ReservedVolumeAttachments_specified",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: 3,
+			},
+			expectedVal: 36,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("t2.medium")
+				return m
 			},
 		},
 		{
-			name: "fail no StagingTargetPath",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnstageVolumeRequest{
-					VolumeId: volumeID,
-				}
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+			name: "m5d.large_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 23,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("m5d.large")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				m.EXPECT().GetNumAttachedENIs().Return(3)
+				return m
 			},
 		},
 		{
-			name: "fail GetDeviceName returns error",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().GetDeviceNameFromMount(gomock.Eq(targetPath)).Return("", 0, errors.New("GetDeviceName faield"))
-
-				req := &csi.NodeUnstageVolumeRequest{
-					StagingTargetPath: targetPath,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+			name: "d3en.12xlarge_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 1,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("d3en.12xlarge")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				m.EXPECT().GetNumAttachedENIs().Return(1)
+				return m
 			},
 		},
 		{
-			name: "fail another operation in-flight on given volumeId",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnstageVolumeRequest{
-					StagingTargetPath: targetPath,
-					VolumeId:          volumeID,
-				}
-
-				awsDriver.inFlight.Insert(volumeID)
-				_, err := awsDriver.NodeUnstageVolume(context.TODO(), req)
-				expectErr(t, err, codes.Aborted)
+			name: "d3.8xlarge_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 1,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("d3.8xlarge")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				m.EXPECT().GetNumAttachedENIs().Return(1)
+				return m
+			},
+		},
+		{
+			name: "nitro_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 127,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("m7i.48xlarge")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				return m
+			},
+		},
+		{
+			name: "attached_max_enis",
+			options: &Options{
+				VolumeAttachLimit: -1,
+			},
+			expectedVal: 1,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("t3.xlarge")
+				m.EXPECT().GetNumAttachedENIs().Return(40)
+				return m
+			},
+		},
+		{
+			name: "inf1.24xlarge_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 9,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("inf1.24xlarge")
+				m.EXPECT().GetNumAttachedENIs().Return(1)
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				return m
+			},
+		},
+		{
+			name: "mac1.metal_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 14,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("mac1.metal")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				m.EXPECT().GetNumAttachedENIs().Return(1)
+				return m
+			},
+		},
+		{
+			name: "u-12tb1.metal_volume_attach_limit",
+			options: &Options{
+				VolumeAttachLimit:         -1,
+				ReservedVolumeAttachments: -1,
+			},
+			expectedVal: 17,
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				m.EXPECT().GetInstanceType().Return("u-12tb1.metal")
+				m.EXPECT().GetNumBlockDeviceMappings().Return(0)
+				m.EXPECT().GetNumAttachedENIs().Return(1)
+				return m
 			},
 		},
 	}
 
 	for _, tc := range testCases {
-		t.Run(tc.name, tc.testFunc)
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
+
+			var mounter *mounter.MockMounter
+
+			var metadata *metadata.MockMetadataService
+			if tc.metadataMock != nil {
+				metadata = tc.metadataMock(ctrl)
+			}
+
+			driver := &NodeService{
+				mounter:  mounter,
+				inFlight: internal.NewInFlight(),
+				options:  tc.options,
+				metadata: metadata,
+			}
+
+			value := driver.getVolumesLimit()
+			if value != tc.expectedVal {
+				t.Fatalf("Expected value %v but got %v", tc.expectedVal, value)
+			}
+		})
 	}
 }
 
 func TestNodePublishVolume(t *testing.T) {
-	targetPath := "/test/path"
-	stagingTargetPath := "/test/staging/path"
-	devicePath := "/dev/fake"
-	deviceFileInfo := fs.FileInfo(&fakeFileInfo{devicePath, os.ModeDevice})
-	stdVolCap := &csi.VolumeCapability{
-		AccessType: &csi.VolumeCapability_Mount{
-			Mount: &csi.VolumeCapability_MountVolume{},
-		},
-		AccessMode: &csi.VolumeCapability_AccessMode{
-			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)
+		name         string
+		req          *csi.NodePublishVolumeRequest
+		mounterMock  func(ctrl *gomock.Controller) *mounter.MockMounter
+		metadataMock func(ctrl *gomock.Controller) *metadata.MockMetadataService
+		expectedErr  error
+		inflight     bool
 	}{
 		{
-			name: "success normal",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().Mount(gomock.Eq(stagingTargetPath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Eq([]string{"bind"})).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability:  stdVolCap,
-					VolumeId:          volumeID,
-				}
+			name: "success_block_device",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().MakeFile(gomock.Any()).Return(nil)
+				m.EXPECT().IsLikelyNotMountPoint(gomock.Any()).Return(true, nil)
+				m.EXPECT().Mount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
 		},
 		{
-			name: "success filesystem mounted already",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(false, nil)
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability:  stdVolCap,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "success_fs",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Mount{
+						Mount: &csi.VolumeCapability_MountVolume{},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PreparePublishTarget(gomock.Any()).Return(nil)
+				m.EXPECT().IsLikelyNotMountPoint(gomock.Any()).Return(true, nil)
+				m.EXPECT().Mount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				return m
 			},
 		},
 		{
-			name: "success filesystem mountpoint error",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				gomock.InOrder(
-					mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(true, nil),
-				)
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, errors.New("Internal system error"))
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability:  stdVolCap,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+			name: "volume_id_not_provided",
+			req: &csi.NodePublishVolumeRequest{
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID not provided"),
 		},
 		{
-			name: "success filesystem corrupted mountpoint error",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(true, errors.New("CorruptedMntError"))
-				mockMounter.EXPECT().IsCorruptedMnt(gomock.Eq(errors.New("CorruptedMntError"))).Return(true)
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, errors.New("internal system error"))
-				mockMounter.EXPECT().Unpublish(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().Mount(gomock.Eq(stagingTargetPath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Eq([]string{"bind"})).Return(nil)
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability:  stdVolCap,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "staging_target_path_not_provided",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:   "vol-test",
+				TargetPath: "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
+					},
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Staging target not provided"),
 		},
 		{
-			name: "success fstype xfs",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().Mount(gomock.Eq(stagingTargetPath), gomock.Eq(targetPath), gomock.Eq(FSTypeXfs), gomock.Eq([]string{"bind", "nouuid"})).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability: &csi.VolumeCapability{
-						AccessType: &csi.VolumeCapability_Mount{
-							Mount: &csi.VolumeCapability_MountVolume{
-								FsType: FSTypeXfs,
-							},
-						},
-						AccessMode: &csi.VolumeCapability_AccessMode{
-							Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
-						},
+			name: "target_path_not_provided",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeId: volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Target path not provided"),
 		},
 		{
-			name: "success readonly",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().Mount(gomock.Eq(stagingTargetPath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Eq([]string{"bind", "ro"})).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					Readonly:          true,
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability:  stdVolCap,
-					VolumeId:          volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "capability_not_provided",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume capability not provided"),
 		},
 		{
-			name: "success mount options",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().Mount(gomock.Eq(stagingTargetPath), gomock.Eq(targetPath), gomock.Eq(defaultFsType), gomock.Eq([]string{"bind", "test-flag"})).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: "/dev/fake"},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability: &csi.VolumeCapability{
-						AccessType: &csi.VolumeCapability_Mount{
-							Mount: &csi.VolumeCapability_MountVolume{
-								// this request will call mount with the bind option,
-								// adding "bind" here we test that we don't add the
-								// same option twice. "test-flag" is a canary to check
-								// that the driver calls mount with that flag
-								MountFlags: []string{"bind", "test-flag"},
-							},
-						},
-						AccessMode: &csi.VolumeCapability_AccessMode{
-							Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
-						},
+			name: "success_block_device",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeId: volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
+			inflight:    true,
+			expectedErr: status.Errorf(codes.Aborted, VolumeOperationAlreadyExists, "vol-test"),
 		},
 		{
-			name: "success normal [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					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)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, 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,
-						},
+			name: "success_block_device",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeId: volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_Mode(csi.ControllerServiceCapability_RPC_UNKNOWN),
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
 			},
+			expectedErr: status.Errorf(codes.InvalidArgument, "Volume capability not supported"),
 		},
 		{
-			name: "success mounted already [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					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().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(false, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, 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,
-						},
+			name: "read_only_enabled",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeId: volumeID,
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+				Readonly: true,
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().MakeFile(gomock.Any()).Return(nil)
+				m.EXPECT().IsLikelyNotMountPoint(gomock.Any()).Return(true, nil)
+				m.EXPECT().Mount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
 		},
 		{
-			name: "success mountpoint error [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				gomock.InOrder(
-					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
-					mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, nil),
-					mockMounter.EXPECT().PathExists(gomock.Eq("/test")).Return(false, nil),
-					mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(true, nil),
-				)
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq("/test")).Return(nil)
-				mockMounter.EXPECT().MakeFile(targetPath).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, errors.New("Internal System Error"))
-
-				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,
-						},
+			name: "nodePublishVolumeForBlock_error",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeId: volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
 			},
+			expectedErr: status.Errorf(codes.InvalidArgument, "Device path not provided"),
 		},
 		{
-			name: "success corrupted mountpoint error [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				gomock.InOrder(
-					mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(true, nil),
-					mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, nil),
-					mockMounter.EXPECT().PathExists(gomock.Eq("/test")).Return(false, nil),
-					mockMounter.EXPECT().PathExists(gomock.Eq(targetPath)).Return(true, errors.New("CorruptedMntError")),
-				)
-
-				mockMounter.EXPECT().IsCorruptedMnt(errors.New("CorruptedMntError")).Return(true)
-
-				mockMounter.EXPECT().MakeDir(gomock.Eq("/test")).Return(nil)
-				mockMounter.EXPECT().MakeFile(targetPath).Return(nil)
-				mockMounter.EXPECT().Unpublish(gomock.Eq(targetPath)).Return(nil)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, errors.New("Internal System Error"))
-				mockMounter.EXPECT().Mount(gomock.Eq(devicePath), gomock.Eq(targetPath), gomock.Any(), gomock.Any()).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,
-						},
+			name: "nodePublishVolumeForBlock_invalid_volume_attribute",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeId: volumeID,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+				VolumeContext: map[string]string{
+					VolumeAttributePartition: "invalid-partition",
+				},
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume Attribute is invalid"),
 		},
 		{
-			name: "success normal with partition [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					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)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, 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,
-						},
+			name: "nodePublishVolumeForBlock_invalid_partition",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeContext: stdVolContext,
-					VolumeId:      volumeID,
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+				VolumeContext: map[string]string{
+					VolumeAttributePartition: "0",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().MakeFile(gomock.Any()).Return(nil)
+				m.EXPECT().IsLikelyNotMountPoint(gomock.Any()).Return(true, nil)
+				m.EXPECT().Mount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
 		},
 		{
-			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 := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					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)
-				mockMounter.EXPECT().IsLikelyNotMountPoint(gomock.Eq(targetPath)).Return(true, nil)
-				mockDeviceIdentifier.EXPECT().Lstat(gomock.Eq(devicePath)).Return(deviceFileInfo, 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,
-						},
+			name: "nodePublishVolumeForBlock_valid_partition",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeContext: map[string]string{VolumeAttributePartition: "0"},
-					VolumeId:      volumeID,
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+				VolumeContext: map[string]string{
+					VolumeAttributePartition: "1",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("/dev/xvdba", nil)
+				m.EXPECT().PathExists(gomock.Any()).Return(true, nil)
+				m.EXPECT().MakeFile(gomock.Any()).Return(nil)
+				m.EXPECT().IsLikelyNotMountPoint(gomock.Any()).Return(true, nil)
+				m.EXPECT().Mount(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
 		},
 		{
-			name: "Fail invalid volumeContext config [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					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,
-						},
+			name: "nodePublishVolumeForBlock_device_path_failure",
+			req: &csi.NodePublishVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+				TargetPath:        "/target/path",
+				VolumeCapability: &csi.VolumeCapability{
+					AccessType: &csi.VolumeCapability_Block{
+						Block: &csi.VolumeCapability_BlockVolume{},
 					},
-					VolumeContext: map[string]string{VolumeAttributePartition: "partition1"},
-					VolumeId:      volumeID,
-				}
+					AccessMode: &csi.VolumeCapability_AccessMode{
+						Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
+					},
+				},
+				PublishContext: map[string]string{
+					DevicePathKey: "/dev/xvdba",
+				},
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+				m.EXPECT().FindDevicePath(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return("", errors.New("device path error"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
 			},
+			expectedErr: status.Error(codes.Internal, "Failed to find device path /dev/xvdba. device path error"),
 		},
-		{
-			name: "fail no device path [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
+	}
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
 
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
+			var mounter *mounter.MockMounter
+			if tc.mounterMock != nil {
+				mounter = tc.mounterMock(ctrl)
+			}
 
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
+			var metadata *metadata.MockMetadataService
+			if tc.metadataMock != nil {
+				metadata = tc.metadataMock(ctrl)
+			}
 
-				req := &csi.NodePublishVolumeRequest{
-					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,
-						},
-					},
-					VolumeId: volumeID,
-				}
+			driver := &NodeService{
+				metadata: metadata,
+				mounter:  mounter,
+				inFlight: internal.NewInFlight(),
+			}
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+			if tc.inflight {
+				driver.inFlight.Insert("vol-test")
+			}
+
+			_, err := driver.NodePublishVolume(context.Background(), tc.req)
+			if !reflect.DeepEqual(err, tc.expectedErr) {
+				t.Fatalf("Expected error '%v' but got '%v'", tc.expectedErr, err)
+			}
+		})
+	}
+}
 
+func TestNodeUnstageVolume(t *testing.T) {
+	testCases := []struct {
+		name        string
+		req         *csi.NodeUnstageVolumeRequest
+		mounterMock func(ctrl *gomock.Controller) *mounter.MockMounter
+		expectedErr error
+		inflight    bool
+	}{
+		{
+			name: "success",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("dev-test", 1, nil)
+				m.EXPECT().Unstage(gomock.Any()).Return(nil)
+				return m
 			},
 		},
 		{
-			name: "fail to find deivce path [raw block]",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
+			name: "missing_volume_id",
+			req: &csi.NodeUnstageVolumeRequest{
+				StagingTargetPath: "/staging/path",
+			},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID not provided"),
+		},
+		{
+			name: "missing_staging_target",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId: "vol-test",
+			},
+			expectedErr: status.Error(codes.InvalidArgument, "Staging target not provided"),
+		},
+		{
+			name: "unstage_failed",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 1, nil)
+				m.EXPECT().Unstage(gomock.Any()).Return(errors.New("unstage failed"))
+				return m
+			},
+			expectedErr: status.Errorf(codes.Internal, "Could not unmount target %q: %v", "/staging/path", errors.New("unstage failed")),
+		},
+		{
+			name: "target_not_mounted",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 0, nil)
+				return m
+			},
+		},
+		{
+			name: "get_device_name_from_mount_failed",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("", 0, errors.New("failed to get device name"))
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "failed to check if target \"/staging/path\" is a mount point: failed to get device name"),
+		},
+		{
+			name: "multiple_references",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Any()).Return("dev-test", 2, nil)
+				m.EXPECT().Unstage(gomock.Any()).Return(nil)
+				return m
+			},
+		},
+		{
+			name: "operation_already_exists",
+			req: &csi.NodeUnstageVolumeRequest{
+				VolumeId:          "vol-test",
+				StagingTargetPath: "/staging/path",
+			},
+			expectedErr: status.Error(codes.Aborted, "An operation with the given volume=\"vol-test\" is already in progress"),
+			inflight:    true,
+		},
+	}
 
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
 
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
+			var mounter *mounter.MockMounter
+			if tc.mounterMock != nil {
+				mounter = tc.mounterMock(ctrl)
+			}
 
-				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,
-						},
-					},
-					VolumeId: volumeID,
-				}
+			driver := &NodeService{
+				mounter:  mounter,
+				inFlight: internal.NewInFlight(),
+			}
 
-				mockMounter.EXPECT().PathExists(gomock.Eq(devicePath)).Return(false, errors.New("findDevicePath failed"))
+			if tc.inflight {
+				driver.inFlight.Insert("vol-test")
+			}
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+			_, err := driver.NodeUnstageVolume(context.Background(), tc.req)
+			if !reflect.DeepEqual(err, tc.expectedErr) {
+				t.Fatalf("Expected error '%v' but got '%v'", tc.expectedErr, err)
+			}
+		})
+	}
+}
 
+func TestNodeGetCapabilities(t *testing.T) {
+	req := &csi.NodeGetCapabilitiesRequest{}
+	expectedCaps := []*csi.NodeServiceCapability{
+		{
+			Type: &csi.NodeServiceCapability_Rpc{
+				Rpc: &csi.NodeServiceCapability_RPC{
+					Type: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
+				},
 			},
 		},
 		{
-			name: "fail no VolumeId",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeCapability:  stdVolCap,
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
-
+			Type: &csi.NodeServiceCapability_Rpc{
+				Rpc: &csi.NodeServiceCapability_RPC{
+					Type: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
+				},
 			},
 		},
 		{
-			name: "fail no StagingTargetPath",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
+			Type: &csi.NodeServiceCapability_Rpc{
+				Rpc: &csi.NodeServiceCapability_RPC{
+					Type: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
+				},
+			},
+		},
+	}
 
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
+	driver := &NodeService{}
 
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
+	resp, err := driver.NodeGetCapabilities(context.Background(), req)
+	if err != nil {
+		t.Fatalf("Unexpected error: %v", err)
+	}
 
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:   map[string]string{DevicePathKey: devicePath},
-					TargetPath:       targetPath,
-					VolumeCapability: stdVolCap,
-					VolumeId:         volumeID,
-				}
+	if len(resp.GetCapabilities()) != len(expectedCaps) {
+		t.Fatalf("Expected %d capabilities, but got %d", len(expectedCaps), len(resp.GetCapabilities()))
+	}
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+	for i, cap := range resp.GetCapabilities() {
+		if cap.GetRpc().GetType() != expectedCaps[i].GetRpc().GetType() {
+			t.Fatalf("Expected capability %v, but got %v", expectedCaps[i].GetRpc().GetType(), cap.GetRpc().GetType())
+		}
+	}
+}
 
+func TestNodeGetInfo(t *testing.T) {
+	testCases := []struct {
+		name         string
+		metadataMock func(ctrl *gomock.Controller) *metadata.MockMetadataService
+		expectedResp *csi.NodeGetInfoResponse
+	}{
+		{
+			name: "without_outpost_arn",
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetInstanceID().Return("i-1234567890abcdef0")
+				m.EXPECT().GetAvailabilityZone().Return("us-west-2a")
+				m.EXPECT().GetOutpostArn().Return(arn.ARN{})
+				return m
+			},
+			expectedResp: &csi.NodeGetInfoResponse{
+				NodeId: "i-1234567890abcdef0",
+				AccessibleTopology: &csi.Topology{
+					Segments: map[string]string{
+						ZoneTopologyKey:          "us-west-2a",
+						WellKnownZoneTopologyKey: "us-west-2a",
+						OSTopologyKey:            runtime.GOOS,
+					},
+				},
 			},
 		},
 		{
-			name: "fail no TargetPath",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
+			name: "with_outpost_arn",
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetInstanceID().Return("i-1234567890abcdef0")
+				m.EXPECT().GetAvailabilityZone().Return("us-west-2a")
+				m.EXPECT().GetOutpostArn().Return(arn.ARN{
+					Partition: "aws",
+					Service:   "outposts",
+					Region:    "us-west-2",
+					AccountID: "123456789012",
+					Resource:  "op-1234567890abcdef0",
+				})
+				return m
+			},
+			expectedResp: &csi.NodeGetInfoResponse{
+				NodeId: "i-1234567890abcdef0",
+				AccessibleTopology: &csi.Topology{
+					Segments: map[string]string{
+						ZoneTopologyKey:          "us-west-2a",
+						WellKnownZoneTopologyKey: "us-west-2a",
+						OSTopologyKey:            runtime.GOOS,
+						AwsRegionKey:             "us-west-2",
+						AwsPartitionKey:          "aws",
+						AwsAccountIDKey:          "123456789012",
+						AwsOutpostIDKey:          "op-1234567890abcdef0",
+					},
+				},
+			},
+		},
+	}
 
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
 
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
+			metadataService := tc.metadataMock(ctrl)
+			mounter := mounter.NewMockMounter(ctrl)
 
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					VolumeCapability:  stdVolCap,
-					VolumeId:          volumeID,
-				}
+			driver := &NodeService{
+				metadata: metadataService,
+				mounter:  mounter,
+				inFlight: internal.NewInFlight(),
+				options:  &Options{},
+			}
+
+			resp, err := driver.NodeGetInfo(context.Background(), &csi.NodeGetInfoRequest{})
+			if err != nil {
+				t.Fatalf("Unexpected error: %v", err)
+			}
 
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+			if !reflect.DeepEqual(resp, tc.expectedResp) {
+				t.Fatalf("Expected response %+v, but got %+v", tc.expectedResp, resp)
+			}
+		})
+	}
+}
 
+func TestNodeUnpublishVolume(t *testing.T) {
+	testCases := []struct {
+		name        string
+		req         *csi.NodeUnpublishVolumeRequest
+		mounterMock func(ctrl *gomock.Controller) *mounter.MockMounter
+		expectedErr error
+		inflight    bool
+	}{
+		{
+			name: "success",
+			req: &csi.NodeUnpublishVolumeRequest{
+				VolumeId:   "vol-test",
+				TargetPath: "/target/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().Unpublish(gomock.Eq("/target/path")).Return(nil)
+				return m
 			},
 		},
 		{
-			name: "fail no VolumeCapability",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: devicePath},
-					StagingTargetPath: stagingTargetPath,
-					TargetPath:        targetPath,
-					VolumeId:          volumeID,
-				}
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
-
+			name: "missing_volume_id",
+			req: &csi.NodeUnpublishVolumeRequest{
+				TargetPath: "/target/path",
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID not provided"),
 		},
 		{
-			name: "fail invalid VolumeCapability",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: "/dev/fake"},
-					StagingTargetPath: "/test/staging/path",
-					TargetPath:        "/test/target/path",
-					VolumeId:          volumeID,
-					VolumeCapability: &csi.VolumeCapability{
-						AccessMode: &csi.VolumeCapability_AccessMode{
-							Mode: csi.VolumeCapability_AccessMode_UNKNOWN,
-						},
-					},
-				}
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
-
+			name: "missing_target_path",
+			req: &csi.NodeUnpublishVolumeRequest{
+				VolumeId: "vol-test",
 			},
+			expectedErr: status.Error(codes.InvalidArgument, "Target path not provided"),
 		},
 		{
-			name: "fail another operation in-flight on given volumeId",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodePublishVolumeRequest{
-					PublishContext:    map[string]string{DevicePathKey: "/dev/fake"},
-					StagingTargetPath: "/test/staging/path",
-					TargetPath:        "/test/target/path",
-					VolumeId:          volumeID,
-					VolumeCapability: &csi.VolumeCapability{
-						AccessType: &csi.VolumeCapability_Block{
-							Block: &csi.VolumeCapability_BlockVolume{},
-						},
-						AccessMode: &csi.VolumeCapability_AccessMode{
-							Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
-						},
-					},
-				}
-				awsDriver.inFlight.Insert(volumeID)
-
-				_, err := awsDriver.NodePublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.Aborted)
-
+			name: "unpublish_failed",
+			req: &csi.NodeUnpublishVolumeRequest{
+				VolumeId:   "vol-test",
+				TargetPath: "/target/path",
 			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().Unpublish(gomock.Eq("/target/path")).Return(errors.New("unpublish failed"))
+				return m
+			},
+			expectedErr: status.Errorf(codes.Internal, "Could not unmount %q: %v", "/target/path", errors.New("unpublish failed")),
+		},
+		{
+			name: "operation_already_exists",
+			req: &csi.NodeUnpublishVolumeRequest{
+				VolumeId:   "vol-test",
+				TargetPath: "/target/path",
+			},
+			expectedErr: status.Error(codes.Aborted, "An operation with the given volume=\"vol-test\" is already in progress"),
+			inflight:    true,
 		},
 	}
 
 	for _, tc := range testCases {
-		t.Run(tc.name, tc.testFunc)
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
+
+			var mounter *mounter.MockMounter
+			if tc.mounterMock != nil {
+				mounter = tc.mounterMock(ctrl)
+			}
+
+			driver := &NodeService{
+				mounter:  mounter,
+				inFlight: internal.NewInFlight(),
+			}
+
+			if tc.inflight {
+				driver.inFlight.Insert("vol-test")
+			}
+
+			_, err := driver.NodeUnpublishVolume(context.Background(), tc.req)
+			if !reflect.DeepEqual(err, tc.expectedErr) {
+				t.Fatalf("Expected error '%v' but got '%v'", tc.expectedErr, err)
+			}
+		})
 	}
 }
-func TestNodeExpandVolume(t *testing.T) {
-	mockCtl := gomock.NewController(t)
-	defer mockCtl.Finish()
-
-	mockMetadata := metadata.NewMockMetadataService(mockCtl)
-	mockMounter := NewMockMounter(mockCtl)
-	mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-	awsDriver := &nodeService{
-		metadata:         mockMetadata,
-		mounter:          mockMounter,
-		deviceIdentifier: mockDeviceIdentifier,
-		inFlight:         internal.NewInFlight(),
-	}
 
-	tests := []struct {
-		name               string
-		request            csi.NodeExpandVolumeRequest
-		expectResponseCode codes.Code
+func TestNodeExpandVolume(t *testing.T) {
+	testCases := []struct {
+		name         string
+		req          *csi.NodeExpandVolumeRequest
+		mounterMock  func(ctrl *gomock.Controller) *mounter.MockMounter
+		metadataMock func(ctrl *gomock.Controller) *metadata.MockMetadataService
+		expectedResp *csi.NodeExpandVolumeResponse
+		expectedErr  error
 	}{
 		{
-			name:               "fail missing volumeId",
-			request:            csi.NodeExpandVolumeRequest{},
-			expectResponseCode: codes.InvalidArgument,
+			name: "success",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(false, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/volume/path")).Return("device-name", 1, nil)
+				m.EXPECT().FindDevicePath(gomock.Eq("device-name"), gomock.Eq("vol-test"), gomock.Eq(""), gomock.Eq("us-west-2")).Return("/dev/xvdba", nil)
+				m.EXPECT().Resize(gomock.Eq("/dev/xvdba"), gomock.Eq("/volume/path")).Return(true, nil)
+				m.EXPECT().GetBlockSizeBytes(gomock.Eq("/dev/xvdba")).Return(int64(1000), nil)
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedResp: &csi.NodeExpandVolumeResponse{CapacityBytes: int64(1000)},
 		},
 		{
-			name: "fail missing volumePath",
-			request: csi.NodeExpandVolumeRequest{
-				StagingTargetPath: "/testDevice/Path",
-				VolumeId:          "test-volume-id",
+			name: "missing_volume_id",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumePath: "/volume/path",
 			},
-			expectResponseCode: codes.InvalidArgument,
+			expectedErr: status.Error(codes.InvalidArgument, "Volume ID not provided"),
 		},
 		{
-			name: "fail volume path not exist",
-			request: csi.NodeExpandVolumeRequest{
-				VolumePath: "./test",
-				VolumeId:   "test-volume-id",
+			name: "missing_volume_path",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId: "vol-test",
 			},
-			expectResponseCode: codes.Internal,
+			expectedErr: status.Error(codes.InvalidArgument, "volume path must be provided"),
 		},
 		{
-			name: "Fail validate VolumeCapability",
-			request: csi.NodeExpandVolumeRequest{
-				VolumePath: "./test",
-				VolumeId:   "test-volume-id",
+			name: "invalid_volume_capability",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
 				VolumeCapability: &csi.VolumeCapability{
 					AccessType: &csi.VolumeCapability_Block{
 						Block: &csi.VolumeCapability_BlockVolume{},
@@ -1653,13 +2003,13 @@ func TestNodeExpandVolume(t *testing.T) {
 					},
 				},
 			},
-			expectResponseCode: codes.InvalidArgument,
+			expectedErr: status.Error(codes.InvalidArgument, "VolumeCapability is invalid: block:<> access_mode:<> "),
 		},
 		{
-			name: "Success [VolumeCapability is block]",
-			request: csi.NodeExpandVolumeRequest{
-				VolumePath: "./test",
-				VolumeId:   "test-volume-id",
+			name: "block_device",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
 				VolumeCapability: &csi.VolumeCapability{
 					AccessType: &csi.VolumeCapability_Block{
 						Block: &csi.VolumeCapability_BlockVolume{},
@@ -1669,697 +2019,308 @@ func TestNodeExpandVolume(t *testing.T) {
 					},
 				},
 			},
-			expectResponseCode: codes.OK,
+			expectedResp: &csi.NodeExpandVolumeResponse{},
 		},
-	}
-
-	for _, test := range tests {
-		t.Run(test.name, func(t *testing.T) {
-			_, err := awsDriver.NodeExpandVolume(context.Background(), &test.request)
-			if err != nil {
-				if test.expectResponseCode != codes.OK {
-					expectErr(t, err, test.expectResponseCode)
-				} else {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
-			}
-		})
-	}
-}
-
-func TestNodeUnpublishVolume(t *testing.T) {
-	targetPath := "/test/path"
-
-	testCases := []struct {
-		name     string
-		testFunc func(t *testing.T)
-	}{
 		{
-			name: "success normal",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnpublishVolumeRequest{
-					TargetPath: targetPath,
-					VolumeId:   volumeID,
-				}
-
-				mockMounter.EXPECT().Unpublish(gomock.Eq(targetPath)).Return(nil)
-				_, err := awsDriver.NodeUnpublishVolume(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name: "is_block_device_error",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(false, errors.New("failed to determine if block device"))
+				return m
 			},
+			expectedErr: status.Error(codes.Internal, "failed to determine if volumePath [/volume/path] is a block device: failed to determine if block device"),
 		},
 		{
-			name: "fail no VolumeId",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnpublishVolumeRequest{
-					TargetPath: targetPath,
-				}
-
-				_, err := awsDriver.NodeUnpublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+			name: "get_block_size_bytes_error",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
 			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(true, nil)
+				m.EXPECT().GetBlockSizeBytes(gomock.Eq("/volume/path")).Return(int64(0), errors.New("failed to get block size"))
+				return m
+			},
+			expectedErr: status.Error(codes.Internal, "failed to get block capacity on path /volume/path: failed to get block size"),
 		},
 		{
-			name: "fail no TargetPath",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnpublishVolumeRequest{
-					VolumeId: volumeID,
-				}
-
-				_, err := awsDriver.NodeUnpublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.InvalidArgument)
+			name: "block_device_success",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(true, nil)
+				m.EXPECT().GetBlockSizeBytes(gomock.Eq("/volume/path")).Return(int64(1000), nil)
+				return m
 			},
+			expectedResp: &csi.NodeExpandVolumeResponse{CapacityBytes: int64(1000)},
 		},
 		{
-			name: "fail error on unpublish",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnpublishVolumeRequest{
-					TargetPath: targetPath,
-					VolumeId:   volumeID,
-				}
-
-				mockMounter.EXPECT().Unpublish(gomock.Eq(targetPath)).Return(errors.New("test Unpublish error"))
-				_, err := awsDriver.NodeUnpublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+			name: "get_device_name_error",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
 			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(false, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/volume/path")).Return("", 0, errors.New("failed to get device name"))
+				return m
+			},
+			metadataMock: nil,
+			expectedResp: nil,
+			expectedErr:  status.Error(codes.Internal, "failed to get device name from mount /volume/path: failed to get device name"),
 		},
 		{
-			name: "fail another operation in-flight on given volumeId",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-				awsDriver := &nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeUnpublishVolumeRequest{
-					TargetPath: targetPath,
-					VolumeId:   volumeID,
-				}
-
-				awsDriver.inFlight.Insert(volumeID)
-				_, err := awsDriver.NodeUnpublishVolume(context.TODO(), req)
-				expectErr(t, err, codes.Aborted)
+			name: "find_device_path_error",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(false, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/volume/path")).Return("device-name", 1, nil)
+				m.EXPECT().FindDevicePath(gomock.Eq("device-name"), gomock.Eq("vol-test"), gomock.Eq(""), gomock.Eq("us-west-2")).Return("", errors.New("failed to find device path"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedResp: nil,
+			expectedErr:  status.Error(codes.Internal, "failed to find device path for device name device-name for mount /volume/path: failed to find device path"),
+		},
+		{
+			name: "resize_error",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
+			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(false, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/volume/path")).Return("device-name", 1, nil)
+				m.EXPECT().FindDevicePath(gomock.Eq("device-name"), gomock.Eq("vol-test"), gomock.Eq(""), gomock.Eq("us-west-2")).Return("/dev/xvdba", nil)
+				m.EXPECT().Resize(gomock.Eq("/dev/xvdba"), gomock.Eq("/volume/path")).Return(false, errors.New("failed to resize volume"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedResp: nil,
+			expectedErr:  status.Error(codes.Internal, "Could not resize volume \"vol-test\" (\"/dev/xvdba\"): failed to resize volume"),
+		},
+		{
+			name: "get_block_size_bytes_error_after_resize",
+			req: &csi.NodeExpandVolumeRequest{
+				VolumeId:   "vol-test",
+				VolumePath: "/volume/path",
 			},
+			mounterMock: func(ctrl *gomock.Controller) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().IsBlockDevice(gomock.Eq("/volume/path")).Return(false, nil)
+				m.EXPECT().GetDeviceNameFromMount(gomock.Eq("/volume/path")).Return("device-name", 1, nil)
+				m.EXPECT().FindDevicePath(gomock.Eq("device-name"), gomock.Eq("vol-test"), gomock.Eq(""), gomock.Eq("us-west-2")).Return("/dev/xvdba", nil)
+				m.EXPECT().Resize(gomock.Eq("/dev/xvdba"), gomock.Eq("/volume/path")).Return(true, nil)
+				m.EXPECT().GetBlockSizeBytes(gomock.Eq("/dev/xvdba")).Return(int64(0), errors.New("failed to get block size"))
+				return m
+			},
+			metadataMock: func(ctrl *gomock.Controller) *metadata.MockMetadataService {
+				m := metadata.NewMockMetadataService(ctrl)
+				m.EXPECT().GetRegion().Return("us-west-2")
+				return m
+			},
+			expectedResp: nil,
+			expectedErr:  status.Error(codes.Internal, "failed to get block capacity on path /volume/path: failed to get block size"),
 		},
 	}
 
 	for _, tc := range testCases {
-		t.Run(tc.name, tc.testFunc)
+		t.Run(tc.name, func(t *testing.T) {
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
+
+			var mounter *mounter.MockMounter
+			if tc.mounterMock != nil {
+				mounter = tc.mounterMock(ctrl)
+			}
+
+			var metadata *metadata.MockMetadataService
+			if tc.metadataMock != nil {
+				metadata = tc.metadataMock(ctrl)
+			}
+
+			driver := &NodeService{
+				mounter:  mounter,
+				metadata: metadata,
+			}
+
+			resp, err := driver.NodeExpandVolume(context.Background(), tc.req)
+			if !reflect.DeepEqual(err, tc.expectedErr) {
+				t.Fatalf("Expected error '%v' but got '%v'", tc.expectedErr, err)
+			}
+
+			if !reflect.DeepEqual(resp, tc.expectedResp) {
+				t.Fatalf("Expected response '%v' but got '%v'", tc.expectedResp, resp)
+			}
+		})
 	}
 }
 
 func TestNodeGetVolumeStats(t *testing.T) {
 	testCases := []struct {
-		name     string
-		testFunc func(t *testing.T)
+		name           string
+		validVolId     bool
+		validPath      bool
+		metricsStatErr bool
+		mounterMock    func(mockCtl *gomock.Controller, dir string) *mounter.MockMounter
+		expectedErr    func(dir string) error
 	}{
 		{
-			name: "success normal",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-				VolumePath := "./test"
-				err := os.MkdirAll(VolumePath, 0644)
-				if err != nil {
-					t.Fatalf("fail to create dir: %v", err)
-				}
-				defer os.RemoveAll(VolumePath)
-
-				mockMounter.EXPECT().PathExists(VolumePath).Return(true, nil)
-
-				awsDriver := nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeGetVolumeStatsRequest{
-					VolumeId:   volumeID,
-					VolumePath: VolumePath,
-				}
-				_, err = awsDriver.NodeGetVolumeStats(context.TODO(), req)
-				if err != nil {
-					t.Fatalf("Expect no error but got: %v", err)
-				}
+			name:       "success normal",
+			validVolId: true,
+			validPath:  true,
+			mounterMock: func(ctrl *gomock.Controller, dir string) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PathExists(dir).Return(true, nil)
+				m.EXPECT().IsBlockDevice(gomock.Eq(dir)).Return(false, nil)
+				return m
+			},
+			expectedErr: func(dir string) error {
+				return nil
 			},
 		},
 		{
-			name: "fail path not exist",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-				VolumePath := "/test"
-
-				mockMounter.EXPECT().PathExists(VolumePath).Return(false, nil)
-
-				awsDriver := nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeGetVolumeStatsRequest{
-					VolumeId:   volumeID,
-					VolumePath: VolumePath,
-				}
-				_, err := awsDriver.NodeGetVolumeStats(context.TODO(), req)
-				expectErr(t, err, codes.NotFound)
+			name:       "invalid_volume_id",
+			validVolId: false,
+			expectedErr: func(dir string) error {
+				return status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume ID was empty")
 			},
 		},
 		{
-			name: "fail can't determine block device",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-				VolumePath := "/test"
-
-				mockMounter.EXPECT().PathExists(VolumePath).Return(true, nil)
-
-				awsDriver := nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeGetVolumeStatsRequest{
-					VolumeId:   volumeID,
-					VolumePath: VolumePath,
-				}
-				_, err := awsDriver.NodeGetVolumeStats(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+			name:       "invalid_volume_path",
+			validVolId: true,
+			validPath:  false,
+			expectedErr: func(dir string) error {
+				return status.Error(codes.InvalidArgument, "NodeGetVolumeStats volume path was empty")
 			},
 		},
 		{
-			name: "fail error calling existsPath",
-			testFunc: func(t *testing.T) {
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockMetadata := metadata.NewMockMetadataService(mockCtl)
-				mockMounter := NewMockMounter(mockCtl)
-				mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-				VolumePath := "/test"
-
-				mockMounter.EXPECT().PathExists(VolumePath).Return(false, errors.New("get existsPath call fail"))
-
-				awsDriver := nodeService{
-					metadata:         mockMetadata,
-					mounter:          mockMounter,
-					deviceIdentifier: mockDeviceIdentifier,
-					inFlight:         internal.NewInFlight(),
-				}
-
-				req := &csi.NodeGetVolumeStatsRequest{
-					VolumeId:   volumeID,
-					VolumePath: VolumePath,
-				}
-				_, err := awsDriver.NodeGetVolumeStats(context.TODO(), req)
-				expectErr(t, err, codes.Internal)
+			name:       "path_exists_error",
+			validVolId: true,
+			validPath:  true,
+			mounterMock: func(ctrl *gomock.Controller, dir string) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PathExists(dir).Return(false, errors.New("path exists error"))
+				return m
+			},
+			expectedErr: func(dir string) error {
+				return status.Errorf(codes.Internal, "unknown error when stat on %s: path exists error", dir)
 			},
 		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, tc.testFunc)
-	}
-
-}
-
-func TestNodeGetCapabilities(t *testing.T) {
-	mockCtl := gomock.NewController(t)
-	defer mockCtl.Finish()
-
-	mockMetadata := metadata.NewMockMetadataService(mockCtl)
-	mockMounter := NewMockMounter(mockCtl)
-	mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-	awsDriver := nodeService{
-		metadata:         mockMetadata,
-		mounter:          mockMounter,
-		deviceIdentifier: mockDeviceIdentifier,
-		inFlight:         internal.NewInFlight(),
-	}
-
-	caps := []*csi.NodeServiceCapability{
 		{
-			Type: &csi.NodeServiceCapability_Rpc{
-				Rpc: &csi.NodeServiceCapability_RPC{
-					Type: csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME,
-				},
+			name:       "path_does_not_exist",
+			validVolId: true,
+			validPath:  true,
+			mounterMock: func(ctrl *gomock.Controller, dir string) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PathExists(dir).Return(false, nil)
+				return m
+			},
+			expectedErr: func(dir string) error {
+				return status.Errorf(codes.NotFound, "path %s does not exist", dir)
 			},
 		},
 		{
-			Type: &csi.NodeServiceCapability_Rpc{
-				Rpc: &csi.NodeServiceCapability_RPC{
-					Type: csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
-				},
+			name:       "is_block_device_error",
+			validVolId: true,
+			validPath:  true,
+			mounterMock: func(ctrl *gomock.Controller, dir string) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PathExists(dir).Return(true, nil)
+				m.EXPECT().IsBlockDevice(gomock.Eq(dir)).Return(false, errors.New("is block device error"))
+				return m
+			},
+			expectedErr: func(dir string) error {
+				return status.Errorf(codes.Internal, "failed to determine whether %s is block device: is block device error", dir)
 			},
 		},
 		{
-			Type: &csi.NodeServiceCapability_Rpc{
-				Rpc: &csi.NodeServiceCapability_RPC{
-					Type: csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
-				},
+			name:       "get_block_size_bytes_error",
+			validVolId: true,
+			validPath:  true,
+			mounterMock: func(ctrl *gomock.Controller, dir string) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PathExists(dir).Return(true, nil)
+				m.EXPECT().IsBlockDevice(gomock.Eq(dir)).Return(true, nil)
+				m.EXPECT().GetBlockSizeBytes(dir).Return(int64(0), errors.New("get block size bytes error"))
+				return m
+			},
+			expectedErr: func(dir string) error {
+				return status.Errorf(codes.Internal, "failed to get block capacity on path %s: %v", dir, "get block size bytes error")
 			},
 		},
-	}
-	expResp := &csi.NodeGetCapabilitiesResponse{Capabilities: caps}
-
-	req := &csi.NodeGetCapabilitiesRequest{}
-	resp, err := awsDriver.NodeGetCapabilities(context.TODO(), req)
-	if err != nil {
-		srvErr, ok := status.FromError(err)
-		if !ok {
-			t.Fatalf("Could not get error status code from error: %v", srvErr)
-		}
-		t.Fatalf("Expected nil error, got %d message %s", srvErr.Code(), srvErr.Message())
-	}
-	if !reflect.DeepEqual(expResp, resp) {
-		t.Fatalf("Expected response {%+v}, got {%+v}", expResp, resp)
-	}
-}
-
-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
-		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,
-			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,
+			name:       "success block device",
+			validVolId: true,
+			validPath:  true,
+			mounterMock: func(ctrl *gomock.Controller, dir string) *mounter.MockMounter {
+				m := mounter.NewMockMounter(ctrl)
+				m.EXPECT().PathExists(dir).Return(true, nil)
+				m.EXPECT().IsBlockDevice(gomock.Eq(dir)).Return(true, nil)
+				m.EXPECT().GetBlockSizeBytes(dir).Return(int64(1024), nil)
+				return m
+			},
+			expectedErr: func(dir string) error {
+				return nil
+			},
 		},
 	}
+
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			mockCtl := gomock.NewController(t)
-			defer mockCtl.Finish()
-
-			options := &Options{
-				VolumeAttachLimit:         tc.volumeAttachLimit,
-				ReservedVolumeAttachments: tc.reservedVolumeAttachments,
-			}
-
-			mockMounter := NewMockMounter(mockCtl)
-			mockDeviceIdentifier := NewMockDeviceIdentifier(mockCtl)
-
-			mockMetadata := metadata.NewMockMetadataService(mockCtl)
-			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)
-				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)
-				}
-			}
-
-			awsDriver := &nodeService{
-				metadata:         mockMetadata,
-				mounter:          mockMounter,
-				deviceIdentifier: mockDeviceIdentifier,
-				inFlight:         internal.NewInFlight(),
-				options:          options,
-			}
+			ctrl := gomock.NewController(t)
+			defer ctrl.Finish()
 
-			resp, err := awsDriver.NodeGetInfo(context.TODO(), &csi.NodeGetInfoRequest{})
-			if err != nil {
-				srvErr, ok := status.FromError(err)
-				if !ok {
-					t.Fatalf("Could not get error status code from error: %v", srvErr)
-				}
-				t.Fatalf("Expected nil error, got %d message %s", srvErr.Code(), srvErr.Message())
-			}
+			dir := t.TempDir()
 
-			if resp.GetNodeId() != tc.instanceID {
-				t.Fatalf("Expected node ID %q, got %q", tc.instanceID, resp.GetNodeId())
+			var mounter *mounter.MockMounter
+			if tc.mounterMock != nil {
+				mounter = tc.mounterMock(ctrl, dir)
 			}
 
-			at := resp.GetAccessibleTopology()
-			if at.GetSegments()[ZoneTopologyKey] != tc.availabilityZone {
-				t.Fatalf("Expected topology %q, got %q", tc.availabilityZone, at.GetSegments()[ZoneTopologyKey])
-			}
-			if at.GetSegments()[WellKnownZoneTopologyKey] != tc.availabilityZone {
-				t.Fatalf("Expected (well-known) topology %q, got %q", tc.availabilityZone, at.GetSegments()[WellKnownZoneTopologyKey])
-			}
-			if at.GetSegments()[OSTopologyKey] != runtime.GOOS {
-				t.Fatalf("Expected os topology %q, got %q", runtime.GOOS, at.GetSegments()[OSTopologyKey])
+			var metadata *metadata.MockMetadataService
+			driver := &NodeService{
+				mounter:  mounter,
+				metadata: metadata,
 			}
 
-			if at.GetSegments()[AwsAccountIDKey] != tc.outpostArn.AccountID {
-				t.Fatalf("Expected AwsAccountId %q, got %q", tc.outpostArn.AccountID, at.GetSegments()[AwsAccountIDKey])
+			req := &csi.NodeGetVolumeStatsRequest{}
+			if tc.validVolId {
+				req.VolumeId = "vol-test"
 			}
-
-			if at.GetSegments()[AwsRegionKey] != tc.outpostArn.Region {
-				t.Fatalf("Expected AwsRegion %q, got %q", tc.outpostArn.Region, at.GetSegments()[AwsRegionKey])
+			if tc.validPath {
+				req.VolumePath = dir
 			}
-
-			if at.GetSegments()[AwsOutpostIDKey] != tc.outpostArn.Resource {
-				t.Fatalf("Expected AwsOutpostID %q, got %q", tc.outpostArn.Resource, at.GetSegments()[AwsOutpostIDKey])
+			if tc.metricsStatErr {
+				req.VolumePath = "fake-path"
 			}
 
-			if at.GetSegments()[AwsPartitionKey] != tc.outpostArn.Partition {
-				t.Fatalf("Expected AwsPartition %q, got %q", tc.outpostArn.Partition, at.GetSegments()[AwsPartitionKey])
-			}
+			_, err := driver.NodeGetVolumeStats(context.TODO(), req)
 
-			if resp.GetMaxVolumesPerNode() != tc.expMaxVolumes {
-				t.Fatalf("Expected %d max volumes per node, got %d", tc.expMaxVolumes, resp.GetMaxVolumesPerNode())
+			if !reflect.DeepEqual(err, tc.expectedErr(dir)) {
+				t.Fatalf("Expected error '%v' but got '%v'", tc.expectedErr(dir), err)
 			}
 		})
 	}
@@ -2372,26 +2333,6 @@ func TestRemoveNotReadyTaint(t *testing.T) {
 		setup     func(t *testing.T, mockCtl *gomock.Controller) func() (kubernetes.Interface, error)
 		expResult error
 	}{
-		{
-			name: "missing CSI_NODE_NAME",
-			setup: func(t *testing.T, mockCtl *gomock.Controller) func() (kubernetes.Interface, error) {
-				return func() (kubernetes.Interface, error) {
-					t.Fatalf("Unexpected call to k8s client getter")
-					return nil, nil
-				}
-			},
-			expResult: nil,
-		},
-		{
-			name: "failed to setup k8s client",
-			setup: func(t *testing.T, mockCtl *gomock.Controller) func() (kubernetes.Interface, error) {
-				t.Setenv("CSI_NODE_NAME", nodeName)
-				return func() (kubernetes.Interface, error) {
-					return nil, fmt.Errorf("Failed setup!")
-				}
-			},
-			expResult: nil,
-		},
 		{
 			name: "failed to get node",
 			setup: func(t *testing.T, mockCtl *gomock.Controller) func() (kubernetes.Interface, error) {
@@ -2694,7 +2635,11 @@ func TestRemoveNotReadyTaint(t *testing.T) {
 			defer mockCtl.Finish()
 
 			k8sClientGetter := tc.setup(t, mockCtl)
-			result := removeNotReadyTaint(k8sClientGetter)
+			client, err := k8sClientGetter()
+			if err != nil {
+				t.Fatalf("Unexpected error: %v", err)
+			}
+			result := removeNotReadyTaint(client)
 
 			if (result == nil) != (tc.expResult == nil) {
 				t.Fatalf("expected %v, got %v", tc.expResult, result)
@@ -2709,18 +2654,32 @@ func TestRemoveNotReadyTaint(t *testing.T) {
 }
 
 func TestRemoveTaintInBackground(t *testing.T) {
-	mockRemovalCount := 0
-	mockRemovalFunc := func(_ metadata.KubernetesAPIClient) error {
-		mockRemovalCount += 1
-		if mockRemovalCount == 3 {
-			return nil
-		} else {
+	t.Run("Successful taint removal", func(t *testing.T) {
+		mockRemovalCount := 0
+		mockRemovalFunc := func(_ kubernetes.Interface) error {
+			mockRemovalCount += 1
+			if mockRemovalCount == 3 {
+				return nil
+			} else {
+				return fmt.Errorf("Taint removal failed!")
+			}
+		}
+		removeTaintInBackground(nil, taintRemovalBackoff, mockRemovalFunc)
+		assert.Equal(t, 3, mockRemovalCount)
+	})
+
+	t.Run("Retries exhausted", func(t *testing.T) {
+		mockRemovalCount := 0
+		mockRemovalFunc := func(_ kubernetes.Interface) error {
+			mockRemovalCount += 1
 			return fmt.Errorf("Taint removal failed!")
 		}
-	}
-
-	removeTaintInBackground(nil, mockRemovalFunc)
-	assert.Equal(t, 3, mockRemovalCount)
+		removeTaintInBackground(nil, wait.Backoff{
+			Steps:    5,
+			Duration: 1 * time.Millisecond,
+		}, mockRemovalFunc)
+		assert.Equal(t, 5, mockRemovalCount)
+	})
 }
 
 func getNodeMock(mockCtl *gomock.Controller, nodeName string, returnNode *corev1.Node, returnError error) (kubernetes.Interface, *MockNodeInterface) {
@@ -2734,18 +2693,3 @@ func getNodeMock(mockCtl *gomock.Controller, nodeName string, returnNode *corev1
 
 	return mockClient, mockNode
 }
-
-func expectErr(t *testing.T, actualErr error, expectedCode codes.Code) {
-	if actualErr == nil {
-		t.Fatalf("Expect error but got no error")
-	}
-
-	status, ok := status.FromError(actualErr)
-	if !ok {
-		t.Fatalf("Failed to get error status code from error: %v", actualErr)
-	}
-
-	if status.Code() != expectedCode {
-		t.Fatalf("Expected error code %d, got %d message %s", codes.InvalidArgument, status.Code(), status.Message())
-	}
-}
diff --git a/pkg/mounter/mount_test.go b/pkg/mounter/mount_test.go
new file mode 100644
index 00000000..0e9c3b34
--- /dev/null
+++ b/pkg/mounter/mount_test.go
@@ -0,0 +1,308 @@
+//go:build linux
+// +build linux
+
+/*
+Copyright 2020 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mounter
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"k8s.io/mount-utils"
+
+	utilexec "k8s.io/utils/exec"
+	fakeexec "k8s.io/utils/exec/testing"
+)
+
+func TestNeedResize(t *testing.T) {
+	testcases := []struct {
+		name            string
+		devicePath      string
+		deviceMountPath string
+		deviceSize      string
+		cmdOutputFsType string
+		expectError     bool
+		expectResult    bool
+	}{
+		{
+			name:            "False - Unsupported fs type",
+			devicePath:      "/dev/test1",
+			deviceMountPath: "/mnt/test1",
+			deviceSize:      "2048",
+			cmdOutputFsType: "TYPE=ntfs",
+			expectError:     true,
+			expectResult:    false,
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			fcmd := fakeexec.FakeCmd{
+				CombinedOutputScript: []fakeexec.FakeAction{
+					func() ([]byte, []byte, error) { return []byte(test.deviceSize), nil, nil },
+					func() ([]byte, []byte, error) { return []byte(test.cmdOutputFsType), nil, nil },
+				},
+			}
+			fexec := fakeexec.FakeExec{
+				CommandScript: []fakeexec.FakeCommandAction{
+					func(cmd string, args ...string) utilexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
+					func(cmd string, args ...string) utilexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
+				},
+			}
+			safe := mount.SafeFormatAndMount{
+				Interface: mount.New(""),
+				Exec:      &fexec,
+			}
+			fakeMounter := NodeMounter{&safe}
+
+			needResize, err := fakeMounter.NeedResize(test.devicePath, test.deviceMountPath)
+			if needResize != test.expectResult {
+				t.Fatalf("Expect result is %v but got %v", test.expectResult, needResize)
+			}
+			if !test.expectError && err != nil {
+				t.Fatalf("Expect no error but got %v", err)
+			}
+		})
+	}
+}
+
+func TestMakeDir(t *testing.T) {
+	// Setup the full driver and its environment
+	dir, err := os.MkdirTemp("", "mount-ebs-csi")
+	if err != nil {
+		t.Fatalf("error creating directory %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	targetPath := filepath.Join(dir, "targetdir")
+
+	mountObj, err := NewNodeMounter()
+	if err != nil {
+		t.Fatalf("error creating mounter %v", err)
+	}
+
+	if mountObj.MakeDir(targetPath) != nil {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+	if mountObj.MakeDir(targetPath) != nil {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+	if exists, err := mountObj.PathExists(targetPath); !exists {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+}
+
+func TestMakeFile(t *testing.T) {
+	// Setup the full driver and its environment
+	dir, err := os.MkdirTemp("", "mount-ebs-csi")
+	if err != nil {
+		t.Fatalf("error creating directory %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	targetPath := filepath.Join(dir, "targetfile")
+
+	mountObj, err := NewNodeMounter()
+	if err != nil {
+		t.Fatalf("error creating mounter %v", err)
+	}
+
+	if mountObj.MakeFile(targetPath) != nil {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+	if mountObj.MakeFile(targetPath) != nil {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+	if exists, err := mountObj.PathExists(targetPath); !exists {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+}
+
+func TestPathExists(t *testing.T) {
+	// Setup the full driver and its environment
+	dir, err := os.MkdirTemp("", "mount-ebs-csi")
+	if err != nil {
+		t.Fatalf("error creating directory %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	targetPath := filepath.Join(dir, "notafile")
+
+	mountObj, err := NewNodeMounter()
+	if err != nil {
+		t.Fatalf("error creating mounter %v", err)
+	}
+
+	exists, err := mountObj.PathExists(targetPath)
+
+	if err != nil {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+	if exists {
+		t.Fatalf("Expected file %s to not exist", targetPath)
+	}
+
+}
+
+func TestGetDeviceName(t *testing.T) {
+	// Setup the full driver and its environment
+	dir, err := os.MkdirTemp("", "mount-ebs-csi")
+	if err != nil {
+		t.Fatalf("error creating directory %v", err)
+	}
+	defer os.RemoveAll(dir)
+
+	targetPath := filepath.Join(dir, "notafile")
+
+	mountObj, err := NewNodeMounter()
+	if err != nil {
+		t.Fatalf("error creating mounter %v", err)
+	}
+
+	if _, _, err := mountObj.GetDeviceNameFromMount(targetPath); err != nil {
+		t.Fatalf("Expect no error but got: %v", err)
+	}
+
+}
+
+func TestFindDevicePath(t *testing.T) {
+	testCases := []struct {
+		name            string
+		volumeID        string
+		partition       string
+		region          string
+		createTempDir   bool
+		symlink         bool
+		verifyErr       error
+		deviceSize      string
+		cmdOutputFsType string
+		expectedErr     error
+	}{
+		{
+			name:            "Device path exists and matches volume ID",
+			volumeID:        "vol-1234567890abcdef0",
+			partition:       "1",
+			createTempDir:   true,
+			symlink:         false,
+			verifyErr:       nil,
+			deviceSize:      "1024",
+			cmdOutputFsType: "ext4",
+			expectedErr:     nil,
+		},
+		{
+			name:            "Device path doesn't exist",
+			volumeID:        "vol-1234567890abcdef0",
+			partition:       "1",
+			createTempDir:   false,
+			symlink:         false,
+			verifyErr:       nil,
+			deviceSize:      "1024",
+			cmdOutputFsType: "ext4",
+			expectedErr:     fmt.Errorf("no device path for device %q volume %q found", "/temp/vol-1234567890abcdef0", "vol-1234567890abcdef0"),
+		},
+		{
+			name:            "SBE region fallback",
+			volumeID:        "vol-1234567890abcdef0",
+			partition:       "1",
+			region:          "snow",
+			createTempDir:   false,
+			symlink:         false,
+			verifyErr:       nil,
+			deviceSize:      "1024",
+			cmdOutputFsType: "ext4",
+			expectedErr:     nil,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			var tmpDir string
+			var err error
+
+			if tc.createTempDir {
+				tmpDir, err = os.MkdirTemp("", "temp-test-device-path")
+				if err != nil {
+					t.Fatalf("Failed to create temporary directory: %v", err)
+				}
+				defer os.RemoveAll(tmpDir)
+			} else {
+				tmpDir = "/temp"
+			}
+
+			devicePath := filepath.Join(tmpDir, tc.volumeID)
+			expectedResult := devicePath + tc.partition
+
+			fcmd := fakeexec.FakeCmd{
+				CombinedOutputScript: []fakeexec.FakeAction{
+					func() ([]byte, []byte, error) { return []byte(tc.deviceSize), nil, nil },
+					func() ([]byte, []byte, error) { return []byte(tc.cmdOutputFsType), nil, nil },
+				},
+			}
+			fexec := fakeexec.FakeExec{
+				CommandScript: []fakeexec.FakeCommandAction{
+					func(cmd string, args ...string) utilexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
+					func(cmd string, args ...string) utilexec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
+				},
+			}
+
+			safe := mount.SafeFormatAndMount{
+				Interface: mount.New(""),
+				Exec:      &fexec,
+			}
+
+			fakeMounter := NodeMounter{&safe}
+
+			if tc.createTempDir {
+				if tc.symlink {
+					symlinkErr := os.Symlink(devicePath, devicePath)
+					if symlinkErr != nil {
+						t.Fatalf("Failed to create symlink: %v", err)
+					}
+				} else {
+					_, osCreateErr := os.Create(devicePath)
+					if osCreateErr != nil {
+						t.Fatalf("Failed to create device path: %v", err)
+					}
+				}
+			}
+
+			result, err := fakeMounter.FindDevicePath(devicePath, tc.volumeID, tc.partition, tc.region)
+
+			if tc.region == "snow" {
+				expectedResult = "/dev/vd" + tc.volumeID[len(tc.volumeID)-1:] + tc.partition
+			}
+
+			if tc.expectedErr == nil {
+				assert.Equal(t, expectedResult, result)
+				assert.NoError(t, err)
+			} else {
+				assert.Empty(t, result)
+				assert.EqualError(t, err, tc.expectedErr.Error())
+			}
+		})
+	}
+}
diff --git a/pkg/resizefs/resizefs_windows_test.go b/pkg/resizefs/resizefs_windows_test.go
deleted file mode 100644
index 088931b9..00000000
--- a/pkg/resizefs/resizefs_windows_test.go
+++ /dev/null
@@ -1,59 +0,0 @@
-//go:build windows
-// +build windows
-
-package resizefs
-
-import (
-	"errors"
-	"github.com/golang/mock/gomock"
-	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/mounter"
-	"testing"
-)
-
-func TestResize(t *testing.T) {
-	testCases := []struct {
-		name            string
-		deviceMountPath string
-		expectedResult  bool
-		expectedError   bool
-		prepare         func(m *mounter.MockProxyMounter)
-	}{
-		{
-			name:            "success: normal",
-			deviceMountPath: "/mnt/test",
-			expectedResult:  true,
-			expectedError:   false,
-			prepare: func(m *mounter.MockProxyMounter) {
-				m.EXPECT().ResizeVolume(gomock.Eq("/mnt/test")).Return(true, nil)
-			},
-		},
-		{
-			name:            "failure: invalid device mount path",
-			deviceMountPath: "/",
-			expectedResult:  false,
-			expectedError:   true,
-			prepare: func(m *mounter.MockProxyMounter) {
-				m.EXPECT().ResizeVolume(gomock.Eq("/")).Return(false, errors.New("Could not resize volume"))
-			},
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			mockCtl := gomock.NewController(t)
-			defer mockCtl.Finish()
-
-			mockProxyMounter := mounter.NewMockProxyMounter(mockCtl)
-			tc.prepare(mockProxyMounter)
-
-			r := NewResizeFs(mockProxyMounter)
-			res, err := r.Resize("", tc.deviceMountPath)
-
-			if tc.expectedError && err == nil {
-				t.Fatalf("Expected error, but got no error")
-			}
-			if res != tc.expectedResult {
-				t.Fatalf("Expected result is: %v, but got: %v", tc.expectedResult, res)
-			}
-		})
-	}
-}