Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #2254 (2024-12-04 16:01:53)

Add csi-sanity tests
#### What type of PR is this?

<!--
Add one of the following kinds:
/kind bug
/kind cleanup
/kind documentation
/kind feature
-->
/kind feature
#### What is this PR about? / Why do we need it?
This PR adds csi-sanity tests to the driver to be enable in CI afterwards.

#### How was this change tested?
Ran `make test-sanity`
and received the following results

```
Ran 76 of 92 Specs in 0.355 seconds
SUCCESS! -- 76 Passed | 0 Failed | 1 Pending | 15 Skipped
--- PASS: TestSanity (0.36s)
PASS
ok  	github.com/kubernetes-sigs/aws-ebs-csi-driver/tests/sanity	1.429s
```
#### Does this PR introduce a user-facing change?
<!--
If no, just write "NONE" in the release-note block below.
If yes, enter your extended release note in the block below.
-->
```release-note
Add csi-sanity tests
```


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 [elijahquinones](https://github.com/elijahquinones). 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":["elijahquinones"]} -->
ElijahQuinones: /hold
github-actions[bot]: <!-- pr-coverage -->
## Code Coverage Diff
This PR does not change the code coverage
k8s-ci-robot: @ElijahQuinones: 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 | 5d1d3b38b0654c6d39bcf2216a53c502b1c8efbb | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1864339364973121536) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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-e2e-external-eks-windows | 72c4b10d8685fdc4a37035e6b5d40c3a3b0195a0 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-test-e2e-external-eks-windows/1864345092077129728) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | 3dc7cd8f09d27c519fb155cc76bc15a749b6ae6e | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1869840859835404288) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
ElijahQuinones: > @ElijahQuinones: 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	[3dc7cd8](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/commit/3dc7cd8f09d27c519fb155cc76bc15a749b6ae6e)	[link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1869840859835404288)	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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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.


