Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #809 (2021-03-23 08:06:17)

Cap IOPS when calculating from iopsPerGB
**Is this a bug fix or adding new feature?**
bugfix

**What is this PR about? / Why do we need it?**
Consider AWS IOPS limits when calculating IOPS from `iopsPerGB` to always provision a supported volume. Users should be create a StorageClass "fast" that provides max. IOPS that AWS can support for any volume size they provide, without errors.

Without the PR, users must calculate `iopsPerGB * PVC size` and choose the right PVC size where resulting IOPS does not violate AWS IOPS limits. For example, with iopsPerGB = 10 and PVC size 5 GB, they get error `Volume iops of 50 is too low; minimum is 100.`

Please check the current AWS limits carefully. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html is not exactly clear what's max IOPS per GB for io2 volumes - on one place it's 500:1 and on another 1000:1, 500 seems to be the right ratio.

**What testing is done?** 
Wrote few new unit tests, checking **only** IOPS in `CreateVolumeInput`.
* Tested with 4GB io1 volume with 1, 50 and 500 IOPS per GB, got volume 100, 100 and 200 IOPS.
* Tested with 4GB io2 volume with 1, 50, 500 and 1000 IOPS per GB, got volume 100, 200, 2000 and 2000 IOPS.

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

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/809#" title="Author self-approved">jsafrane</a>*

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

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

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

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

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

* **18** of **18**   **(100.0%)**  changed or added relevant lines in **1** file are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage increased (+**0.1%**) to **81.923%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/38166707/badge)](https://coveralls.io/builds/38166707) |
| :-- | --: |
| Change from base [Build 1747](https://coveralls.io/builds/38151055): |  0.1% |
| Covered Lines: | 1772 |
| Relevant Lines: | 2163 |

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

k8s-ci-robot: @jsafrane: 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 | af5148938389c62246c09b1af3a914fa1437e347 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/809/pull-aws-ebs-csi-driver-e2e-multi-az/1374271272141197312) | `/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=809). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajsafrane). 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 -->
jsafrane: /retest

k8s-ci-robot: @jsafrane: 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 | af5148938389c62246c09b1af3a914fa1437e347 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/809/pull-aws-ebs-csi-driver-e2e-multi-az/1374282039989112832) | `/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=809). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajsafrane). 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 -->
jsafrane: ```
/home/prow/go/src/github.com/kubernetes-sigs/aws-ebs-csi-driver/tests/e2e/dynamic_provisioning.go:535
Expected
    <string>: topology.kubernetes.io/zone
to equal
    <string>: topology.ebs.csi.aws.com/zone
/home/prow/go/src/github.com/kubernetes-sigs/aws-ebs-csi-driver/tests/e2e/testsuites/testsuites.go:369
```
Did I break it?
ayberk: That looks related to the recent change I made. I'll take a look today.
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/809#" title="Author self-approved">jsafrane</a>*, *<a href="https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/809#pullrequestreview-618967695" title="Approved">wongma7</a>*

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

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

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

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

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":[]} -->
ayberk: /hold
ayberk: /retest
jsafrane: BTW, my motivations for this PR are:
* Be in sync with in-tree.
* Better usability. 
  * Admins can prepare "default" storage class (gp2 / gp3?) and something "fast" (fast for *every* volume that users create, regardless of the size).
  * Users should not do any size+iops calculations at all. It's not users who actually create PVCs these days, they use StatefulSets, helm charts or other software.
 
