Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #561 (2020-09-15 01:28:19)

Create volumes in outpost for outpost instances
**Is this a bug fix or adding new feature?**
Bug Fix

**What is this PR about? / Why do we need it?**
[Outposts Documentation](https://docs.aws.amazon.com/outposts/latest/userguide/what-is-outposts.html) for background.

Currently, we're running oblivious to outposts. Volumes are created in the parent region instead of the outpost itself. In order to fix it, we need to pass the `outpostArn` to [CreateVolume](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateVolume.html).

I'm leveraging the `outpost-arn` instance metadata to determine if we're in an outpost instance. There are two possible responses from the endpoint:

- The outpost-arn if it's an outpost instance
- `404` if it's a regular non-outpost instance.

I've verified the behavior using [this test code](https://gist.github.com/ayberk/102a8995569d5e9002bbb27db209b42e) on a test outpost.

**What testing is done?** 
Tested on an actual outpost instance and verified it works. 


k8s-ci-robot: Hi @ayberk. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Faws-ebs-csi-driver).

<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: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/561#" title="Author self-approved">ayberk</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **leakingtapan**
You can assign the PR to them by writing `/assign @leakingtapan` in a comment when ready.

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":["leakingtapan"]} -->
coveralls: ## Pull Request Test Coverage Report for [Build 1198](https://coveralls.io/builds/33459372)

* **26** of **32**   **(81.25%)**  changed or added relevant lines in **4** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.01%**) to **79.946%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [pkg/driver/controller.go](https://coveralls.io/builds/33459372/source?filename=pkg%2Fdriver%2Fcontroller.go#L538) | 11 | 17 | 64.71%
<!-- | **Total:** | **26** | **32** | **81.25%** | -->


|  Totals | [![Coverage Status](https://coveralls.io/builds/33459372/badge)](https://coveralls.io/builds/33459372) |
| :-- | --: |
| Change from base [Build 1196](https://coveralls.io/builds/33406238): |  0.01% |
| Covered Lines: | 1471 |
| Relevant Lines: | 1840 |

---
##### 💛  - [Coveralls](https://coveralls.io)

wongma7: /ok-to-test
k8s-ci-robot: @ayberk: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | f5d046e0780050838e8817b2704e1f5735873a00 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1305917962540027904) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | f5d046e0780050838e8817b2704e1f5735873a00 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1305917962540027904) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | f5d046e0780050838e8817b2704e1f5735873a00 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1305917962535833600) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | f5d046e0780050838e8817b2704e1f5735873a00 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1305917962540027904) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | b05e87dfdf080e3bc7c3fb889f07818af73e6c8f | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1305947407132921861) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | b05e87dfdf080e3bc7c3fb889f07818af73e6c8f | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1305947407132921861) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`
pull-aws-ebs-csi-driver-e2e-single-az | b05e87dfdf080e3bc7c3fb889f07818af73e6c8f | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1305947407137116161) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | b05e87dfdf080e3bc7c3fb889f07818af73e6c8f | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1305947407137116161) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | 0143b8cebcfdef3aea6e6c7201dd6f2aa3b6be0d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1305997522728128514) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | 0143b8cebcfdef3aea6e6c7201dd6f2aa3b6be0d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1305997522728128514) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`
pull-aws-ebs-csi-driver-e2e-single-az | 0143b8cebcfdef3aea6e6c7201dd6f2aa3b6be0d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1305997522732322816) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | 77006e82d7f66de97bc822a812bd27a13afda16d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1306303413931216897) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | 77006e82d7f66de97bc822a812bd27a13afda16d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1306303413931216897) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`
pull-aws-ebs-csi-driver-unit | 77006e82d7f66de97bc822a812bd27a13afda16d | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-unit/1306303413931216896) | `/test pull-aws-ebs-csi-driver-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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 -->
ayberk: /retest
k8s-ci-robot: @ayberk: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | e37b31dcc49a517ab69ea915115db42874629776 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1307107971284602880) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | e37b31dcc49a517ab69ea915115db42874629776 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1307107971284602880) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | e37b31dcc49a517ab69ea915115db42874629776 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1307107971322351616) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | e37b31dcc49a517ab69ea915115db42874629776 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1307107971284602880) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1307136661615808512) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1307136661615808512) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`
pull-aws-ebs-csi-driver-e2e-single-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1307136661582254080) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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 -->
ayberk: /retest

Ayberk
On Sep 18, 2020, 7:16 PM -0700, Kubernetes Prow Robot <notifications@github.com>, wrote:
> @ayberk: The following tests failed, say /retest to rerun all failed tests:
> Test name
> Commit
> Details
> Rerun command
> pull-aws-ebs-csi-driver-e2e-multi-az
> a7ad0a0
> link
> /test pull-aws-ebs-csi-driver-e2e-multi-az
> pull-aws-ebs-csi-driver-e2e-single-az
> a7ad0a0
> link
> /test pull-aws-ebs-csi-driver-e2e-single-az
> Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR.
> Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. I understand the commands that are listed here.
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub, or unsubscribe.

k8s-ci-robot: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1307136661582254080) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1307150114531315712) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1307150114531315712) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`
pull-aws-ebs-csi-driver-e2e-single-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1307150114510344192) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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 -->
ayberk: /retest
k8s-ci-robot: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-multi-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1307150114531315712) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`
pull-aws-ebs-csi-driver-e2e-single-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1308129394153230336) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1308129394153230336) | `/test pull-aws-ebs-csi-driver-e2e-single-az`
pull-aws-ebs-csi-driver-e2e-multi-az | a7ad0a06152a916934ff32cba0f8b32c02d072bd | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-multi-az/1308129394178396160) | `/test pull-aws-ebs-csi-driver-e2e-multi-az`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ayberk). 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 -->
ayberk: /retest
wongma7: lgtm but i think there is an edgecase where GetDiskByName returns an existing volume that needs tobe handled.
/hold
for end to end test with real outpost
wongma7: /lgtm
nice work!!!
Please post testing result, like the output of `kubectl get pv -o yaml <name of some outpost pv created by the driver>` and/or `kubectl get csinodeinfo -o yaml <name of some outpost node where the csi driver is installed>` for sanity check then let's unhold and merge.. If you create a cluster with *only* outpost nodes, it should be possible to reuse the e2e tests, if there is a mix of outpost with non outpost nodes then I guess it will be random whether the test pods get scheduled to the outpost nodes or not...
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: *<a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/561#" title="Author self-approved">ayberk</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **wongma7**
You can assign the PR to them by writing `/assign @wongma7` in a comment when ready.

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":["wongma7"]} -->
k8s-ci-robot: @ayberk: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-verify | 02f4634ad618d285e79814081dfd05a1d3c98ea0 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-verify/1310996552155664384) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aayberk). 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: @ayberk: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-verify | 02f4634ad618d285e79814081dfd05a1d3c98ea0 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-verify/1310996552155664384) | `/test pull-aws-ebs-csi-driver-verify`
pull-aws-ebs-csi-driver-e2e-single-az | 02f4634ad618d285e79814081dfd05a1d3c98ea0 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/561/pull-aws-ebs-csi-driver-e2e-single-az/1310996552210190336) | `/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=561). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aayberk). 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/pkg/cloud/cloud.go b/pkg/cloud/cloud.go
index cfa44be6..c06aa3c8 100644
--- a/pkg/cloud/cloud.go
+++ b/pkg/cloud/cloud.go
@@ -59,10 +59,6 @@ var (
 		VolumeTypeST1,
 		VolumeTypeStandard,
 	}
-
-	volumeModificationDuration   = 1 * time.Second
-	volumeModificationWaitFactor = 1.7
-	volumeModificationWaitSteps  = 10
 )
 
 // AWS provisioning limits.
@@ -118,9 +114,6 @@ var (
 
 	// ErrInvalidMaxResults is returned when a MaxResults pagination parameter is between 1 and 4
 	ErrInvalidMaxResults = errors.New("MaxResults parameter must be 0 or greater than or equal to 5")
-
-	// VolumeNotBeingModified is returned if volume being described is not being modified
-	VolumeNotBeingModified = fmt.Errorf("volume is not being modified")
 )
 
 // Disk represents a EBS volume
@@ -129,6 +122,7 @@ type Disk struct {
 	CapacityGiB      int64
 	AvailabilityZone string
 	SnapshotID       string
+	OutpostArn       string
 }
 
 // DiskOptions represents parameters to create an EBS volume
@@ -138,6 +132,7 @@ type DiskOptions struct {
 	VolumeType       string
 	IOPSPerGB        int
 	AvailabilityZone string
+	OutpostArn       string
 	Encrypted        bool
 	// KmsKeyID represents a fully qualified resource name to the key to use for encryption.
 	// example: arn:aws:kms:us-east-1:012345678910:key/abcd1234-a123-456a-a12b-a123b4cd56ef
@@ -284,6 +279,12 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
 		TagSpecifications: []*ec2.TagSpecification{&tagSpec},
 		Encrypted:         aws.Bool(diskOptions.Encrypted),
 	}
+
+	// EBS doesn't handle empty outpost arn, so we have to include it only when it's non-empty
+	if len(diskOptions.OutpostArn) > 0 {
+		request.OutpostArn = aws.String(diskOptions.OutpostArn)
+	}
+
 	if len(diskOptions.KmsKeyID) > 0 {
 		request.KmsKeyId = aws.String(diskOptions.KmsKeyID)
 		request.Encrypted = aws.Bool(true)
@@ -318,7 +319,9 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
 		return nil, fmt.Errorf("failed to get an available volume in EC2: %v", err)
 	}
 