k8s-ci-robot: New changes are detected. LGTM label has been removed.
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 [andrewsirenko](https://github.com/andrewsirenko). 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":["andrewsirenko"]} -->
k8s-ci-robot: New changes are detected. LGTM label has been removed.
rdpsin: Thanks for the changes. :) 
torredil: /approve
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/2254#issuecomment-2568074021" title="Approved">torredil</a>*

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).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

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

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

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":[]} -->
k8s-ci-robot: @ElijahQuinones: 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 | e209fbd356124aeb604324e1d08a045605aa6656 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874861267408130048) | unknown | `/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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | e209fbd356124aeb604324e1d08a045605aa6656 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874863781339729920) | unknown | `/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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
ElijahQuinones: /retest
k8s-ci-robot: New changes are detected. LGTM label has been removed.
k8s-ci-robot: @ElijahQuinones: 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 | 94d36e0e8756dfe37ece8c68e76d025ff35ac301 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874884371861737472) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | 8e23c54adc5a5af5f5be02a3f6a2c3eacd77029b | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874892515358806016) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | bff5590e61be3a176391db3b78c24b9005248ed2 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874894968208756736) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | 523d75aec8af832ec759ff7554a0008a35e948b6 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874902551657189376) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | 523d75aec8af832ec759ff7554a0008a35e948b6 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874907369289289728) | unknown | `/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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | 2b7aa1efdbc1e11c58b2b6c5c5503286585c644b | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874914378155823104) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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: @ElijahQuinones: 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 | 2c5af7ce70c5fba77cba65826af22be363ffbdf2 | [link](https://prow.k8s.io/view/gs/kubernetes-ci-logs/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/2254/pull-aws-ebs-csi-driver-verify/1874919940390129664) | 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=2254). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3AElijahQuinones). 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-sigs/prow](https://github.com/kubernetes-sigs/prow/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/.golangci.yml b/.golangci.yml
index f77b17e7..eedba533 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -23,6 +23,9 @@ issues:
     - path: tests/e2e
       linters:
         - gosec
+    - path: tests/sanity
+      linters:
+        - gosec
 linters-settings:
   revive:
     rules:
diff --git a/Makefile b/Makefile
index 04439570..4db3aa5e 100644
--- a/Makefile
+++ b/Makefile
@@ -82,13 +82,6 @@ test/coverage:
 	go tool cover -html=filtered_cover.out -o coverage.html
 	rm cover.out filtered_cover.out
 
-# TODO: Re-enable sanity tests
-# sanity tests have been disabled with the removal of NewFakeDriver, which was previously created to instantiate a fake driver utilized for testing. 
-# to re-enable tests, implement sanity tests creating a new driver instance by injecting mocked dependencies.
-#.PHONY: test-sanity
-#test-sanity:
-#	go test -v -race ./tests/sanity/...
-
 .PHONY: tools
 tools: bin/aws bin/ct bin/eksctl bin/ginkgo bin/golangci-lint bin/gomplate bin/helm bin/kops bin/kubetest2 bin/mockgen bin/shfmt
 
@@ -127,6 +120,10 @@ cluster/uninstall: bin/helm bin/aws
 ## E2E targets
 # Targets to run e2e tests
 
+.PHONY: sanity
+sanity:
+	go test -v -race ./tests/sanity/...
+
 .PHONY: e2e/single-az
 e2e/single-az: bin/helm bin/ginkgo
 	AWS_AVAILABILITY_ZONES=us-west-2a \
diff --git a/go.mod b/go.mod
index 37d0308f..33ea412d 100644
--- a/go.mod
+++ b/go.mod
@@ -13,6 +13,7 @@ require (
 	github.com/google/uuid v1.6.0
 	github.com/kubernetes-csi/csi-proxy/client v1.1.3
 	github.com/kubernetes-csi/csi-proxy/v2 v2.0.0-alpha.1
+	github.com/kubernetes-csi/csi-test/v5 v5.3.1
 	github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0
 	github.com/onsi/ginkgo/v2 v2.22.1
 	github.com/onsi/gomega v1.36.2
@@ -144,6 +145,7 @@ require (
 	gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	k8s.io/apiextensions-apiserver v0.32.0 // indirect
 	k8s.io/apiserver v0.32.0 // indirect
diff --git a/go.sum b/go.sum
index 28791c35..5732c987 100644
--- a/go.sum
+++ b/go.sum
@@ -249,6 +249,8 @@ github.com/kubernetes-csi/csi-proxy/client v1.1.3 h1:FdGU7NtxGhQX2wTfnuscmThG920
 github.com/kubernetes-csi/csi-proxy/client v1.1.3/go.mod h1:SfK4HVKQdMH5KrffivddAWgX5hl3P5KmnuOTBbDNboU=
 github.com/kubernetes-csi/csi-proxy/v2 v2.0.0-alpha.1 h1:tVPvlL5N5X598hrO3g9rhyoi6h0LP4RpSJlGHItsbEE=
 github.com/kubernetes-csi/csi-proxy/v2 v2.0.0-alpha.1/go.mod h1:pacx+PW7lLlu6kAvpr8Lgq/5fdiAsKxOtXXFHMaLMb8=
+github.com/kubernetes-csi/csi-test/v5 v5.3.1 h1:Wiukp1In+kif+BFo6q2ExjgB+MbrAz4jZWzGfijypuY=
+github.com/kubernetes-csi/csi-test/v5 v5.3.1/go.mod h1:7hA2cSYJ6T8CraEZPA6zqkLZwemjBD54XAnPsPC3VpA=
 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0 h1:nHHjmvjitIiyPlUHk/ofpgvBcNcawJLtf4PYHORLjAA=
 github.com/kubernetes-csi/external-snapshotter/client/v4 v4.2.0/go.mod h1:YBCo4DoEeDndqvAn6eeu0vWM7QdXmHEeI9cFWplmBys=
 github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
@@ -737,6 +739,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/pkg/cloud/metadata/mock_metadata.go b/pkg/cloud/metadata/mock_metadata.go
index 01a7c0af..1aac423f 100644
--- a/pkg/cloud/metadata/mock_metadata.go
+++ b/pkg/cloud/metadata/mock_metadata.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/cloud/mock_cloud.go b/pkg/cloud/mock_cloud.go
index 2cc08c9d..39cad554 100644
--- a/pkg/cloud/mock_cloud.go
+++ b/pkg/cloud/mock_cloud.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/cloud/mock_ec2.go b/pkg/cloud/mock_ec2.go
index 0aa38267..8217cc19 100644
--- a/pkg/cloud/mock_ec2.go
+++ b/pkg/cloud/mock_ec2.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/driver/controller_modify_volume.go b/pkg/driver/controller_modify_volume.go
index 10f40b8c..1a160809 100644
--- a/pkg/driver/controller_modify_volume.go
+++ b/pkg/driver/controller_modify_volume.go
@@ -150,6 +150,8 @@ func executeModifyVolumeRequest(c cloud.Cloud) func(string, modifyVolumeRequest)
 			if err != nil {
 				if errors.Is(err, cloud.ErrInvalidArgument) {
 					return 0, status.Errorf(codes.InvalidArgument, "Could not modify volume (invalid argument) %q: %v", volumeID, err)
+				} else if errors.Is(err, cloud.ErrNotFound) {
+					return 0, status.Errorf(codes.NotFound, "Could not modify volume (not found) %q: %v", volumeID, err)
 				}
 				return 0, status.Errorf(codes.Internal, "Could not modify volume %q: %v", volumeID, err)
 			} else {
diff --git a/pkg/driver/mock_k8s_client.go b/pkg/driver/mock_k8s_client.go
index 0a851085..701c14fd 100644
--- a/pkg/driver/mock_k8s_client.go
+++ b/pkg/driver/mock_k8s_client.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/driver/mock_k8s_corev1.go b/pkg/driver/mock_k8s_corev1.go
index 89f31c31..ec936602 100644
--- a/pkg/driver/mock_k8s_corev1.go
+++ b/pkg/driver/mock_k8s_corev1.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/driver/mock_k8s_storagev1.go b/pkg/driver/mock_k8s_storagev1.go
index 026218ae..ae410de5 100644
--- a/pkg/driver/mock_k8s_storagev1.go
+++ b/pkg/driver/mock_k8s_storagev1.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/driver/mock_k8s_storagev1_csinode.go b/pkg/driver/mock_k8s_storagev1_csinode.go
index 3b27d5dd..dd8f7c79 100644
--- a/pkg/driver/mock_k8s_storagev1_csinode.go
+++ b/pkg/driver/mock_k8s_storagev1_csinode.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.
diff --git a/pkg/driver/node.go b/pkg/driver/node.go
index 0d03a74c..ef7ae326 100644
--- a/pkg/driver/node.go
+++ b/pkg/driver/node.go
@@ -207,7 +207,7 @@ func (d *NodeService) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol
 
 	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)
+		return nil, status.Errorf(codes.NotFound, "Failed to find device path %s. %v", devicePath, err)
 	}
 
 	klog.V(4).InfoS("NodeStageVolume: find device path", "devicePath", devicePath, "source", source)
@@ -387,7 +387,6 @@ func (d *NodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV
 			return &csi.NodeExpandVolumeResponse{CapacityBytes: bcap}, nil
 		}
 	}
-
 	deviceName, _, err := d.mounter.GetDeviceNameFromMount(volumePath)
 	if err != nil {
 		return nil, status.Errorf(codes.Internal, "failed to get device name from mount %s: %v", volumePath, err)
@@ -395,7 +394,7 @@ func (d *NodeService) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandV
 
 	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)
+		return nil, status.Errorf(codes.NotFound, "failed to find device path for device name %s for mount %s: %v", deviceName, req.GetVolumePath(), err)
 	}
 
 	// TODO: lock per volume ID to have some idempotency
@@ -626,7 +625,7 @@ func (d *NodeService) nodePublishVolumeForBlock(req *csi.NodePublishVolumeReques
 
 	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)
+		return status.Errorf(codes.NotFound, "Failed to find device path %s. %v", devicePath, err)
 	}
 
 	klog.V(4).InfoS("NodePublishVolume [block]: find device path", "devicePath", devicePath, "source", source)
diff --git a/pkg/mounter/mock_mount.go b/pkg/mounter/mock_mount.go
index 0ad2852b..171e8894 100644
--- a/pkg/mounter/mock_mount.go
+++ b/pkg/mounter/mock_mount.go
@@ -1,4 +1,4 @@
-// Copyright 2024 The Kubernetes Authors.
+// Copyright 2025 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.


Test patch:
diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go
index 59f6ebab..3d15923b 100644
--- a/pkg/driver/node_test.go
+++ b/pkg/driver/node_test.go
@@ -642,7 +642,7 @@ func TestNodeStageVolume(t *testing.T) {
 				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")),
+			expectedErr: status.Errorf(codes.NotFound, "Failed to find device path %s. %v", "/dev/xvdba", errors.New("find device path error")),
 		},
 		{
 			name: "path_exists_error",
@@ -1743,7 +1743,7 @@ func TestNodePublishVolume(t *testing.T) {
 				m.EXPECT().GetRegion().Return("us-west-2")
 				return m
 			},
-			expectedErr: status.Error(codes.Internal, "Failed to find device path /dev/xvdba. device path error"),
+			expectedErr: status.Error(codes.NotFound, "Failed to find device path /dev/xvdba. device path error"),
 		},
 	}
 	for _, tc := range testCases {
@@ -2271,7 +2271,7 @@ func TestNodeExpandVolume(t *testing.T) {
 				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"),
+			expectedErr:  status.Error(codes.NotFound, "failed to find device path for device name device-name for mount /volume/path: failed to find device path"),
 		},
 		{
 			name: "resize_error",
diff --git a/tests/sanity/fake_sanity_cloud.go b/tests/sanity/fake_sanity_cloud.go
new file mode 100644
index 00000000..3861db1f
--- /dev/null
+++ b/tests/sanity/fake_sanity_cloud.go
@@ -0,0 +1,219 @@
+// 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.
+// 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 sanity
+
+import (
+	"context"
+	"fmt"
+	"math/rand"
+	"strconv"
+	"time"
+
+	"github.com/aws/aws-sdk-go-v2/service/ec2"
+	"github.com/aws/aws-sdk-go-v2/service/ec2/types"
+	"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/util"
+)
+
+type fakeCloud struct {
+	fakeMetadata     metadata.Metadata
+	mountPath        string
+	disks            map[string]*cloud.Disk
+	snapshots        map[string]*cloud.Snapshot
+	snapshotNameToID map[string]string
+}
+
+func newFakeCloud(fmd metadata.Metadata, mp string) *fakeCloud {
+	return &fakeCloud{
+		fakeMetadata:     fmd,
+		mountPath:        mp,
+		disks:            make(map[string]*cloud.Disk),
+		snapshots:        make(map[string]*cloud.Snapshot),
+		snapshotNameToID: make(map[string]string),
+	}
+}
+
+func (d *fakeCloud) CreateDisk(ctx context.Context, volumeID string, diskOptions *cloud.DiskOptions) (*cloud.Disk, error) {
+	for _, existingDisk := range d.disks {
+		if existingDisk.VolumeID == volumeID && existingDisk.CapacityGiB != util.BytesToGiB(diskOptions.CapacityBytes) {
+			return nil, cloud.ErrAlreadyExists
+		}
+	}
+
+	if diskOptions.SnapshotID != "" {
+		if _, exists := d.snapshots[diskOptions.SnapshotID]; !exists {
+			return nil, cloud.ErrNotFound
+		}
+		newDisk := &cloud.Disk{
+			SnapshotID:       diskOptions.SnapshotID,
+			VolumeID:         volumeID,
+			AvailabilityZone: diskOptions.AvailabilityZone,
+			CapacityGiB:      util.BytesToGiB(diskOptions.CapacityBytes),
+		}
+		d.disks[volumeID] = newDisk
+		return newDisk, nil
+	}
+
+	newDisk := &cloud.Disk{
+		VolumeID:         volumeID,
+		AvailabilityZone: diskOptions.AvailabilityZone,
+		CapacityGiB:      util.BytesToGiB(diskOptions.CapacityBytes),
+	}
+	d.disks[volumeID] = newDisk
+	return newDisk, nil
+}
+func (d *fakeCloud) DeleteDisk(ctx context.Context, volumeID string) (bool, error) {
+	_, exists := d.disks[volumeID]
+	if !exists {
+		return false, cloud.ErrNotFound
+	}
+	delete(d.disks, volumeID)
+	return true, nil
+}
+
+func (d *fakeCloud) GetDiskByID(ctx context.Context, volumeID string) (*cloud.Disk, error) {
+	disk, exists := d.disks[volumeID]
+	if !exists {
+		return nil, cloud.ErrNotFound
+	}
+	return disk, nil
+}
+
+func (d *fakeCloud) CreateSnapshot(ctx context.Context, volumeID string, opts *cloud.SnapshotOptions) (*cloud.Snapshot, error) {
+	snapshotID := fmt.Sprintf("snap-%d", rand.New(rand.NewSource(time.Now().UnixNano())).Uint64())
+
+	_, exists := d.snapshots[snapshotID]
+	if exists {
+		return nil, cloud.ErrAlreadyExists
+	}
+	newSnapshot := &cloud.Snapshot{
+		SnapshotID:     snapshotID,
+		SourceVolumeID: volumeID,
+		CreationTime:   time.Now(),
+		ReadyToUse:     true,
+	}
+	d.snapshots[snapshotID] = newSnapshot
+	d.snapshotNameToID[opts.Tags["CSIVolumeSnapshotName"]] = snapshotID
+	return newSnapshot, nil
+}
+
+func (d *fakeCloud) DeleteSnapshot(ctx context.Context, snapshotID string) (bool, error) {
+	if _, exists := d.snapshots[snapshotID]; !exists {
+		return false, cloud.ErrNotFound
+	}
+	for name, id := range d.snapshotNameToID {
+		if id == snapshotID {
+			delete(d.snapshotNameToID, name)
+			break
+		}
+	}
+	delete(d.snapshots, snapshotID)
+	return true, nil
+}
+func (d *fakeCloud) GetSnapshotByID(ctx context.Context, snapshotID string) (*cloud.Snapshot, error) {
+	snapshot, exists := d.snapshots[snapshotID]
+	if !exists {
+		return nil, cloud.ErrNotFound
+	}
+	return snapshot, nil
+}
+
+func (d *fakeCloud) GetSnapshotByName(ctx context.Context, name string) (*cloud.Snapshot, error) {
+	if snapshotID, exists := d.snapshotNameToID[name]; exists {
+		return d.snapshots[snapshotID], nil
+	}
+	return nil, cloud.ErrNotFound
+}
+
+func (d *fakeCloud) ListSnapshots(ctx context.Context, sourceVolumeID string, maxResults int32, nextToken string) (*cloud.ListSnapshotsResponse, error) {
+	var s []*cloud.Snapshot
+	startIndex := 0
+	var err error
+
+	if nextToken != "" {
+		startIndex, err = strconv.Atoi(nextToken)
+		if err != nil {
+			return nil, fmt.Errorf("invalid next token %s", nextToken)
+		}
+	}
+	var nextTokenStr string
+	count := 0
+	for _, snap := range d.snapshots {
+		if snap.SourceVolumeID == sourceVolumeID || sourceVolumeID == "" {
+			if startIndex <= count {
+				s = append(s, snap)
+				if maxResults > 0 && int32(len(s)) >= maxResults {
+					nextTokenStr = strconv.Itoa(startIndex + int(maxResults))
+					break
+				}
+			}
+			count++
+		}
+	}
+
+	return &cloud.ListSnapshotsResponse{
+		Snapshots: s,
+		NextToken: nextTokenStr,
+	}, nil
+}
+
+func (d *fakeCloud) AttachDisk(ctx context.Context, volumeID string, instanceID string) (string, error) {
+	_, diskExists := d.disks[volumeID]
+	if !diskExists || instanceID != d.fakeMetadata.InstanceID {
+		return "", cloud.ErrNotFound
+	}
+	return d.mountPath, nil
+}
+
+func (d *fakeCloud) DetachDisk(ctx context.Context, volumeID string, instanceID string) error {
+	_, diskExists := d.disks[volumeID]
+	if !diskExists || instanceID != d.fakeMetadata.InstanceID {
+		return cloud.ErrNotFound
+	}
+	return nil
+}
+
+func (d *fakeCloud) ResizeOrModifyDisk(ctx context.Context, volumeID string, newSizeBytes int64, modifyOptions *cloud.ModifyDiskOptions) (int32, error) {
+	disk, exists := d.disks[volumeID]
+	if !exists {
+		return 0, cloud.ErrNotFound
+	}
+	newSizeGiB := util.BytesToGiB(newSizeBytes)
+	disk.CapacityGiB = newSizeGiB
+	d.disks[volumeID] = disk
+	realSizeGiB := newSizeGiB
+	return realSizeGiB, nil
+}
+
+func (d *fakeCloud) AvailabilityZones(ctx context.Context) (map[string]struct{}, error) {
+	return map[string]struct{}{}, nil
+}
+
+func (d *fakeCloud) EnableFastSnapshotRestores(ctx context.Context, availabilityZones []string, snapshotID string) (*ec2.EnableFastSnapshotRestoresOutput, error) {
+	return &ec2.EnableFastSnapshotRestoresOutput{}, nil
+}
+
+func (d *fakeCloud) GetDiskByName(ctx context.Context, name string, capacityBytes int64) (*cloud.Disk, error) {
+	return &cloud.Disk{}, nil
+}
+
+func (d *fakeCloud) ModifyTags(ctx context.Context, volumeID string, tagOptions cloud.ModifyTagsOptions) error {
+	return nil
+}
+
+func (d *fakeCloud) WaitForAttachmentState(ctx context.Context, volumeID, expectedState, expectedInstance, expectedDevice string, alreadyAssigned bool) (*types.VolumeAttachment, error) {
+	return &types.VolumeAttachment{}, nil
+}
diff --git a/tests/sanity/fake_sanity_metadata_service.go b/tests/sanity/fake_sanity_metadata_service.go
new file mode 100644
index 00000000..ea6b7eba
--- /dev/null
+++ b/tests/sanity/fake_sanity_metadata_service.go
@@ -0,0 +1,62 @@
+// 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.
+// 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 sanity
+
+import (
+	"github.com/aws/aws-sdk-go-v2/aws/arn"
+)
+
+type fakeMetadataService struct {
+	instanceID       string
+	region           string
+	availabilityZone string
+	outpostArn       arn.ARN
+}
+
+func newFakeMetadataService(id string, r string, az string, oa arn.ARN) *fakeMetadataService {
+	return &fakeMetadataService{
+		instanceID:       id,
+		region:           r,
+		availabilityZone: az,
+		outpostArn:       oa,
+	}
+}
+func (m *fakeMetadataService) GetInstanceID() string {
+	return m.instanceID
+}
+
+func (m *fakeMetadataService) GetInstanceType() string {
+	return ""
+}
+
+func (m *fakeMetadataService) GetRegion() string {
+	return m.region
+}
+
+func (m *fakeMetadataService) GetAvailabilityZone() string {
+	return m.availabilityZone
+}
+
+func (m *fakeMetadataService) GetNumAttachedENIs() int {
+	return 0
+}
+
+func (m *fakeMetadataService) GetNumBlockDeviceMappings() int {
+	return 0
+}
+
+func (m *fakeMetadataService) GetOutpostArn() arn.ARN {
+	return m.outpostArn
+}
diff --git a/tests/sanity/fake_sanity_mounter.go b/tests/sanity/fake_sanity_mounter.go
new file mode 100644
index 00000000..6ef63fee
--- /dev/null
+++ b/tests/sanity/fake_sanity_mounter.go
@@ -0,0 +1,147 @@
+// 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.
+// 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 sanity
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
+	"k8s.io/mount-utils"
+)
+
+type fakeMounter struct {
+	mounts map[string]string
+}
+
+func newFakeMounter() *fakeMounter {
+	return &fakeMounter{
+		mounts: make(map[string]string),
+	}
+}
+
+func (m *fakeMounter) FindDevicePath(devicePath, volumeID, partition, region string) (string, error) {
+	if len(devicePath) == 0 {
+		return devicePath, cloud.ErrNotFound
+	}
+	return devicePath, nil
+}
+
+func (m *fakeMounter) PreparePublishTarget(target string) error {
+	if err := m.MakeDir(target); err != nil {
+		return fmt.Errorf("could not create dir %q: %w", target, err)
+	}
+	return nil
+}
+
+func (m *fakeMounter) IsBlockDevice(fullPath string) (bool, error) {
+	return false, nil
+}
+
+func (m *fakeMounter) GetBlockSizeBytes(devicePath string) (int64, error) {
+	return 0, nil
+}
+
+func (m *fakeMounter) GetDeviceNameFromMount(mountPath string) (string, int, error) {
+	return m.mounts[mountPath], 0, nil
+}
+
+func (m *fakeMounter) IsCorruptedMnt(err error) bool {
+	return false
+}
+
+func (m *fakeMounter) MakeFile(path string) error {
+	return nil
+}
+
+func (m *fakeMounter) MakeDir(path string) error {
+	err := os.MkdirAll(path, os.FileMode(0755))
+	if err != nil {
+		if !os.IsExist(err) {
+			return err
+		}
+	}
+	return nil
+}
+
+func (m *fakeMounter) PathExists(path string) (bool, error) {
+	_, exists := m.mounts[path]
+	if !exists {
+		return false, nil
+	}
+	return true, nil
+}
+
+func (m *fakeMounter) Resize(devicePath, deviceMountPath string) (bool, error) {
+	return false, nil
+}
+
+func (m *fakeMounter) NeedResize(devicePath string, deviceMountPath string) (bool, error) {
+	return false, nil
+}
+
+func (m *fakeMounter) Unpublish(path string) error {
+	return m.Unstage(path)
+}
+
+func (m *fakeMounter) Unstage(path string) error {
+	err := os.RemoveAll(path)
+	return err
+}
+
+func (m *fakeMounter) Mount(source string, target string, fstype string, options []string) error {
+	m.mounts[target] = source
+	return nil
+}
+
+func (m *fakeMounter) CanSafelySkipMountPointCheck() bool {
+	return false
+}
+
+func (m *fakeMounter) FormatAndMountSensitiveWithFormatOptions(source, target, fstype string, options, sensitiveOptions, formatOptions []string) error {
+	return nil
+}
+
+func (m *fakeMounter) GetMountRefs(pathname string) ([]string, error) {
+	return nil, nil
+}
+
+func (m *fakeMounter) IsLikelyNotMountPoint(file string) (bool, error) {
+	return true, nil
+}
+
+func (m *fakeMounter) IsMountPoint(file string) (bool, error) {
+	return false, nil
+}
+
+func (m *fakeMounter) List() ([]mount.MountPoint, error) {
+	return nil, nil
+}
+
+func (m *fakeMounter) MountSensitive(source, target, fstype string, options, sensitiveOptions []string) error {
+	return nil
+}
+
+func (m *fakeMounter) MountSensitiveWithoutSystemd(source, target, fstype string, options, sensitiveOptions []string) error {
+	return nil
+}
+
+func (m *fakeMounter) MountSensitiveWithoutSystemdWithMountFlags(source, target, fstype string, options, sensitiveOptions, mountFlags []string) error {
+	return nil
+}
+
+func (m *fakeMounter) Unmount(target string) error {
+	return nil
+}
diff --git a/tests/sanity/sanity_test.go b/tests/sanity/sanity_test.go
new file mode 100644
index 00000000..59d6d19d
--- /dev/null
+++ b/tests/sanity/sanity_test.go
@@ -0,0 +1,106 @@
+// 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.
+// 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.
+
+//go:build linux
+// +build linux
+
+package sanity
+
+import (
+	"fmt"
+	"os"
+	"path"
+	"testing"
+
+	"github.com/aws/aws-sdk-go-v2/aws/arn"
+	"github.com/golang/mock/gomock"
+	csisanity "github.com/kubernetes-csi/csi-test/v5/pkg/sanity"
+	"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/util"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/credentials/insecure"
+)
+
+func TestSanity(t *testing.T) {
+	defer func() {
+		if r := recover(); r != nil {
+			t.Errorf("Test panicked: %v", r)
+		}
+	}()
+
+	mockCtrl := gomock.NewController(t)
+	defer mockCtrl.Finish()
+
+	tmpDir, err := os.MkdirTemp("", "csi-sanity-")
+	if err != nil {
+		t.Fatalf("Failed to create sanity temp working dir: %v", err)
+	}
+	defer os.RemoveAll(tmpDir)
+
+	defer func() {
+		if err = os.RemoveAll(tmpDir); err != nil {
+			t.Fatalf("Failed to clean up sanity temp working dir %s: %v", tmpDir, err.Error())
+		}
+	}()
+
+	endpoint := fmt.Sprintf("unix:%s/csi.sock", tmpDir)
+	mountPath := path.Join(tmpDir, "mount")
+	stagePath := path.Join(tmpDir, "stage")
+	instanceID := "i-1234567890abcdef0"
+	region := "us-west-2"
+	availabilityZone := "us-west-2a"
+
+	driverOptions := &driver.Options{
+		Mode:                              driver.AllMode,
+		ModifyVolumeRequestHandlerTimeout: 60,
+		Endpoint:                          endpoint,
+	}
+
+	fakeMetadata := &metadata.Metadata{
+		InstanceID: instanceID,
+		Region:     region,
+	}
+
+	outpostArn := &arn.ARN{
+		Partition: "aws",
+		Service:   "outposts",
+		Region:    "us-west-2",
+		AccountID: "123456789012",
+		Resource:  "op-1234567890abcdef0",
+	}
+
+	drv, err := driver.NewDriver(newFakeCloud(*fakeMetadata, mountPath), driverOptions, newFakeMounter(), newFakeMetadataService(instanceID, region, availabilityZone, *outpostArn), nil)
+	if err != nil {
+		t.Fatalf("Failed to create fake driver: %v", err.Error())
+	}
+	go func() {
+		if err := drv.Run(); err != nil {
+			panic(fmt.Sprintf("%v", err))
+		}
+	}()
+
+	config := csisanity.TestConfig{
+		TargetPath:                  mountPath,
+		StagingPath:                 stagePath,
+		Address:                     endpoint,
+		DialOptions:                 []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials())},
+		IDGen:                       csisanity.DefaultIDGenerator{},
+		TestVolumeSize:              10 * util.GiB,
+		TestVolumeAccessType:        "mount",
+		TestVolumeMutableParameters: map[string]string{"iops": "3014", "throughput": "153"},
+		TestVolumeParameters:        map[string]string{"type": "gp3", "iops": "3000"},
+	}
+	csisanity.Test(t, config)
+}