(I don't like `iops` parameter in gp3 volumes because of this - users MUST do the calculations there. The error messages when users get out of the supported bounds are not exactly clear. And a storage class with 10 000 IOPS is fast for some volumes and super slow for others.)
jsafrane: Updated README.
k8s-ci-robot: @jsafrane: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-unit | f2f4b7513f3f7e94ccf31d0ea001b12e6f6f2cf4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/809/pull-aws-ebs-csi-driver-unit/1375387041046466560) | `/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=809). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Ajsafrane). 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/CHANGELOG-0.x.md b/CHANGELOG-0.x.md
index dacfa70e..172254f2 100644
--- a/CHANGELOG-0.x.md
+++ b/CHANGELOG-0.x.md
@@ -1,15 +1,3 @@
-# v0.9.1
-
-## Notable changes
-- Change helm deploy settings: default tolerationAllTaints to false, NoExecute toleration time is 300s and will tolerate `CriticalAddonsOnly`
-
-### New features
-* Integrate external e2e test in the testsuits ([#726](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/726), [@AndyXiangLi](https://github.com/AndyXiangLi))
-
-### Bug fixes
-* delete leaked volume if driver don't know the volume status ([#771](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/771), [@AndyXiangLi](https://github.com/AndyXiangLi))
-* Update test k8s version to 1.18.16 ([#759](https://github.com/kubernetes-sigs/aws-ebs-csi-driver/pull/759), [@ayberk](https://github.com/ayberk))
-
 # v0.9.0
 
 ## Notable changes
diff --git a/Makefile b/Makefile
index 8e51f052..096e2076 100644
--- a/Makefile
+++ b/Makefile
@@ -14,7 +14,7 @@
 
 PKG=github.com/kubernetes-sigs/aws-ebs-csi-driver
 IMAGE?=amazon/aws-ebs-csi-driver
-VERSION=v0.9.1
+VERSION=v0.9.0
 VERSION_AMAZONLINUX=$(VERSION)-amazonlinux
 GIT_COMMIT?=$(shell git rev-parse HEAD)
 BUILD_DATE?=$(shell date -u +"%Y-%m-%dT%H:%M:%SZ")
diff --git a/charts/aws-ebs-csi-driver/Chart.yaml b/charts/aws-ebs-csi-driver/Chart.yaml
index 8ca745c0..e2a26cd9 100644
--- a/charts/aws-ebs-csi-driver/Chart.yaml
+++ b/charts/aws-ebs-csi-driver/Chart.yaml
@@ -1,5 +1,5 @@
 apiVersion: v1
-appVersion: "0.9.1"
+appVersion: "0.9.0"
 name: aws-ebs-csi-driver
 description: A Helm chart for AWS EBS CSI Driver
 version: 0.9.14
diff --git a/charts/aws-ebs-csi-driver/values.yaml b/charts/aws-ebs-csi-driver/values.yaml
index 4231c74b..062e1477 100644
--- a/charts/aws-ebs-csi-driver/values.yaml
+++ b/charts/aws-ebs-csi-driver/values.yaml
@@ -6,7 +6,7 @@ replicaCount: 2
 
 image:
   repository: k8s.gcr.io/provider-aws/aws-ebs-csi-driver
-  tag: "v0.9.1"
+  tag: "v0.9.0"
   pullPolicy: IfNotPresent
 
 sidecars:
diff --git a/deploy/kubernetes/base/controller.yaml b/deploy/kubernetes/base/controller.yaml
index 70818f28..ac911e04 100644
--- a/deploy/kubernetes/base/controller.yaml
+++ b/deploy/kubernetes/base/controller.yaml
@@ -32,7 +32,7 @@ spec:
           tolerationSeconds: 300
       containers:
         - name: ebs-plugin
-          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.1
+          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.0
           imagePullPolicy: IfNotPresent
           args:
             # - {all,controller,node} # specify the driver mode
diff --git a/deploy/kubernetes/base/node.yaml b/deploy/kubernetes/base/node.yaml
index 8f105917..78399b16 100644
--- a/deploy/kubernetes/base/node.yaml
+++ b/deploy/kubernetes/base/node.yaml
@@ -43,7 +43,7 @@ spec:
         - name: ebs-plugin
           securityContext:
             privileged: true
-          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.1
+          image: k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.0
           args:
             - node
             - --endpoint=$(CSI_ENDPOINT)
diff --git a/deploy/kubernetes/overlays/stable/ecr/kustomization.yaml b/deploy/kubernetes/overlays/stable/ecr/kustomization.yaml
index d7fec612..6ce8adcb 100644
--- a/deploy/kubernetes/overlays/stable/ecr/kustomization.yaml
+++ b/deploy/kubernetes/overlays/stable/ecr/kustomization.yaml
@@ -6,15 +6,15 @@ images:
 - name: k8s.gcr.io/provider-aws/aws-ebs-csi-driver
   newName: 602401143452.dkr.ecr.us-west-2.amazonaws.com/eks/aws-ebs-csi-driver
   newTag: v0.9.0
-- name: k8s.gcr.io/sig-storage/csi-provisioner
+- name: quay.io/k8scsi/csi-provisioner
   newName: public.ecr.aws/eks-distro/kubernetes-csi/external-provisioner
   newTag: v2.0.3-eks-1-18-1
-- name: k8s.gcr.io/sig-storage/csi-attacher
+- name: quay.io/k8scsi/csi-attacher
   newName: public.ecr.aws/eks-distro/kubernetes-csi/external-attacher
   newTag: v3.0.1-eks-1-18-1
-- name: k8s.gcr.io/sig-storage/livenessprobe
+- name: quay.io/k8scsi/livenessprobe
   newName: public.ecr.aws/eks-distro/kubernetes-csi/livenessprobe
   newTag: v2.1.0-eks-1-18-1
-- name: k8s.gcr.io/sig-storage/csi-node-driver-registrar
+- name: quay.io/k8scsi/csi-node-driver-registrar
   newName: public.ecr.aws/eks-distro/kubernetes-csi/node-driver-registrar
   newTag: v2.0.1-eks-1-18-1
diff --git a/deploy/kubernetes/overlays/stable/kustomization.yaml b/deploy/kubernetes/overlays/stable/kustomization.yaml
index b3d3a8fa..8d159534 100644
--- a/deploy/kubernetes/overlays/stable/kustomization.yaml
+++ b/deploy/kubernetes/overlays/stable/kustomization.yaml
@@ -4,7 +4,7 @@ bases:
   - ../../base
 images:
   - name: k8s.gcr.io/provider-aws/aws-ebs-csi-driver
-    newTag: v0.9.1
+    newTag: v0.9.0
   - name: k8s.gcr.io/sig-storage/csi-provisioner
     newTag: v2.0.2
   - name: k8s.gcr.io/sig-storage/csi-attacher
diff --git a/docs/README.md b/docs/README.md
index 6740cca9..ccd2e4bc 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -12,7 +12,7 @@ The [Amazon Elastic Block Store](https://aws.amazon.com/ebs/) Container Storage
 | AWS EBS CSI Driver \ CSI Version       | v0.3.0| v1.0.0 | v1.1.0 |
 |----------------------------------------|-------|--------|--------|
 | master branch                          | no    | no     | yes    |
-| v0.9.x                                 | no    | no     | yes    |
+| v0.9.0                                 | no    | no     | yes    |
 | v0.8.x                                 | no    | no     | yes    |
 | v0.7.1                                 | no    | no     | yes    |
 | v0.6.0                                 | no    | no     | yes    |
@@ -33,9 +33,10 @@ There are several optional parameters that could be passed into `CreateVolumeReq
 
 | Parameters                  | Values                                 | Default  | Description         |
 |-----------------------------|----------------------------------------|----------|---------------------|
