Repository: kubernetes-sigs/aws-ebs-csi-driver
Pull request #394 (2019-10-23 22:56:31)

Refactor NewCloud by pass in region
**Is this a bug fix or adding new feature?**
Refactor `NewCloud` method by pass in region. This will benefits: #378 

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/394#" title="Author self-approved">leakingtapan</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)~~ [leakingtapan]

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 887](https://coveralls.io/builds/26510553)

* **14** of **42**   **(33.33%)**  changed or added relevant lines in **5** files are covered.
* **2** unchanged lines in **1** file lost coverage.
* Overall coverage increased (+**0.07%**) to **71.343%**

---

|  Changes Missing Coverage | Covered Lines | Changed/Added Lines | % |
| :-----|--------------|--------|---: |
| [pkg/driver/node.go](https://coveralls.io/builds/26510553/source?filename=pkg%2Fdriver%2Fnode.go#L80) | 2 | 4 | 50.0%
| [pkg/cloud/metadata.go](https://coveralls.io/builds/26510553/source?filename=pkg%2Fcloud%2Fmetadata.go#L69) | 0 | 5 | 0.0%
| [pkg/driver/controller.go](https://coveralls.io/builds/26510553/source?filename=pkg%2Fdriver%2Fcontroller.go#L61) | 0 | 5 | 0.0%
| [pkg/driver/fakes.go](https://coveralls.io/builds/26510553/source?filename=pkg%2Fdriver%2Ffakes.go#L33) | 0 | 5 | 0.0%
| [pkg/cloud/cloud.go](https://coveralls.io/builds/26510553/source?filename=pkg%2Fcloud%2Fcloud.go#L200) | 12 | 23 | 52.17%
<!-- | **Total:** | **14** | **42** | **33.33%** | -->

|  Files with Coverage Reduction | New Missed Lines | % |
| :-----|--------------|--: |
| [pkg/cloud/cloud.go](https://coveralls.io/builds/26510553/source?filename=pkg%2Fcloud%2Fcloud.go#L202) | 2 | 78.55% |
<!-- | **Total:** | **2** |  | -->

|  Totals | [![Coverage Status](https://coveralls.io/builds/26510553/badge)](https://coveralls.io/builds/26510553) |
| :-- | --: |
| Change from base [Build 884](https://coveralls.io/builds/26291428): |  0.07% |
| Covered Lines: | 1185 |
| Relevant Lines: | 1661 |

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

k8s-ci-robot: @leakingtapan: The following test **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-verify | a248fdf42009c1a24c3d5e4a01c60ab808656158 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-verify/1187140961860325378) | `/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=394). [Your PR dashboard](https://gubernator.k8s.io/pr/leakingtapan). 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: @leakingtapan: The following tests **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-verify | a248fdf42009c1a24c3d5e4a01c60ab808656158 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-verify/1187140961860325378) | `/test pull-aws-ebs-csi-driver-verify`
pull-aws-ebs-csi-driver-integration | a248fdf42009c1a24c3d5e4a01c60ab808656158 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-integration/1187140961860325376) | `/test pull-aws-ebs-csi-driver-integration`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=aws-ebs-csi-driver&pr=394). [Your PR dashboard](https://gubernator.k8s.io/pr/leakingtapan). 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: @leakingtapan: The following tests **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-verify | a248fdf42009c1a24c3d5e4a01c60ab808656158 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-verify/1187140961860325378) | `/test pull-aws-ebs-csi-driver-verify`
pull-aws-ebs-csi-driver-integration | a248fdf42009c1a24c3d5e4a01c60ab808656158 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-integration/1187140961860325376) | `/test pull-aws-ebs-csi-driver-integration`
pull-aws-ebs-csi-driver-e2e-single-az | a248fdf42009c1a24c3d5e4a01c60ab808656158 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-e2e-single-az/1187140961860325379) | `/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=394). [Your PR dashboard](https://gubernator.k8s.io/pr/leakingtapan). 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: @leakingtapan: The following test **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | 66e0483fa52311d3f4e0f75e011a47bf2c180768 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-e2e-single-az/1189976547071627265) | `/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=394). [Your PR dashboard](https://gubernator.k8s.io/pr/leakingtapan). 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: @leakingtapan: The following test **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | 4350902e73933f9255eefd2f0d559db986c01058 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-e2e-single-az/1190033172570574850) | `/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=394). [Your PR dashboard](https://gubernator.k8s.io/pr/leakingtapan). 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 -->
leakingtapan: /retest
k8s-ci-robot: @leakingtapan: The following test **failed**, say `/retest` to rerun them all:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-aws-ebs-csi-driver-e2e-single-az | 4350902e73933f9255eefd2f0d559db986c01058 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_aws-ebs-csi-driver/394/pull-aws-ebs-csi-driver-e2e-single-az/1190044123462111232) | `/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=394). [Your PR dashboard](https://gubernator.k8s.io/pr/leakingtapan). 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 a24c549f..7ee1aa76 100644
--- a/pkg/cloud/cloud.go
+++ b/pkg/cloud/cloud.go
@@ -25,7 +25,6 @@ import (
 
 	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/awserr"
-	"github.com/aws/aws-sdk-go/aws/ec2metadata"
 	"github.com/aws/aws-sdk-go/aws/request"
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/ec2"
@@ -180,10 +179,10 @@ type EC2 interface {
 	DescribeSnapshotsWithContext(ctx aws.Context, input *ec2.DescribeSnapshotsInput, opts ...request.Option) (*ec2.DescribeSnapshotsOutput, error)
 	ModifyVolumeWithContext(ctx aws.Context, input *ec2.ModifyVolumeInput, opts ...request.Option) (*ec2.ModifyVolumeOutput, error)
 	DescribeVolumesModificationsWithContext(ctx aws.Context, input *ec2.DescribeVolumesModificationsInput, opts ...request.Option) (*ec2.DescribeVolumesModificationsOutput, error)
+	DescribeAvailabilityZonesWithContext(ctx aws.Context, input *ec2.DescribeAvailabilityZonesInput, opts ...request.Option) (*ec2.DescribeAvailabilityZonesOutput, error)
 }
 
 type Cloud interface {
-	GetMetadata() MetadataService
 	CreateDisk(ctx context.Context, volumeName string, diskOptions *DiskOptions) (disk *Disk, err error)
 	DeleteDisk(ctx context.Context, volumeID string) (success bool, err error)
 	AttachDisk(ctx context.Context, volumeID string, nodeID string) (devicePath string, err error)
@@ -201,38 +200,22 @@ type Cloud interface {
 }
 
 type cloud struct {
-	metadata MetadataService
-	ec2      EC2
-	dm       dm.DeviceManager
+	region string
+	ec2    EC2
+	dm     dm.DeviceManager
 }
 
 var _ Cloud = &cloud{}
 
 // NewCloud returns a new instance of AWS cloud
 // It panics if session is invalid
-func NewCloud() (Cloud, error) {
-	svc := newEC2MetadataSvc()
-
-	metadata, err := NewMetadataService(svc)
-	if err != nil {
-		return nil, fmt.Errorf("could not get metadata from AWS: %v", err)
-	}
-
-	return newEC2Cloud(metadata, svc)
-}
-
-func NewCloudWithMetadata(metadata MetadataService) (Cloud, error) {
-	return newEC2Cloud(metadata, newEC2MetadataSvc())
-}
-
-func newEC2MetadataSvc() *ec2metadata.EC2Metadata {
-	sess := session.Must(session.NewSession(&aws.Config{}))
-	return ec2metadata.New(sess)
+func NewCloud(region string) (Cloud, error) {
+	return newEC2Cloud(region)
 }
 
-func newEC2Cloud(metadata MetadataService, svc *ec2metadata.EC2Metadata) (Cloud, error) {
+func newEC2Cloud(region string) (Cloud, error) {
 	awsConfig := &aws.Config{
-		Region:                        aws.String(metadata.GetRegion()),
+		Region:                        aws.String(region),
 		CredentialsChainVerboseErrors: aws.Bool(true),
 	}
 
@@ -242,16 +225,12 @@ func newEC2Cloud(metadata MetadataService, svc *ec2metadata.EC2Metadata) (Cloud,
 	}
 
 	return &cloud{
-		metadata: metadata,
-		dm:       dm.NewDeviceManager(),
-		ec2:      ec2.New(session.Must(session.NewSession(awsConfig))),
+		region: region,
+		dm:     dm.NewDeviceManager(),
+		ec2:    ec2.New(session.Must(session.NewSession(awsConfig))),
 	}, nil
 }
 
-func (c *cloud) GetMetadata() MetadataService {
-	return c.metadata
-}
-
 func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *DiskOptions) (*Disk, error) {
 	var (
 		createType string
@@ -290,8 +269,12 @@ func (c *cloud) CreateDisk(ctx context.Context, volumeName string, diskOptions *
 
 	zone := diskOptions.AvailabilityZone
 	if zone == "" {
-		zone = c.metadata.GetAvailabilityZone()
 		klog.V(5).Infof("AZ is not provided. Using node AZ [%s]", zone)
+		var err error
+		zone, err = c.randomAvailabilityZone(ctx, c.region)
+		if err != nil {
+			return nil, fmt.Errorf("failed to get availability zone %s", err)
+		}
 	}
 
 	request := &ec2.CreateVolumeInput{
@@ -938,3 +921,20 @@ func (c *cloud) getLatestVolumeModification(ctx context.Context, volumeID string
 
 	return volumeMods[len(volumeMods)-1], nil
 }
+
+// randomAvailabilityZone returns a random zone from the given region
+// the randomness relies on the response of DescribeAvailabilityZones
+func (c *cloud) randomAvailabilityZone(ctx context.Context, region string) (string, error) {
+	request := &ec2.DescribeAvailabilityZonesInput{}
+	response, err := c.ec2.DescribeAvailabilityZonesWithContext(ctx, request)
+	if err != nil {
+		return "", err
+	}
+
+	zones := []string{}
+	for _, zone := range response.AvailabilityZones {
+		zones = append(zones, *zone.ZoneName)
+	}
+
+	return zones[0], nil
+}
diff --git a/pkg/cloud/metadata.go b/pkg/cloud/metadata.go
index c265b466..64968413 100644
--- a/pkg/cloud/metadata.go
+++ b/pkg/cloud/metadata.go
@@ -19,7 +19,9 @@ package cloud
 import (
 	"fmt"
 
+	"github.com/aws/aws-sdk-go/aws"
 	"github.com/aws/aws-sdk-go/aws/ec2metadata"
+	"github.com/aws/aws-sdk-go/aws/session"
 )
 
 type EC2Metadata interface {
@@ -64,6 +66,12 @@ func (m *Metadata) GetAvailabilityZone() string {
 	return m.AvailabilityZone
 }
 
+func NewMetadata() (MetadataService, error) {
+	sess := session.Must(session.NewSession(&aws.Config{}))
+	svc := ec2metadata.New(sess)
+	return NewMetadataService(svc)
+}
+
 // NewMetadataService returns a new MetadataServiceImplementation.
 func NewMetadataService(svc EC2Metadata) (MetadataService, error) {
 	if !svc.Available() {
diff --git a/pkg/cloud/mocks/mock_ec2.go b/pkg/cloud/mocks/mock_ec2.go
index d11e64be..e541a07f 100644
--- a/pkg/cloud/mocks/mock_ec2.go
+++ b/pkg/cloud/mocks/mock_ec2.go
@@ -5,7 +5,7 @@
 package mocks
 
 import (
-	aws "github.com/aws/aws-sdk-go/aws"
+	context "context"
 	request "github.com/aws/aws-sdk-go/aws/request"
 	ec2 "github.com/aws/aws-sdk-go/service/ec2"
 	gomock "github.com/golang/mock/gomock"
@@ -36,7 +36,7 @@ func (m *MockEC2) EXPECT() *MockEC2MockRecorder {
 }
 
 // AttachVolumeWithContext mocks base method
-func (m *MockEC2) AttachVolumeWithContext(arg0 aws.Context, arg1 *ec2.AttachVolumeInput, arg2 ...request.Option) (*ec2.VolumeAttachment, error) {
+func (m *MockEC2) AttachVolumeWithContext(arg0 context.Context, arg1 *ec2.AttachVolumeInput, arg2 ...request.Option) (*ec2.VolumeAttachment, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -54,7 +54,7 @@ func (mr *MockEC2MockRecorder) AttachVolumeWithContext(arg0, arg1 interface{}, a
 }
 
 // CreateSnapshotWithContext mocks base method
-func (m *MockEC2) CreateSnapshotWithContext(arg0 aws.Context, arg1 *ec2.CreateSnapshotInput, arg2 ...request.Option) (*ec2.Snapshot, error) {
+func (m *MockEC2) CreateSnapshotWithContext(arg0 context.Context, arg1 *ec2.CreateSnapshotInput, arg2 ...request.Option) (*ec2.Snapshot, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -72,7 +72,7 @@ func (mr *MockEC2MockRecorder) CreateSnapshotWithContext(arg0, arg1 interface{},
 }
 
 // CreateVolumeWithContext mocks base method
-func (m *MockEC2) CreateVolumeWithContext(arg0 aws.Context, arg1 *ec2.CreateVolumeInput, arg2 ...request.Option) (*ec2.Volume, error) {
+func (m *MockEC2) CreateVolumeWithContext(arg0 context.Context, arg1 *ec2.CreateVolumeInput, arg2 ...request.Option) (*ec2.Volume, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -90,7 +90,7 @@ func (mr *MockEC2MockRecorder) CreateVolumeWithContext(arg0, arg1 interface{}, a
 }
 
 // DeleteSnapshotWithContext mocks base method
-func (m *MockEC2) DeleteSnapshotWithContext(arg0 aws.Context, arg1 *ec2.DeleteSnapshotInput, arg2 ...request.Option) (*ec2.DeleteSnapshotOutput, error) {
+func (m *MockEC2) DeleteSnapshotWithContext(arg0 context.Context, arg1 *ec2.DeleteSnapshotInput, arg2 ...request.Option) (*ec2.DeleteSnapshotOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -108,7 +108,7 @@ func (mr *MockEC2MockRecorder) DeleteSnapshotWithContext(arg0, arg1 interface{},
 }
 
 // DeleteVolumeWithContext mocks base method
-func (m *MockEC2) DeleteVolumeWithContext(arg0 aws.Context, arg1 *ec2.DeleteVolumeInput, arg2 ...request.Option) (*ec2.DeleteVolumeOutput, error) {
+func (m *MockEC2) DeleteVolumeWithContext(arg0 context.Context, arg1 *ec2.DeleteVolumeInput, arg2 ...request.Option) (*ec2.DeleteVolumeOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -125,8 +125,26 @@ func (mr *MockEC2MockRecorder) DeleteVolumeWithContext(arg0, arg1 interface{}, a
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteVolumeWithContext", reflect.TypeOf((*MockEC2)(nil).DeleteVolumeWithContext), varargs...)
 }
 
+// DescribeAvailabilityZonesWithContext mocks base method
+func (m *MockEC2) DescribeAvailabilityZonesWithContext(arg0 context.Context, arg1 *ec2.DescribeAvailabilityZonesInput, arg2 ...request.Option) (*ec2.DescribeAvailabilityZonesOutput, error) {
+	varargs := []interface{}{arg0, arg1}
+	for _, a := range arg2 {
+		varargs = append(varargs, a)
+	}
+	ret := m.ctrl.Call(m, "DescribeAvailabilityZonesWithContext", varargs...)
+	ret0, _ := ret[0].(*ec2.DescribeAvailabilityZonesOutput)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// DescribeAvailabilityZonesWithContext indicates an expected call of DescribeAvailabilityZonesWithContext
+func (mr *MockEC2MockRecorder) DescribeAvailabilityZonesWithContext(arg0, arg1 interface{}, arg2 ...interface{}) *gomock.Call {
+	varargs := append([]interface{}{arg0, arg1}, arg2...)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DescribeAvailabilityZonesWithContext", reflect.TypeOf((*MockEC2)(nil).DescribeAvailabilityZonesWithContext), varargs...)
+}
+
 // DescribeInstancesWithContext mocks base method
-func (m *MockEC2) DescribeInstancesWithContext(arg0 aws.Context, arg1 *ec2.DescribeInstancesInput, arg2 ...request.Option) (*ec2.DescribeInstancesOutput, error) {
+func (m *MockEC2) DescribeInstancesWithContext(arg0 context.Context, arg1 *ec2.DescribeInstancesInput, arg2 ...request.Option) (*ec2.DescribeInstancesOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -144,7 +162,7 @@ func (mr *MockEC2MockRecorder) DescribeInstancesWithContext(arg0, arg1 interface
 }
 
 // DescribeSnapshotsWithContext mocks base method
-func (m *MockEC2) DescribeSnapshotsWithContext(arg0 aws.Context, arg1 *ec2.DescribeSnapshotsInput, arg2 ...request.Option) (*ec2.DescribeSnapshotsOutput, error) {
+func (m *MockEC2) DescribeSnapshotsWithContext(arg0 context.Context, arg1 *ec2.DescribeSnapshotsInput, arg2 ...request.Option) (*ec2.DescribeSnapshotsOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -162,7 +180,7 @@ func (mr *MockEC2MockRecorder) DescribeSnapshotsWithContext(arg0, arg1 interface
 }
 
 // DescribeVolumesModificationsWithContext mocks base method
-func (m *MockEC2) DescribeVolumesModificationsWithContext(arg0 aws.Context, arg1 *ec2.DescribeVolumesModificationsInput, arg2 ...request.Option) (*ec2.DescribeVolumesModificationsOutput, error) {
+func (m *MockEC2) DescribeVolumesModificationsWithContext(arg0 context.Context, arg1 *ec2.DescribeVolumesModificationsInput, arg2 ...request.Option) (*ec2.DescribeVolumesModificationsOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -180,7 +198,7 @@ func (mr *MockEC2MockRecorder) DescribeVolumesModificationsWithContext(arg0, arg
 }
 
 // DescribeVolumesWithContext mocks base method
-func (m *MockEC2) DescribeVolumesWithContext(arg0 aws.Context, arg1 *ec2.DescribeVolumesInput, arg2 ...request.Option) (*ec2.DescribeVolumesOutput, error) {
+func (m *MockEC2) DescribeVolumesWithContext(arg0 context.Context, arg1 *ec2.DescribeVolumesInput, arg2 ...request.Option) (*ec2.DescribeVolumesOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -198,7 +216,7 @@ func (mr *MockEC2MockRecorder) DescribeVolumesWithContext(arg0, arg1 interface{}
 }
 
 // DetachVolumeWithContext mocks base method
-func (m *MockEC2) DetachVolumeWithContext(arg0 aws.Context, arg1 *ec2.DetachVolumeInput, arg2 ...request.Option) (*ec2.VolumeAttachment, error) {
+func (m *MockEC2) DetachVolumeWithContext(arg0 context.Context, arg1 *ec2.DetachVolumeInput, arg2 ...request.Option) (*ec2.VolumeAttachment, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
@@ -216,7 +234,7 @@ func (mr *MockEC2MockRecorder) DetachVolumeWithContext(arg0, arg1 interface{}, a
 }
 
 // ModifyVolumeWithContext mocks base method
-func (m *MockEC2) ModifyVolumeWithContext(arg0 aws.Context, arg1 *ec2.ModifyVolumeInput, arg2 ...request.Option) (*ec2.ModifyVolumeOutput, error) {
+func (m *MockEC2) ModifyVolumeWithContext(arg0 context.Context, arg1 *ec2.ModifyVolumeInput, arg2 ...request.Option) (*ec2.ModifyVolumeOutput, error) {
 	varargs := []interface{}{arg0, arg1}
 	for _, a := range arg2 {
 		varargs = append(varargs, a)
diff --git a/pkg/driver/controller.go b/pkg/driver/controller.go
index c955ed15..5e0c1ce7 100644
--- a/pkg/driver/controller.go
+++ b/pkg/driver/controller.go
@@ -59,7 +59,12 @@ type controllerService struct {
 // newControllerService creates a new controller service
 // it panics if failed to create the service
 func newControllerService(driverOptions *DriverOptions) controllerService {
-	cloud, err := cloud.NewCloud()
+	metadata, err := cloud.NewMetadata()
+	if err != nil {
+		panic(err)
+	}
+	region := metadata.GetRegion()
+	cloud, err := cloud.NewCloud(region)
 	if err != nil {
 		panic(err)
 	}
diff --git a/pkg/driver/fakes.go b/pkg/driver/fakes.go
index a91a1f8d..872aaead 100644
--- a/pkg/driver/fakes.go
+++ b/pkg/driver/fakes.go
@@ -34,7 +34,11 @@ func NewFakeDriver(endpoint string, fakeCloud cloud.Cloud, fakeMounter *mount.Fa
 			driverOptions: driverOptions,
 		},
 		nodeService: nodeService{
-			metadata: fakeCloud.GetMetadata(),
+			metadata: &cloud.Metadata{
+				InstanceID:       "instanceID",
+				Region:           "region",
+				AvailabilityZone: "az",
+			},
 			mounter:  &NodeMounter{mount.SafeFormatAndMount{Interface: fakeMounter, Exec: mount.NewFakeExec(nil)}},
 			inFlight: internal.NewInFlight(),
 		},
diff --git a/pkg/driver/mocks/mock_cloud.go b/pkg/driver/mocks/mock_cloud.go
index 20c3a213..c87c8713 100644
--- a/pkg/driver/mocks/mock_cloud.go
+++ b/pkg/driver/mocks/mock_cloud.go
@@ -137,18 +137,6 @@ func (mr *MockCloudMockRecorder) GetDiskByName(arg0, arg1, arg2 interface{}) *go
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDiskByName", reflect.TypeOf((*MockCloud)(nil).GetDiskByName), arg0, arg1, arg2)
 }
 
-// GetMetadata mocks base method
-func (m *MockCloud) GetMetadata() cloud.MetadataService {
-	ret := m.ctrl.Call(m, "GetMetadata")
-	ret0, _ := ret[0].(cloud.MetadataService)
-	return ret0
-}
-
-// GetMetadata indicates an expected call of GetMetadata
-func (mr *MockCloudMockRecorder) GetMetadata() *gomock.Call {
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetMetadata", reflect.TypeOf((*MockCloud)(nil).GetMetadata))
-}
-
 // GetSnapshotById mocks base method
 func (m *MockCloud) GetSnapshotById(arg0 context.Context, arg1 string) (*cloud.Snapshot, error) {
 	ret := m.ctrl.Call(m, "GetSnapshotById", arg0, arg1)
diff --git a/pkg/driver/mocks/mock_mounter.go b/pkg/driver/mocks/mock_mounter.go
index 35fb3784..e53feeb6 100644
--- a/pkg/driver/mocks/mock_mounter.go
+++ b/pkg/driver/mocks/mock_mounter.go
@@ -34,18 +34,6 @@ func (m *MockMounter) EXPECT() *MockMounterMockRecorder {
 	return m.recorder
 }
 
-// CleanSubPaths mocks base method
-func (m *MockMounter) CleanSubPaths(arg0, arg1 string) error {
-	ret := m.ctrl.Call(m, "CleanSubPaths", arg0, arg1)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// CleanSubPaths indicates an expected call of CleanSubPaths
-func (mr *MockMounterMockRecorder) CleanSubPaths(arg0, arg1 interface{}) *gomock.Call {
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanSubPaths", reflect.TypeOf((*MockMounter)(nil).CleanSubPaths), arg0, arg1)
-}
-
 // DeviceOpened mocks base method
 func (m *MockMounter) DeviceOpened(arg0 string) (bool, error) {
 	ret := m.ctrl.Call(m, "DeviceOpened", arg0)
@@ -214,19 +202,6 @@ func (mr *MockMounterMockRecorder) IsMountPointMatch(arg0, arg1 interface{}) *go
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsMountPointMatch", reflect.TypeOf((*MockMounter)(nil).IsMountPointMatch), arg0, arg1)
 }
 
-// IsNotMountPoint mocks base method
-func (m *MockMounter) IsNotMountPoint(arg0 string) (bool, error) {
-	ret := m.ctrl.Call(m, "IsNotMountPoint", arg0)
-	ret0, _ := ret[0].(bool)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
-}
-
-// IsNotMountPoint indicates an expected call of IsNotMountPoint
-func (mr *MockMounterMockRecorder) IsNotMountPoint(arg0 interface{}) *gomock.Call {
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IsNotMountPoint", reflect.TypeOf((*MockMounter)(nil).IsNotMountPoint), arg0)
-}
-
 // List mocks base method
 func (m *MockMounter) List() ([]mount.MountPoint, error) {
 	ret := m.ctrl.Call(m, "List")
@@ -301,20 +276,6 @@ func (mr *MockMounterMockRecorder) PathIsDevice(arg0 interface{}) *gomock.Call {
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PathIsDevice", reflect.TypeOf((*MockMounter)(nil).PathIsDevice), arg0)
 }
 
-// PrepareSafeSubpath mocks base method
-func (m *MockMounter) PrepareSafeSubpath(arg0 mount.Subpath) (string, func(), error) {
-	ret := m.ctrl.Call(m, "PrepareSafeSubpath", arg0)
-	ret0, _ := ret[0].(string)
-	ret1, _ := ret[1].(func())
-	ret2, _ := ret[2].(error)
-	return ret0, ret1, ret2
-}
-
-// PrepareSafeSubpath indicates an expected call of PrepareSafeSubpath
-func (mr *MockMounterMockRecorder) PrepareSafeSubpath(arg0 interface{}) *gomock.Call {
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PrepareSafeSubpath", reflect.TypeOf((*MockMounter)(nil).PrepareSafeSubpath), arg0)
-}
-
 // Run mocks base method
 func (m *MockMounter) Run(arg0 string, arg1 ...string) ([]byte, error) {
 	varargs := []interface{}{arg0}
@@ -333,18 +294,6 @@ func (mr *MockMounterMockRecorder) Run(arg0 interface{}, arg1 ...interface{}) *g
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockMounter)(nil).Run), varargs...)
 }
 
-// SafeMakeDir mocks base method
-func (m *MockMounter) SafeMakeDir(arg0, arg1 string, arg2 os.FileMode) error {
-	ret := m.ctrl.Call(m, "SafeMakeDir", arg0, arg1, arg2)
-	ret0, _ := ret[0].(error)
-	return ret0
-}
-
-// SafeMakeDir indicates an expected call of SafeMakeDir
-func (mr *MockMounterMockRecorder) SafeMakeDir(arg0, arg1, arg2 interface{}) *gomock.Call {
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SafeMakeDir", reflect.TypeOf((*MockMounter)(nil).SafeMakeDir), arg0, arg1, arg2)
-}
-
 // Unmount mocks base method
 func (m *MockMounter) Unmount(arg0 string) error {
 	ret := m.ctrl.Call(m, "Unmount", arg0)
diff --git a/pkg/driver/node.go b/pkg/driver/node.go
index 38bea00c..650b4fb5 100644
--- a/pkg/driver/node.go
+++ b/pkg/driver/node.go
@@ -77,13 +77,13 @@ type nodeService struct {
 // newNodeService creates a new node service
 // it panics if failed to create the service
 func newNodeService() nodeService {
-	cloud, err := cloud.NewCloud()
+	metadata, err := cloud.NewMetadata()
 	if err != nil {
 		panic(err)
 	}
 
 	return nodeService{
-		metadata: cloud.GetMetadata(),
+		metadata: metadata,
 		mounter:  newNodeMounter(),
 		inFlight: internal.NewInFlight(),
 	}
@@ -353,14 +353,13 @@ 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)
-	m := d.metadata
 
 	topology := &csi.Topology{
-		Segments: map[string]string{TopologyKey: m.GetAvailabilityZone()},
+		Segments: map[string]string{TopologyKey: d.metadata.GetAvailabilityZone()},
 	}
 
 	return &csi.NodeGetInfoResponse{
-		NodeId:             m.GetInstanceID(),
+		NodeId:             d.metadata.GetInstanceID(),
 		MaxVolumesPerNode:  d.getVolumesLimit(),
 		AccessibleTopology: topology,
 	}, nil


Test patch:
diff --git a/pkg/cloud/cloud_test.go b/pkg/cloud/cloud_test.go
index 4929bd2d..09647d4f 100644
--- a/pkg/cloud/cloud_test.go
+++ b/pkg/cloud/cloud_test.go
@@ -175,6 +175,14 @@ func TestCreateDisk(t *testing.T) {
 				mockEC2.EXPECT().DescribeSnapshotsWithContext(gomock.Eq(ctx), gomock.Any()).Return(&ec2.DescribeSnapshotsOutput{Snapshots: []*ec2.Snapshot{snapshot}}, nil).AnyTimes()
 			}
 
+			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)
 			if err != nil {
 				if tc.expErr == nil {
@@ -1041,13 +1049,9 @@ func TestListSnapshots(t *testing.T) {
 
 func newCloud(mockEC2 EC2) Cloud {
 	return &cloud{
-		metadata: &Metadata{
-			InstanceID:       "test-instance",
-			Region:           "test-region",
-			AvailabilityZone: defaultZone,
-		},
-		dm:  dm.NewDeviceManager(),
-		ec2: mockEC2,
+		region: "test-region",
+		dm:     dm.NewDeviceManager(),
+		ec2:    mockEC2,
 	}
 }
 
diff --git a/tests/e2e/dynamic_provisioning.go b/tests/e2e/dynamic_provisioning.go
index f90bfa40..5f2faabb 100644
--- a/tests/e2e/dynamic_provisioning.go
+++ b/tests/e2e/dynamic_provisioning.go
@@ -363,8 +363,8 @@ var _ = Describe("[ebs-csi-e2e] [single-az] Dynamic Provisioning", func() {
 		}
 		availabilityZones := strings.Split(os.Getenv(awsAvailabilityZonesEnv), ",")
 		availabilityZone := availabilityZones[rand.Intn(len(availabilityZones))]
-		metadata := e2eMetdataService{availabilityZone: availabilityZone}
-		cloud, err := awscloud.NewCloudWithMetadata(metadata)
+		region := availabilityZone[0 : len(availabilityZone)-1]
+		cloud, err := awscloud.NewCloud(region)
 		if err != nil {
 			Fail(fmt.Sprintf("could not get NewCloud: %v", err))
 		}
diff --git a/tests/e2e/pre_provsioning.go b/tests/e2e/pre_provsioning.go
index 0bd72a66..01c1df49 100644
--- a/tests/e2e/pre_provsioning.go
+++ b/tests/e2e/pre_provsioning.go
@@ -45,28 +45,6 @@ var (
 	defaultDiskSizeBytes int64 = defaultDiskSize * 1024 * 1024 * 1024
 )
 
-type e2eMetdataService struct {
-	availabilityZone string
-}
-
-// GetInstanceID will always return an empty string as the test does not need to run on an EC2 machine
-func (s e2eMetdataService) GetInstanceID() string {
-	return ""
-}
-
-func (s e2eMetdataService) GetInstanceType() string {
-	return ""
-}
-
-func (s e2eMetdataService) GetAvailabilityZone() string {
-	return s.availabilityZone
-}
-
-// GetRegion will try to determine the Region from the specified AZ, specifically trims the last character
-func (s e2eMetdataService) GetRegion() string {
-	return s.availabilityZone[0 : len(s.availabilityZone)-1]
-}
-
 // Requires env AWS_AVAILABILITY_ZONES a comma separated list of AZs to be set
 var _ = Describe("[ebs-csi-e2e] [single-az] Pre-Provisioned", func() {
 	f := framework.NewDefaultFramework("ebs")
@@ -93,15 +71,16 @@ var _ = Describe("[ebs-csi-e2e] [single-az] Pre-Provisioned", func() {
 		}
 		availabilityZones := strings.Split(os.Getenv(awsAvailabilityZonesEnv), ",")
 		availabilityZone := availabilityZones[rand.Intn(len(availabilityZones))]
+		region := availabilityZone[0 : len(availabilityZone)-1]
+
 		diskOptions := &awscloud.DiskOptions{
 			CapacityBytes:    defaultDiskSizeBytes,
 			VolumeType:       defaultVoluemType,
 			AvailabilityZone: availabilityZone,
 			Tags:             map[string]string{awscloud.VolumeNameTagKey: dummyVolumeName},
 		}
-		metadata := e2eMetdataService{availabilityZone: availabilityZone}
 		var err error
-		cloud, err = awscloud.NewCloudWithMetadata(metadata)
+		cloud, err = awscloud.NewCloud(region)
 		if err != nil {
 			Fail(fmt.Sprintf("could not get NewCloud: %v", err))
 		}
diff --git a/tests/sanity/fake_cloud_provider.go b/tests/sanity/fake_cloud_provider.go
index 9ddca6a0..2e74c08b 100644
--- a/tests/sanity/fake_cloud_provider.go
+++ b/tests/sanity/fake_cloud_provider.go
@@ -30,7 +30,6 @@ type fakeCloudProvider struct {
 	disks map[string]*fakeDisk
 	// snapshots contains mapping from snapshot ID to snapshot
 	snapshots map[string]*fakeSnapshot
-	m         *cloud.Metadata
 	pub       map[string]string
 	tokens    map[string]int64
 }
@@ -50,19 +49,10 @@ func newFakeCloudProvider() *fakeCloudProvider {
 		disks:     make(map[string]*fakeDisk),
 		snapshots: make(map[string]*fakeSnapshot),
 		pub:       make(map[string]string),
-		m: &cloud.Metadata{
-			InstanceID:       "instanceID",
-			Region:           "region",
-			AvailabilityZone: "az",
-		},
-		tokens: make(map[string]int64),
+		tokens:    make(map[string]int64),
 	}
 }
 
-func (c *fakeCloudProvider) GetMetadata() cloud.MetadataService {
-	return c.m
-}
-
 func (c *fakeCloudProvider) CreateDisk(ctx context.Context, volumeName string, diskOptions *cloud.DiskOptions) (*cloud.Disk, error) {
 	r1 := rand.New(rand.NewSource(time.Now().UnixNano()))
 	if len(diskOptions.SnapshotID) > 0 {
@@ -137,7 +127,7 @@ func (c *fakeCloudProvider) GetDiskByID(ctx context.Context, volumeID string) (*
 }
 
 func (c *fakeCloudProvider) IsExistInstance(ctx context.Context, nodeID string) bool {
-	return nodeID == c.m.GetInstanceID()
+	return nodeID == "instanceID"
 }
 
 func (c *fakeCloudProvider) CreateSnapshot(ctx context.Context, volumeID string, snapshotOptions *cloud.SnapshotOptions) (snapshot *cloud.Snapshot, err error) {