-	return &Disk{CapacityGiB: size, VolumeID: volumeID, AvailabilityZone: zone, SnapshotID: snapshotID}, nil
+	outpostArn := aws.StringValue(response.OutpostArn)
+
+	return &Disk{CapacityGiB: size, VolumeID: volumeID, AvailabilityZone: zone, SnapshotID: snapshotID, OutpostArn: outpostArn}, nil
 }
 
 func (c *cloud) DeleteDisk(ctx context.Context, volumeID string) (bool, error) {
@@ -486,6 +489,7 @@ func (c *cloud) GetDiskByName(ctx context.Context, name string, capacityBytes in
 		CapacityGiB:      volSizeBytes,
 		AvailabilityZone: aws.StringValue(volume.AvailabilityZone),
 		SnapshotID:       aws.StringValue(volume.SnapshotId),
+		OutpostArn:       aws.StringValue(volume.OutpostArn),
 	}, nil
 }
 
@@ -505,6 +509,7 @@ func (c *cloud) GetDiskByID(ctx context.Context, volumeID string) (*Disk, error)
 		VolumeID:         aws.StringValue(volume.VolumeId),
 		CapacityGiB:      aws.Int64Value(volume.Size),
 		AvailabilityZone: aws.StringValue(volume.AvailabilityZone),
+		OutpostArn:       aws.StringValue(volume.OutpostArn),
 	}, nil
 }
 