-| "csi.storage.k8s.io/fstype" | xfs, ext2, ext3, ext4                  | ext4     | File system type that will be formatted during volume creation. This parameter is case sensitive! |
+| "csi.storage.k8s.io/fsType" | xfs, ext2, ext3, ext4                  | ext4     | File system type that will be formatted during volume creation |
 | "type"                      | io1, io2, gp2, gp3, sc1, st1,standard  | gp3*     | EBS volume type     |
-| "iopsPerGB"                 |                                        |          | I/O operations per second per GiB. Required when io1 or io2 volume type is specified. If this value multiplied by the size of a requested volume produces a value below the minimum or above the maximum IOPs allowed for the volume type, as documented [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html), AWS will return an error and volume creation will fail |
+| "iopsPerGB"                 |                                        |          | I/O operations per second per GiB. Required when io1 or io2 volume type is specified. If this value multiplied by the size of a requested volume produces a value above the maximum IOPs allowed for the volume type, as documented [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html), AWS will cap the IOPS to maximum supported value. If the value is lower than minimal supported IOPS value per volume, either error is returned (the default behavior) or the value is increased to fit into the supported range when `allowautoiopspergbincrease` is `"true"`.|
+| "allowAutoIOPSPerGBIncrease"| true, false                            | false    | When `"true"`, the CSI driver increases IOPS for a volume when `iopsPerGB * <volume size>` is too low to fit into IOPS range supported by AWS. This allows dynamic provisioning to always succeed, even when user specifies too small PVC capacity or `iopsPerGB` value. On the other hand, it may introduce additional costs, as such volumes have higher IOPS than requested in `iopsPerGB`.|
 | "iops"                      |                                        | 3000     | I/O operations per second. Only effetive when gp3 volume type is specified. If empty, it will set to 3000 as documented [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html). |
 | "throughput"                |                                        | 125      | Throughput in MiB/s. Only effective when gp3 volume type is specified. If empty, it will set to 125MiB/s as documented [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ebs-volume-types.html). |
 | "encrypted"                 |                                        |          | Whether the volume should be encrypted or not. Valid values are "true" or "false" |
@@ -43,7 +44,7 @@ There are several optional parameters that could be passed into `CreateVolumeReq
 
 **Notes**:
 * `gp3` is currently not supported on outposts. Outpost customers need to use a different type for their volumes.
-* Unless explicitly noted, all parameters are case insensitive (e.g. "kmsKeyId", "kmskeyid" and any other combination of upper/lowercase characters can be used).
+* The parameters are case insensitive.
 
 # EBS CSI Driver on Kubernetes
 Following sections are Kubernetes specific. If you are Kubernetes user, use followings for driver features, installation steps and examples.
@@ -52,7 +53,7 @@ Following sections are Kubernetes specific. If you are Kubernetes user, use foll
 | AWS EBS CSI Driver \ Kubernetes Version| v1.12 | v1.13 | v1.14 | v1.15 | v1.16 | v1.17 | v1.18+ |
 |----------------------------------------|-------|-------|-------|-------|-------|-------|-------|
 | master branch                          | no    | no+   | no    | no    | no    | yes   | yes   |
-| v0.9.x                                 | no    | no+   | no    | no    | no    | yes   | yes   |
+| v0.9.0                                 | no    | no+   | no    | no    | no    | yes   | yes   |
 | v0.8.x                                 | no    | no+   | yes   | yes   | yes   | yes   | yes   |
 | v0.7.1                                 | no    | no+   | yes   | yes   | yes   | yes   | yes   |
 | v0.6.0                                 | no    | no+   | yes   | yes   | yes   | yes   | yes   |
@@ -68,7 +69,6 @@ Following sections are Kubernetes specific. If you are Kubernetes user, use foll
 |AWS EBS CSI Driver Version | Image                                            |
 |---------------------------|--------------------------------------------------|
 |master branch              |amazon/aws-ebs-csi-driver:latest                  |
-|v0.9.1                     |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.1 |
 |v0.9.0                     |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.9.0 |
 |v0.8.1                     |k8s.gcr.io/provider-aws/aws-ebs-csi-driver:v0.8.1 |
 |v0.7.1                     |amazon/aws-ebs-csi-driver:v0.7.1                  |
@@ -112,7 +112,7 @@ If your cluster is v1.14+, you can skip this step. Install the `CSINodeInfo` CRD
 kubectl create -f https://raw.githubusercontent.com/kubernetes/csi-api/release-1.13/pkg/crd/manifests/csinodeinfo.yaml
 ```
 #### Config node toleration settings
