Repository: kubernetes-sigs/gcp-filestore-csi-driver
Pull request #356 (2022-08-12 21:04:20)

Cherry pick of #337 and #347 from master to release-1.3
<!--  Thanks for sending a pull request!  Here are some tips for you:

1. If this is your first time, please read our contributor guidelines: https://git.k8s.io/community/contributors/guide#your-first-contribution and developer guide https://git.k8s.io/community/contributors/devel/development.md#development-guide
2. Follow the instructions for writing a release note: https://git.k8s.io/community/contributors/guide/release-notes.md
3. If the PR is unfinished, see how to mark it: https://git.k8s.io/community/contributors/guide/pull-requests.md#marking-unfinished-pull-requests
-->

**What type of PR is this?**
> Uncomment only one ` /kind <>` line, hit enter to put that in a new line, and remove leading whitespaces from that line:
>
> /kind api-change
> /kind bug
> /kind cleanup
> /kind design
> /kind documentation
> /kind failing-test
> /kind feature
> /kind flake

**What this PR does / why we need it**:
Cherry pick of #337 and #347 from master to release-1.3:
1. Instance validation improvement when choosing eligible instances.
2. Add reserved ip range check and fail earlier if it's invalid.

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note
N/A
```


k8s-ci-robot: Hi @tyuchn. 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%2Fgcp-filestore-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/gcp-filestore-csi-driver/pull/356#" title="Author self-approved">tyuchn</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign saad-ali for approval by writing `/assign @saad-ali` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

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

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

- **[OWNERS](https://github.com/kubernetes-sigs/gcp-filestore-csi-driver/blob/release-1.3/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":["saad-ali"]} -->
saikat-royc: /lgtm
/approve
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/gcp-filestore-csi-driver/pull/356#issuecomment-1213529272" title="Approved">saikat-royc</a>*, *<a href="https://github.com/kubernetes-sigs/gcp-filestore-csi-driver/pull/356#" title="Author self-approved">tyuchn</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fgcp-filestore-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/gcp-filestore-csi-driver/blob/release-1.3/OWNERS)~~ [saikat-royc]

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":[]} -->
saikat-royc: /ok-to-test
k8s-ci-robot: @tyuchn: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-gcp-filestore-csi-driver-e2e | f5c1b1c2ffe9522dd8866cec91b3df4401d9aeff | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/sigs.k8s.io_gcp-filestore-csi-driver/356/pull-gcp-filestore-csi-driver-e2e/1558202069897187328/) | true | `/test pull-gcp-filestore-csi-driver-e2e`

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

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-gcp-filestore-csi-driver-e2e | f5c1b1c2ffe9522dd8866cec91b3df4401d9aeff | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/sigs.k8s.io_gcp-filestore-csi-driver/356/pull-gcp-filestore-csi-driver-e2e/1558202974612754432/) | true | `/test pull-gcp-filestore-csi-driver-e2e`

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

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-gcp-filestore-csi-driver-e2e | f5c1b1c2ffe9522dd8866cec91b3df4401d9aeff | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/sigs.k8s.io_gcp-filestore-csi-driver/356/pull-gcp-filestore-csi-driver-e2e/1558203853604655104/) | unknown | `/test pull-gcp-filestore-csi-driver-e2e`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=gcp-filestore-csi-driver&pr=356). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atyuchn). 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/gcp-filestore-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: New changes are detected. LGTM label has been removed.
k8s-ci-robot: @tyuchn: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-gcp-filestore-csi-driver-e2e | f5c1b1c2ffe9522dd8866cec91b3df4401d9aeff | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/sigs.k8s.io_gcp-filestore-csi-driver/356/pull-gcp-filestore-csi-driver-e2e/1558220412549074944/) | true | `/test pull-gcp-filestore-csi-driver-e2e`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=gcp-filestore-csi-driver&pr=356). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Atyuchn). 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/gcp-filestore-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 -->
saikat-royc: /lgtm
/approve

Patch:
diff --git a/pkg/cloud_provider/file/fake.go b/pkg/cloud_provider/file/fake.go
index d66aa57a..e2d1e412 100644
--- a/pkg/cloud_provider/file/fake.go
+++ b/pkg/cloud_provider/file/fake.go
@@ -295,17 +295,14 @@ func (manager *fakeServiceManager) ListMultishareInstances(ctx context.Context,
 func (manager *fakeServiceManager) StartCreateMultishareInstanceOp(ctx context.Context, obj *MultishareInstance) (*filev1beta1multishare.Operation, error) {
 	instance := &MultishareInstance{
 		Project:       defaultProject,
-		Location:      defaultRegion,
+		Location:      obj.Location,
 		Name:          obj.Name,
 		Tier:          obj.Tier,
 		CapacityBytes: obj.CapacityBytes,
-		Network: Network{
-			Name:            obj.Network.Name,
-			Ip:              obj.Network.Ip,
-			ReservedIpRange: obj.Network.ReservedIpRange,
-		},
-		Labels: obj.Labels,
-		State:  "READY",
+		Network:       obj.Network,
+		KmsKeyName:    obj.KmsKeyName,
+		Labels:        obj.Labels,
+		State:         "READY",
 	}
 	manager.createdMultishareInstance[obj.Name] = instance
 	meta := &filev1beta1multishare.OperationMetadata{
diff --git a/pkg/csi_driver/controller.go b/pkg/csi_driver/controller.go
index b12e86c5..f72e657d 100644
--- a/pkg/csi_driver/controller.go
+++ b/pkg/csi_driver/controller.go
@@ -211,6 +211,9 @@ func (s *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVolu
 		// If the param was not provided, we default reservedIPRange to "" and cloud provider takes care of the allocation
 		if newFiler.Network.ConnectMode == privateServiceAccess {
 			if reservedIPRange, ok := param[paramReservedIPRange]; ok {
+				if IsCIDR(reservedIPRange) {
+					return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("When using connect mode PRIVATE_SERVICE_ACCESS, if reserved IP range is specified, it must be a named address range instead of direct CIDR value %v", reservedIPRange))
+				}
 				newFiler.Network.ReservedIpRange = reservedIPRange
 			}
 		} else if reservedIPV4CIDR, ok := param[paramReservedIPV4CIDR]; ok {
diff --git a/pkg/csi_driver/multishare_ops_manager.go b/pkg/csi_driver/multishare_ops_manager.go
index cfc6e64a..d03a9569 100644
--- a/pkg/csi_driver/multishare_ops_manager.go
+++ b/pkg/csi_driver/multishare_ops_manager.go
@@ -68,10 +68,6 @@ func (m *MultishareOpsManager) setupEligibleInstanceAndStartWorkflow(ctx context
 
 	// Check ShareCreateMap if a share create is already in progress.
 	shareName := util.ConvertVolToShareName(req.Name)
-	instanceScPrefix, err := getInstanceSCPrefix(req)
-	if err != nil {
-		return nil, nil, status.Error(codes.InvalidArgument, err.Error())
-	}
 
 	ops, err := m.listMultishareResourceRunningOps(ctx)
 	if err != nil {
@@ -103,7 +99,7 @@ func (m *MultishareOpsManager) setupEligibleInstanceAndStartWorkflow(ctx context
 	}
 
 	// No share or running share create op found. Proceed to eligible instance check.
-	eligible, numIneligible, err := m.runEligibleInstanceCheck(ctx, instanceScPrefix, ops, req)
+	eligible, numIneligible, err := m.runEligibleInstanceCheck(ctx, req, ops, instance)
 	if err != nil {
 		return nil, nil, status.Error(codes.Aborted, err.Error())
 	}
@@ -142,6 +138,9 @@ func (m *MultishareOpsManager) setupEligibleInstanceAndStartWorkflow(ctx context
 	// If the param was not provided, we default reservedIPRange to "" and cloud provider takes care of the allocation
 	if instance.Network.ConnectMode == privateServiceAccess {
 		if reservedIPRange, ok := param[paramReservedIPRange]; ok {
+			if IsCIDR(reservedIPRange) {
+				return nil, nil, status.Error(codes.InvalidArgument, "When using connect mode PRIVATE_SERVICE_ACCESS, if reserved IP range is specified, it must be a named address range instead of direct CIDR value")
+			}
 			instance.Network.ReservedIpRange = reservedIPRange
 		}
 	} else if reservedIPV4CIDR, ok := param[paramReservedIPV4CIDR]; ok {
@@ -344,9 +343,9 @@ func (m *MultishareOpsManager) verifyNoRunningInstanceOrShareOpsForInstance(inst
 }
 
 // runEligibleInstanceCheck returns a list of ready and non-ready instances.
-func (m *MultishareOpsManager) runEligibleInstanceCheck(ctx context.Context, instanceScPrefix string, ops []*OpInfo, req *csi.CreateVolumeRequest) ([]*file.MultishareInstance, int, error) {
+func (m *MultishareOpsManager) runEligibleInstanceCheck(ctx context.Context, req *csi.CreateVolumeRequest, ops []*OpInfo, target *file.MultishareInstance) ([]*file.MultishareInstance, int, error) {
 	klog.Infof("ListMultishareInstances call initiated for request %+v.", req)
-	instances, err := m.listInstanceForStorageClassPrefix(ctx, instanceScPrefix)
+	instances, err := m.listMatchedInstances(ctx, req, target)
 	if err != nil {
 		return nil, 0, err
 	}
@@ -651,16 +650,69 @@ func containsOpWithInstanceTargetPrefix(instance *file.MultishareInstance, ops [
 	return nil, nil
 }
 
-func (m *MultishareOpsManager) listInstanceForStorageClassPrefix(ctx context.Context, prefix string) ([]*file.MultishareInstance, error) {
+// listMatchedInstances will list all instances in current project,
+// but only matched instances will be returned.
+func (m *MultishareOpsManager) listMatchedInstances(ctx context.Context, req *csi.CreateVolumeRequest, target *file.MultishareInstance) ([]*file.MultishareInstance, error) {
 	instances, err := m.cloud.File.ListMultishareInstances(ctx, &file.ListFilter{Project: m.cloud.Project, Location: "-"})
 	if err != nil {
 		return nil, err
 	}
 	var finalInstances []*file.MultishareInstance
 	for _, i := range instances {
-		if val, ok := i.Labels[util.ParamMultishareInstanceScLabelKey]; ok && val == prefix {
+		matched, err := isMatchedInstance(i, target, req)
+		if err != nil {
+			return nil, err
+		}
+		klog.Infof("Found source instance %+v, comparing with target instance %+v and StorageClass parameters %v, matched = %t", *i, *target, req.GetParameters(), matched)
+		if matched {
 			finalInstances = append(finalInstances, i)
 		}
 	}
 	return finalInstances, nil
 }
+
+// A source instance will be considered as "matched" with the target instance
+// if and only if the following requirements were met:
+// 1. Both source and target instance should have a label with key
+//    "storage_gke_io_storage-class-id", and the value should be the same.
+// 2. (Check if exists) The ip address of the target instance should be
+//    within the ip range specified in "reserved-ipv4-cidr".
+// 3. (Check if exists) The ip address of the target instance should be
+//    within the ip range specified in "reserved-ip-range".
+// 4. Both source and target instance should be in the same location.
+// 5. Both source and target instance should be under the same tier.
+// 6. Both source and target instance should be in the same VPC network.
+// 7, Both source and target instance should have the same connect mode.
+// 8. Both source and target instance should have the same KmsKeyName.
+func isMatchedInstance(source, target *file.MultishareInstance, req *csi.CreateVolumeRequest) (bool, error) {
+	if scPrefixSource, ok := source.Labels[util.ParamMultishareInstanceScLabelKey]; ok {
+		if _, ok := target.Labels[util.ParamMultishareInstanceScLabelKey]; !ok {
+			return false, fmt.Errorf("label %q missing in target instance %+v", util.ParamMultishareInstanceScLabelKey, target)
+		}
+		if scPrefixSource != target.Labels[util.ParamMultishareInstanceScLabelKey] {
+			return false, nil
+		}
+	} else {
+		return false, nil
+	}
+	params := req.GetParameters()
+	if instanceCIDR, ok := params[paramReservedIPV4CIDR]; ok {
+		withinRange, err := IsIpWithinRange(source.Network.Ip, instanceCIDR)
+		if err != nil {
+			return false, err
+		}
+		if !withinRange {
+			return false, nil
+		}
+	}
+	// Skip validation for parameter "reserved-ip-range" since it requires
+	// extra compute api auth and not clear if it's required.
+	if strings.EqualFold(source.Location, target.Location) &&
+		strings.EqualFold(source.Tier, target.Tier) &&
+		strings.EqualFold(source.Network.Name, target.Network.Name) &&
+		strings.EqualFold(source.Network.ConnectMode, target.Network.ConnectMode) &&
+		strings.EqualFold(source.KmsKeyName, target.KmsKeyName) {
+		return true, nil
+	}
+	return false, nil
+}
diff --git a/pkg/csi_driver/utils.go b/pkg/csi_driver/utils.go
index 68f06bc2..33856f1c 100644
--- a/pkg/csi_driver/utils.go
+++ b/pkg/csi_driver/utils.go
@@ -17,6 +17,9 @@ limitations under the License.
 package driver
 
 import (
+	"fmt"
+	"net"
+
 	csi "github.com/container-storage-interface/spec/lib/go/csi"
 	"github.com/golang/glog"
 	pbSanitizer "github.com/kubernetes-csi/csi-lib-utils/protosanitizer"
@@ -59,3 +62,18 @@ func logGRPC(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, h
 	}
 	return resp, err
 }
+
+// IsIpWithinRange checks if an ip address is within the given ip range.
+func IsIpWithinRange(ipAddress, ipRange string) (bool, error) {
+	_, ipnet, err := net.ParseCIDR(ipRange)
+	if err != nil {
+		return false, fmt.Errorf("failed to parse cidr range %s: %v", ipRange, err)
+	}
+	return ipnet.Contains(net.ParseIP(ipAddress)), nil
+}
+
+// IsCIDR verifies if the given ip range is a valid CIDR value.
+func IsCIDR(ipRange string) bool {
+	_, _, err := net.ParseCIDR(ipRange)
+	return err == nil
+}


Test patch:
diff --git a/pkg/csi_driver/multishare_controller_test.go b/pkg/csi_driver/multishare_controller_test.go
index 661fc608..a7fa773a 100644
--- a/pkg/csi_driver/multishare_controller_test.go
+++ b/pkg/csi_driver/multishare_controller_test.go
@@ -761,9 +761,11 @@ func TestMultishareCreateVolume(t *testing.T) {
 						util.ParamMultishareInstanceScLabelKey: testInstanceScPrefix,
 					},
 					CapacityBytes: 1 * util.Tb,
-					Tier:          "Enterprise",
+					Tier:          "enterprise",
 					Network: file.Network{
-						Ip: testIP,
+						Ip:          testIP,
+						Name:        defaultNetwork,
+						ConnectMode: directPeering,
 					},
 					State: "READY",
 				},
@@ -775,9 +777,11 @@ func TestMultishareCreateVolume(t *testing.T) {
 						util.ParamMultishareInstanceScLabelKey: testInstanceScPrefix,
 					},
 					CapacityBytes: 1 * util.Tb,
-					Tier:          "Enterprise",
+					Tier:          "enterprise",
 					Network: file.Network{
-						Ip: testIP,
+						Ip:          testIP,
+						Name:        defaultNetwork,
+						ConnectMode: directPeering,
 					},
 					State: "READY",
 				},
@@ -796,6 +800,7 @@ func TestMultishareCreateVolume(t *testing.T) {
 				},
 				Parameters: map[string]string{
 					paramMultishareInstanceScLabel: testInstanceScPrefix,
+					paramTier:                      "enterprise",
 				},
 				VolumeCapabilities: []*csi.VolumeCapability{
 					{
diff --git a/pkg/csi_driver/multishare_ops_manager_test.go b/pkg/csi_driver/multishare_ops_manager_test.go
index 1cb0207f..f90a7075 100644
--- a/pkg/csi_driver/multishare_ops_manager_test.go
+++ b/pkg/csi_driver/multishare_ops_manager_test.go
@@ -449,7 +449,7 @@ func TestInstanceNeedsExpand(t *testing.T) {
 	}
 }
 
-func TestListInstanceForStorageClassPrefix(t *testing.T) {
+func TestListMatchedInstances(t *testing.T) {
 	found := func(inputList []*file.MultishareInstance, i *file.MultishareInstance) bool {
 		for _, f := range inputList {
 			if f.Project == i.Project && f.Location == i.Location && f.Name == i.Name {
@@ -462,16 +462,42 @@ func TestListInstanceForStorageClassPrefix(t *testing.T) {
 	tests := []struct {
 		name             string
 		initInstanceList []*file.MultishareInstance
-		prefix           string
 		expectedList     []*file.MultishareInstance
+		req              *csi.CreateVolumeRequest
+		target           *file.MultishareInstance
+		expectError      bool
 	}{
 		{
-			name:   "empty init inistance list",
-			prefix: "testprefix",
+			name: "empty init inistance list",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 		},
 		{
-			name:   "non-empty init inistance list",
-			prefix: "testprefix",
+			name: "non-empty init inistance list",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstanceList: []*file.MultishareInstance{
 				{
 					Name:     "test-instance",
@@ -494,8 +520,20 @@ func TestListInstanceForStorageClassPrefix(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-empty init inistance list, 1 instance match",
-			prefix: "testprefix-1",
+			name: "non-empty init inistance list, 1 instance match",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix-1",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix-1",
+				},
+			},
 			initInstanceList: []*file.MultishareInstance{
 				{
 					Name:     "test-instance-1",
@@ -526,8 +564,20 @@ func TestListInstanceForStorageClassPrefix(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-empty init inistance list, 2 instances match",
-			prefix: "testprefix",
+			name: "non-empty init inistance list, 2 instances match",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstanceList: []*file.MultishareInstance{
 				{
 					Name:     "test-instance-1",
@@ -573,6 +623,260 @@ func TestListInstanceForStorageClassPrefix(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "non-specified sc prefix in init instance list",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
+			initInstanceList: []*file.MultishareInstance{
+				{
+					Name:     "test-instance",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels:   map[string]string{},
+				},
+			},
+		},
+		{
+			name: "1 ip address within, 1 out of reserved-ipv4-cidr",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+					paramReservedIPV4CIDR:          "10.0.0.0/24",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+				Network: file.Network{
+					ReservedIpRange: "10.0.0.0/24",
+				},
+			},
+			initInstanceList: []*file.MultishareInstance{
+				{
+					Name:     "test-instance-0",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/21",
+						Ip:              "10.0.0.1",
+					},
+				},
+				{
+					Name:     "test-instance-1",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "11.0.0.0/24",
+						Ip:              "11.0.0.1",
+					},
+				},
+			},
+			expectedList: []*file.MultishareInstance{
+				{
+					Name:     "test-instance-0",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/24",
+						Ip:              "10.0.0.1",
+					},
+				},
+			},
+		},
+		{
+			name: "location, tier, network, connect-mode and cmek alignment test",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+					paramReservedIPV4CIDR:          "10.0.0.0/24",
+					paramTier:                      "enterprise",
+					paramNetwork:                   "shared-network",
+					paramInstanceEncryptionKmsKey:  "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+				Network: file.Network{
+					ReservedIpRange: "10.0.0.0/24",
+					ConnectMode:     directPeering,
+					Name:            "shared-network",
+				},
+				Tier:       "enterprise",
+				KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+			},
+			initInstanceList: []*file.MultishareInstance{
+				{
+					Name:     "test-instance-0",
+					Project:  "test-project",
+					Location: "us-west1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/24",
+						ConnectMode:     directPeering,
+						Name:            "shared-network",
+						Ip:              "10.0.0.2",
+					},
+					Tier:       "enterprise",
+					KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+				},
+				{
+					Name:     "test-instance-1",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/24",
+						ConnectMode:     directPeering,
+						Name:            "shared-network",
+						Ip:              "10.0.0.2",
+					},
+					Tier:       "standard",
+					KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+				},
+				{
+					Name:     "test-instance-2",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/24",
+						ConnectMode:     directPeering,
+						Name:            "default",
+						Ip:              "10.0.0.2",
+					},
+					Tier:       "enterprise",
+					KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+				},
+				{
+					Name:     "test-instance-3",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/24",
+						ConnectMode:     directPeering,
+						Name:            "shared-network",
+						Ip:              "10.0.0.2",
+					},
+					Tier:       "enterprise",
+					KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key-1",
+				},
+				{
+					Name:     "test-instance-4",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/24",
+						ConnectMode:     directPeering,
+						Name:            "shared-network",
+						Ip:              "10.0.0.2",
+					},
+					Tier: "enterprise",
+				},
+				{
+					Name:     "test-instance-5",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/21",
+						ConnectMode:     directPeering,
+						Name:            "shared-network",
+						Ip:              "10.0.0.2",
+					},
+					Tier:       "enterprise",
+					KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+				},
+			},
+			expectedList: []*file.MultishareInstance{
+				{
+					Name:     "test-instance-5",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+					Network: file.Network{
+						ReservedIpRange: "10.0.0.0/21",
+						ConnectMode:     directPeering,
+						Name:            "shared-network",
+						Ip:              "10.0.0.2",
+					},
+					Tier:       "enterprise",
+					KmsKeyName: "projects/test-project/locations/us-central1/keyRings/test-cmek-key-ring/cryptoKeys/test-cmek-key",
+				},
+			},
+		},
+		{
+			name: "invalid reserved-ipv4-cidr",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+					paramReservedIPV4CIDR:          "test-ip-range",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
+			initInstanceList: []*file.MultishareInstance{
+				{
+					Name:     "test-instance",
+					Project:  "test-project",
+					Location: "us-central1",
+					Labels: map[string]string{
+						util.ParamMultishareInstanceScLabelKey: "testprefix",
+					},
+				},
+			},
+			expectError: true,
+		},
 	}
 	for _, tc := range tests {
 		t.Run(tc.name, func(t *testing.T) {
@@ -586,7 +890,13 @@ func TestListInstanceForStorageClassPrefix(t *testing.T) {
 			}
 
 			manager := NewMultishareOpsManager(cloudProvider)
-			filteredList, err := manager.listInstanceForStorageClassPrefix(context.Background(), tc.prefix)
+			filteredList, err := manager.listMatchedInstances(context.Background(), tc.req, tc.target)
+			if tc.expectError && err == nil {
+				t.Errorf("expected error: %v", err)
+			}
+			if !tc.expectError && err != nil {
+				t.Errorf("unexpectded error: %v", err)
+			}
 			for _, fi := range filteredList {
 				if !found(tc.expectedList, fi) {
 					t.Errorf("Failed to find instance")
@@ -1198,20 +1508,32 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 	}
 	tests := []struct {
 		name                  string
-		prefix                string
 		ops                   []*OpInfo
 		initInstances         []*file.MultishareInstance
 		initShares            []*file.Share
 		expectedNonReadyCount int
 		expectedReadyInstance []*file.MultishareInstance
 		req                   *csi.CreateVolumeRequest
+		target                *file.MultishareInstance
 	}{
 		{
 			name: "no instances",
 		},
 		{
-			name:   "all ready instances",
-			prefix: "testprefix",
+			name: "all ready instances",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "test-instance-1",
@@ -1254,8 +1576,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-ready instances (instance update)",
-			prefix: "testprefix",
+			name: "non-ready instances (instance update)",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1277,8 +1611,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-ready instances (share create)",
-			prefix: "testprefix",
+			name: "non-ready instances (share create)",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1300,8 +1646,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-ready instances (share update)",
-			prefix: "testprefix",
+			name: "non-ready instances (share update)",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1323,8 +1681,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-ready instances (share delete)",
-			prefix: "testprefix",
+			name: "non-ready instances (share delete)",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1346,8 +1716,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-ready instances 0, instance delete not counted as ready",
-			prefix: "testprefix",
+			name: "non-ready instances 0, instance delete not counted as ready",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1368,8 +1750,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "non-ready instances (share delete), ready instance",
-			prefix: "testprefix",
+			name: "non-ready instances (share delete), ready instance",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1411,8 +1805,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "no ready instance, no non-ready instance, instance with 10 shares not eligible",
-			prefix: "testprefix",
+			name: "no ready instance, no non-ready instance, instance with 10 shares not eligible",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1538,8 +1944,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "ready instance, non-ready instances, other instance state not count",
-			prefix: "testprefix",
+			name: "ready instance, non-ready instances, other instance state not count",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1617,8 +2035,20 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			},
 		},
 		{
-			name:   "creating instance count as non-ready",
-			prefix: "testprefix",
+			name: "creating instance count as non-ready",
+			req: &csi.CreateVolumeRequest{
+				Parameters: map[string]string{
+					paramMultishareInstanceScLabel: "testprefix",
+				},
+			},
+			target: &file.MultishareInstance{
+				Name:     "test-target-instance",
+				Project:  "test-project",
+				Location: "us-central1",
+				Labels: map[string]string{
+					util.ParamMultishareInstanceScLabelKey: "testprefix",
+				},
+			},
 			initInstances: []*file.MultishareInstance{
 				{
 					Name:     "instance-1",
@@ -1658,7 +2088,7 @@ func TestRunEligibleInstanceCheck(t *testing.T) {
 			cloudProvider, err := cloud.NewFakeCloud()
 			cloudProvider.File = s
 			manager := NewMultishareOpsManager(cloudProvider)
-			ready, nonReady, err := manager.runEligibleInstanceCheck(context.Background(), tc.prefix, tc.ops, tc.req)
+			ready, nonReady, err := manager.runEligibleInstanceCheck(context.Background(), tc.req, tc.ops, tc.target)
 			if err != nil {
 				t.Errorf("unexpected error")
 			}
diff --git a/test/remote/instance.go b/test/remote/instance.go
index 4f973c74..aa040160 100644
--- a/test/remote/instance.go
+++ b/test/remote/instance.go
@@ -84,7 +84,7 @@ func (i *InstanceInfo) CreateOrGetInstance(serviceAccount string) error {
 		return fmt.Errorf("Failed to create firewall rule: %v", err)
 	}
 
-	imageURL := "projects/debian-cloud/global/images/family/debian-9"
+	imageURL := "projects/debian-cloud/global/images/family/debian-11"
 	inst := &compute.Instance{
 		Name:        i.name,
 		MachineType: machineType(i.zone, ""),