@@ -658,6 +663,7 @@ func (c *cloud) ec2SnapshotResponseToStruct(ec2Snapshot *ec2.Snapshot) *Snapshot
 func (c *cloud) getVolume(ctx context.Context, request *ec2.DescribeVolumesInput) (*ec2.Volume, error) {
 	var volumes []*ec2.Volume
 	var nextToken *string
+
 	for {
 		response, err := c.ec2.DescribeVolumesWithContext(ctx, request)
 		if err != nil {
@@ -805,6 +811,13 @@ func isAWSError(err error, code string) bool {
 	return false
 }
 
+// isAWSErrorIncorrectModification returns a boolean indicating whether the given error
+// is an AWS IncorrectModificationState error. This error means that a modification action
+// on an EBS volume cannot occur because the volume is currently being modified.
+func isAWSErrorIncorrectModification(err error) bool {
+	return isAWSError(err, "IncorrectModificationState")
+}
+
 // isAWSErrorInstanceNotFound returns a boolean indicating whether the
 // given error is an AWS InvalidInstanceID.NotFound error. This error is
 // reported when the specified instance doesn't exist.
@@ -833,12 +846,6 @@ func isAWSErrorInvalidAttachmentNotFound(err error) bool {
 	return isAWSError(err, "InvalidAttachment.NotFound")
 }
 
-// isAWSErrorModificationNotFound returns a boolean indicating whether the given
-// error is an AWS InvalidVolumeModification.NotFound error
-func isAWSErrorModificationNotFound(err error) bool {
-	return isAWSError(err, "InvalidVolumeModification.NotFound")
-}
-
 // isAWSErrorSnapshotNotFound returns a boolean indicating whether the
 // given error is an AWS InvalidSnapshot.NotFound error. This error is
 // reported when the specified snapshot doesn't exist.
@@ -863,33 +870,8 @@ func (c *cloud) ResizeDisk(ctx context.Context, volumeID string, newSizeBytes in
 	newSizeGiB := util.RoundUpGiB(newSizeBytes)
 	oldSizeGiB := aws.Int64Value(volume.Size)
 
-	latestMod, modFetchError := c.getLatestVolumeModification(ctx, volumeID)
-
-	if latestMod != nil && modFetchError == nil {
-		state := aws.StringValue(latestMod.ModificationState)
-		if state == ec2.VolumeModificationStateModifying {
-			_, err = c.waitForVolumeSize(ctx, volumeID)
-			if err != nil {
-				return oldSizeGiB, err
-			}
-			return c.checkDesiredSize(ctx, volumeID, newSizeGiB)
-		}
-	}
-
-	// if there was an error fetching volume modifications and it was anything other than VolumeNotBeingModified error
-	// that means we have an API problem.
-	if modFetchError != nil && modFetchError != VolumeNotBeingModified {
-		return oldSizeGiB, fmt.Errorf("error fetching volume modifications for %q: %v", volumeID, modFetchError)
-	}
-
-	// Even if existing volume size is greater than user requested size, we should ensure that there are no pending
-	// volume modifications objects or volume has completed previously issued modification request.
 	if oldSizeGiB >= newSizeGiB {
-		klog.V(5).Infof("Volume %q current size (%d GiB) is greater or equal to the new size (%d GiB)", volumeID, oldSizeGiB, newSizeGiB)
-		_, err = c.waitForVolumeSize(ctx, volumeID)
-		if err != nil && err != VolumeNotBeingModified {
-			return oldSizeGiB, err
-		}
+		klog.V(5).Infof("Volume %q's current size (%d GiB) is greater or equal to the new size (%d GiB)", volumeID, oldSizeGiB, newSizeGiB)
 		return oldSizeGiB, nil
 	}
 
@@ -898,54 +880,38 @@ func (c *cloud) ResizeDisk(ctx context.Context, volumeID string, newSizeBytes in
 		Size:     aws.Int64(newSizeGiB),
 	}
 
-	klog.Infof("expanding volume %q to size %d", volumeID, newSizeGiB)
+	var mod *ec2.VolumeModification
 	response, err := c.ec2.ModifyVolumeWithContext(ctx, req)
 	if err != nil {
-		return 0, fmt.Errorf("could not modify AWS volume %q: %v", volumeID, err)
-	}
-
-	mod := response.VolumeModification
+		if !isAWSErrorIncorrectModification(err) {
+			return 0, fmt.Errorf("could not modify AWS volume %q: %v", volumeID, err)
+		}
 
-	state := aws.StringValue(mod.ModificationState)
-	if volumeModificationDone(state) {
-		return c.checkDesiredSize(ctx, volumeID, newSizeGiB)
+		m, err := c.getLatestVolumeModification(ctx, volumeID)
+		if err != nil {
+			return 0, err
+		}
+		mod = m
 	}
 
-	_, err = c.waitForVolumeSize(ctx, volumeID)
-	if err != nil {
-		return oldSizeGiB, err
+	if mod == nil {
+		mod = response.VolumeModification
 	}
-	return c.checkDesiredSize(ctx, volumeID, newSizeGiB)
-}
 
-// Checks for desired size on volume by also verifying volume size by describing volume.
-// This is to get around potential eventual consistency problems with describing volume modifications
-// objects and ensuring that we read two different objects to verify volume state.
-func (c *cloud) checkDesiredSize(ctx context.Context, volumeID string, newSizeGiB int64) (int64, error) {
-	request := &ec2.DescribeVolumesInput{
-		VolumeIds: []*string{
-			aws.String(volumeID),
-		},
-	}
-	volume, err := c.getVolume(ctx, request)
-	if err != nil {
-		return 0, err
+	state := aws.StringValue(mod.ModificationState)
+	if state == ec2.VolumeModificationStateCompleted || state == ec2.VolumeModificationStateOptimizing {
+		return aws.Int64Value(mod.TargetSize), nil
 	}
 
-	// AWS resizes in chunks of GiB (not GB)
-	oldSizeGiB := aws.Int64Value(volume.Size)
-	if oldSizeGiB >= newSizeGiB {
-		return oldSizeGiB, nil
-	}
-	return oldSizeGiB, fmt.Errorf("volume %q is still being expanded to %d size", volumeID, newSizeGiB)
+	return c.waitForVolumeSize(ctx, volumeID)
 }
 
 // waitForVolumeSize waits for a volume modification to finish and return its size.
 func (c *cloud) waitForVolumeSize(ctx context.Context, volumeID string) (int64, error) {
 	backoff := wait.Backoff{
-		Duration: volumeModificationDuration,
-		Factor:   volumeModificationWaitFactor,
-		Steps:    volumeModificationWaitSteps,
+		Duration: 1 * time.Second,
+		Factor:   1.8,
+		Steps:    20,
 	}
 
 	var modVolSizeGiB int64
@@ -956,7 +922,7 @@ func (c *cloud) waitForVolumeSize(ctx context.Context, volumeID string) (int64,
 		}
 
 		state := aws.StringValue(m.ModificationState)
-		if volumeModificationDone(state) {
+		if state == ec2.VolumeModificationStateCompleted || state == ec2.VolumeModificationStateOptimizing {
 			modVolSizeGiB = aws.Int64Value(m.TargetSize)
 			return true, nil
 		}
@@ -980,15 +946,12 @@ func (c *cloud) getLatestVolumeModification(ctx context.Context, volumeID string
 	}
 	mod, err := c.ec2.DescribeVolumesModificationsWithContext(ctx, request)
 	if err != nil {
-		if isAWSErrorModificationNotFound(err) {
-			return nil, VolumeNotBeingModified
-		}
 		return nil, fmt.Errorf("error describing modifications in volume %q: %v", volumeID, err)
 	}
 
 	volumeMods := mod.VolumesModifications
 	if len(volumeMods) == 0 {
-		return nil, VolumeNotBeingModified
+		return nil, fmt.Errorf("could not find any modifications for volume %q", volumeID)
 	}
 
 	return volumeMods[len(volumeMods)-1], nil
@@ -1010,10 +973,3 @@ func (c *cloud) randomAvailabilityZone(ctx context.Context) (string, error) {
 
 	return zones[0], nil
 }
-
-func volumeModificationDone(state string) bool {
-	if state == ec2.VolumeModificationStateCompleted || state == ec2.VolumeModificationStateOptimizing {
-		return true
-	}
-	return false
-}
diff --git a/pkg/cloud/metadata.go b/pkg/cloud/metadata.go
index 64968413..60f6cf78 100644
--- a/pkg/cloud/metadata.go
+++ b/pkg/cloud/metadata.go
@@ -18,14 +18,20 @@ package cloud
 
 import (
 	"fmt"
+	"strings"
 
 	"github.com/aws/aws-sdk-go/aws"
+	"github.com/aws/aws-sdk-go/aws/arn"
 	"github.com/aws/aws-sdk-go/aws/ec2metadata"
 	"github.com/aws/aws-sdk-go/aws/session"
+
+	"k8s.io/klog"
 )
 
 type EC2Metadata interface {
 	Available() bool
+	// ec2 instance metadata endpoints: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
+	GetMetadata(string) (string, error)
 	GetInstanceIdentityDocument() (ec2metadata.EC2InstanceIdentityDocument, error)
 }
 
@@ -35,6 +41,7 @@ type MetadataService interface {
 	GetInstanceType() string
 	GetRegion() string
 	GetAvailabilityZone() string
+	GetOutpostArn() arn.ARN
 }
 
 type Metadata struct {
@@ -42,8 +49,12 @@ type Metadata struct {
 	InstanceType     string
 	Region           string
 	AvailabilityZone string
+	OutpostArn       arn.ARN
 }
 
+// OutpostArnEndpoint is the ec2 instance metadata endpoint to query to get the outpost arn
+const OutpostArnEndpoint string = "outpost-arn"
+
 var _ MetadataService = &Metadata{}
 
 // GetInstanceID returns the instance identification.
@@ -51,7 +62,7 @@ func (m *Metadata) GetInstanceID() string {
 	return m.InstanceID
 }
 
-// GetInstanceID returns the instance type.
+// GetInstanceType returns the instance type.
 func (m *Metadata) GetInstanceType() string {
 	return m.InstanceType
 }
@@ -66,6 +77,11 @@ func (m *Metadata) GetAvailabilityZone() string {
 	return m.AvailabilityZone
 }
 
+// GetOutpostArn returns outpost arn if instance is running on an outpost. empty otherwise.
+func (m *Metadata) GetOutpostArn() arn.ARN {
+	return m.OutpostArn
+}
+
 func NewMetadata() (MetadataService, error) {
 	sess := session.Must(session.NewSession(&aws.Config{}))
 	svc := ec2metadata.New(sess)
@@ -99,10 +115,28 @@ func NewMetadataService(svc EC2Metadata) (MetadataService, error) {
 		return nil, fmt.Errorf("could not get valid EC2 availavility zone")
 	}
 
-	return &Metadata{
+	outpostArn, err := svc.GetMetadata(OutpostArnEndpoint)
+	// "outpust-arn" returns 404 for non-outpost instances. note that the request is made to a link-local address.
+	// it's guaranteed to be in the form `arn:<partition>:outposts:<region>:<account>:outpost/<outpost-id>`
+	// There's a case to be made here to ignore the error so a failure here wouldn't affect non-outpost calls.
+	if err != nil && !strings.Contains(err.Error(), "404") {
+		return nil, fmt.Errorf("something went wrong while getting EC2 outpost arn")
+	}
+
+	metadata := Metadata{
 		InstanceID:       doc.InstanceID,
 		InstanceType:     doc.InstanceType,
 		Region:           doc.Region,
 		AvailabilityZone: doc.AvailabilityZone,
-	}, nil
+	}
+
+	outpostArn = strings.ReplaceAll(outpostArn, "outpost/", "")
+	parsedArn, err := arn.Parse(outpostArn)
+	if err != nil {
+		klog.Warningf("Failed to parse the outpost arn: %s", outpostArn)
+	} else {
+		metadata.OutpostArn = parsedArn
+	}
+
+	return &metadata, nil
 }
diff --git a/pkg/cloud/mocks/mock_ec2metadata.go b/pkg/cloud/mocks/mock_ec2metadata.go
index 37e2ea0d..2719846d 100644
--- a/pkg/cloud/mocks/mock_ec2metadata.go
+++ b/pkg/cloud/mocks/mock_ec2metadata.go
@@ -61,3 +61,18 @@ func (mr *MockEC2MetadataMockRecorder) GetInstanceIdentityDocument() *gomock.Cal
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceIdentityDocument", reflect.TypeOf((*MockEC2Metadata)(nil).GetInstanceIdentityDocument))
 }
+
+// GetMetadata mocks base method
+func (m *MockEC2Metadata) GetMetadata(arg0 string) (string, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetMetadata", arg0)
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetMetadata indicates an expected call of GetMetadata
+func (mr *MockEC2MetadataMockRecorder) GetMetadata(arg0 interface{}) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockEC2Metadata)(nil).GetMetadata), arg0)
+}
diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go
index 1e37dc00..ea515cc0 100644
--- a/pkg/driver/controller.go
+++ b/pkg/driver/controller.go
@@ -18,10 +18,12 @@ package driver
 
 import (
 	"context"
+	"fmt"
 	"os"
 	"strconv"
 	"strings"
 
+	"github.com/aws/aws-sdk-go/aws/arn"
 	csi "github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/golang/protobuf/ptypes"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
@@ -107,10 +109,7 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 	}
 
 	if !isValidVolumeCapabilities(volCaps) {
-		modes := util.GetAccessModes(volCaps)
-		stringModes := strings.Join(*modes, ", ")
-		errString := "Volume capabilities " + stringModes + " not supported. Only AccessModes[ReadWriteOnce] supported."
-		return nil, status.Error(codes.InvalidArgument, errString)
+		return nil, status.Error(codes.InvalidArgument, "Volume capabilities not supported")
 	}
 
 	disk, err := d.cloud.GetDiskByName(ctx, volName, volSizeBytes)
@@ -189,6 +188,7 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 
 	// create a new volume
 	zone := pickAvailabilityZone(req.GetAccessibilityRequirements())
+	outpostArn := getOutpostArn(req.GetAccessibilityRequirements())
 
 	// fill volume tags
 	if d.driverOptions.kubernetesClusterID != "" {
@@ -206,6 +206,7 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 		VolumeType:       volumeType,
 		IOPSPerGB:        iopsPerGB,
 		AvailabilityZone: zone,
+		OutpostArn:       outpostArn,
 		Encrypted:        isEncrypted,
 		KmsKeyID:         kmsKeyID,
 		SnapshotID:       snapshotID,
@@ -259,10 +260,7 @@ func (d *controllerService) ControllerPublishVolume(ctx context.Context, req *cs
 
 	caps := []*csi.VolumeCapability{volCap}
 	if !isValidVolumeCapabilities(caps) {
-		modes := util.GetAccessModes(caps)
-		stringModes := strings.Join(*modes, ", ")
-		errString := "Volume capabilities " + stringModes + " not supported. Only AccessModes[ReadWriteOnce] supported."
-		return nil, status.Error(codes.InvalidArgument, errString)
+		return nil, status.Error(codes.InvalidArgument, "Volume capability not supported")
 	}
 
 	if !d.cloud.IsExistInstance(ctx, nodeID) {
@@ -534,6 +532,26 @@ func pickAvailabilityZone(requirement *csi.TopologyRequirement) string {
 	return ""
 }
 
+func getOutpostArn(requirement *csi.TopologyRequirement) string {
+	if requirement == nil {
+		return ""
+	}
+	for _, topology := range requirement.GetPreferred() {
+		_, exists := topology.GetSegments()[AwsOutpostIDKey]
+		if exists {
+			return BuildOutpostArn(topology.GetSegments())
+		}
+	}
+	for _, topology := range requirement.GetRequisite() {
+		_, exists := topology.GetSegments()[AwsOutpostIDKey]
+		if exists {
+			return BuildOutpostArn(topology.GetSegments())
+		}
+	}
+
+	return ""
+}
+
 func newCreateVolumeResponse(disk *cloud.Disk) *csi.CreateVolumeResponse {
 	var src *csi.VolumeContentSource
 	if disk.SnapshotID != "" {
@@ -545,6 +563,18 @@ func newCreateVolumeResponse(disk *cloud.Disk) *csi.CreateVolumeResponse {
 			},
 		}
 	}
+
+	segments := map[string]string{TopologyKey: disk.AvailabilityZone}
+
+	arn, err := arn.Parse(disk.OutpostArn)
+
+	if err == nil {
+		segments[AwsRegionKey] = arn.Region
+		segments[AwsPartitionKey] = arn.Partition
+		segments[AwsAccountIDKey] = arn.AccountID
+		segments[AwsOutpostIDKey] = strings.ReplaceAll(arn.Resource, "outpost/", "")
+	}
+
 	return &csi.CreateVolumeResponse{
 		Volume: &csi.Volume{
 			VolumeId:      disk.VolumeID,
@@ -552,7 +582,7 @@ func newCreateVolumeResponse(disk *cloud.Disk) *csi.CreateVolumeResponse {
 			VolumeContext: map[string]string{},
 			AccessibleTopology: []*csi.Topology{
 				{
-					Segments: map[string]string{TopologyKey: disk.AvailabilityZone},
+					Segments: segments,
 				},
 			},
 			ContentSource: src,
@@ -622,3 +652,28 @@ func getVolSizeBytes(req *csi.CreateVolumeRequest) (int64, error) {
 	}
 	return volSizeBytes, nil
 }
+
+// BuildOutpostArn returns the string representation of the outpost ARN from the given csi.TopologyRequirement.segments
+func BuildOutpostArn(segments map[string]string) string {
+
+	if len(segments[AwsPartitionKey]) <= 0 {
+		return ""
+	}
+
+	if len(segments[AwsRegionKey]) <= 0 {
+		return ""
+	}
+	if len(segments[AwsOutpostIDKey]) <= 0 {
+		return ""
+	}
+	if len(segments[AwsAccountIDKey]) <= 0 {
+		return ""
+	}
+
+	return fmt.Sprintf("arn:%s:outposts:%s:%s:outpost/%s",
+		segments[AwsPartitionKey],
+		segments[AwsRegionKey],
+		segments[AwsAccountIDKey],
+		segments[AwsOutpostIDKey],
+	)
+}
diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go
index 1be2e86d..186bdf9e 100644
--- a/pkg/driver/driver.go
+++ b/pkg/driver/driver.go
@@ -40,8 +40,12 @@ const (
 )
 
 const (
-	DriverName  = "ebs.csi.aws.com"
-	TopologyKey = "topology." + DriverName + "/zone"
+	DriverName      = "ebs.csi.aws.com"
+	TopologyKey     = "topology." + DriverName + "/zone"
+	AwsPartitionKey = "topology." + DriverName + "/partition"
+	AwsAccountIDKey = "topology." + DriverName + "/account-id"
+	AwsRegionKey    = "topology." + DriverName + "/region"
+	AwsOutpostIDKey = "topology." + DriverName + "/outpost-id"
 )
 
 type Driver struct {
diff --git a/pkg/driver/mocks/mock_metadata_service.go b/pkg/driver/mocks/mock_metadata_service.go
index dba05fda..ee2f6717 100644
--- a/pkg/driver/mocks/mock_metadata_service.go
+++ b/pkg/driver/mocks/mock_metadata_service.go
@@ -5,6 +5,7 @@
 package mocks
 
 import (
+	arn "github.com/aws/aws-sdk-go/aws/arn"
 	gomock "github.com/golang/mock/gomock"
 	reflect "reflect"
 )
@@ -74,6 +75,20 @@ func (mr *MockMetadataServiceMockRecorder) GetInstanceType() *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceType", reflect.TypeOf((*MockMetadataService)(nil).GetInstanceType))
 }
 
+// GetOutpostArn mocks base method
+func (m *MockMetadataService) GetOutpostArn() arn.ARN {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetOutpostArn")
+	ret0, _ := ret[0].(arn.ARN)
+	return ret0
+}
+
+// GetOutpostArn indicates an expected call of GetOutpostArn
+func (mr *MockMetadataServiceMockRecorder) GetOutpostArn() *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOutpostArn", reflect.TypeOf((*MockMetadataService)(nil).GetOutpostArn))
+}
+
 // GetRegion mocks base method
 func (m *MockMetadataService) GetRegion() string {
 	m.ctrl.T.Helper()
diff --git a/pkg/driver/node.go b/pkg/driver/node.go
index 45682364..2dc3dc28 100644
--- a/pkg/driver/node.go
+++ b/pkg/driver/node.go
@@ -366,10 +366,22 @@ func (d *nodeService) NodeGetCapabilities(ctx context.Context, req *csi.NodeGetC
 func (d *nodeService) NodeGetInfo(ctx context.Context, req *csi.NodeGetInfoRequest) (*csi.NodeGetInfoResponse, error) {
 	klog.V(4).Infof("NodeGetInfo: called with args %+v", *req)
 
-	topology := &csi.Topology{
-		Segments: map[string]string{TopologyKey: d.metadata.GetAvailabilityZone()},
+	segments := map[string]string{
+		TopologyKey: d.metadata.GetAvailabilityZone(),
 	}
 
+	outpostArn := d.metadata.GetOutpostArn()
+
+	// to my surprise ARN's string representation is not empty for empty ARN
+	if len(outpostArn.Resource) > 0 {
+		segments[AwsRegionKey] = outpostArn.Region
+		segments[AwsPartitionKey] = outpostArn.Partition
+		segments[AwsAccountIDKey] = outpostArn.AccountID
+		segments[AwsOutpostIDKey] = outpostArn.Resource
+	}
+
+	topology := &csi.Topology{Segments: segments}
+
 	return &csi.NodeGetInfoResponse{
 		NodeId:             d.metadata.GetInstanceID(),
 		MaxVolumesPerNode:  d.getVolumesLimit(),
diff --git a/pkg/util/util.go b/pkg/util/util.go
index 7e5b79a3..7a51a90b 100644
--- a/pkg/util/util.go
+++ b/pkg/util/util.go
@@ -23,8 +23,6 @@ import (
 	"path"
 	"path/filepath"
 	"strings"
-
-	csi "github.com/container-storage-interface/spec/lib/go/csi"
 )
 
 const (
@@ -80,13 +78,3 @@ func ParseEndpoint(endpoint string) (string, string, error) {
 func roundUpSize(volumeSizeBytes int64, allocationUnitBytes int64) int64 {
 	return (volumeSizeBytes + allocationUnitBytes - 1) / allocationUnitBytes
 }
-
-// GetAccessModes returns a slice containing all of the access modes defined
-// in the passed in VolumeCapabilities.
-func GetAccessModes(caps []*csi.VolumeCapability) *[]string {
-	modes := []string{}
-	for _, c := range caps {
-		modes = append(modes, c.AccessMode.GetMode().String())
-	}
-	return &modes
-}
diff --git a/vendor/github.com/aws/aws-sdk-go/aws/arn/arn.go b/vendor/github.com/aws/aws-sdk-go/aws/arn/arn.go
new file mode 100644
index 00000000..1c496742
--- /dev/null
+++ b/vendor/github.com/aws/aws-sdk-go/aws/arn/arn.go
@@ -0,0 +1,93 @@
+// Package arn provides a parser for interacting with Amazon Resource Names.
+package arn
+
+import (
+	"errors"
+	"strings"
+)
+
+const (
+	arnDelimiter = ":"
+	arnSections  = 6
+	arnPrefix    = "arn:"
+
+	// zero-indexed
+	sectionPartition = 1
+	sectionService   = 2
+	sectionRegion    = 3
+	sectionAccountID = 4
+	sectionResource  = 5
+
+	// errors
+	invalidPrefix   = "arn: invalid prefix"
+	invalidSections = "arn: not enough sections"
+)
+
+// ARN captures the individual fields of an Amazon Resource Name.
+// See http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html for more information.
+type ARN struct {
+	// The partition that the resource is in. For standard AWS regions, the partition is "aws". If you have resources in
+	// other partitions, the partition is "aws-partitionname". For example, the partition for resources in the China
+	// (Beijing) region is "aws-cn".
+	Partition string
+
+	// The service namespace that identifies the AWS product (for example, Amazon S3, IAM, or Amazon RDS). For a list of
+	// namespaces, see
+	// http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#genref-aws-service-namespaces.
+	Service string
+
+	// The region the resource resides in. Note that the ARNs for some resources do not require a region, so this
+	// component might be omitted.
+	Region string
+
+	// The ID of the AWS account that owns the resource, without the hyphens. For example, 123456789012. Note that the
+	// ARNs for some resources don't require an account number, so this component might be omitted.
+	AccountID string
+
+	// The content of this part of the ARN varies by service. It often includes an indicator of the type of resource —
+	// for example, an IAM user or Amazon RDS database - followed by a slash (/) or a colon (:), followed by the
+	// resource name itself. Some services allows paths for resource names, as described in
+	// http://docs.aws.amazon.com/general/latest/gr/aws-arns-and-namespaces.html#arns-paths.
+	Resource string
+}
+
+// Parse parses an ARN into its constituent parts.
+//
+// Some example ARNs:
+// arn:aws:elasticbeanstalk:us-east-1:123456789012:environment/My App/MyEnvironment
+// arn:aws:iam::123456789012:user/David
+// arn:aws:rds:eu-west-1:123456789012:db:mysql-db
+// arn:aws:s3:::my_corporate_bucket/exampleobject.png
+func Parse(arn string) (ARN, error) {
+	if !strings.HasPrefix(arn, arnPrefix) {
+		return ARN{}, errors.New(invalidPrefix)
+	}
+	sections := strings.SplitN(arn, arnDelimiter, arnSections)
+	if len(sections) != arnSections {
+		return ARN{}, errors.New(invalidSections)
+	}
+	return ARN{
+		Partition: sections[sectionPartition],
+		Service:   sections[sectionService],
+		Region:    sections[sectionRegion],
+		AccountID: sections[sectionAccountID],
+		Resource:  sections[sectionResource],
+	}, nil
+}
+
+// IsARN returns whether the given string is an ARN by looking for
+// whether the string starts with "arn:" and contains the correct number
+// of sections delimited by colons(:).
+func IsARN(arn string) bool {
+	return strings.HasPrefix(arn, arnPrefix) && strings.Count(arn, ":") >= arnSections-1
+}
+
+// String returns the canonical representation of the ARN
+func (arn ARN) String() string {
+	return arnPrefix +
+		arn.Partition + arnDelimiter +
+		arn.Service + arnDelimiter +
+		arn.Region + arnDelimiter +
+		arn.AccountID + arnDelimiter +
+		arn.Resource
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 8a546655..961ba40b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,5 +1,6 @@
 # github.com/aws/aws-sdk-go v1.29.11
 github.com/aws/aws-sdk-go/aws
+github.com/aws/aws-sdk-go/aws/arn
 github.com/aws/aws-sdk-go/aws/awserr
 github.com/aws/aws-sdk-go/aws/awsutil
 github.com/aws/aws-sdk-go/aws/client


Test patch:
diff --git a/pkg/cloud/cloud_test.go b/pkg/cloud/cloud_test.go
index e7f6c1cc..2e882e1c 100644
--- a/pkg/cloud/cloud_test.go
+++ b/pkg/cloud/cloud_test.go
@@ -94,6 +94,39 @@ func TestCreateDisk(t *testing.T) {
 			},
 			expErr: nil,
 		},
+		{
+			name:       "success: outpost volume",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes:    util.GiBToBytes(1),
+				Tags:             map[string]string{VolumeNameTagKey: "vol-test"},
+				AvailabilityZone: expZone,
+				OutpostArn:       "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0",
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      1,
+				AvailabilityZone: expZone,
+				OutpostArn:       "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0",
+			},
+			expErr: nil,
+		},
+		{
+			name:       "success: empty outpost arn",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes:    util.GiBToBytes(1),
+				Tags:             map[string]string{VolumeNameTagKey: "vol-test"},
+				AvailabilityZone: expZone,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      1,
+				AvailabilityZone: expZone,
+				OutpostArn:       "",
+			},
+			expErr: nil,
+		},
 		{
 			name:       "fail: CreateVolume returned CreateVolume error",
 			volumeName: "vol-test-name-error",
@@ -162,6 +195,7 @@ func TestCreateDisk(t *testing.T) {
 				Size:             aws.Int64(util.BytesToGiB(tc.diskOptions.CapacityBytes)),
 				State:            aws.String(volState),
 				AvailabilityZone: aws.String(tc.diskOptions.AvailabilityZone),
+				OutpostArn:       aws.String(tc.diskOptions.OutpostArn),
 			}
 			snapshot := &ec2.Snapshot{
 				SnapshotId: aws.String(tc.diskOptions.SnapshotID),
@@ -203,6 +237,9 @@ func TestCreateDisk(t *testing.T) {
 					if tc.expDisk.AvailabilityZone != disk.AvailabilityZone {
 						t.Fatalf("CreateDisk() failed: expected availabilityZone %q, got %q", tc.expDisk.AvailabilityZone, disk.AvailabilityZone)
 					}
+					if tc.expDisk.OutpostArn != disk.OutpostArn {
+						t.Fatalf("CreateDisk() failed: expected outpoustArn %q, got %q", tc.expDisk.OutpostArn, disk.OutpostArn)
+					}
 				}
 			}
 
@@ -380,6 +417,7 @@ func TestGetDiskByName(t *testing.T) {
 		volumeName       string
 		volumeCapacity   int64
 		availabilityZone string
+		outpostArn       string
 		expErr           error
 	}{
 		{
@@ -389,6 +427,14 @@ func TestGetDiskByName(t *testing.T) {
 			availabilityZone: expZone,
 			expErr:           nil,
 		},
+		{
+			name:             "success: outpost volume",
+			volumeName:       "vol-test-1234",
+			volumeCapacity:   util.GiBToBytes(1),
+			availabilityZone: expZone,
+			outpostArn:       "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0",
+			expErr:           nil,
+		},
 		{
 			name:           "fail: DescribeVolumes returned generic error",
 			volumeName:     "vol-test-1234",
@@ -407,6 +453,7 @@ func TestGetDiskByName(t *testing.T) {
 				VolumeId:         aws.String(tc.volumeName),
 				Size:             aws.Int64(util.BytesToGiB(tc.volumeCapacity)),
 				AvailabilityZone: aws.String(tc.availabilityZone),
+				OutpostArn:       aws.String(tc.outpostArn),
 			}
 
 			ctx := context.Background()
@@ -427,6 +474,9 @@ func TestGetDiskByName(t *testing.T) {
 				if tc.availabilityZone != disk.AvailabilityZone {
 					t.Fatalf("GetDiskByName() failed: expected availabilityZone %q, got %q", tc.availabilityZone, disk.AvailabilityZone)
 				}
+				if tc.outpostArn != disk.OutpostArn {
+					t.Fatalf("GetDiskByName() failed: expected outpostArn %q, got %q", tc.outpostArn, disk.OutpostArn)
+				}
 			}
 
 			mockCtrl.Finish()
@@ -439,6 +489,7 @@ func TestGetDiskByID(t *testing.T) {
 		name             string
 		volumeID         string
 		availabilityZone string
+		outpostArn       string
 		expErr           error
 	}{
 		{
@@ -447,6 +498,13 @@ func TestGetDiskByID(t *testing.T) {
 			availabilityZone: expZone,
 			expErr:           nil,
 		},
+		{
+			name:             "success: outpost volume",
+			volumeID:         "vol-test-1234",
+			availabilityZone: expZone,
+			outpostArn:       "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0",
+			expErr:           nil,
+		},
 		{
 			name:     "fail: DescribeVolumes returned generic error",
 			volumeID: "vol-test-1234",
@@ -467,6 +525,7 @@ func TestGetDiskByID(t *testing.T) {
 						{
 							VolumeId:         aws.String(tc.volumeID),
 							AvailabilityZone: aws.String(tc.availabilityZone),
+							OutpostArn:       aws.String(tc.outpostArn),
 						},
 					},
 				},
@@ -488,6 +547,9 @@ func TestGetDiskByID(t *testing.T) {
 				if tc.availabilityZone != disk.AvailabilityZone {
 					t.Fatalf("GetDiskByName() failed: expected availabilityZone %q, got %q", tc.availabilityZone, disk.AvailabilityZone)
 				}
+				if disk.OutpostArn != tc.outpostArn {
+					t.Fatalf("GetDisk() failed: expected outpostArn %q, got %q", tc.outpostArn, disk.OutpostArn)
+				}
 			}
 
 			mockCtrl.Finish()
@@ -626,7 +688,7 @@ func TestResizeDisk(t *testing.T) {
 				VolumeModification: &ec2.VolumeModification{
 					VolumeId:          aws.String("vol-test"),
 					TargetSize:        aws.Int64(2),
-					ModificationState: aws.String(ec2.VolumeModificationStateCompleted),
+					ModificationState: aws.String(ec2.VolumeModificationStateOptimizing),
 				},
 			},
 			reqSizeGiB: 2,
@@ -659,26 +721,6 @@ func TestResizeDisk(t *testing.T) {
 			reqSizeGiB: 2,
 			expErr:     nil,
 		},
-		{
-			name:     "success: with previous expansion",
-			volumeID: "vol-test",
-			existingVolume: &ec2.Volume{
-				VolumeId:         aws.String("vol-test"),
-				Size:             aws.Int64(2),
-				AvailabilityZone: aws.String(defaultZone),
-			},
-			descModVolume: &ec2.DescribeVolumesModificationsOutput{
-				VolumesModifications: []*ec2.VolumeModification{
-					{
-						VolumeId:          aws.String("vol-test"),
-						TargetSize:        aws.Int64(2),
-						ModificationState: aws.String(ec2.VolumeModificationStateCompleted),
-					},
-				},
-			},
-			reqSizeGiB: 2,
-			expErr:     nil,
-		},
 		{
 			name:                "fail: volume doesn't exist",
 			volumeID:            "vol-test",
@@ -687,24 +729,25 @@ func TestResizeDisk(t *testing.T) {
 			expErr:              fmt.Errorf("ResizeDisk generic error"),
 		},
 		{
-			name:     "failure: volume in modifying state",
+			name:     "success: there is a resizing in progress",
 			volumeID: "vol-test",
 			existingVolume: &ec2.Volume{
 				VolumeId:         aws.String("vol-test"),
 				Size:             aws.Int64(1),
 				AvailabilityZone: aws.String(defaultZone),
 			},
+			modifiedVolumeError: awserr.New("IncorrectModificationState", "", nil),
 			descModVolume: &ec2.DescribeVolumesModificationsOutput{
 				VolumesModifications: []*ec2.VolumeModification{
 					{
 						VolumeId:          aws.String("vol-test"),
 						TargetSize:        aws.Int64(2),
-						ModificationState: aws.String(ec2.VolumeModificationStateModifying),
+						ModificationState: aws.String(ec2.VolumeModificationStateCompleted),
 					},
 				},
 			},
 			reqSizeGiB: 2,
-			expErr:     fmt.Errorf("ResizeDisk generic error"),
+			expErr:     nil,
 		},
 	}
 
@@ -712,41 +755,20 @@ func TestResizeDisk(t *testing.T) {
 		t.Run(tc.name, func(t *testing.T) {
 			mockCtrl := gomock.NewController(t)
 			mockEC2 := mocks.NewMockEC2(mockCtrl)
-			// reduce number of steps to reduce test time
-			volumeModificationWaitSteps = 3
 			c := newCloud(mockEC2)
 
 			ctx := context.Background()
 			if tc.existingVolume != nil || tc.existingVolumeError != nil {
 				mockEC2.EXPECT().DescribeVolumesWithContext(gomock.Eq(ctx), gomock.Any()).Return(
 					&ec2.DescribeVolumesOutput{
-						Volumes: []*ec2.Volume{
-							tc.existingVolume,
-						},
-					}, tc.existingVolumeError)
-
-				if tc.expErr == nil && aws.Int64Value(tc.existingVolume.Size) != tc.reqSizeGiB {
-					resizedVolume := &ec2.Volume{
-						VolumeId:         aws.String("vol-test"),
-						Size:             aws.Int64(tc.reqSizeGiB),
-						AvailabilityZone: aws.String(defaultZone),
-					}
-					mockEC2.EXPECT().DescribeVolumesWithContext(gomock.Eq(ctx), gomock.Any()).Return(
-						&ec2.DescribeVolumesOutput{
-							Volumes: []*ec2.Volume{
-								resizedVolume,
-							},
-						}, tc.existingVolumeError)
-				}
+						Volumes: []*ec2.Volume{tc.existingVolume},
+					}, tc.existingVolumeError).AnyTimes()
 			}
 			if tc.modifiedVolume != nil || tc.modifiedVolumeError != nil {
 				mockEC2.EXPECT().ModifyVolumeWithContext(gomock.Eq(ctx), gomock.Any()).Return(tc.modifiedVolume, tc.modifiedVolumeError).AnyTimes()
 			}
 			if tc.descModVolume != nil {
 				mockEC2.EXPECT().DescribeVolumesModificationsWithContext(gomock.Eq(ctx), gomock.Any()).Return(tc.descModVolume, nil).AnyTimes()
-			} else {
-				emptyOutput := &ec2.DescribeVolumesModificationsOutput{}
-				mockEC2.EXPECT().DescribeVolumesModificationsWithContext(gomock.Eq(ctx), gomock.Any()).Return(emptyOutput, nil).AnyTimes()
 			}
 
 			newSize, err := c.ResizeDisk(ctx, tc.volumeID, util.GiBToBytes(tc.reqSizeGiB))
diff --git a/pkg/cloud/metadata_test.go b/pkg/cloud/metadata_test.go
index 7a9db3fb..8898e238 100644
--- a/pkg/cloud/metadata_test.go
+++ b/pkg/cloud/metadata_test.go
@@ -18,8 +18,10 @@ package cloud
 
 import (
 	"fmt"
+	"strings"
 	"testing"
 
+	"github.com/aws/aws-sdk-go/aws/arn"
 	"github.com/aws/aws-sdk-go/aws/ec2metadata"
 	"github.com/golang/mock/gomock"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud/mocks"
@@ -33,12 +35,19 @@ var (
 )
 
 func TestNewMetadataService(t *testing.T) {
+
+	validRawOutpostArn := "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0"
+	validOutpostArn, _ := arn.Parse(strings.ReplaceAll(validRawOutpostArn, "outpost/", ""))
+
 	testCases := []struct {
 		name             string
 		isAvailable      bool
 		isPartial        bool
 		identityDocument ec2metadata.EC2InstanceIdentityDocument
+		rawOutpostArn    string
+		outpostArn       arn.ARN
 		err              error
+		getOutpostArnErr error // We should keep this specific to outpost-arn until we need to use more endpoints
 	}{
 		{
 			name:        "success: normal",
@@ -51,6 +60,42 @@ func TestNewMetadataService(t *testing.T) {
 			},
 			err: nil,
 		},
+		{
+			name:        "success: outpost-arn is available",
+			isAvailable: true,
+			identityDocument: ec2metadata.EC2InstanceIdentityDocument{
+				InstanceID:       stdInstanceID,
+				InstanceType:     stdInstanceType,
+				Region:           stdRegion,
+				AvailabilityZone: stdAvailabilityZone,
+			},
+			rawOutpostArn: validRawOutpostArn,
+			outpostArn:    validOutpostArn,
+			err:           nil,
+		},
+		{
+			name:        "success: outpost-arn is invalid",
+			isAvailable: true,
+			identityDocument: ec2metadata.EC2InstanceIdentityDocument{
+				InstanceID:       stdInstanceID,
+				InstanceType:     stdInstanceType,
+				Region:           stdRegion,
+				AvailabilityZone: stdAvailabilityZone,
+			},
+			err: nil,
+		},
+		{
+			name:        "success: outpost-arn is not found",
+			isAvailable: true,
+			identityDocument: ec2metadata.EC2InstanceIdentityDocument{
+				InstanceID:       stdInstanceID,
+				InstanceType:     stdInstanceType,
+				Region:           stdRegion,
+				AvailabilityZone: stdAvailabilityZone,
+			},
+			err:              nil,
+			getOutpostArnErr: fmt.Errorf("404"),
+		},
 		{
 			name:        "fail: metadata not available",
 			isAvailable: false,
@@ -109,6 +154,18 @@ func TestNewMetadataService(t *testing.T) {
 			},
 			err: nil,
 		},
+		{
+			name:        "fail: outpost-arn failed",
+			isAvailable: true,
+			identityDocument: ec2metadata.EC2InstanceIdentityDocument{
+				InstanceID:       stdInstanceID,
+				InstanceType:     stdInstanceType,
+				Region:           stdRegion,
+				AvailabilityZone: stdAvailabilityZone,
+			},
+			err:              nil,
+			getOutpostArnErr: fmt.Errorf("405"),
+		},
 	}
 
 	for _, tc := range testCases {
@@ -121,8 +178,12 @@ func TestNewMetadataService(t *testing.T) {
 				mockEC2Metadata.EXPECT().GetInstanceIdentityDocument().Return(tc.identityDocument, tc.err)
 			}
 
-			m, err := NewMetadataService(mockEC2Metadata)
 			if tc.isAvailable && tc.err == nil && !tc.isPartial {
+				mockEC2Metadata.EXPECT().GetMetadata(OutpostArnEndpoint).Return(tc.rawOutpostArn, tc.getOutpostArnErr)
+			}
+
+			m, err := NewMetadataService(mockEC2Metadata)
+			if tc.isAvailable && tc.err == nil && tc.getOutpostArnErr == nil && !tc.isPartial {
 				if err != nil {
 					t.Fatalf("NewMetadataService() failed: expected no error, got %v", err)
 				}
@@ -142,8 +203,12 @@ func TestNewMetadataService(t *testing.T) {
 				if m.GetAvailabilityZone() != tc.identityDocument.AvailabilityZone {
 					t.Fatalf("GetAvailabilityZone() failed: expected %v, got %v", tc.identityDocument.AvailabilityZone, m.GetAvailabilityZone())
 				}
+
+				if m.GetOutpostArn() != tc.outpostArn {
+					t.Fatalf("GetOutpostArn() failed: expected %v, got %v", tc.outpostArn, m.GetOutpostArn())
+				}
 			} else {
-				if err == nil {
+				if err == nil && tc.getOutpostArnErr == nil {
 					t.Fatal("NewMetadataService() failed: expected error when GetInstanceIdentityDocument returns partial data, got nothing")
 				}
 			}
diff --git a/pkg/driver/controller_test.go b/pkg/driver/controller_test.go
index 9a460173..3fff9c26 100644
--- a/pkg/driver/controller_test.go
+++ b/pkg/driver/controller_test.go
@@ -23,9 +23,11 @@ import (
 	"math/rand"
 	"os"
 	"reflect"
+	"strings"
 	"testing"
 	"time"
 
+	"github.com/aws/aws-sdk-go/aws/arn"
 	"github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/golang/mock/gomock"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/cloud"
@@ -152,16 +154,11 @@ func TestCreateVolume(t *testing.T) {
 			},
 		},
 	}
-	invalidVolCap := []*csi.VolumeCapability{
-		{
-			AccessMode: &csi.VolumeCapability_AccessMode{
-				Mode: csi.VolumeCapability_AccessMode_MULTI_NODE_SINGLE_WRITER,
-			},
-		},
-	}
 	stdVolSize := int64(5 * 1024 * 1024 * 1024)
 	stdCapRange := &csi.CapacityRange{RequiredBytes: stdVolSize}
 	stdParams := map[string]string{}
+	rawOutpostArn := "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0"
+	strippedOutpostArn, _ := arn.Parse(strings.ReplaceAll(rawOutpostArn, "outpost/", ""))
 
 	testCases := []struct {
 		name     string
@@ -206,6 +203,96 @@ func TestCreateVolume(t *testing.T) {
 				}
 			},
 		},
+		{
+			name: "success outposts",
+			testFunc: func(t *testing.T) {
+				outpostArn := strippedOutpostArn
+				req := &csi.CreateVolumeRequest{
+					Name:               "test-vol",
+					CapacityRange:      stdCapRange,
+					VolumeCapabilities: stdVolCap,
+					Parameters:         map[string]string{},
+					AccessibilityRequirements: &csi.TopologyRequirement{
+						Requisite: []*csi.Topology{
+							{
+								Segments: map[string]string{
+									TopologyKey:     expZone,
+									AwsAccountIDKey: outpostArn.AccountID,
+									AwsOutpostIDKey: outpostArn.Resource,
+									AwsRegionKey:    outpostArn.Region,
+									AwsPartitionKey: outpostArn.Partition,
+								},
+							},
+						},
+					},
+				}
+				expVol := &csi.Volume{
+					CapacityBytes: stdVolSize,
+					VolumeId:      "vol-test",
+					VolumeContext: map[string]string{},
+					AccessibleTopology: []*csi.Topology{
+						{
+							Segments: map[string]string{
+								TopologyKey:     expZone,
+								AwsAccountIDKey: outpostArn.AccountID,
+								AwsOutpostIDKey: outpostArn.Resource,
+								AwsRegionKey:    outpostArn.Region,
+								AwsPartitionKey: outpostArn.Partition,
+							},
+						},
+					},
+				}
+
+				ctx := context.Background()
+
+				mockDisk := &cloud.Disk{
+					VolumeID:         req.Name,
+					AvailabilityZone: expZone,
+					CapacityGiB:      util.BytesToGiB(stdVolSize),
+					OutpostArn:       outpostArn.String(),
+				}
+
+				mockCtl := gomock.NewController(t)
+				defer mockCtl.Finish()
+
+				mockCloud := mocks.NewMockCloud(mockCtl)
+				mockCloud.EXPECT().GetDiskByName(gomock.Eq(ctx), gomock.Eq(req.Name), gomock.Eq(stdVolSize)).Return(nil, cloud.ErrNotFound)
+				mockCloud.EXPECT().CreateDisk(gomock.Eq(ctx), gomock.Eq(req.Name), gomock.Any()).Return(mockDisk, nil)
+
+				awsDriver := controllerService{
+					cloud:         mockCloud,
+					driverOptions: &DriverOptions{},
+				}
+
+				resp, err := awsDriver.CreateVolume(ctx, 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("Unexpected error: %v", srvErr.Code())
+				}
+
+				// mockCloud.EXPECT().GetDiskByName(gomock.Eq(ctx), gomock.Eq(req.Name), gomock.Eq(stdVolSize)).Return(mockDisk, nil)
+				vol := resp.GetVolume()
+				if vol == nil {
+					t.Fatalf("Expected volume %v, got nil", expVol)
+				}
+
+				for expKey, expVal := range expVol.GetVolumeContext() {
+					ctx := vol.GetVolumeContext()
+					if gotVal, ok := ctx[expKey]; !ok || gotVal != expVal {
+						t.Fatalf("Expected volume context for key %v: %v, got: %v", expKey, expVal, gotVal)
+					}
+				}
+
+				if expVol.GetAccessibleTopology() != nil {
+					if !reflect.DeepEqual(expVol.GetAccessibleTopology(), vol.GetAccessibleTopology()) {
+						t.Fatalf("Expected AccessibleTopology to be %+v, got: %+v", expVol.GetAccessibleTopology(), vol.GetAccessibleTopology())
+					}
+				}
+			},
+		},
 		{
 			name: "restore snapshot",
 			testFunc: func(t *testing.T) {
@@ -1230,45 +1317,6 @@ func TestCreateVolume(t *testing.T) {
 				}
 			},
 		},
-		{
-			name: "fail with invalid volume access modes",
-			testFunc: func(t *testing.T) {
-				req := &csi.CreateVolumeRequest{
-					Name:               "vol-test",
-					CapacityRange:      stdCapRange,
-					VolumeCapabilities: invalidVolCap,
-					Parameters: map[string]string{
-						VolumeTypeKey: cloud.VolumeTypeIO1,
-						"unknownKey":  "unknownValue",
-					},
-				}
-
-				ctx := context.Background()
-
-				mockCtl := gomock.NewController(t)
-				defer mockCtl.Finish()
-
-				mockCloud := mocks.NewMockCloud(mockCtl)
-
-				awsDriver := controllerService{
-					cloud:         mockCloud,
-					driverOptions: &DriverOptions{},
-				}
-
-				_, err := awsDriver.CreateVolume(ctx, req)
-				if err == nil {
-					t.Fatalf("Expected CreateVolume to fail but got no error")
-				}
-
-				srvErr, ok := status.FromError(err)
-				if !ok {
-					t.Fatalf("Could not get error status code from error: %v", srvErr)
-				}
-				if srvErr.Code() != codes.InvalidArgument {
-					t.Fatalf("Expect InvalidArgument but got: %s", srvErr.Code())
-				}
-			},
-		},
 	}
 
 	for _, tc := range testCases {
@@ -1396,7 +1444,7 @@ func TestPickAvailabilityZone(t *testing.T) {
 			requirement: &csi.TopologyRequirement{
 				Requisite: []*csi.Topology{
 					{
-						Segments: map[string]string{TopologyKey: expZone},
+						Segments: map[string]string{TopologyKey: ""},
 					},
 				},
 				Preferred: []*csi.Topology{
@@ -1443,6 +1491,147 @@ func TestPickAvailabilityZone(t *testing.T) {
 	}
 }
 
+func TestGetOutpostArn(t *testing.T) {
+	expRawOutpostArn := "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0"
+	outpostArn, _ := arn.Parse(strings.ReplaceAll(expRawOutpostArn, "outpost/", ""))
+	testCases := []struct {
+		name          string
+		requirement   *csi.TopologyRequirement
+		expZone       string
+		expOutpostArn string
+	}{
+		{
+			name: "Get from preferred",
+			requirement: &csi.TopologyRequirement{
+				Requisite: []*csi.Topology{
+					{
+						Segments: map[string]string{TopologyKey: expZone},
+					},
+				},
+				Preferred: []*csi.Topology{
+					{
+						Segments: map[string]string{
+							TopologyKey:     expZone,
+							AwsAccountIDKey: outpostArn.AccountID,
+							AwsOutpostIDKey: outpostArn.Resource,
+							AwsRegionKey:    outpostArn.Region,
+							AwsPartitionKey: outpostArn.Partition,
+						},
+					},
+				},
+			},
+			expZone:       expZone,
+			expOutpostArn: expRawOutpostArn,
+		},
+		{
+			name: "Get from requisite",
+			requirement: &csi.TopologyRequirement{
+				Requisite: []*csi.Topology{
+					{
+						Segments: map[string]string{
+							TopologyKey:     expZone,
+							AwsAccountIDKey: outpostArn.AccountID,
+							AwsOutpostIDKey: outpostArn.Resource,
+							AwsRegionKey:    outpostArn.Region,
+							AwsPartitionKey: outpostArn.Partition,
+						},
+					},
+				},
+			},
+			expZone:       expZone,
+			expOutpostArn: expRawOutpostArn,
+		},
+		{
+			name: "Get from empty topology",
+			requirement: &csi.TopologyRequirement{
+				Preferred: []*csi.Topology{{}},
+				Requisite: []*csi.Topology{{}},
+			},
+			expZone:       "",
+			expOutpostArn: "",
+		},
+		{
+			name:          "Topology Requirement is nil",
+			requirement:   nil,
+			expZone:       "",
+			expOutpostArn: "",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			actual := getOutpostArn(tc.requirement)
+			if actual != tc.expOutpostArn {
+				t.Fatalf("Expected %v, got outpostArn: %v", tc.expOutpostArn, actual)
+			}
+		})
+	}
+}
+
+func TestBuildOutpostArn(t *testing.T) {
+	expRawOutpostArn := "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0"
+	testCases := []struct {
+		name         string
+		awsPartition string
+		awsRegion    string
+		awsAccountID string
+		awsOutpostID string
+		expectedArn  string
+	}{
+		{
+			name:         "all fields are present",
+			awsPartition: "aws",
+			awsRegion:    "us-west-2",
+			awsOutpostID: "op-0aaa000a0aaaa00a0",
+			awsAccountID: "111111111111",
+			expectedArn:  expRawOutpostArn,
+		},
+		{
+			name:         "partition is missing",
+			awsRegion:    "us-west-2",
+			awsOutpostID: "op-0aaa000a0aaaa00a0",
+			awsAccountID: "111111111111",
+			expectedArn:  "",
+		},
+		{
+			name:         "region is missing",
+			awsPartition: "aws",
+			awsOutpostID: "op-0aaa000a0aaaa00a0",
+			awsAccountID: "111111111111",
+			expectedArn:  "",
+		},
+		{
+			name:         "account id is missing",
+			awsPartition: "aws",
+			awsRegion:    "us-west-2",
+			awsOutpostID: "op-0aaa000a0aaaa00a0",
+			expectedArn:  "",
+		},
+		{
+			name:         "outpost id is missing",
+			awsPartition: "aws",
+			awsRegion:    "us-west-2",
+			awsAccountID: "111111111111",
+			expectedArn:  "",
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			segment := map[string]string{
+				AwsRegionKey:    tc.awsRegion,
+				AwsPartitionKey: tc.awsPartition,
+				AwsAccountIDKey: tc.awsAccountID,
+				AwsOutpostIDKey: tc.awsOutpostID,
+			}
+			actual := BuildOutpostArn(segment)
+			if actual != tc.expectedArn {
+				t.Fatalf("Expected %v, got outpostArn: %v", tc.expectedArn, actual)
+			}
+		})
+	}
+}
+
 func TestCreateSnapshot(t *testing.T) {
 	testCases := []struct {
 		name     string
diff --git a/pkg/driver/node_test.go b/pkg/driver/node_test.go
index 67da47ac..7430d5fd 100644
--- a/pkg/driver/node_test.go
+++ b/pkg/driver/node_test.go
@@ -20,8 +20,10 @@ import (
 	"context"
 	"errors"
 	"reflect"
+	"strings"
 	"testing"
 
+	"github.com/aws/aws-sdk-go/aws/arn"
 	"github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/golang/mock/gomock"
 	"github.com/kubernetes-sigs/aws-ebs-csi-driver/pkg/driver/internal"
@@ -1242,6 +1244,8 @@ func TestNodeGetCapabilities(t *testing.T) {
 }
 
 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
@@ -1249,6 +1253,7 @@ func TestNodeGetInfo(t *testing.T) {
 		availabilityZone  string
 		volumeAttachLimit int64
 		expMaxVolumes     int64
+		outpostArn        arn.ARN
 	}{
 		{
 			name:              "success normal",
@@ -1257,6 +1262,7 @@ func TestNodeGetInfo(t *testing.T) {
 			availabilityZone:  "us-west-2b",
 			volumeAttachLimit: -1,
 			expMaxVolumes:     39,
+			outpostArn:        emptyOutpostArn,
 		},
 		{
 			name:              "success normal with overwrite",
@@ -1265,6 +1271,7 @@ func TestNodeGetInfo(t *testing.T) {
 			availabilityZone:  "us-west-2b",
 			volumeAttachLimit: 42,
 			expMaxVolumes:     42,
+			outpostArn:        emptyOutpostArn,
 		},
 		{
 			name:              "success normal with NVMe",
@@ -1273,6 +1280,7 @@ func TestNodeGetInfo(t *testing.T) {
 			availabilityZone:  "us-west-2b",
 			volumeAttachLimit: -1,
 			expMaxVolumes:     25,
+			outpostArn:        emptyOutpostArn,
 		},
 		{
 			name:              "success normal with NVMe and overwrite",
@@ -1281,6 +1289,16 @@ func TestNodeGetInfo(t *testing.T) {
 			availabilityZone:  "us-west-2b",
 			volumeAttachLimit: 30,
 			expMaxVolumes:     30,
+			outpostArn:        emptyOutpostArn,
+		},
+		{
+			name:              "success normal outposts",
+			instanceID:        "i-123456789abcdef01",
+			instanceType:      "m5d.large",
+			availabilityZone:  "us-west-2b",
+			volumeAttachLimit: 30,
+			expMaxVolumes:     30,
+			outpostArn:        validOutpostArn,
 		},
 	}
 	for _, tc := range testCases {
@@ -1297,6 +1315,7 @@ func TestNodeGetInfo(t *testing.T) {
 			mockMetadata := mocks.NewMockMetadataService(mockCtl)
 			mockMetadata.EXPECT().GetInstanceID().Return(tc.instanceID)
 			mockMetadata.EXPECT().GetAvailabilityZone().Return(tc.availabilityZone)
+			mockMetadata.EXPECT().GetOutpostArn().Return(tc.outpostArn)
 
 			if tc.volumeAttachLimit < 0 {
 				mockMetadata.EXPECT().GetInstanceType().Return(tc.instanceType)
@@ -1327,6 +1346,22 @@ func TestNodeGetInfo(t *testing.T) {
 				t.Fatalf("Expected topology %q, got %q", tc.availabilityZone, at.Segments[TopologyKey])
 			}
 
+			if at.Segments[AwsAccountIDKey] != tc.outpostArn.AccountID {
+				t.Fatalf("Expected AwsAccountId %q, got %q", tc.outpostArn.AccountID, at.Segments[AwsAccountIDKey])
+			}
+
+			if at.Segments[AwsRegionKey] != tc.outpostArn.Region {
+				t.Fatalf("Expected AwsRegion %q, got %q", tc.outpostArn.Region, at.Segments[AwsRegionKey])
+			}
+
+			if at.Segments[AwsOutpostIDKey] != tc.outpostArn.Resource {
+				t.Fatalf("Expected AwsOutpostID %q, got %q", tc.outpostArn.Resource, at.Segments[AwsOutpostIDKey])
+			}
+
+			if at.Segments[AwsPartitionKey] != tc.outpostArn.Partition {
+				t.Fatalf("Expected AwsPartition %q, got %q", tc.outpostArn.Partition, at.Segments[AwsPartitionKey])
+			}
+
 			if resp.GetMaxVolumesPerNode() != tc.expMaxVolumes {
 				t.Fatalf("Expected %d max volumes per node, got %d", tc.expMaxVolumes, resp.GetMaxVolumesPerNode())
 			}
diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go
index fd285d64..e560e5f8 100644
--- a/pkg/util/util_test.go
+++ b/pkg/util/util_test.go
@@ -18,10 +18,7 @@ package util
 
 import (
 	"fmt"
-	"reflect"
 	"testing"
-
-	csi "github.com/container-storage-interface/spec/lib/go/csi"
 )
 
 func TestRoundUpBytes(t *testing.T) {
@@ -128,26 +125,3 @@ func TestParseEndpoint(t *testing.T) {
 	}
 
 }
-
-func TestGetAccessModes(t *testing.T) {
-	testVolCap := []*csi.VolumeCapability{
-		{
-			AccessMode: &csi.VolumeCapability_AccessMode{
-				Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER,
-			},
-		},
-		{
-			AccessMode: &csi.VolumeCapability_AccessMode{
-				Mode: csi.VolumeCapability_AccessMode_SINGLE_NODE_READER_ONLY,
-			},
-		},
-	}
-	expectedModes := []string{
-		"SINGLE_NODE_WRITER",
-		"SINGLE_NODE_READER_ONLY",
-	}
-	actualModes := GetAccessModes(testVolCap)
-	if !reflect.DeepEqual(expectedModes, *actualModes) {
-		t.Fatalf("Wrong values returned for volume capabilities. Expected %v, got %v", expectedModes, actualModes)
-	}
-}
diff --git a/tester/migration-test-config.yaml b/tester/migration-test-config.yaml
index e33ec99c..ad458cc0 100644
--- a/tester/migration-test-config.yaml
+++ b/tester/migration-test-config.yaml
@@ -4,7 +4,7 @@ cluster:
     zones: us-west-2a
     nodeCount: 3
     nodeSize: c5.large
-    kubernetesVersion: 1.18.9
+    kubernetesVersion: 1.15.3
     featureGates: |2
         kubeAPIServer:
           featureGates:
@@ -94,7 +94,7 @@ install: |
       --set image.tag=$IMAGE_TAG \
       ./aws-ebs-csi-driver
 
-uninstall: |
+uninstall: | 
   echo "Removing driver"
   helm del --purge aws-ebs-csi-driver
 
diff --git a/tester/multi-az-config.yaml b/tester/multi-az-config.yaml
index 77eefcc1..baef58c3 100644
--- a/tester/multi-az-config.yaml
+++ b/tester/multi-az-config.yaml
@@ -4,7 +4,7 @@ cluster:
     zones: us-west-2a,us-west-2b,us-west-2c
     nodeCount: 3
     nodeSize: c5.large
-    kubernetesVersion: 1.18.9
+    kubernetesVersion: 1.16.0
     featureGates: |2
         kubeAPIServer:
           featureGates:
@@ -87,7 +87,7 @@ install: |
 
   kubectl get po -n kube-system
 
-uninstall: |
+uninstall: | 
   echo "Removing driver"
   bin/helm del --purge aws-ebs-csi-driver
 
diff --git a/tester/single-az-config.yaml b/tester/single-az-config.yaml
index bae3cc3f..40ba5500 100644
--- a/tester/single-az-config.yaml
+++ b/tester/single-az-config.yaml
@@ -4,7 +4,7 @@ cluster:
     zones: us-west-2a
     nodeCount: 3
     nodeSize: c5.large
-    kubernetesVersion: 1.18.9
+    kubernetesVersion: 1.16.0
     featureGates: |2
         kubeAPIServer:
           featureGates:
diff --git a/tests/e2e/driver/driver.go b/tests/e2e/driver/driver.go
index a3a25dd4..4b54e691 100644
--- a/tests/e2e/driver/driver.go
+++ b/tests/e2e/driver/driver.go
@@ -16,7 +16,7 @@ package driver
 
 import (
 	"github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1"
-	"k8s.io/api/core/v1"
+	v1 "k8s.io/api/core/v1"
 	storagev1 "k8s.io/api/storage/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
diff --git a/tests/e2e/driver/ebs_csi_driver.go b/tests/e2e/driver/ebs_csi_driver.go
index 1565458f..7333c5a8 100644
--- a/tests/e2e/driver/ebs_csi_driver.go
+++ b/tests/e2e/driver/ebs_csi_driver.go
@@ -140,8 +140,8 @@ func MinimumSizeForVolumeType(volumeType string) string {
 func IOPSPerGBForVolumeType(volumeType string) string {
 	switch volumeType {
 	case "io1", "io2":
-		// Maximum IOPS/GB for io1 is 50
-		return "50"
+		// Minimum disk size is 4, minimum IOPS is 100
+		return "25"
 	default:
 		return ""
 	}