-By default, driver tolerates taint `CriticalAddonsOnly` and has `tolerationSeconds` configured as `300`, to deploy the driver on any nodes, please set helm `Value.node.tolerateAllTaints` and `Value.tolerateAllTaints` to true before deployment
+By default, driver can be deployed on any nodes, to have driver tolerate taint `CriticalAddonsOnly`, please set helm `Value.node.tolerateAllTaints` and `Value.tolerateAllTaints` to false before deployment
 
 #### Deploy driver
 Please see the compatibility matrix above before you deploy the driver
diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go
index 29cd53e1..8835b912 100644
--- a/pkg/cloud/cloud.go
+++ b/pkg/cloud/cloud.go
@@ -53,6 +53,17 @@ const (
 	VolumeTypeStandard = "standard"
 )
 
+// AWS provisioning limits.
+// Source: http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/EBSVolumeTypes.html
+const (
+	io1MinTotalIOPS = 100
+	io1MaxTotalIOPS = 64000
+	io1MaxIOPSPerGB = 50
+	io2MinTotalIOPS = 100
+	io2MaxTotalIOPS = 64000
+	io2MaxIOPSPerGB = 500
+)
+
 var (
 	ValidVolumeTypes = []string{
 		VolumeTypeIO1,
@@ -142,15 +153,16 @@ type Disk struct {
 
 // DiskOptions represents parameters to create an EBS volume
 type DiskOptions struct {
-	CapacityBytes    int64
-	Tags             map[string]string
-	VolumeType       string
-	IOPSPerGB        int
-	IOPS             int
-	Throughput       int
-	AvailabilityZone string
-	OutpostArn       string
-	Encrypted        bool
+	CapacityBytes          int64
+	Tags                   map[string]string
+	VolumeType             string
+	IOPSPerGB              int
+	AllowIOPSPerGBIncrease bool
+	IOPS                   int
+	Throughput             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
 	KmsKeyID   string
@@ -256,15 +268,25 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
 		createType string
 		iops       int64
 		throughput int64
+		err        error
 	)
 	capacityGiB := util.BytesToGiB(diskOptions.CapacityBytes)
 
 	switch diskOptions.VolumeType {
 	case VolumeTypeGP2, VolumeTypeSC1, VolumeTypeST1, VolumeTypeStandard:
 		createType = diskOptions.VolumeType
-	case VolumeTypeIO1, VolumeTypeIO2:
+	case VolumeTypeIO1:
+		createType = diskOptions.VolumeType
+		iops, err = capIOPS(diskOptions.VolumeType, capacityGiB, int64(diskOptions.IOPSPerGB), io1MinTotalIOPS, io1MaxTotalIOPS, io1MaxIOPSPerGB, diskOptions.AllowIOPSPerGBIncrease)
+		if err != nil {
+			return nil, err
+		}
+	case VolumeTypeIO2:
 		createType = diskOptions.VolumeType
-		iops = capacityGiB * int64(diskOptions.IOPSPerGB)
+		iops, err = capIOPS(diskOptions.VolumeType, capacityGiB, int64(diskOptions.IOPSPerGB), io2MinTotalIOPS, io2MaxTotalIOPS, io2MaxIOPSPerGB, diskOptions.AllowIOPSPerGBIncrease)
+		if err != nil {
+			return nil, err
+		}
 	case VolumeTypeGP3:
 		createType = diskOptions.VolumeType
 		iops = int64(diskOptions.IOPS)
@@ -1073,3 +1095,29 @@ func getVolumeAttachmentsList(volume *ec2.Volume) []string {
 
 	return volumeAttachmentList
 }
+
+// Calculate actual IOPS for a volume and cap it at supported AWS limits.
+// Using requstedIOPSPerGB allows users to create a "fast" storage class
+// (requstedIOPSPerGB = 50 for io1), which can provide the maximum iops
+// that AWS supports for any requestedCapacityGiB.
+func capIOPS(volumeType string, requestedCapacityGiB int64, requstedIOPSPerGB, minTotalIOPS, maxTotalIOPS, maxIOPSPerGB int64, allowIncrease bool) (int64, error) {
+	iops := requestedCapacityGiB * requstedIOPSPerGB
+
+	if iops < minTotalIOPS {
+		if allowIncrease {
+			iops = minTotalIOPS
+			klog.V(5).Infof("Increased IOPS for %s %d GB volume to the min supported limit: %d", volumeType, requestedCapacityGiB, iops)
+		} else {
+			return 0, fmt.Errorf("invalid combination of volume size %d GB and iopsPerGB %d: the resulting IOPS %d is too low for AWS, it must be at least %d", requestedCapacityGiB, requstedIOPSPerGB, iops, minTotalIOPS)
+		}
+	}
+	if iops > maxTotalIOPS {
+		iops = maxTotalIOPS
+		klog.V(5).Infof("Capped IOPS for %s %d GB volume at the max supported limit: %d", volumeType, requestedCapacityGiB, iops)
+	}
+	if iops > maxIOPSPerGB*requestedCapacityGiB {
+		iops = maxIOPSPerGB * requestedCapacityGiB
+		klog.V(5).Infof("Capped IOPS for %s %d GB volume at %d IOPS/GB: %d", volumeType, requestedCapacityGiB, maxIOPSPerGB, iops)
+	}
+	return iops, nil
+}
diff --git a/pkg/driver/constants.go b/pkg/driver/constants.go
index 67a65f44..29e43d1d 100644
--- a/pkg/driver/constants.go
+++ b/pkg/driver/constants.go
@@ -31,6 +31,9 @@ const (
 	// IopsPerGBKey represents key for IOPS per GB
 	IopsPerGBKey = "iopspergb"
 
+	// AllowAutoIOPSPerGBIncreaseKey represents key for allowing automatic increase of IOPS
+	AllowAutoIOPSPerGBIncreaseKey = "allowautoiopspergbincrease"
+
 	// Iops represents key for IOPS for volume
 	IopsKey = "iops"
 
diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go
index 45d3b5c7..b5653576 100644
--- a/pkg/driver/controller.go
+++ b/pkg/driver/controller.go
@@ -132,13 +132,14 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 	}
 
 	var (
-		volumeType  string
-		iopsPerGB   int
-		iops        int
-		throughput  int
-		isEncrypted bool
-		kmsKeyID    string
-		volumeTags  = map[string]string{
+		volumeType             string
+		iopsPerGB              int
+		allowIOPSPerGBIncrease bool
+		iops                   int
+		throughput             int
+		isEncrypted            bool
+		kmsKeyID               string
+		volumeTags             = map[string]string{
 			cloud.VolumeNameTagKey: volName,
 		}
 	)
@@ -154,6 +155,8 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 			if err != nil {
 				return nil, status.Errorf(codes.InvalidArgument, "Could not parse invalid iopsPerGB: %v", err)
 			}
+		case AllowAutoIOPSPerGBIncreaseKey:
+			allowIOPSPerGBIncrease = value == "true"
 		case IopsKey:
 			iops, err = strconv.Atoi(value)
 			if err != nil {
@@ -232,17 +235,18 @@ func (d *controllerService) CreateVolume(ctx context.Context, req *csi.CreateVol
 	}
 
 	opts := &cloud.DiskOptions{
-		CapacityBytes:    volSizeBytes,
-		Tags:             volumeTags,
-		VolumeType:       volumeType,
-		IOPSPerGB:        iopsPerGB,
-		IOPS:             iops,
-		Throughput:       throughput,
-		AvailabilityZone: zone,
-		OutpostArn:       outpostArn,
-		Encrypted:        isEncrypted,
-		KmsKeyID:         kmsKeyID,
-		SnapshotID:       snapshotID,
+		CapacityBytes:          volSizeBytes,
+		Tags:                   volumeTags,
+		VolumeType:             volumeType,
+		IOPSPerGB:              iopsPerGB,
+		AllowIOPSPerGBIncrease: allowIOPSPerGBIncrease,
+		IOPS:                   iops,
+		Throughput:             throughput,
+		AvailabilityZone:       zone,
+		OutpostArn:             outpostArn,
+		Encrypted:              isEncrypted,
+		KmsKeyID:               kmsKeyID,
+		SnapshotID:             snapshotID,
 	}
 
 	disk, err = d.cloud.CreateDisk(ctx, volName, opts)


Test patch:
diff --git a/hack/e2e/run.sh b/hack/e2e/run.sh
index c4d2dc3a..bb26e411 100755
--- a/hack/e2e/run.sh
+++ b/hack/e2e/run.sh
@@ -25,7 +25,6 @@ source "${BASE_DIR}"/util.sh
 
 DRIVER_NAME=${DRIVER_NAME:-aws-ebs-csi-driver}
 CONTAINER_NAME=${CONTAINER_NAME:-ebs-plugin}
-DRIVER_START_TIME_THRESHOLD_SECONDS=60
 
 TEST_ID=${TEST_ID:-$RANDOM}
 CLUSTER_NAME=test-cluster-${TEST_ID}.k8s.local
@@ -105,13 +104,11 @@ if [[ $? -ne 0 ]]; then
 fi
 
 loudecho "Deploying driver"
-startSec=$(date +'%s')
 "${HELM_BIN}" upgrade --install "${DRIVER_NAME}" \
   --namespace kube-system \
   --set image.repository="${IMAGE_NAME}" \
   --set image.tag="${IMAGE_TAG}" \
   -f "${HELM_VALUES_FILE}" \
-  --wait \
   ./charts/"${DRIVER_NAME}"
 
 if [[ -r "${EBS_SNAPSHOT_CRD}" ]]; then
@@ -119,15 +116,6 @@ if [[ -r "${EBS_SNAPSHOT_CRD}" ]]; then
   kubectl apply -f "$EBS_SNAPSHOT_CRD"
   # TODO deploy snapshot controller too instead of including in helm chart
 fi
-endSec=$(date +'%s')
-secondUsed=$(( (endSec-startSec)/1 ))
-# Set timeout threshold as 20 seconds for now, usually it takes less than 10s to startup
-if [ $secondUsed -gt $DRIVER_START_TIME_THRESHOLD_SECONDS ]; then
-  loudecho "Driver start timeout, took $secondUsed but the threshold is $DRIVER_START_TIME_THRESHOLD_SECONDS. Fail the test."
-  exit 1
-fi
-loudecho "Driver deployment complete, time used: $secondUsed seconds"
-
 
 loudecho "Testing focus ${GINKGO_FOCUS}"
 eval "EXPANDED_TEST_EXTRA_FLAGS=$TEST_EXTRA_FLAGS"
diff --git a/pkg/cloud/cloud_test.go b/pkg/cloud/cloud_test.go
index 81d3b92d..28550d1d 100644
--- a/pkg/cloud/cloud_test.go
+++ b/pkg/cloud/cloud_test.go
@@ -41,15 +41,16 @@ const (
 
 func TestCreateDisk(t *testing.T) {
 	testCases := []struct {
-		name                string
-		volumeName          string
-		volState            string
-		diskOptions         *DiskOptions
-		expDisk             *Disk
-		cleanUpFailedVolume bool
-		expErr              error
-		expCreateVolumeErr  error
-		expDescVolumeErr    error
+		name                 string
+		volumeName           string
+		volState             string
+		diskOptions          *DiskOptions
+		expDisk              *Disk
+		cleanUpFailedVolume  bool
+		expErr               error
+		expCreateVolumeErr   error
+		expDescVolumeErr     error
+		expCreateVolumeInput *ec2.CreateVolumeInput
 	}{
 		{
 			name:       "success: normal",
@@ -58,6 +59,7 @@ func TestCreateDisk(t *testing.T) {
 				CapacityBytes: util.GiBToBytes(1),
 				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
 			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
 			expDisk: &Disk{
 				VolumeID:         "vol-test",
 				CapacityGiB:      1,
@@ -79,6 +81,9 @@ func TestCreateDisk(t *testing.T) {
 				CapacityGiB:      1,
 				AvailabilityZone: defaultZone,
 			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(100),
+			},
 			expErr: nil,
 		},
 		{
@@ -96,6 +101,9 @@ func TestCreateDisk(t *testing.T) {
 				CapacityGiB:      1,
 				AvailabilityZone: defaultZone,
 			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(3000),
+			},
 			expErr: nil,
 		},
 		{
@@ -111,7 +119,8 @@ func TestCreateDisk(t *testing.T) {
 				CapacityGiB:      1,
 				AvailabilityZone: expZone,
 			},
-			expErr: nil,
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               nil,
 		},
 		{
 			name:       "success: normal with encrypted volume",
@@ -128,7 +137,8 @@ func TestCreateDisk(t *testing.T) {
 				CapacityGiB:      1,
 				AvailabilityZone: expZone,
 			},
-			expErr: nil,
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               nil,
 		},
 		{
 			name:       "success: outpost volume",
@@ -145,7 +155,8 @@ func TestCreateDisk(t *testing.T) {
 				AvailabilityZone: expZone,
 				OutpostArn:       "arn:aws:outposts:us-west-2:111111111111:outpost/op-0aaa000a0aaaa00a0",
 			},
-			expErr: nil,
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               nil,
 		},
 		{
 			name:       "success: empty outpost arn",
@@ -161,7 +172,8 @@ func TestCreateDisk(t *testing.T) {
 				AvailabilityZone: expZone,
 				OutpostArn:       "",
 			},
-			expErr: nil,
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               nil,
 		},
 		{
 			name:       "fail: ec2.CreateVolume returned CreateVolume error",
@@ -171,8 +183,9 @@ func TestCreateDisk(t *testing.T) {
 				Tags:             map[string]string{VolumeNameTagKey: "vol-test"},
 				AvailabilityZone: expZone,
 			},
-			expErr:             fmt.Errorf("could not create volume in EC2: CreateVolume generic error"),
-			expCreateVolumeErr: fmt.Errorf("CreateVolume generic error"),
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               fmt.Errorf("could not create volume in EC2: CreateVolume generic error"),
+			expCreateVolumeErr:   fmt.Errorf("CreateVolume generic error"),
 		},
 		{
 			name:       "fail: ec2.DescribeVolumes error after volume created",
@@ -183,9 +196,10 @@ func TestCreateDisk(t *testing.T) {
 				Tags:             map[string]string{VolumeNameTagKey: "vol-test"},
 				AvailabilityZone: "",
 			},
-			expErr:              fmt.Errorf("failed to get an available volume in EC2: DescribeVolumes generic error"),
-			expDescVolumeErr:    fmt.Errorf("DescribeVolumes generic error"),
-			cleanUpFailedVolume: true,
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               fmt.Errorf("failed to get an available volume in EC2: DescribeVolumes generic error"),
+			expDescVolumeErr:     fmt.Errorf("DescribeVolumes generic error"),
+			cleanUpFailedVolume:  true,
 		},
 		{
 			name:       "fail: Volume is not ready to use, volume stuck in creating status and controller timed out waiting for the condition",
@@ -196,8 +210,9 @@ func TestCreateDisk(t *testing.T) {
 				Tags:             map[string]string{VolumeNameTagKey: "vol-test"},
 				AvailabilityZone: "",
 			},
-			cleanUpFailedVolume: true,
-			expErr:              fmt.Errorf("failed to get an available volume in EC2: timed out waiting for the condition"),
+			cleanUpFailedVolume:  true,
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               fmt.Errorf("failed to get an available volume in EC2: timed out waiting for the condition"),
 		},
 		{
 			name:       "success: normal from snapshot",
@@ -213,6 +228,157 @@ func TestCreateDisk(t *testing.T) {
 				CapacityGiB:      1,
 				AvailabilityZone: expZone,
 			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{},
+			expErr:               nil,
+		},
+		{
+			name:       "success: io1 with too low iopsPerGB and AllowIOPSPerGBIncrease",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes:          util.GiBToBytes(4),
+				Tags:                   map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:             VolumeTypeIO1,
+				IOPSPerGB:              1,
+				AllowIOPSPerGBIncrease: true,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(100),
+			},
+			expErr: nil,
+		},
+		{
+			name:       "fail: io1 with too low iopsPerGB",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes: util.GiBToBytes(4),
+				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:    VolumeTypeIO1,
+				IOPSPerGB:     1,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: nil,
+			expErr:               fmt.Errorf("invalid combination of volume size 4 GB and iopsPerGB 1: the resulting IOPS 4 is too low for AWS, it must be at least 100"),
+		},
+		{
+			name:       "success: small io1 with too high iopsPerGB",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes: util.GiBToBytes(4),
+				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:    VolumeTypeIO1,
+				IOPSPerGB:     10000,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(200),
+			},
+			expErr: nil,
+		},
+		{
+			name:       "success: large io1 with too high iopsPerGB",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes: util.GiBToBytes(4000),
+				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:    VolumeTypeIO1,
+				IOPSPerGB:     10000,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4000,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(64000),
+			},
+			expErr: nil,
+		},
+		{
+			name:       "success: io2 with too low iopsPerGB and AllowIOPSPerGBIncrease",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes:          util.GiBToBytes(4),
+				Tags:                   map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:             VolumeTypeIO2,
+				IOPSPerGB:              1,
+				AllowIOPSPerGBIncrease: true,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(100),
+			},
+			expErr: nil,
+		},
+		{
+			name:       "fail: io2 with too low iopsPerGB",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes: util.GiBToBytes(4),
+				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:    VolumeTypeIO2,
+				IOPSPerGB:     1,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: nil,
+			expErr:               fmt.Errorf("invalid combination of volume size 4 GB and iopsPerGB 1: the resulting IOPS 4 is too low for AWS, it must be at least 100"),
+		},
+		{
+			name:       "success: small io2 with too high iopsPerGB",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes: util.GiBToBytes(4),
+				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:    VolumeTypeIO2,
+				IOPSPerGB:     10000,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(2000),
+			},
+			expErr: nil,
+		},
+		{
+			name:       "success: large io2 with too high iopsPerGB",
+			volumeName: "vol-test-name",
+			diskOptions: &DiskOptions{
+				CapacityBytes: util.GiBToBytes(4000),
+				Tags:          map[string]string{VolumeNameTagKey: "vol-test"},
+				VolumeType:    VolumeTypeIO2,
+				IOPSPerGB:     100000,
+			},
+			expDisk: &Disk{
+				VolumeID:         "vol-test",
+				CapacityGiB:      4000,
+				AvailabilityZone: defaultZone,
+			},
+			expCreateVolumeInput: &ec2.CreateVolumeInput{
+				Iops: aws.Int64(64000),
+			},
 			expErr: nil,
 		},
 	}
@@ -241,20 +407,24 @@ func TestCreateDisk(t *testing.T) {
 				State:      aws.String("completed"),
 			}
 			ctx := context.Background()
-			mockEC2.EXPECT().CreateVolumeWithContext(gomock.Eq(ctx), gomock.Any()).Return(vol, tc.expCreateVolumeErr)
-			mockEC2.EXPECT().DescribeVolumesWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeVolumesOutput{Volumes: []*ec2.Volume{vol}}, tc.expDescVolumeErr).AnyTimes()
-			if len(tc.diskOptions.SnapshotID) > 0 {
-				mockEC2.EXPECT().DescribeSnapshotsWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeSnapshotsOutput{Snapshots: []*ec2.Snapshot{snapshot}}, nil).AnyTimes()
-			}
-			if tc.cleanUpFailedVolume == true {
-				mockEC2.EXPECT().DeleteVolumeWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DeleteVolumeOutput{}, nil)
-			}
-			if len(tc.diskOptions.AvailabilityZone) == 0 {
-				mockEC2.EXPECT().DescribeAvailabilityZonesWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeAvailabilityZonesOutput{
-					AvailabilityZones: []*ec2.AvailabilityZone{
-						{ZoneName: aws.String(defaultZone)},
-					},
-				}, nil)
+
+			if tc.expCreateVolumeInput != nil {
+				matcher := eqCreateVolume(tc.expCreateVolumeInput)
+				mockEC2.EXPECT().CreateVolumeWithContext(gomock.Eq(ctx), matcher).Return(vol, tc.expCreateVolumeErr)
+				mockEC2.EXPECT().DescribeVolumesWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeVolumesOutput{Volumes: []*ec2.Volume{vol}}, tc.expDescVolumeErr).AnyTimes()
+				if len(tc.diskOptions.SnapshotID) > 0 {
+					mockEC2.EXPECT().DescribeSnapshotsWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeSnapshotsOutput{Snapshots: []*ec2.Snapshot{snapshot}}, nil).AnyTimes()
+				}
+				if tc.cleanUpFailedVolume == true {
+					mockEC2.EXPECT().DeleteVolumeWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DeleteVolumeOutput{}, nil)
+				}
+				if len(tc.diskOptions.AvailabilityZone) == 0 {
+					mockEC2.EXPECT().DescribeAvailabilityZonesWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeAvailabilityZonesOutput{
+						AvailabilityZones: []*ec2.AvailabilityZone{
+							{ZoneName: aws.String(defaultZone)},
+						},
+					}, nil)
+				}
 			}
 
 			disk, err := c.CreateDisk(ctx, tc.volumeName, tc.diskOptions)
