Repository: kubernetes-csi/external-provisioner
Pull request #412 (2020-02-24 21:00:27)

Accept provision/delete calls from PVCs that have the 'migrated-to' annotation of the provisioner name
/kind feature

Final piece to fix: https://github.com/kubernetes/kubernetes/issues/79043

```release-note
CSI Migration: Fixes CSI migration issue where PVCs provisioned before migration was turned on could not be deleted
```

davidz627: /assign @msau42 @jsafrane 
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

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

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

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

- **[OWNERS](https://github.com/kubernetes-csi/external-provisioner/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["msau42"]} -->
k8s-ci-robot: @davidz627: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-kubernetes-csi-external-provisioner-unit | aea29353ce1a7c76681a9a4605eac3740c0a52a3 | [link](https://prow.k8s.io/view/gcs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/412/pull-kubernetes-csi-external-provisioner-unit/1232047801223876608) | `/test pull-kubernetes-csi-external-provisioner-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=412). [Your PR dashboard](https://gubernator.k8s.io/pr/davidz627). 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-csi/external-provisioner/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: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

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

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

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

- **[OWNERS](https://github.com/kubernetes-csi/external-provisioner/blob/master/OWNERS)**

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":["jsafrane"]} -->

Patch:
diff --git a/OWNERS b/OWNERS
index 503b31c17..dcb0630ac 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,4 +1,5 @@
 approvers:
+- childsb
 - jsafrane
 - lpabon
 - msau42
diff --git a/README.md b/README.md
index 4e1e3bc7d..16df4fb41 100644
--- a/README.md
+++ b/README.md
@@ -65,8 +65,6 @@ Note that the external-provisioner does not scale with more replicas. Only one e
 
 * `--metrics-path`: The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.
 
-* `--extraCreateMetadata`: Enables the injection of extra PVC and PV metadata as parameters when calling `CreateVolume` on the driver (keys: "csi.storage.k8s.io/pvc/name", "csi.storage.k8s.io/pvc/namespace", "csi.storage.k8s.io/pv/name") 
-
 #### Other recognized arguments
 * `--feature-gates <gates>`: A set of comma separated `<feature-name>=<true|false>` pairs that describe feature gates for alpha/experimental features. See [list of features](#feature-status) or `--help` output for list of recognized features. Example: `--feature-gates Topology=true` to enable Topology feature that's disabled by default.
 
diff --git a/cmd/csi-provisioner/csi-provisioner.go b/cmd/csi-provisioner/csi-provisioner.go
index 669a0a700..d63976117 100644
--- a/cmd/csi-provisioner/csi-provisioner.go
+++ b/cmd/csi-provisioner/csi-provisioner.go
@@ -45,6 +45,7 @@ import (
 	utilfeature "k8s.io/apiserver/pkg/util/feature"
 	"k8s.io/client-go/informers"
 	v1 "k8s.io/client-go/listers/core/v1"
+	storagelistersv1 "k8s.io/client-go/listers/storage/v1"
 	storagelistersv1beta1 "k8s.io/client-go/listers/storage/v1beta1"
 	utilflag "k8s.io/component-base/cli/flag"
 	csitrans "k8s.io/csi-translation-lib"
@@ -68,7 +69,6 @@ var (
 	leaderElectionType      = flag.String("leader-election-type", "endpoints", "the type of leader election, options are 'endpoints' (default) or 'leases' (strongly recommended). The 'endpoints' option is deprecated in favor of 'leases'.")
 	leaderElectionNamespace = flag.String("leader-election-namespace", "", "Namespace where the leader election resource lives. Defaults to the pod namespace if not set.")
 	strictTopology          = flag.Bool("strict-topology", false, "Passes only selected node topology to CreateVolume Request, unlike default behavior of passing aggregated cluster topologies that match with topology keys of the selected node.")
-	extraCreateMetadata     = flag.Bool("extra-create-metadata", false, "If set, add pv/pvc metadata to plugin create requests as parameters.")
 
 	metricsAddress = flag.String("metrics-address", "", "The TCP network address where the prometheus metrics endpoint will listen (example: `:8080`). The default is empty string, which means metrics endpoint is disabled.")
 	metricsPath    = flag.String("metrics-path", "/metrics", "The HTTP path where prometheus metrics will be exposed. Default is `/metrics`.")
@@ -193,13 +193,14 @@ func main() {
 		provisionerOptions = append(provisionerOptions, controller.AdditionalProvisionerNames([]string{supportsMigrationFromInTreePluginName}))
 	}
 
-	// Create informer to prevent hit the API server for all resource request
-	factory := informers.NewSharedInformerFactory(clientset, ctrl.ResyncPeriodOfCsiNodeInformer)
-	scLister := factory.Storage().V1().StorageClasses().Lister()
-
+	var scLister storagelistersv1.StorageClassLister
 	var csiNodeLister storagelistersv1beta1.CSINodeLister
 	var nodeLister v1.NodeLister
+	var factory informers.SharedInformerFactory
 	if ctrl.SupportsTopology(pluginCapabilities) {
+		// Create informer to prevent hit the API server for all resource request
+		factory = informers.NewSharedInformerFactory(clientset, ctrl.ResyncPeriodOfCsiNodeInformer)
+		scLister = factory.Storage().V1().StorageClasses().Lister()
 		csiNodeLister = factory.Storage().V1beta1().CSINodes().Lister()
 		nodeLister = factory.Core().V1().Nodes().Lister()
 	}
@@ -222,9 +223,7 @@ func main() {
 		translator,
 		scLister,
 		csiNodeLister,
-		nodeLister,
-		*extraCreateMetadata,
-	)
+		nodeLister)
 
 	provisionController = controller.NewProvisionController(
 		clientset,
@@ -235,12 +234,14 @@ func main() {
 	)
 
 	run := func(context.Context) {
-		stopCh := context.Background().Done()
-		factory.Start(stopCh)
-		cacheSyncResult := factory.WaitForCacheSync(stopCh)
-		for _, v := range cacheSyncResult {
-			if !v {
-				klog.Fatalf("Failed to sync Informers!")
+		if factory != nil {
+			stopCh := context.Background().Done()
+			factory.Start(stopCh)
+			cacheSyncResult := factory.WaitForCacheSync(stopCh)
+			for _, v := range cacheSyncResult {
+				if !v {
+					klog.Fatalf("Failed to sync Informers!")
+				}
 			}
 		}
 
diff --git a/go.mod b/go.mod
index 9a2a3e901..b60913ce2 100644
--- a/go.mod
+++ b/go.mod
@@ -19,7 +19,7 @@ require (
 	k8s.io/csi-translation-lib v0.17.0
 	k8s.io/klog v1.0.0
 	k8s.io/kubernetes v1.14.0
-	sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.1+incompatible
+	sigs.k8s.io/sig-storage-lib-external-provisioner v4.1.0+incompatible
 )
 
 replace k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.17.0
diff --git a/go.sum b/go.sum
index 17c0e0c66..ab0d38d19 100644
--- a/go.sum
+++ b/go.sum
@@ -403,8 +403,8 @@ modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
 modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
 modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
-sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.1+incompatible h1:Wc09EvmeiMCZf7UlasU0W2Rn4GVX/b+Lus4UwmUBvD4=
-sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.1+incompatible/go.mod h1:qhqLyNwJC49PoUalmtzYb4s9fT8HOMBTLbTY1QoVOqI=
+sigs.k8s.io/sig-storage-lib-external-provisioner v4.1.0+incompatible h1:pp7GUmQZKI57EjGnjkY88V4QbVuMpkw/ijKXqL67EsI=
+sigs.k8s.io/sig-storage-lib-external-provisioner v4.1.0+incompatible/go.mod h1:qhqLyNwJC49PoUalmtzYb4s9fT8HOMBTLbTY1QoVOqI=
 sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
 sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
index 4dda40950..d58c715e8 100644
--- a/pkg/controller/controller.go
+++ b/pkg/controller/controller.go
@@ -72,9 +72,6 @@ const (
 
 	prefixedFsTypeKey = csiParameterPrefix + "fstype"
 
-	prefixedDefaultSecretNameKey      = csiParameterPrefix + "secret-name"
-	prefixedDefaultSecretNamespaceKey = csiParameterPrefix + "secret-namespace"
-
 	prefixedProvisionerSecretNameKey      = csiParameterPrefix + "provisioner-secret-name"
 	prefixedProvisionerSecretNamespaceKey = csiParameterPrefix + "provisioner-secret-namespace"
 
@@ -104,11 +101,6 @@ const (
 	nodePublishSecretNameKey      = "csiNodePublishSecretName"
 	nodePublishSecretNamespaceKey = "csiNodePublishSecretNamespace"
 
-	// PV and PVC metadata, used for sending to drivers in the  create requests, added as parameters, optional.
-	pvcNameKey      = "csi.storage.k8s.io/pvc/name"
-	pvcNamespaceKey = "csi.storage.k8s.io/pvc/namespace"
-	pvNameKey       = "csi.storage.k8s.io/pv/name"
-
 	// Defines parameters for ExponentialBackoff used for executing
 	// CSI CreateVolume API call, it gives approx 4 minutes for the CSI
 	// driver to complete a volume creation.
@@ -130,18 +122,13 @@ const (
 
 	deleteVolumeRetryCount = 5
 
+	annMigratedTo         = "pv.kubernetes.io/migrated-to"
 	annStorageProvisioner = "volume.beta.kubernetes.io/storage-provisioner"
 
 	snapshotNotBound = "snapshot %s not bound"
 )
 
 var (
-	defaultSecretParams = secretParamsMap{
-		name:               "Default",
-		secretNameKey:      prefixedDefaultSecretNameKey,
-		secretNamespaceKey: prefixedDefaultSecretNamespaceKey,
-	}
-
 	provisionerSecretParams = secretParamsMap{
 		name:                         "Provisioner",
 		deprecatedSecretNameKey:      provisionerSecretNameKey,
@@ -219,7 +206,6 @@ type csiProvisioner struct {
 	scLister                              storagelistersv1.StorageClassLister
 	csiNodeLister                         storagelistersv1beta1.CSINodeLister
 	nodeLister                            corelisters.NodeLister
-	extraCreateMetadata                   bool
 }
 
 var _ controller.Provisioner = &csiProvisioner{}
@@ -282,9 +268,7 @@ func NewCSIProvisioner(client kubernetes.Interface,
 	translator ProvisionerCSITranslator,
 	scLister storagelistersv1.StorageClassLister,
 	csiNodeLister storagelistersv1beta1.CSINodeLister,
-	nodeLister corelisters.NodeLister,
-	extraCreateMetadata bool,
-) controller.Provisioner {
+	nodeLister corelisters.NodeLister) controller.Provisioner {
 
 	csiClient := csi.NewControllerClient(grpcClient)
 	provisioner := &csiProvisioner{
@@ -305,7 +289,6 @@ func NewCSIProvisioner(client kubernetes.Interface,
 		scLister:                              scLister,
 		csiNodeLister:                         csiNodeLister,
 		nodeLister:                            nodeLister,
-		extraCreateMetadata:                   extraCreateMetadata,
 	}
 	return provisioner
 }
@@ -421,12 +404,16 @@ func (p *csiProvisioner) ProvisionExt(options controller.ProvisionOptions) (*v1.
 		return nil, controller.ProvisioningFinished, errors.New("storage class was nil")
 	}
 
-	if options.PVC.Annotations[annStorageProvisioner] != p.driverName {
+	if options.PVC.Annotations[annStorageProvisioner] != p.driverName && options.PVC.Annotations[annMigratedTo] != p.driverName {
+		// The storage provisioner annotation may not equal driver name but the
+		// PVC could have annotation "migrated-to" which is the new way to
+		// signal a PVC is migrated (k8s v1.17+)
 		return nil, controller.ProvisioningFinished, &controller.IgnoredError{
 			Reason: fmt.Sprintf("PVC annotated with external-provisioner name %s does not match provisioner driver name %s. This could mean the PVC is not migrated",
 				options.PVC.Annotations[annStorageProvisioner],
 				p.driverName),
 		}
+
 	}
 
 	migratedVolume := false
@@ -585,13 +572,6 @@ func (p *csiProvisioner) ProvisionExt(options controller.ProvisionOptions) (*v1.
 		return nil, controller.ProvisioningFinished, fmt.Errorf("failed to strip CSI Parameters of prefixed keys: %v", err)
 	}
 
-	if p.extraCreateMetadata {
-		// add pvc and pv metadata to request for use by the plugin
-		req.Parameters[pvcNameKey] = options.PVC.GetName()
-		req.Parameters[pvcNamespaceKey] = options.PVC.GetNamespace()
-		req.Parameters[pvNameKey] = pvName
-	}
-
 	ctx, cancel := context.WithTimeout(context.Background(), p.timeout)
 	defer cancel()
 	rep, err = p.csiClient.CreateVolume(ctx, &req)
@@ -727,8 +707,6 @@ func removePrefixedParameters(param map[string]string) (map[string]string, error
 			case prefixedNodePublishSecretNamespaceKey:
 			case prefixedControllerExpandSecretNameKey:
 			case prefixedControllerExpandSecretNamespaceKey:
-			case prefixedDefaultSecretNameKey:
-			case prefixedDefaultSecretNamespaceKey:
 			default:
 				return map[string]string{}, fmt.Errorf("found unknown parameter key \"%s\" with reserved namespace %s", k, csiParameterPrefix)
 			}
@@ -982,7 +960,8 @@ func (p *csiProvisioner) SupportsBlock() bool {
 
 func (p *csiProvisioner) ShouldProvision(claim *v1.PersistentVolumeClaim) bool {
 	provisioner := claim.Annotations[annStorageProvisioner]
-	if provisioner == p.driverName {
+	migratedTo := claim.Annotations[annMigratedTo]
+	if provisioner == p.driverName || migratedTo == p.driverName {
 		// Either CSI volume is requested or in-tree volume is migrated to CSI in PV controller
 		// and therefore PVC has CSI annotation.
 		return true
@@ -1069,15 +1048,6 @@ func getSecretReference(secretParams secretParamsMap, storageClassParams map[str
 	if err != nil {
 		return nil, fmt.Errorf("failed to get name and namespace template from params: %v", err)
 	}
-
-	// if didn't find secrets for specific call, try to check default values
-	if nameTemplate == "" && namespaceTemplate == "" {
-		nameTemplate, namespaceTemplate, err = verifyAndGetSecretNameAndNamespaceTemplate(defaultSecretParams, storageClassParams)
-		if err != nil {
-			return nil, fmt.Errorf("failed to get default name and namespace template from params: %v", err)
-		}
-	}
-
 	if nameTemplate == "" && namespaceTemplate == "" {
 		return nil, nil
 	}
diff --git a/release-tools/README.md b/release-tools/README.md
index 60eab2a98..cc40f161c 100644
--- a/release-tools/README.md
+++ b/release-tools/README.md
@@ -81,7 +81,7 @@ on what is enabled in Prow, see
 https://github.com/kubernetes/test-infra/tree/master/config/jobs/kubernetes-csi
 
 Test results for periodic jobs are visible in
-https://testgrid.k8s.io/sig-storage-csi-ci
+https://testgrid.k8s.io/sig-storage-csi
 
 It is possible to reproduce the Prow testing locally on a suitable machine:
 - Linux host
@@ -141,17 +141,6 @@ The `vendor` directory is optional. It is still present in projects
 because it avoids downloading sources during CI builds. If this is no
 longer deemed necessary, then a project can also remove the directory.
 
-Conversion of a repository that uses `dep` to `go mod` can be done with:
-
-    GO111MODULE=on go mod init
-    release-tools/go-get-kubernetes.sh <current Kubernetes version from Gopkg.toml>
-    GO111MODULE=on go mod tidy
-    GO111MODULE=on go mod vendor
-    git rm -f Gopkg.toml Gopkg.lock
-    git add go.mod go.sum vendor
-
-### Updating Kubernetes dependencies
-
 When using packages that are part of the Kubernetes source code, the
 commands above are not enough because the [lack of semantic
 versioning](https://github.com/kubernetes/kubernetes/issues/72638)
@@ -160,7 +149,14 @@ prevents `go mod` from finding newer releases. Importing directly from
 the fake `v0.0.0` versions
 (https://github.com/kubernetes/kubernetes/issues/79384). The
 `go-get-kubernetes.sh` script can be used to update all packages in
-lockstep to a different Kubernetes version. Example usage:
-```
-$ ./release-tools/go-get-kubernetes.sh 1.16.4
-```
+lockstep to a different Kubernetes version. It takes a single version
+number like "1.16.0".
+
+Conversion of a repository that uses `dep` to `go mod` can be done with:
+
+    GO111MODULE=on go mod init
+    release-tools/go-get-kubernetes.sh <current Kubernetes version from Gopkg.toml>
+    GO111MODULE=on go mod tidy
+    GO111MODULE=on go mod vendor
+    git rm -f Gopkg.toml Gopkg.lock
+    git add go.mod go.sum vendor
diff --git a/release-tools/SIDECAR_RELEASE_PROCESS.md b/release-tools/SIDECAR_RELEASE_PROCESS.md
deleted file mode 100644
index 91a1e1b82..000000000
--- a/release-tools/SIDECAR_RELEASE_PROCESS.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Sidecar Release Process
-
-This page describes the process for releasing a kubernetes-csi sidecar.
-
-## Prerequisites
-
-The release manager must:
-
-* Be a member of the kubernetes-csi organization. Open an
-  [issue](https://github.com/kubernetes/org/issues/new?assignees=&labels=area%2Fgithub-membership&template=membership.md&title=REQUEST%3A+New+membership+for+%3Cyour-GH-handle%3E) in
-  kubernetes/org to request membership
-* Be a top level approver for the repository. To become a top level approver,
-  the candidate must demonstrate ownership and deep knowledge of the repository
-  through active maintainence, responding to and fixing issues, reviewing PRs,
-  test triage.
-* Be part of the maintainers or admin group for the repository. admin is a
-  superset of maintainers, only maintainers level is required for cutting a
-  release.  Membership can be requested by submitting a PR to kubernetes/org.
-  [Example](https://github.com/kubernetes/org/pull/1467)
-
-## Updating CI Jobs
-Whenever a new Kubernetes minor version is released, our kubernetes-csi CI jobs
-must be updated.
-
-[Our CI jobs](https://k8s-testgrid.appspot.com/sig-storage-csi-ci) have the
-naming convention `<hostpath-deployment-version>-on-<kubernetes-version>`.
-
-1. Jobs should be actively monitored to find and fix failures in sidecars and
-   infrastructure changes early in the development cycle. Test failures are sent
-   to kubernetes-sig-storage-test-failures@googlegroups.com.
-1. "-on-master" jobs are the closest reflection to the new Kubernetes version.
-1. Fixes to our prow.sh CI script can be tested in the [CSI hostpath
-   repo](https://github.com/kubernetes-csi/csi-driver-host-path) by modifying
-   [prow.sh](https://github.com/kubernetes-csi/csi-driver-host-path/blob/master/release-tools/prow.sh)
-   along with any overrides in
-   [.prow.sh](https://github.com/kubernetes-csi/csi-driver-host-path/blob/master/.prow.sh)
-   to mirror the failing environment. Once e2e tests are passing (verify-unit tests
-   will fail), then the prow.sh changes can be submitted to [csi-release-tools](https://github.com/kubernetes-csi/csi-release-tools).
-1. Changes can then be updated in all the sidecar repos and hostpath driver repo
-   by following the [update
-   instructions](https://github.com/kubernetes-csi/csi-release-tools/blob/master/README.md#sharing-and-updating).
-1. New pull and CI jobs are configured by
-   [gen-jobs.sh](https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-csi/gen-jobs.sh).
-   New pull jobs that have been unverified should be initially made optional.
-   [Example](https://github.com/kubernetes/test-infra/pull/15055)
-1. Once new pull and CI jobs have been verified, and the new Kubernetes version
-   is released, we can make the optional jobs required, and also remove the
-   Kubernetes versions that are no longer supported.
-
-## Release Process
-1. Identify all issues and ongoing PRs that should go into the release, and
-  drive them to resolution.
-1. Download [K8s release notes
-  generator](https://github.com/kubernetes/release/tree/master/cmd/release-notes)
-1. Generate release notes for the release. Replace arguments with the relevant
-  information.
-    ```
-    GITHUB_TOKEN=<token> ./release-notes --start-sha=0ed6978fd199e3ca10326b82b4b8b8e916211c9b --end-sha=3cb3d2f18ed8cb40371c6d8886edcabd1f27e7b9 \
-    --github-org=kubernetes-csi --github-repo=external-attacher -branch=master -output out.md
-    ```
-    * `--start-sha` should point to the last release from the same branch. For
-    example:
-        * `1.X-1.0` tag when releasing `1.X.0`
-        * `1.X.Y-1` tag when releasing `1.X.Y`
-1. Compare the generated output to the new commits for the release to check if
-   any notable change missed a release note.
-1. Reword release notes as needed. Make sure to check notes for breaking
-   changes and deprecations.
-1. If release is a new major/minor version, create a new `CHANGELOG-<major>.<minor>.md`
-   file. Otherwise, add the release notes to the top of the existing CHANGELOG
-   file for that minor version.
-1. Submit a PR for the CHANGELOG changes.
-1. Submit a PR for README changes, in particular, Compatibility, Feature status,
-   and any other sections that may need updating.
-1. Check that all [canary CI
-  jobs](https://k8s-testgrid.appspot.com/sig-storage-csi-ci) are passing,
-  and that test coverage is adequate for the changes that are going into the release.
-1. Make sure that no new PRs have merged in the meantime, and no PRs are in
-   flight and soon to be merged.
-1. Create a new release following a previous release as a template. Be sure to select the correct
-   branch. This requires Github release permissions as required by the prerequisites.
-   [external-provisioner example](https://github.com/kubernetes-csi/external-provisioner/releases/new)
-1. If release was a new major/minor version, create a new `release-<minor>`
-   branch at that commit.
-1. Update [kubernetes-csi/docs](https://github.com/kubernetes-csi/docs) sidecar
-   and feature pages with the new released version.
-1. After all the sidecars have been released, update
-   CSI hostpath driver with the new sidecars in the [CSI repo](https://github.com/kubernetes-csi/csi-driver-host-path/tree/master/deploy)
-   and [k/k
-   in-tree](https://github.com/kubernetes/kubernetes/tree/master/test/e2e/testing-manifests/storage-csi/hostpath/hostpath)
diff --git a/release-tools/build.make b/release-tools/build.make
index 3bf3391c2..1b6f35fe1 100644
--- a/release-tools/build.make
+++ b/release-tools/build.make
@@ -70,7 +70,6 @@ build-%: check-go-version-go
 	CGO_ENABLED=0 GOOS=linux go build $(GOFLAGS_VENDOR) -a -ldflags '-X main.version=$(REV) -extldflags "-static"' -o ./bin/$* ./cmd/$*
 	if [ "$$ARCH" = "amd64" ]; then \
 		CGO_ENABLED=0 GOOS=windows go build $(GOFLAGS_VENDOR) -a -ldflags '-X main.version=$(REV) -extldflags "-static"' -o ./bin/$*.exe ./cmd/$* ; \
-		CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le go build $(GOFLAGS_VENDOR) -a -ldflags '-X main.version=$(REV) -extldflags "-static"' -o ./bin/$*-ppc64le ./cmd/$* ; \
 	fi
 
 container-%: build-%
@@ -113,7 +112,7 @@ test-go:
 test: test-vet
 test-vet:
 	@ echo; echo "### $@:"
-	go vet $(GOFLAGS_VENDOR) `go list $(GOFLAGS_VENDOR) ./... | grep -v vendor $(TEST_VET_FILTER_CMD)`
+	go test $(GOFLAGS_VENDOR) `go list $(GOFLAGS_VENDOR) ./... | grep -v vendor $(TEST_VET_FILTER_CMD)`
 
 .PHONY: test-fmt
 test: test-fmt
diff --git a/release-tools/prow.sh b/release-tools/prow.sh
index d54dea518..8b431c564 100755
--- a/release-tools/prow.sh
+++ b/release-tools/prow.sh
@@ -107,7 +107,8 @@ configvar CSI_PROW_GO_VERSION_GINKGO "${CSI_PROW_GO_VERSION_BUILD}" "Go version
 # kind version to use. If the pre-installed version is different,
 # the desired version is downloaded from https://github.com/kubernetes-sigs/kind/releases/download/
 # (if available), otherwise it is built from source.
-configvar CSI_PROW_KIND_VERSION "v0.6.0" "kind"
+# TODO: https://github.com/kubernetes-csi/csi-release-tools/issues/39
+configvar CSI_PROW_KIND_VERSION "86bc23d84ac12dcb56a0528890736e2c347c2dc3" "kind"
 
 # ginkgo test runner version to use. If the pre-installed version is
 # different, the desired version is built from source.
@@ -132,7 +133,7 @@ configvar CSI_PROW_BUILD_JOB true "building code in repo enabled"
 # use the same settings as for "latest" Kubernetes. This works
 # as long as there are no breaking changes in Kubernetes, like
 # deprecating or changing the implementation of an alpha feature.
-configvar CSI_PROW_KUBERNETES_VERSION 1.17.0 "Kubernetes"
+configvar CSI_PROW_KUBERNETES_VERSION 1.15.3 "Kubernetes"
 
 # This is a hack to workaround the issue that each version
 # of kind currently only supports specific patch versions of
@@ -142,6 +143,7 @@ configvar CSI_PROW_KUBERNETES_VERSION 1.17.0 "Kubernetes"
 #
 # If the version is prefixed with "release-", then nothing
 # is overridden.
+override_k8s_version "1.14.6"
 override_k8s_version "1.15.3"
 
 # CSI_PROW_KUBERNETES_VERSION reduced to first two version numbers and
@@ -157,9 +159,7 @@ csi_prow_kubernetes_version_suffix="$(echo "${CSI_PROW_KUBERNETES_VERSION}" | tr
 # the caller.
 configvar CSI_PROW_WORK "$(mkdir -p "$GOPATH/pkg" && mktemp -d "$GOPATH/pkg/csiprow.XXXXXXXXXX")" "work directory"
 
-# By default, this script tests sidecars with the CSI hostpath driver,
-# using the install_csi_driver function. That function depends on
-# a deployment script that it searches for in several places:
+# The hostpath deployment script is searched for in several places.
 #
 # - The "deploy" directory in the current repository: this is useful
 #   for the situation that a component becomes incompatible with the
@@ -167,11 +167,11 @@ configvar CSI_PROW_WORK "$(mkdir -p "$GOPATH/pkg" && mktemp -d "$GOPATH/pkg/csip
 #   own example until the shared one can be updated; it's also how
 #   csi-driver-host-path itself provides the example.
 #
-# - CSI_PROW_DRIVER_VERSION of the CSI_PROW_DRIVER_REPO is checked
+# - CSI_PROW_HOSTPATH_VERSION of the CSI_PROW_HOSTPATH_REPO is checked
 #   out: this allows other repos to reference a version of the example
 #   that is known to be compatible.
 #
-# - The <driver repo>/deploy directory can have multiple sub-directories,
+# - The csi-driver-host-path/deploy directory has multiple sub-directories,
 #   each with different deployments (stable set of images for Kubernetes 1.13,
 #   stable set of images for Kubernetes 1.14, canary for latest Kubernetes, etc.).
 #   This is necessary because there may be incompatible changes in the
@@ -188,26 +188,16 @@ configvar CSI_PROW_WORK "$(mkdir -p "$GOPATH/pkg" && mktemp -d "$GOPATH/pkg/csip
 #   "none" disables the deployment of the hostpath driver.
 #
 # When no deploy script is found (nothing in `deploy` directory,
-# CSI_PROW_DRIVER_REPO=none), nothing gets deployed.
-#
-# If the deployment script is called with CSI_PROW_TEST_DRIVER=<file name> as
-# environment variable, then it must write a suitable test driver configuration
-# into that file in addition to installing the driver.
-configvar CSI_PROW_DRIVER_VERSION "v1.3.0" "CSI driver version"
-configvar CSI_PROW_DRIVER_REPO https://github.com/kubernetes-csi/csi-driver-host-path "CSI driver repo"
+# CSI_PROW_HOSTPATH_REPO=none), nothing gets deployed.
+configvar CSI_PROW_HOSTPATH_VERSION "v1.2.0" "hostpath driver"
+configvar CSI_PROW_HOSTPATH_REPO https://github.com/kubernetes-csi/csi-driver-host-path "hostpath repo"
 configvar CSI_PROW_DEPLOYMENT "" "deployment"
+configvar CSI_PROW_HOSTPATH_DRIVER_NAME "hostpath.csi.k8s.io" "the hostpath driver name"
 
-# The install_csi_driver function may work also for other CSI drivers,
-# as long as they follow the conventions of the CSI hostpath driver.
-# If they don't, then a different install function can be provided in
-# a .prow.sh file and this config variable can be overridden.
-configvar CSI_PROW_DRIVER_INSTALL "install_csi_driver" "name of the shell function which installs the CSI driver"
-
-# If CSI_PROW_DRIVER_CANARY is set (typically to "canary", but also
-# version tag. Usually empty. CSI_PROW_HOSTPATH_CANARY is
-# accepted as alternative name because some test-infra jobs
-# still use that name.
-configvar CSI_PROW_DRIVER_CANARY "${CSI_PROW_HOSTPATH_CANARY}" "driver image override for canary images"
+# If CSI_PROW_HOSTPATH_CANARY is set (typically to "canary", but also
+# "1.0-canary"), then all image versions are replaced with that
+# version tag.
+configvar CSI_PROW_HOSTPATH_CANARY "" "hostpath image"
 
 # The E2E testing can come from an arbitrary repo. The expectation is that
 # the repo supports "go test ./test/e2e -args --storage.testdriver" (https://github.com/kubernetes/kubernetes/pull/72836)
@@ -217,9 +207,9 @@ configvar CSI_PROW_DRIVER_CANARY "${CSI_PROW_HOSTPATH_CANARY}" "driver image ove
 #
 # CSI_PROW_E2E_REPO=none disables E2E testing.
 # TOOO: remove versioned variables and make e2e version match k8s version
+configvar CSI_PROW_E2E_VERSION_1_14 v1.14.0 "E2E version for Kubernetes 1.14.x"
 configvar CSI_PROW_E2E_VERSION_1_15 v1.15.0 "E2E version for Kubernetes 1.15.x"
 configvar CSI_PROW_E2E_VERSION_1_16 v1.16.0 "E2E version for Kubernetes 1.16.x"
-configvar CSI_PROW_E2E_VERSION_1_17 v1.17.0 "E2E version for Kubernetes 1.17.x"
 # TODO: add new CSI_PROW_E2E_VERSION entry for future Kubernetes releases
 configvar CSI_PROW_E2E_VERSION_LATEST master "E2E version for Kubernetes master" # testing against Kubernetes master is already tracking a moving target, so we might as well use a moving E2E version
 configvar CSI_PROW_E2E_REPO_LATEST https://github.com/kubernetes/kubernetes "E2E repo for Kubernetes >= 1.13.x" # currently the same for all versions
@@ -289,14 +279,6 @@ tests_need_alpha_cluster () {
     tests_enabled "parallel-alpha" "serial-alpha"
 }
 
-# Regex for non-alpha, feature-tagged tests that should be run.
-#
-# Starting with 1.17, snapshots is beta, but the E2E tests still have the
-# [Feature:] tag. They need to be explicitly enabled.
-configvar CSI_PROW_E2E_FOCUS_1_15 '^' "non-alpha, feature-tagged tests for Kubernetes = 1.15" # no tests to run, match nothing
-configvar CSI_PROW_E2E_FOCUS_1_16 '^' "non-alpha, feature-tagged tests for Kubernetes = 1.16" # no tests to run, match nothing
-configvar CSI_PROW_E2E_FOCUS_LATEST '\[Feature:VolumeSnapshotDataSource\]' "non-alpha, feature-tagged tests for Kubernetes >= 1.17"
-configvar CSI_PROW_E2E_FOCUS "$(get_versioned_variable CSI_PROW_E2E_FOCUS "${csi_prow_kubernetes_version_suffix}")" "non-alpha, feature-tagged tests"
 
 # Serial vs. parallel is always determined by these regular expressions.
 # Individual regular expressions are seperated by spaces for readability
@@ -332,27 +314,21 @@ configvar CSI_PROW_E2E_ALPHA "$(get_versioned_variable CSI_PROW_E2E_ALPHA "${csi
 # kubernetes-csi components must be updated, either by disabling
 # the failing test for "latest" or by updating the test and not running
 # it anymore for older releases.
+configvar CSI_PROW_E2E_ALPHA_GATES_1_14 'VolumeSnapshotDataSource=true,ExpandCSIVolumes=true' "alpha feature gates for Kubernetes 1.14"
 configvar CSI_PROW_E2E_ALPHA_GATES_1_15 'VolumeSnapshotDataSource=true,ExpandCSIVolumes=true' "alpha feature gates for Kubernetes 1.15"
 configvar CSI_PROW_E2E_ALPHA_GATES_1_16 'VolumeSnapshotDataSource=true' "alpha feature gates for Kubernetes 1.16"
 # TODO: add new CSI_PROW_ALPHA_GATES_xxx entry for future Kubernetes releases and
 # add new gates to CSI_PROW_E2E_ALPHA_GATES_LATEST.
-configvar CSI_PROW_E2E_ALPHA_GATES_LATEST '' "alpha feature gates for latest Kubernetes"
+configvar CSI_PROW_E2E_ALPHA_GATES_LATEST 'VolumeSnapshotDataSource=true' "alpha feature gates for latest Kubernetes"
 configvar CSI_PROW_E2E_ALPHA_GATES "$(get_versioned_variable CSI_PROW_E2E_ALPHA_GATES "${csi_prow_kubernetes_version_suffix}")" "alpha E2E feature gates"
 
-# Which external-snapshotter tag to use for the snapshotter CRD and snapshot-controller deployment
-configvar CSI_SNAPSHOTTER_VERSION 'v2.0.0' "external-snapshotter version tag"
-
 # Some tests are known to be unusable in a KinD cluster. For example,
 # stopping kubelet with "ssh <node IP> systemctl stop kubelet" simply
 # doesn't work. Such tests should be written in a way that they verify
 # whether they can run with the current cluster provider, but until
 # they are, we filter them out by name. Like the other test selection
 # variables, this is again a space separated list of regular expressions.
-#
-# "different node" test skips can be removed once
-# https://github.com/kubernetes/kubernetes/pull/82678 has been backported
-# to all the K8s versions we test against
-configvar CSI_PROW_E2E_SKIP 'Disruptive|different\s+node' "tests that need to be skipped"
+configvar CSI_PROW_E2E_SKIP 'Disruptive' "tests that need to be skipped"
 
 # This is the directory for additional result files. Usually set by Prow, but
 # if not (for example, when invoking manually) it defaults to the work directory.
@@ -548,7 +524,6 @@ apiVersion: kind.sigs.k8s.io/v1alpha3
 nodes:
 - role: control-plane
 - role: worker
-- role: worker
 EOF
 
     # kubeadm has API dependencies between apiVersion and Kubernetes version
@@ -580,13 +555,6 @@ kubeadmConfigPatches:
   nodeRegistration:
     kubeletExtraArgs:
       "feature-gates": "$gates"
-- |
-  apiVersion: kubelet.config.k8s.io/v1beta1
-  kind: KubeletConfiguration
-  metadata:
-    name: config
-  featureGates:
-$(list_gates "$gates")
 - |
   apiVersion: kubeproxy.config.k8s.io/v1alpha1
   kind: KubeProxyConfiguration
@@ -608,7 +576,8 @@ EOF
             die "Cluster creation failed again, giving up. See the 'kind-cluster' artifact directory for additional logs."
         fi
     fi
-    export KUBECONFIG="${HOME}/.kube/config"
+    KUBECONFIG="$(kind get kubeconfig-path --name=csi-prow)"
+    export KUBECONFIG
 }
 
 # Deletes kind cluster inside a prow job
@@ -632,7 +601,7 @@ find_deployment () {
 
     # Fixed deployment name? Use it if it exists, otherwise fail.
     if [ "${CSI_PROW_DEPLOYMENT}" ]; then
-        file="$dir/${CSI_PROW_DEPLOYMENT}/deploy.sh"
+        file="$dir/${CSI_PROW_DEPLOYMENT}/deploy-hostpath.sh"
         if ! [ -e "$file" ]; then
             return 1
         fi
@@ -642,9 +611,9 @@ find_deployment () {
 
     # Ignore: See if you can use ${variable//search/replace} instead.
     # shellcheck disable=SC2001
-    file="$dir/kubernetes-$(echo "${CSI_PROW_KUBERNETES_VERSION}" | sed -e 's/\([0-9]*\)\.\([0-9]*\).*/\1.\2/')/deploy.sh"
+    file="$dir/kubernetes-$(echo "${CSI_PROW_KUBERNETES_VERSION}" | sed -e 's/\([0-9]*\)\.\([0-9]*\).*/\1.\2/')/deploy-hostpath.sh"
     if ! [ -e "$file" ]; then
-        file="$dir/kubernetes-latest/deploy.sh"
+        file="$dir/kubernetes-latest/deploy-hostpath.sh"
         if ! [ -e "$file" ]; then
             return 1
         fi
@@ -652,11 +621,12 @@ find_deployment () {
     echo "$file"
 }
 
-# This installs the CSI driver. It's called with a list of env variables
-# that override the default images. CSI_PROW_DRIVER_CANARY overrides all
-# image versions with that canary version.
-install_csi_driver () {
-    local images deploy_driver
+# This installs the hostpath driver example. CSI_PROW_HOSTPATH_CANARY overrides all
+# image versions with that canary version. The parameters of install_hostpath can be
+# used to override registry and/or tag of individual images (CSI_PROVISIONER_REGISTRY=localhost:9000
+# CSI_PROVISIONER_TAG=latest).
+install_hostpath () {
+    local images deploy_hostpath
     images="$*"
 
     if [ "${CSI_PROW_DEPLOYMENT}" = "none" ]; then
@@ -672,86 +642,32 @@ install_csi_driver () {
         done
     fi
 
-    if deploy_driver="$(find_deployment "$(pwd)/deploy")"; then
+    if deploy_hostpath="$(find_deployment "$(pwd)/deploy")"; then
         :
-    elif [ "${CSI_PROW_DRIVER_REPO}" = "none" ]; then
+    elif [ "${CSI_PROW_HOSTPATH_REPO}" = "none" ]; then
         return 1
     else
-        git_checkout "${CSI_PROW_DRIVER_REPO}" "${CSI_PROW_WORK}/csi-driver" "${CSI_PROW_DRIVER_VERSION}" --depth=1 || die "checking out CSI driver repo failed"
-        if deploy_driver="$(find_deployment "${CSI_PROW_WORK}/csi-driver/deploy")"; then
+        git_checkout "${CSI_PROW_HOSTPATH_REPO}" "${CSI_PROW_WORK}/hostpath" "${CSI_PROW_HOSTPATH_VERSION}" --depth=1 || die "checking out hostpath repo failed"
+        if deploy_hostpath="$(find_deployment "${CSI_PROW_WORK}/hostpath/deploy")"; then
             :
         else
-            die "deploy.sh not found in ${CSI_PROW_DRIVER_REPO} ${CSI_PROW_DRIVER_VERSION}. To disable E2E testing, set CSI_PROW_DRIVER_REPO=none"
+            die "deploy-hostpath.sh not found in ${CSI_PROW_HOSTPATH_REPO} ${CSI_PROW_HOSTPATH_VERSION}. To disable E2E testing, set CSI_PROW_HOSTPATH_REPO=none"
         fi
     fi
 
-    if [ "${CSI_PROW_DRIVER_CANARY}" != "stable" ]; then
-        images="$images IMAGE_TAG=${CSI_PROW_DRIVER_CANARY}"
+    if [ "${CSI_PROW_HOSTPATH_CANARY}" != "stable" ]; then
+        images="$images IMAGE_TAG=${CSI_PROW_HOSTPATH_CANARY}"
     fi
     # Ignore: Double quote to prevent globbing and word splitting.
     # It's intentional here for $images.
     # shellcheck disable=SC2086
-    if ! run env "CSI_PROW_TEST_DRIVER=${CSI_PROW_WORK}/test-driver.yaml" $images "${deploy_driver}"; then
+    if ! run env $images "${deploy_hostpath}"; then
         # Collect information about failed deployment before failing.
         collect_cluster_info
         (start_loggers >/dev/null; wait)
         info "For container output see job artifacts."
-        die "deploying the CSI driver with ${deploy_driver} failed"
-    fi
-}
-
-# Installs all nessesary snapshotter CRDs  
-install_snapshot_crds() {
-  # Wait until volumesnapshot CRDs are in place.
-  CRD_BASE_DIR="https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/${CSI_SNAPSHOTTER_VERSION}/config/crd"
-  kubectl apply -f "${CRD_BASE_DIR}/snapshot.storage.k8s.io_volumesnapshotclasses.yaml" --validate=false
-  kubectl apply -f "${CRD_BASE_DIR}/snapshot.storage.k8s.io_volumesnapshots.yaml" --validate=false
-  kubectl apply -f "${CRD_BASE_DIR}/snapshot.storage.k8s.io_volumesnapshotcontents.yaml" --validate=false
-  cnt=0
-  until kubectl get volumesnapshotclasses.snapshot.storage.k8s.io \
-    && kubectl get volumesnapshots.snapshot.storage.k8s.io \
-    && kubectl get volumesnapshotcontents.snapshot.storage.k8s.io; do
-    if [ $cnt -gt 30 ]; then
-        echo >&2 "ERROR: snapshot CRDs not ready after over 1 min"
-        exit 1
-    fi
-    echo "$(date +%H:%M:%S)" "waiting for snapshot CRDs, attempt #$cnt"
-	cnt=$((cnt + 1))
-    sleep 2
-  done
-}
-
-# Install snapshot controller and associated RBAC, retrying until the pod is running.
-install_snapshot_controller() {
-  kubectl apply -f "https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/${CSI_SNAPSHOTTER_VERSION}/deploy/kubernetes/snapshot-controller/rbac-snapshot-controller.yaml"
-  cnt=0
-  until kubectl get clusterrolebinding snapshot-controller-role; do
-     if [ $cnt -gt 30 ]; then
-        echo "Cluster role bindings:"
-        kubectl describe clusterrolebinding
-        echo >&2 "ERROR: snapshot controller RBAC not ready after over 5 min"
-        exit 1
-    fi
-    echo "$(date +%H:%M:%S)" "waiting for snapshot RBAC setup complete, attempt #$cnt"
-	cnt=$((cnt + 1))
-    sleep 10   
-  done
-
-
-  kubectl apply -f "https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/${CSI_SNAPSHOTTER_VERSION}/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml"
-  cnt=0
-  expected_running_pods=$(curl https://raw.githubusercontent.com/kubernetes-csi/external-snapshotter/"${CSI_SNAPSHOTTER_VERSION}"/deploy/kubernetes/snapshot-controller/setup-snapshot-controller.yaml | grep replicas | cut -d ':' -f 2-)
-  while [ "$(kubectl get pods -l app=snapshot-controller | grep 'Running' -c)" -lt "$expected_running_pods" ]; do
-    if [ $cnt -gt 30 ]; then
-        echo "snapshot-controller pod status:"
-        kubectl describe pods -l app=snapshot-controller
-        echo >&2 "ERROR: snapshot controller not ready after over 5 min"
-        exit 1
+        die "deploying the hostpath driver with ${deploy_hostpath} failed"
     fi
-    echo "$(date +%H:%M:%S)" "waiting for snapshot controller deployment to complete, attempt #$cnt"
-	cnt=$((cnt + 1))
-    sleep 10   
-  done
 }
 
 # collect logs and cluster status (like the version of all components, Kubernetes version, test version)
@@ -822,6 +738,33 @@ install_sanity () (
     run_with_go "${CSI_PROW_GO_VERSION_SANITY}" go test -c -o "${CSI_PROW_WORK}/csi-sanity" "${CSI_PROW_SANITY_IMPORT_PATH}/cmd/csi-sanity" || die "building csi-sanity failed"
 )
 
+# The default implementation of this function generates a external
+# driver test configuration for the hostpath driver.
+#
+# The content depends on both what the E2E suite expects and what the
+# installed hostpath driver supports. Generating it here seems prone
+# to breakage, but it is uncertain where a better place might be.
+generate_test_driver () {
+    cat <<EOF
+ShortName: csiprow
+StorageClass:
+  FromName: true
+SnapshotClass:
+  FromName: true
+DriverInfo:
+  Name: ${CSI_PROW_HOSTPATH_DRIVER_NAME}
+  Capabilities:
+    block: true
+    persistence: true
+    dataSource: true
+    multipods: true
+    nodeExpansion: true
+    controllerExpansion: true
+    snapshotDataSource: true
+    singleNodeVolume: true
+EOF
+}
+
 # Captures pod output while running some other command.
 run_with_loggers () (
     loggers=$(start_loggers -f)
@@ -843,6 +786,12 @@ run_e2e () (
     install_e2e || die "building e2e.test failed"
     install_ginkgo || die "installing ginkgo failed"
 
+    # TODO (?): multi-node cluster (depends on https://github.com/kubernetes-csi/csi-driver-host-path/pull/14).
+    # When running on a multi-node cluster, we need to figure out where the
+    # hostpath driver was deployed and set ClientNodeName accordingly.
+
+    generate_test_driver >"${CSI_PROW_WORK}/test-driver.yaml" || die "generating test-driver.yaml failed"
+
     # Rename, merge and filter JUnit files. Necessary in case that we run the E2E suite again
     # and to avoid the large number of "skipped" tests that we get from using
     # the full Kubernetes E2E testsuite while only running a few tests.
@@ -991,34 +940,6 @@ make_test_to_junit () {
     fi
 }
 
-# version_gt returns true if arg1 is greater than arg2.
-#
-# This function expects versions to be one of the following formats:
-#   X.Y.Z, release-X.Y.Z, vX.Y.Z
-#
-#   where X,Y, and Z are any number.
-#
-# Partial versions (1.2, release-1.2) work as well.
-# The follow substrings are stripped before version comparison:
-#   - "v"
-#   - "release-"
-#   - "kubernetes-"
-#
-# Usage:
-# version_gt release-1.3 v1.2.0  (returns true)
-# version_gt v1.1.1 v1.2.0  (returns false)
-# version_gt 1.1.1 v1.2.0  (returns false)
-# version_gt 1.3.1 v1.2.0  (returns true)
-# version_gt 1.1.1 release-1.2.0  (returns false)
-# version_gt 1.2.0 1.2.2  (returns false)
-function version_gt() { 
-    versions=$(for ver in "$@"; do ver=${ver#release-}; ver=${ver#kubernetes-}; echo "${ver#v}"; done)
-    greaterVersion=${1#"release-"};
-    greaterVersion=${greaterVersion#"kubernetes-"};
-    greaterVersion=${greaterVersion#"v"};
-    test "$(printf '%s' "$versions" | sort -V | head -n 1)" != "$greaterVersion"
-}
-
 main () {
     local images ret
     ret=0
@@ -1052,7 +973,7 @@ main () {
             cmds="$(grep '^\s*CMDS\s*=' Makefile | sed -e 's/\s*CMDS\s*=//')"
             # Get the image that was just built (if any) from the
             # top-level Makefile CMDS variable and set the
-            # deploy.sh env variables for it. We also need to
+            # deploy-hostpath.sh env variables for it. We also need to
             # side-load those images into the cluster.
             for i in $cmds; do
                 e=$(echo "$i" | tr '[:lower:]' '[:upper:]' | tr - _)
@@ -1079,18 +1000,8 @@ main () {
         if tests_need_non_alpha_cluster; then
             start_cluster || die "starting the non-alpha cluster failed"
 
-            # Install necessary snapshot CRDs and snapshot controller
-            # For Kubernetes 1.17+, we will install the CRDs and snapshot controller.
-            if version_gt "${CSI_PROW_KUBERNETES_VERSION}" "1.16.255" || "${CSI_PROW_KUBERNETES_VERSION}" == "latest"; then
-                info "Version ${CSI_PROW_KUBERNETES_VERSION}, installing CRDs and snapshot controller"
-                install_snapshot_crds
-                install_snapshot_controller
-            else
-                info "Version ${CSI_PROW_KUBERNETES_VERSION}, skipping CRDs and snapshot controller"
-            fi
-
             # Installing the driver might be disabled.
-            if ${CSI_PROW_DRIVER_INSTALL} "$images"; then
+            if install_hostpath "$images"; then
                 collect_cluster_info
 
                 if sanity_enabled; then
@@ -1108,16 +1019,6 @@ main () {
                         warn "E2E parallel failed"
                         ret=1
                     fi
-
-                    # Run tests that are feature tagged, but non-alpha
-                    # Ignore: Double quote to prevent globbing and word splitting.
-                    # shellcheck disable=SC2086
-                    if ! run_e2e parallel-features ${CSI_PROW_GINKO_PARALLEL} \
-                         -focus="External.Storage.*($(regex_join "${CSI_PROW_E2E_FOCUS}"))" \
-                         -skip="$(regex_join "${CSI_PROW_E2E_SERIAL}")"; then
-                        warn "E2E parallel features failed"
-                        ret=1
-                    fi
                 fi
 
                 if tests_enabled "serial"; then
@@ -1136,18 +1037,8 @@ main () {
             # Need to (re)create the cluster.
             start_cluster "${CSI_PROW_E2E_ALPHA_GATES}" || die "starting alpha cluster failed"
 
-            # Install necessary snapshot CRDs and snapshot controller
-            # For Kubernetes 1.17+, we will install the CRDs and snapshot controller.
-            if version_gt "${CSI_PROW_KUBERNETES_VERSION}" "1.16.255" || "${CSI_PROW_KUBERNETES_VERSION}" == "latest"; then
-                info "Version ${CSI_PROW_KUBERNETES_VERSION}, installing CRDs and snapshot controller"
-                install_snapshot_crds
-                install_snapshot_controller
-            else
-                info "Version ${CSI_PROW_KUBERNETES_VERSION}, skipping CRDs and snapshot controller"
-            fi
-
             # Installing the driver might be disabled.
-            if ${CSI_PROW_DRIVER_INSTALL} "$images"; then
+            if install_hostpath "$images"; then
                 collect_cluster_info
 
                 if tests_enabled "parallel-alpha"; then
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 46e382682..c8daec17c 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -489,7 +489,7 @@ k8s.io/kubernetes/pkg/apis/core/helper
 k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/trace
-# sigs.k8s.io/sig-storage-lib-external-provisioner v4.0.1+incompatible
+# sigs.k8s.io/sig-storage-lib-external-provisioner v4.1.0+incompatible
 sigs.k8s.io/sig-storage-lib-external-provisioner/controller
 sigs.k8s.io/sig-storage-lib-external-provisioner/controller/metrics
 sigs.k8s.io/sig-storage-lib-external-provisioner/util
diff --git a/vendor/sigs.k8s.io/sig-storage-lib-external-provisioner/controller/controller.go b/vendor/sigs.k8s.io/sig-storage-lib-external-provisioner/controller/controller.go
index 094d5b213..b58d70ca1 100644
--- a/vendor/sigs.k8s.io/sig-storage-lib-external-provisioner/controller/controller.go
+++ b/vendor/sigs.k8s.io/sig-storage-lib-external-provisioner/controller/controller.go
@@ -71,12 +71,22 @@ const annClass = "volume.beta.kubernetes.io/storage-class"
 // recognize dynamically provisioned PVs in its decisions).
 const annDynamicallyProvisioned = "pv.kubernetes.io/provisioned-by"
 
+// AnnMigratedTo annotation is added to a PVC that is supposed to be
+// dynamically provisioned/deleted by by its corresponding CSI driver
+// through the CSIMigration feature flags. It allows external provisioners
+// to determine which PVs are considered migrated and safe to operate on for
+// Deletion.
+const annMigratedTo = "pv.kubernetes.io/migrated-to"
+
 const annStorageProvisioner = "volume.beta.kubernetes.io/storage-provisioner"
 
 // This annotation is added to a PVC that has been triggered by scheduler to
 // be dynamically provisioned. Its value is the name of the selected node.
 const annSelectedNode = "volume.kubernetes.io/selected-node"
 
+// This annotation is present on K8s 1.11 release.
+const annAlphaSelectedNode = "volume.alpha.kubernetes.io/selected-node"
+
 // Finalizer for PVs so we know to clean them up
 const finalizerPV = "external-provisioner.volume.kubernetes.io/finalizer"
 
@@ -1140,7 +1150,9 @@ func (ctrl *ProvisionController) shouldDelete(volume *v1.PersistentVolume) bool
 		return false
 	}
 
-	if ann := volume.Annotations[annDynamicallyProvisioned]; ann != ctrl.provisionerName {
+	ann := volume.Annotations[annDynamicallyProvisioned]
+	migratedTo := volume.Annotations[annMigratedTo]
+	if ann != ctrl.provisionerName && migratedTo != ctrl.provisionerName {
 		return false
 	}
 
@@ -1242,7 +1254,7 @@ func (ctrl *ProvisionController) provisionClaimOperation(claim *v1.PersistentVol
 	var selectedNode *v1.Node
 	if ctrl.kubeVersion.AtLeast(utilversion.MustParseSemantic("v1.11.0")) {
 		// Get SelectedNode
-		if nodeName, ok := claim.Annotations[annSelectedNode]; ok {
+		if nodeName, ok := getString(claim.Annotations, annSelectedNode, annAlphaSelectedNode); ok {
 			selectedNode, err = ctrl.client.CoreV1().Nodes().Get(nodeName, metav1.GetOptions{}) // TODO (verult) cache Nodes
 			if err != nil {
 				err = fmt.Errorf("failed to get target node: %v", err)
@@ -1455,3 +1467,16 @@ func (ctrl *ProvisionController) supportsBlock() bool {
 	}
 	return false
 }
+
+func getString(m map[string]string, key string, alts ...string) (string, bool) {
+	if m == nil {
+		return "", false
+	}
+	keys := append([]string{key}, alts...)
+	for _, k := range keys {
+		if v, ok := m[k]; ok {
+			return v, true
+		}
+	}
+	return "", false
+}


Test patch:
diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go
index bbe9fd7b8..a7e265830 100644
--- a/pkg/controller/controller_test.go
+++ b/pkg/controller/controller_test.go
@@ -213,8 +213,6 @@ func TestStripPrefixedCSIParams(t *testing.T) {
 				prefixedNodePublishSecretNamespaceKey:       "csiBar",
 				prefixedControllerExpandSecretNameKey:       "csiBar",
 				prefixedControllerExpandSecretNamespaceKey:  "csiBar",
-				prefixedDefaultSecretNameKey:                "csiBar",
-				prefixedDefaultSecretNamespaceKey:           "csiBar",
 			},
 			expectedParams: map[string]string{},
 		},
@@ -405,7 +403,7 @@ func TestCreateDriverReturnsInvalidCapacityDuringProvision(t *testing.T) {
 
 	pluginCaps, controllerCaps := provisionCapabilities()
 	csiProvisioner := NewCSIProvisioner(nil, 5*time.Second, "test-provisioner", "test",
-		5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil, false)
+		5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil)
 
 	// Requested PVC with requestedBytes storage
 	deletePolicy := v1.PersistentVolumeReclaimDelete
@@ -477,18 +475,13 @@ func provisionFromPVCCapabilities() (rpc.PluginCapabilitySet, rpc.ControllerCapa
 		}
 }
 
-func createFakeNamedPVC(requestBytes int64, name string, userAnnotations map[string]string) *v1.PersistentVolumeClaim {
-	annotations := map[string]string{annStorageProvisioner: driverName}
-	for k, v := range userAnnotations {
-		annotations[k] = v
-	}
-
+// Minimal PVC required for tests to function
+func createFakePVC(requestBytes int64) *v1.PersistentVolumeClaim {
 	return &v1.PersistentVolumeClaim{
 		ObjectMeta: metav1.ObjectMeta{
 			UID:         "testid",
-			Name:        name,
-			Namespace:   "fake-ns",
-			Annotations: annotations,
+			Name:        "fake-pvc",
+			Annotations: map[string]string{annStorageProvisioner: driverName},
 		},
 		Spec: v1.PersistentVolumeClaimSpec{
 			Selector: nil, // Provisioner doesn't support selector
@@ -501,11 +494,6 @@ func createFakeNamedPVC(requestBytes int64, name string, userAnnotations map[str
 	}
 }
 
-// Minimal PVC required for tests to function
-func createFakePVC(requestBytes int64) *v1.PersistentVolumeClaim {
-	return createFakeNamedPVC(requestBytes, "fake-pvc", nil)
-}
-
 // createFakePVCWithVolumeMode returns PVC with VolumeMode
 func createFakePVCWithVolumeMode(requestBytes int64, volumeMode v1.PersistentVolumeMode) *v1.PersistentVolumeClaim {
 	claim := createFakePVC(requestBytes)
@@ -790,12 +778,11 @@ type provisioningTestcase struct {
 	volWithLessCap    bool
 	volWithZeroCap    bool
 	expectedPVSpec    *pvSpec
-	clientSetObjects  []runtime.Object
+	withSecretRefs    bool
 	createVolumeError error
 	expectErr         bool
 	expectState       controller.ProvisioningState
 	expectCreateVolDo interface{}
-	withExtraMetadata bool
 }
 
 type pvSpec struct {
@@ -808,47 +795,6 @@ type pvSpec struct {
 	CSIPVS        *v1.CSIPersistentVolumeSource
 }
 
-const defaultSecretNsName = "default"
-
-func getDefaultStorageClassSecretParameters() map[string]string {
-	return map[string]string{
-		controllerPublishSecretNameKey:             "ctrlpublishsecret",
-		controllerPublishSecretNamespaceKey:        defaultSecretNsName,
-		nodeStageSecretNameKey:                     "nodestagesecret",
-		nodeStageSecretNamespaceKey:                defaultSecretNsName,
-		nodePublishSecretNameKey:                   "nodepublishsecret",
-		nodePublishSecretNamespaceKey:              defaultSecretNsName,
-		prefixedControllerExpandSecretNameKey:      "controllerexpandsecret",
-		prefixedControllerExpandSecretNamespaceKey: defaultSecretNsName,
-	}
-}
-
-func getDefaultSecretObjects() []runtime.Object {
-	return []runtime.Object{
-		&v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "ctrlpublishsecret",
-				Namespace: defaultSecretNsName,
-			},
-		}, &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "nodestagesecret",
-				Namespace: defaultSecretNsName,
-			},
-		}, &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "nodepublishsecret",
-				Namespace: defaultSecretNsName,
-			},
-		}, &v1.Secret{
-			ObjectMeta: metav1.ObjectMeta{
-				Name:      "controllerexpandsecret",
-				Namespace: defaultSecretNsName,
-			},
-		},
-	}
-}
-
 func TestProvision(t *testing.T) {
 	var requestedBytes int64 = 100
 	deletePolicy := v1.PersistentVolumeReclaimDelete
@@ -881,47 +827,6 @@ func TestProvision(t *testing.T) {
 			},
 			expectState: controller.ProvisioningFinished,
 		},
-		"normal provision with extra metadata": {
-			volOpts: controller.ProvisionOptions{
-				StorageClass: &storagev1.StorageClass{
-					ReclaimPolicy: &deletePolicy,
-					Parameters: map[string]string{
-						"fstype": "ext3",
-					},
-				},
-				PVName: "test-name",
-				PVC:    createFakePVC(requestedBytes),
-			},
-			withExtraMetadata: true,
-			expectedPVSpec: &pvSpec{
-				Name:          "test-testi",
-				ReclaimPolicy: v1.PersistentVolumeReclaimDelete,
-				Capacity: v1.ResourceList{
-					v1.ResourceName(v1.ResourceStorage): bytesToGiQuantity(requestedBytes),
-				},
-				CSIPVS: &v1.CSIPersistentVolumeSource{
-					Driver:       "test-driver",
-					VolumeHandle: "test-volume-id",
-					FSType:       "ext3",
-					VolumeAttributes: map[string]string{
-						"storage.kubernetes.io/csiProvisionerIdentity": "test-provisioner",
-					},
-				},
-			},
-			expectCreateVolDo: func(ctx context.Context, req *csi.CreateVolumeRequest) {
-				pvc := createFakePVC(requestedBytes)
-				expectedParams := map[string]string{
-					pvcNameKey:      pvc.GetName(),
-					pvcNamespaceKey: pvc.GetNamespace(),
-					pvNameKey:       "test-testi",
-					"fstype":        "ext3",
-				}
-				if fmt.Sprintf("%v", req.Parameters) != fmt.Sprintf("%v", expectedParams) { // only pvc name/namespace left
-					t.Errorf("Unexpected parameters: %v", req.Parameters)
-				}
-			},
-			expectState: controller.ProvisioningFinished,
-		},
 		"multiple fsType provision": {
 			volOpts: controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{
@@ -1188,12 +1093,12 @@ func TestProvision(t *testing.T) {
 			volOpts: controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{
 					ReclaimPolicy: &deletePolicy,
-					Parameters:    getDefaultStorageClassSecretParameters(),
+					Parameters:    map[string]string{},
 				},
 				PVName: "test-name",
 				PVC:    createFakePVC(requestedBytes),
 			},
-			clientSetObjects: getDefaultSecretObjects(),
+			withSecretRefs: true,
 			expectedPVSpec: &pvSpec{
 				Name: "test-testi",
 				Capacity: v1.ResourceList{
@@ -1227,108 +1132,6 @@ func TestProvision(t *testing.T) {
 			},
 			expectState: controller.ProvisioningFinished,
 		},
-		"provision with default secrets": {
-			volOpts: controller.ProvisionOptions{
-				StorageClass: &storagev1.StorageClass{
-					ReclaimPolicy: &deletePolicy,
-					Parameters: map[string]string{
-						prefixedDefaultSecretNameKey:      "default-secret",
-						prefixedDefaultSecretNamespaceKey: "default-ns",
-					},
-				},
-				PVName: "test-name",
-				PVC:    createFakePVC(requestedBytes),
-			},
-			clientSetObjects: []runtime.Object{&v1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "default-secret",
-					Namespace: "default-ns",
-				},
-			}},
-			expectedPVSpec: &pvSpec{
-				Name: "test-testi",
-				Capacity: v1.ResourceList{
-					v1.ResourceName(v1.ResourceStorage): bytesToGiQuantity(requestedBytes),
-				},
-				ReclaimPolicy: v1.PersistentVolumeReclaimDelete,
-				CSIPVS: &v1.CSIPersistentVolumeSource{
-					Driver:       "test-driver",
-					VolumeHandle: "test-volume-id",
-					FSType:       "ext4",
-					VolumeAttributes: map[string]string{
-						"storage.kubernetes.io/csiProvisionerIdentity": "test-provisioner",
-					},
-					ControllerPublishSecretRef: &v1.SecretReference{
-						Name:      "default-secret",
-						Namespace: "default-ns",
-					},
-					NodeStageSecretRef: &v1.SecretReference{
-						Name:      "default-secret",
-						Namespace: "default-ns",
-					},
-					NodePublishSecretRef: &v1.SecretReference{
-						Name:      "default-secret",
-						Namespace: "default-ns",
-					},
-					ControllerExpandSecretRef: &v1.SecretReference{
-						Name:      "default-secret",
-						Namespace: "default-ns",
-					},
-				},
-			},
-			expectState: controller.ProvisioningFinished,
-		},
-		"provision with default secrets with template": {
-			volOpts: controller.ProvisionOptions{
-				StorageClass: &storagev1.StorageClass{
-					ReclaimPolicy: &deletePolicy,
-					Parameters: map[string]string{
-						prefixedDefaultSecretNameKey:      "${pvc.name}",
-						prefixedDefaultSecretNamespaceKey: "default-ns",
-					},
-				},
-				PVName: "test-name",
-				PVC:    createFakeNamedPVC(requestedBytes, "my-pvc", nil),
-			},
-			clientSetObjects: []runtime.Object{&v1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "my-pvc",
-					Namespace: "default-ns",
-				},
-			}},
-			expectedPVSpec: &pvSpec{
-				Name: "test-testi",
-				Capacity: v1.ResourceList{
-					v1.ResourceName(v1.ResourceStorage): bytesToGiQuantity(requestedBytes),
-				},
-				ReclaimPolicy: v1.PersistentVolumeReclaimDelete,
-				CSIPVS: &v1.CSIPersistentVolumeSource{
-					Driver:       "test-driver",
-					VolumeHandle: "test-volume-id",
-					FSType:       "ext4",
-					VolumeAttributes: map[string]string{
-						"storage.kubernetes.io/csiProvisionerIdentity": "test-provisioner",
-					},
-					ControllerPublishSecretRef: &v1.SecretReference{
-						Name:      "my-pvc",
-						Namespace: "default-ns",
-					},
-					NodeStageSecretRef: &v1.SecretReference{
-						Name:      "my-pvc",
-						Namespace: "default-ns",
-					},
-					NodePublishSecretRef: &v1.SecretReference{
-						Name:      "my-pvc",
-						Namespace: "default-ns",
-					},
-					ControllerExpandSecretRef: &v1.SecretReference{
-						Name:      "my-pvc",
-						Namespace: "default-ns",
-					},
-				},
-			},
-			expectState: controller.ProvisioningFinished,
-		},
 		"provision with volume mode(Filesystem)": {
 			volOpts: controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{
@@ -1424,32 +1227,6 @@ func TestProvision(t *testing.T) {
 			expectErr:         true,
 			expectState:       controller.ProvisioningNoChange,
 		},
-		"fail to get secret reference for invalid default secret parameter template": {
-			volOpts: controller.ProvisionOptions{
-				StorageClass: &storagev1.StorageClass{
-					ReclaimPolicy: &deletePolicy,
-					Parameters: map[string]string{
-						prefixedDefaultSecretNameKey:      "default-${pvc.annotations['team.example.com/key']}",
-						prefixedDefaultSecretNamespaceKey: "default-ns",
-					},
-				},
-				PVName: "test-name",
-				PVC: createFakeNamedPVC(
-					requestedBytes,
-					"fake-pvc",
-					map[string]string{"team.example.com/key": "secret-from-annotation"},
-				),
-			},
-			clientSetObjects: []runtime.Object{&v1.Secret{
-				ObjectMeta: metav1.ObjectMeta{
-					Name:      "default-secret-from-annotation",
-					Namespace: "default-ns",
-				},
-			}},
-			getSecretRefErr: true,
-			expectErr:       true,
-			expectState:     controller.ProvisioningNoChange,
-		},
 		"fail vol with less capacity": {
 			volOpts: controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{
@@ -1658,11 +1435,37 @@ func runProvisionTest(t *testing.T, k string, tc provisioningTestcase, requested
 	defer mockController.Finish()
 	defer driver.Stop()
 
-	clientSet := fakeclientset.NewSimpleClientset(tc.clientSetObjects...)
+	var clientSet kubernetes.Interface
+
+	if tc.withSecretRefs {
+		clientSet = fakeclientset.NewSimpleClientset(&v1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "ctrlpublishsecret",
+				Namespace: "default",
+			},
+		}, &v1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "nodestagesecret",
+				Namespace: "default",
+			},
+		}, &v1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "nodepublishsecret",
+				Namespace: "default",
+			},
+		}, &v1.Secret{
+			ObjectMeta: metav1.ObjectMeta{
+				Name:      "controllerexpandsecret",
+				Namespace: "default",
+			},
+		})
+	} else {
+		clientSet = fakeclientset.NewSimpleClientset()
+	}
 
 	pluginCaps, controllerCaps := provisionCapabilities()
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-		nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, csitrans.New(), nil, nil, nil, tc.withExtraMetadata)
+		nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, csitrans.New(), nil, nil, nil)
 
 	out := &csi.CreateVolumeResponse{
 		Volume: &csi.Volume{
@@ -1671,6 +1474,17 @@ func runProvisionTest(t *testing.T, k string, tc provisioningTestcase, requested
 		},
 	}
 
+	if tc.withSecretRefs {
+		tc.volOpts.StorageClass.Parameters[controllerPublishSecretNameKey] = "ctrlpublishsecret"
+		tc.volOpts.StorageClass.Parameters[controllerPublishSecretNamespaceKey] = "default"
+		tc.volOpts.StorageClass.Parameters[nodeStageSecretNameKey] = "nodestagesecret"
+		tc.volOpts.StorageClass.Parameters[nodeStageSecretNamespaceKey] = "default"
+		tc.volOpts.StorageClass.Parameters[nodePublishSecretNameKey] = "nodepublishsecret"
+		tc.volOpts.StorageClass.Parameters[nodePublishSecretNamespaceKey] = "default"
+		tc.volOpts.StorageClass.Parameters[prefixedControllerExpandSecretNameKey] = "controllerexpandsecret"
+		tc.volOpts.StorageClass.Parameters[prefixedControllerExpandSecretNamespaceKey] = "default"
+	}
+
 	if tc.notNilSelector {
 		tc.volOpts.PVC.Spec.Selector = &metav1.LabelSelector{}
 	} else if tc.makeVolumeNameErr {
@@ -2398,7 +2212,7 @@ func TestProvisionFromSnapshot(t *testing.T) {
 
 		pluginCaps, controllerCaps := provisionFromSnapshotCapabilities()
 		csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-			client, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil, false)
+			client, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil)
 
 		out := &csi.CreateVolumeResponse{
 			Volume: &csi.Volume{
@@ -2572,7 +2386,7 @@ func TestProvisionWithTopologyEnabled(t *testing.T) {
 			defer close(stopChan)
 
 			csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5,
-				csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), scLister, csiNodeLister, nodeLister, false)
+				csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), scLister, csiNodeLister, nodeLister)
 
 			pv, err := csiProvisioner.Provision(controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{},
@@ -2627,7 +2441,7 @@ func TestProvisionWithTopologyDisabled(t *testing.T) {
 	clientSet := fakeclientset.NewSimpleClientset()
 	pluginCaps, controllerCaps := provisionWithTopologyCapabilities()
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5,
-		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil, false)
+		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil)
 
 	out := &csi.CreateVolumeResponse{
 		Volume: &csi.Volume{
@@ -2668,11 +2482,11 @@ type deleteTestcase struct {
 // TestDelete is a test of the delete operation
 func TestDelete(t *testing.T) {
 	tt := map[string]deleteTestcase{
-		"fail - nil PV": {
+		"fail - nil PV": deleteTestcase{
 			persistentVolume: nil,
 			expectErr:        true,
 		},
-		"fail - nil volume.Spec.CSI": {
+		"fail - nil volume.Spec.CSI": deleteTestcase{
 			persistentVolume: &v1.PersistentVolume{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: "pv",
@@ -2686,7 +2500,7 @@ func TestDelete(t *testing.T) {
 			},
 			expectErr: true,
 		},
-		"fail - pvc.annotations not supported for Create/Delete Volume Secret": {
+		"fail - pvc.annotations not supported": deleteTestcase{
 			persistentVolume: &v1.PersistentVolume{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: "pv",
@@ -2713,7 +2527,7 @@ func TestDelete(t *testing.T) {
 			},
 			expectErr: true,
 		},
-		"simple - valid case": {
+		"simple - valid case": deleteTestcase{
 			persistentVolume: &v1.PersistentVolume{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: "pv",
@@ -2737,7 +2551,7 @@ func TestDelete(t *testing.T) {
 			expectErr:  false,
 			mockDelete: true,
 		},
-		"simple - valid case with ClaimRef set": {
+		"simple - valid case with ClaimRef set": deleteTestcase{
 			persistentVolume: &v1.PersistentVolume{
 				ObjectMeta: metav1.ObjectMeta{
 					Name: "pv",
@@ -2799,7 +2613,7 @@ func runDeleteTest(t *testing.T, k string, tc deleteTestcase) {
 	pluginCaps, controllerCaps := provisionCapabilities()
 	scLister, _, _, _ := listers(clientSet)
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5,
-		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), scLister, nil, nil, false)
+		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), scLister, nil, nil)
 
 	err = csiProvisioner.Delete(tc.persistentVolume)
 	if tc.expectErr && err == nil {
@@ -3501,7 +3315,7 @@ func TestProvisionFromPVC(t *testing.T) {
 		}
 
 		csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-			nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil, false)
+			nil, driverName, pluginCaps, controllerCaps, "", false, csitrans.New(), nil, nil, nil)
 
 		pv, err := csiProvisioner.Provision(tc.volOpts)
 		if tc.expectErr && err == nil {
@@ -3539,28 +3353,52 @@ func TestProvisionWithMigration(t *testing.T) {
 	deletePolicy := v1.PersistentVolumeReclaimDelete
 	testcases := []struct {
 		name              string
-		provisioner       string
+		scProvisioner     string
 		annotation        map[string]string
 		expectTranslation bool
 		expectErr         bool
 	}{
 		{
 			name:              "provision with migration on",
-			provisioner:       inTreePluginName,
+			scProvisioner:     inTreePluginName,
 			annotation:        map[string]string{annStorageProvisioner: driverName},
 			expectTranslation: true,
 		},
 		{
 			name:              "provision without migration for native CSI",
-			provisioner:       driverName,
+			scProvisioner:     driverName,
 			annotation:        map[string]string{annStorageProvisioner: driverName},
 			expectTranslation: false,
 		},
 		{
-			name:        "ignore in-tree PVC when provisioned by in-tree",
-			provisioner: inTreePluginName,
-			annotation:  map[string]string{annStorageProvisioner: inTreePluginName},
-			expectErr:   true,
+			name:              "provision with migration for migrated-to CSI",
+			scProvisioner:     inTreePluginName,
+			annotation:        map[string]string{annStorageProvisioner: inTreePluginName, annMigratedTo: driverName},
+			expectTranslation: true,
+		},
+		{
+			name:          "provision with migration-to some random driver",
+			scProvisioner: inTreePluginName,
+			annotation:    map[string]string{annStorageProvisioner: inTreePluginName, annMigratedTo: "foo"},
+			expectErr:     true,
+		},
+		{
+			name:          "provision with migration-to some random driver with random storageProvisioner",
+			scProvisioner: inTreePluginName,
+			annotation:    map[string]string{annStorageProvisioner: "foo", annMigratedTo: "foo"},
+			expectErr:     true,
+		},
+		{
+			name:              "provision with migration for migrated-to CSI with CSI Provisioner",
+			scProvisioner:     inTreePluginName,
+			annotation:        map[string]string{annStorageProvisioner: driverName, annMigratedTo: driverName},
+			expectTranslation: true,
+		},
+		{
+			name:          "ignore in-tree PVC when provisioned by in-tree",
+			scProvisioner: inTreePluginName,
+			annotation:    map[string]string{annStorageProvisioner: inTreePluginName},
+			expectErr:     true,
 		},
 	}
 
@@ -3580,7 +3418,7 @@ func TestProvisionWithMigration(t *testing.T) {
 			pluginCaps, controllerCaps := provisionCapabilities()
 			csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner",
 				"test", 5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps,
-				inTreePluginName, false, mockTranslator, nil, nil, nil, false)
+				inTreePluginName, false, mockTranslator, nil, nil, nil)
 
 			// Set up return values (AnyTimes to avoid overfitting on implementation)
 
@@ -3635,7 +3473,7 @@ func TestProvisionWithMigration(t *testing.T) {
 			// Make a Provision call
 			volOpts := controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{
-					Provisioner:   tc.provisioner,
+					Provisioner:   tc.scProvisioner,
 					Parameters:    map[string]string{"fstype": "ext3"},
 					ReclaimPolicy: &deletePolicy,
 				},
@@ -3740,7 +3578,7 @@ func TestDeleteMigration(t *testing.T) {
 			pluginCaps, controllerCaps := provisionCapabilities()
 			csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner",
 				"test", 5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "",
-				false, mockTranslator, nil, nil, nil, false)
+				false, mockTranslator, nil, nil, nil)
 
 			// Set mock return values (AnyTimes to avoid overfitting on implementation details)
 			mockTranslator.EXPECT().IsPVMigratable(gomock.Any()).Return(tc.expectTranslation).AnyTimes()