@@ -1278,3 +1448,29 @@ func (m *eqCreateSnapshotInputMatcher) Matches(x interface{}) bool {
 func (m *eqCreateSnapshotInputMatcher) String() string {
 	return m.expected.String()
 }
+
+type eqCreateVolumeMatcher struct {
+	expected *ec2.CreateVolumeInput
+}
+
+func eqCreateVolume(expected *ec2.CreateVolumeInput) gomock.Matcher {
+	return &eqCreateVolumeMatcher{expected}
+}
+
+func (m *eqCreateVolumeMatcher) Matches(x interface{}) bool {
+	input, ok := x.(*ec2.CreateVolumeInput)
+	if !ok {
+		return false
+	}
+
+	if input == nil {
+		return false
+	}
+	// Compare only IOPS for now
+	ret := reflect.DeepEqual(m.expected.Iops, input.Iops)
+	return ret
+}
+
+func (m *eqCreateVolumeMatcher) String() string {
+	return m.expected.String()
+}
diff --git a/tests/e2e/driver/ebs_csi_driver.go b/tests/e2e/driver/ebs_csi_driver.go
index a59860a0..9a72392e 100644
--- a/tests/e2e/driver/ebs_csi_driver.go
+++ b/tests/e2e/driver/ebs_csi_driver.go
@@ -51,7 +51,6 @@ func (d *ebsCSIDriver) GetDynamicProvisionStorageClass(parameters map[string]str
 			{
 				MatchLabelExpressions: []v1.TopologySelectorLabelRequirement{
 					{
-						// TODO we should use the new topology key eventually
 						Key:    ebscsidriver.TopologyKey,
 						Values: allowedTopologyValues,
 					},
diff --git a/tests/e2e/testsuites/testsuites.go b/tests/e2e/testsuites/testsuites.go
index 1cf25f2f..ab41342b 100644
--- a/tests/e2e/testsuites/testsuites.go
+++ b/tests/e2e/testsuites/testsuites.go
@@ -17,11 +17,10 @@ package testsuites
 import (
 	"context"
 	"fmt"
+	"github.com/aws/aws-sdk-go/aws"
 	"math/rand"
 	"time"
 
-	"github.com/aws/aws-sdk-go/aws"
-
 	"github.com/kubernetes-csi/external-snapshotter/v2/pkg/apis/volumesnapshot/v1beta1"
 
 	snapshotclientset "github.com/kubernetes-csi/external-snapshotter/v2/pkg/client/clientset/versioned"
@@ -366,25 +365,8 @@ func (t *TestPersistentVolumeClaim) ValidateProvisionedPersistentVolume() {
 				To(HaveLen(1))
 		}
 		if len(t.storageClass.AllowedTopologies) > 0 {
-			// Since we're chaging our topology key, assume we have the values below to compare:
-			// NodeSelectorTerms: [{[{topology.ebs.csi.aws.com/zone In [us-west-2a]} {topology.kubernetes.io/zone In [us-west-2a]}] []}]
-			// AllowedTopologies: [{[{topology.ebs.csi.aws.com/zone [us-west-2a us-west-2b us-west-2c]}]}]
-			// As you can see tests might fail depending on the ordering of the NodeSelectorTerms. That's why we're doing this "hack".
-			// This is a quick fix to unblock the PRs we have. We really need to improve this. TODO
-
-			keyFound := false
-			for _, v := range t.persistentVolume.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions {
-				if v.Key == "topology.ebs.csi.aws.com/zone" {
-					keyFound = true
-					Expect(v.Key).To(Equal(t.storageClass.AllowedTopologies[0].MatchLabelExpressions[0].Key))
-				}
-			}
-
-			// additional sanity check so we can catch an unintended test case that'd hide failures
-			if !keyFound {
-				Fail("Volume is expected to have a node selector term.")
-			}
-
+			Expect(t.persistentVolume.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions[0].Key).
+				To(Equal(t.storageClass.AllowedTopologies[0].MatchLabelExpressions[0].Key))
 			for _, v := range t.persistentVolume.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions[0].Values {
 				Expect(t.storageClass.AllowedTopologies[0].MatchLabelExpressions[0].Values).To(ContainElement(v))
 			}