Repository: kubernetes-csi/external-provisioner
Pull request #726 (2022-04-13 13:13:42)

Provisioner changes to prevent unauthorised volume mode conversion
<!--  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?**
/kind feature

**What this PR does / why we need it**:

KEP - https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/3141-prevent-volume-mode-conversion#changes-to-external-provisioner

**Testing**

Setup: 
- Enable feature `prevent-volume-mode-conversion` on `external-provisioner` and `external-snapshotter` 
- create VolumeSnapshot APIs from `release-6.0` branch.
- Create PVC and VolumeSnapshot of the PVC. Identify the VolumeSnapshotContent corresponding to that VolumeSnapshot.

1. Create a new PVC from Snapshot with annotation `snapshot.storage.kubernetes.io/allowVolumeModeChange: true` on snapshotcontent and attempt to convert volume mode.

PVC creation succeeds:

```
% kubectl get volumesnapshotcontent -o yaml snapcontent-aa1a4b98-5619-41e5-85fe-26255fdea661
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  annotations:
    snapshot.storage.kubernetes.io/allowVolumeModeChange: "true"
...

% kubectl get pvc
hpvc-restore   Bound    pvc-8599897d-9806-4987-9462-2ae69e6928e2   1Gi        RWO            csi-hostpath-sc   2s
```


2. Create a new PVC from Snapshot with annotation `snapshot.storage.kubernetes.io/allowVolumeModeChange: false` on snapshotcontent and attempt to convert volume mode.. 

PVC creation fails:
```
% kubectl get volumesnapshotcontent -o yaml snapcontent-aa1a4b98-5619-41e5-85fe-26255fdea661
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  annotations:
    snapshot.storage.kubernetes.io/allowVolumeModeChange: "false"
...

% kubectl get pvc
hpvc-restore   Pending                                                                        csi-hostpath-sc   2s

% kubectl describe pvc
Name:          hpvc-restore
Namespace:     default
StorageClass:  csi-hostpath-sc
Status:        Pending
...
  Warning  ProvisioningFailed    0s (x3 over 3s)  hostpath.csi.k8s.io_csi-hostpathplugin-0_728d7f4b-f33b-47c8-9e52-f894facd43c5  failed to provision volume with StorageClass "csi-hostpath-sc": error getting handle for DataSource Type VolumeSnapshot by Name new-snapshot-demo-v1: requested volume default/hpvc-restore modifies the mode of the source volume but does not have permission to do so.
```

3. Create a new PVC from Snapshot with annotation `snapshot.storage.kubernetes.io/allowVolumeModeChange: random` on snapshotcontent and attempt to convert volume mode.

PVC creation fails:

```
% kubectl get volumesnapshotcontent -o yaml snapcontent-aa1a4b98-5619-41e5-85fe-26255fdea661
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  annotations:
    snapshot.storage.kubernetes.io/allowVolumeModeChange: random
...

% kubectl get pvc
hpvc-restore   Pending                                                                        csi-hostpath-sc   2s

% kubectl describe pvc   
Name:          hpvc-restore
Namespace:     default
StorageClass:  csi-hostpath-sc
Status:        Pending
...
  Warning  ProvisioningFailed    3s (x5 over 18s)  hostpath.csi.k8s.io_csi-hostpathplugin-0_728d7f4b-f33b-47c8-9e52-f894facd43c5  failed to provision volume with StorageClass "csi-hostpath-sc": error getting handle for DataSource Type VolumeSnapshot by Name new-snapshot-demo-v1: failed to convert snapshot.storage.kubernetes.io/allowVolumeModeChange annotation value to boolean
```

4. Create a new PVC from Snapshot with annotation `snapshot.storage.kubernetes.io/allowVolumeModeChange` not present on snapshotcontent and attempt to convert volume mode.

PVC creation fails:

```
% kubectl get volumesnapshotcontent -o yaml snapcontent-aa1a4b98-5619-41e5-85fe-26255fdea661
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotContent
metadata:
  creationTimestamp: "2022-04-07T13:46:42Z"
  finalizers:
...

% kubectl get pvc
hpvc-restore   Pending                                                                        csi-hostpath-sc   4s

% kubectl describe pvc 
Name:          hpvc-restore
Namespace:     default
StorageClass:  csi-hostpath-sc
Status:        Pending
...
  Warning  ProvisioningFailed    6s (x4 over 13s)   hostpath.csi.k8s.io_csi-hostpathplugin-0_728d7f4b-f33b-47c8-9e52-f894facd43c5  failed to provision volume with StorageClass "csi-hostpath-sc": error getting handle for DataSource Type VolumeSnapshot by Name new-snapshot-demo-v1: requested volume default/hpvc-restore modifies the mode of the source volume but does not have permission to do so.
```


**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
Provisioner changes to prevent unauthorised volume mode conversion
```


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/726#" title="Author self-approved">RaunakShah</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please assign **saad-ali** after the PR has been reviewed.
You can assign the PR to them by writing `/assign @saad-ali` 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":["saad-ali"]} -->
k8s-ci-robot: @RaunakShah: 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-kubernetes-csi-external-provisioner-unit | 6133c9cc6acead102b70f9b07409f69176f8bac3 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1514230351365410816) | true | `/test pull-kubernetes-csi-external-provisioner-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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 -->
RaunakShah: /assign @xing-yang 

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/726#" title="Author self-approved">RaunakShah</a>*
To complete the [pull request process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process), please ask for approval from **xing-yang** after the PR has been reviewed.

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":["xing-yang"]} -->
k8s-ci-robot: @RaunakShah: 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-kubernetes-csi-external-provisioner-unit | 7115e7351f6f51b49a274af948107174dea90bce | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519219328942084096) | true | `/test pull-kubernetes-csi-external-provisioner-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: 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-kubernetes-csi-external-provisioner-unit | 75cd702b308e87e2da3cbb4f4b8b62f079a0bcf4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519262546538795008) | true | `/test pull-kubernetes-csi-external-provisioner-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: PR needs rebase.

<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: @RaunakShah: The following tests **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-kubernetes-csi-external-provisioner-unit | 75cd702b308e87e2da3cbb4f4b8b62f079a0bcf4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519262546538795008) | true | `/test pull-kubernetes-csi-external-provisioner-unit`
pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21/1519388431514341376) | true | `/test pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: The following tests **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-kubernetes-csi-external-provisioner-unit | 75cd702b308e87e2da3cbb4f4b8b62f079a0bcf4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519262546538795008) | true | `/test pull-kubernetes-csi-external-provisioner-unit`
pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21/1519388431514341376) | true | `/test pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21`
pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23/1519388431610810368) | false | `/test pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: The following tests **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-kubernetes-csi-external-provisioner-unit | 75cd702b308e87e2da3cbb4f4b8b62f079a0bcf4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519262546538795008) | true | `/test pull-kubernetes-csi-external-provisioner-unit`
pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21/1519388431514341376) | true | `/test pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21`
pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23/1519388431610810368) | false | `/test pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23/1519388431719862272) | true | `/test pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: The following tests **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-kubernetes-csi-external-provisioner-unit | 75cd702b308e87e2da3cbb4f4b8b62f079a0bcf4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519262546538795008) | true | `/test pull-kubernetes-csi-external-provisioner-unit`
pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21/1519388431514341376) | true | `/test pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21`
pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23/1519388431610810368) | false | `/test pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23/1519388431719862272) | true | `/test pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22/1519388431568867328) | true | `/test pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: The following tests **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-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21/1519388431514341376) | true | `/test pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21`
pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23/1519388431610810368) | false | `/test pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23/1519388431719862272) | true | `/test pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22/1519388431568867328) | true | `/test pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22`
pull-kubernetes-csi-external-provisioner-unit | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519388431656947712) | true | `/test pull-kubernetes-csi-external-provisioner-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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: @RaunakShah: The following tests **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-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21/1519388431514341376) | true | `/test pull-kubernetes-csi-external-provisioner-1-21-on-kubernetes-1-21`
pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23/1519388431610810368) | false | `/test pull-kubernetes-csi-external-provisioner-1-23-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23/1519388431719862272) | true | `/test pull-kubernetes-csi-external-provisioner-distributed-on-kubernetes-1-23`
pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22 | 62bfbb9e56550bdfd3fa8c43f4cc977f1dfb1cb4 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22/1519388431568867328) | true | `/test pull-kubernetes-csi-external-provisioner-1-22-on-kubernetes-1-22`
pull-kubernetes-csi-external-provisioner-unit | 13007b922a485e1a740dd707efcca5bc4ccb36a6 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-csi_external-provisioner/726/pull-kubernetes-csi-external-provisioner-unit/1519544068420407296) | true | `/test pull-kubernetes-csi-external-provisioner-unit`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-csi&repo=external-provisioner&pr=726). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3ARaunakShah). 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 -->
xing-yang: Just one minor comment.  Looks good other than that.

Patch:
diff --git a/README.md b/README.md
index 5da53bea1..f1083d361 100644
--- a/README.md
+++ b/README.md
@@ -19,13 +19,14 @@ Various external-provisioner releases come with different alpha / beta features.
 
 Following table reflects the head of this branch.
 
-| Feature        | Status  | Default | Description                                                                                   | Provisioner Feature Gate Required |
-| -------------- | ------- | ------- | --------------------------------------------------------------------------------------------- | --------------------------------- |
-| Snapshots      | GA      | On      | [Snapshots and Restore](https://kubernetes-csi.github.io/docs/snapshot-restore-feature.html). | No |
-| CSIMigration   | Beta    | On      | [Migrating in-tree volume plugins to CSI](https://kubernetes.io/docs/concepts/storage/volumes/#csi-migration). | No |
-| CSIStorageCapacity | Beta  | On  | Publish [capacity information](https://kubernetes.io/docs/concepts/storage/volumes/#storage-capacity) for the Kubernetes scheduler. | No |
-| ReadWriteOncePod   | Alpha | Off | [Single pod access mode for PersistentVolumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes). | No |
-| HonorPVReclaimPolicy| Alpha |Off | [Honor the PV reclaim policy](https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/2644-honor-pv-reclaim-policy) | No
+| Feature        | Status  | Default | Description                                                                                                                                                          | Provisioner Feature Gate Required |
+| -------------- | ------- | ------- |----------------------------------------------------------------------------------------------------------------------------------------------------------------------| --------------------------------- |
+| Snapshots      | GA      | On      | [Snapshots and Restore](https://kubernetes-csi.github.io/docs/snapshot-restore-feature.html).                                                                        | No |
+| CSIMigration   | Beta    | On      | [Migrating in-tree volume plugins to CSI](https://kubernetes.io/docs/concepts/storage/volumes/#csi-migration).                                                       | No |
+| CSIStorageCapacity | Beta  | On  | Publish [capacity information](https://kubernetes.io/docs/concepts/storage/volumes/#storage-capacity) for the Kubernetes scheduler.                                  | No |
+| ReadWriteOncePod   | Alpha | Off | [Single pod access mode for PersistentVolumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/#access-modes).                                        | No |
+| HonorPVReclaimPolicy| Alpha |Off | [Honor the PV reclaim policy](https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/2644-honor-pv-reclaim-policy)                                  | No
+| PreventVolumeModeConversion | Alpha |Off | [Prevent unauthorized conversion of source volume mode](https://github.com/kubernetes/enhancements/tree/master/keps/sig-storage/3141-prevent-volume-mode-conversion) | `--prevent-volume-mode-conversion` (No in-tree feature gate)
 
 All other external-provisioner features and the external-provisioner itself is considered GA and fully supported.
 
@@ -123,6 +124,8 @@ See the [storage capacity section](#capacity-support) below for details.
 
 * `--version`: Prints current external-provisioner version and quits.
 
+* `--prevent-volume-mode-conversion`: Prevents an unauthorized user from modifying the volume mode when creating a PVC from an existing VolumeSnapshot. Defaults to false.
+
 * All glog / klog arguments are supported, such as `-v <log level>` or `-alsologtostderr`.
 
 ### Design
diff --git a/cmd/csi-provisioner/csi-provisioner.go b/cmd/csi-provisioner/csi-provisioner.go
index 07586e675..9dd1b080b 100644
--- a/cmd/csi-provisioner/csi-provisioner.go
+++ b/cmd/csi-provisioner/csi-provisioner.go
@@ -113,6 +113,8 @@ var (
 	nodeDeploymentMaxDelay         = flag.Duration("node-deployment-max-delay", 60*time.Second, "Determines how long the external-provisioner sleeps at most before trying to own a PVC with immediate binding.")
 	controllerPublishReadOnly      = flag.Bool("controller-publish-readonly", false, "This option enables PV to be marked as readonly at controller publish volume call if PVC accessmode has been set to ROX.")
 
+	preventVolumeModeConversion = flag.Bool("prevent-volume-mode-conversion", false, "Prevents an unauthorised user from modifying the volume mode when creating a PVC from an existing VolumeSnapshot.")
+
 	featureGates        map[string]bool
 	provisionController *controller.ProvisionController
 	version             = "unknown"
@@ -403,6 +405,7 @@ func main() {
 		*defaultFSType,
 		nodeDeployment,
 		*controllerPublishReadOnly,
+		*preventVolumeModeConversion,
 	)
 
 	var capacityController *capacity.Controller
diff --git a/go.mod b/go.mod
index e049fa6a1..dda14885b 100644
--- a/go.mod
+++ b/go.mod
@@ -10,7 +10,7 @@ require (
 	github.com/imdario/mergo v0.3.12 // indirect
 	github.com/kubernetes-csi/csi-lib-utils v0.11.0
 	github.com/kubernetes-csi/csi-test/v4 v4.0.2
-	github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc3
+	github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc4
 	github.com/miekg/dns v1.1.48 // indirect
 	github.com/prometheus/client_golang v1.12.1
 	github.com/spf13/pflag v1.0.5
diff --git a/go.sum b/go.sum
index 4f867492e..c49717444 100644
--- a/go.sum
+++ b/go.sum
@@ -375,8 +375,8 @@ github.com/kubernetes-csi/csi-lib-utils v0.11.0 h1:FHWOBtAZBA/hVk7v/qaXgG9Sxv0/n
 github.com/kubernetes-csi/csi-lib-utils v0.11.0/go.mod h1:BmGZZB16L18+9+Lgg9YWwBKfNEHIDdgGfAyuW6p2NV0=
 github.com/kubernetes-csi/csi-test/v4 v4.0.2 h1:MNj94SFHOGK6lOy+yDgxI+zlFWaPcgByqBH3JZZGyZI=
 github.com/kubernetes-csi/csi-test/v4 v4.0.2/go.mod h1:z3FYigjLFAuzmFzKdHQr8gUPm5Xr4Du2twKcxfys0eI=
-github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc3 h1:77oebiYuV7yLyjW3J4MG4ttikt9vbuSeAHdmlMaICWc=
-github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc3/go.mod h1:tnHiLn3P10N95fjn7O40QH5ovN0EFGAxqdTpUMrX6bU=
+github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc4 h1:uspy64y8fTrwchSk4dKx/CAndt0y2V70BFM2C9/jTIE=
+github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc4/go.mod h1:tnHiLn3P10N95fjn7O40QH5ovN0EFGAxqdTpUMrX6bU=
 github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
 github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go
index b55b90c54..c12987c42 100644
--- a/pkg/controller/controller.go
+++ b/pkg/controller/controller.go
@@ -140,6 +140,8 @@ const (
 	snapshotNotBound = "snapshot %s not bound"
 
 	pvcCloneFinalizer = "provisioner.storage.kubernetes.io/cloning-protection"
+
+	annAllowVolumeModeChange = "snapshot.storage.kubernetes.io/allowVolumeModeChange"
 )
 
 var (
@@ -258,6 +260,7 @@ type csiProvisioner struct {
 	eventRecorder                         record.EventRecorder
 	nodeDeployment                        *internalNodeDeployment
 	controllerPublishReadOnly             bool
+	preventVolumeModeConversion           bool
 }
 
 var (
@@ -337,6 +340,7 @@ func NewCSIProvisioner(client kubernetes.Interface,
 	defaultFSType string,
 	nodeDeployment *NodeDeployment,
 	controllerPublishReadOnly bool,
+	preventVolumeModeConversion bool,
 ) controller.Provisioner {
 	broadcaster := record.NewBroadcaster()
 	broadcaster.StartLogging(klog.Infof)
@@ -370,6 +374,7 @@ func NewCSIProvisioner(client kubernetes.Interface,
 		extraCreateMetadata:                   extraCreateMetadata,
 		eventRecorder:                         eventRecorder,
 		controllerPublishReadOnly:             controllerPublishReadOnly,
+		preventVolumeModeConversion:           preventVolumeModeConversion,
 	}
 	if nodeDeployment != nil {
 		provisioner.nodeDeployment = &internalNodeDeployment{
@@ -1124,6 +1129,27 @@ func (p *csiProvisioner) getSnapshotSource(ctx context.Context, claim *v1.Persis
 		}
 	}
 
+	if p.preventVolumeModeConversion {
+		if snapContentObj.Spec.SourceVolumeMode != nil && claim.Spec.VolumeMode != nil && snapContentObj.Spec.SourceVolumeMode != claim.Spec.VolumeMode {
+			// Attempt to modify volume mode during volume creation.
+			// Verify if this volume is allowed to alter its mode.
+			allowVolumeModeChange, ok := snapContentObj.Annotations[annAllowVolumeModeChange]
+			if !ok {
+				return nil, fmt.Errorf("requested volume %s/%s modifies the mode of the source volume but does not have permission to do so. "+
+					"%s annotation is not present on snapshotcontent %s", claim.Namespace, claim.Name, annAllowVolumeModeChange, snapContentObj.Name)
+			}
+			allowVolumeModeChangeBool, err := strconv.ParseBool(allowVolumeModeChange)
+			if err != nil {
+				return nil, fmt.Errorf("requested volume %s/%s modifies the mode of the source volume but does not have permission to do so. "+
+					"failed to convert %s annotation value to boolean with error: %v", claim.Namespace, claim.Name, annAllowVolumeModeChange, err)
+			}
+			if !allowVolumeModeChangeBool {
+				return nil, fmt.Errorf("requested volume %s/%s modifies the mode of the source volume but does not have permission to do so. "+
+					"%s is set to false on snapshotcontent %s", claim.Namespace, claim.Name, annAllowVolumeModeChange, snapContentObj.Name)
+			}
+		}
+	}
+
 	volumeContentSource := &csi.VolumeContentSource{
 		Type: &snapshotSource,
 	}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index f2fe72c34..2d1d037ac 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -103,7 +103,7 @@ github.com/kubernetes-csi/csi-lib-utils/rpc
 ## explicit; go 1.15
 github.com/kubernetes-csi/csi-test/v4/driver
 github.com/kubernetes-csi/csi-test/v4/utils
-# github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc3
+# github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.0-rc4
 ## explicit; go 1.17
 github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1
 github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1beta1


Test patch:
diff --git a/pkg/controller/controller_test.go b/pkg/controller/controller_test.go
index 2e94a54a0..4fd48c5b1 100644
--- a/pkg/controller/controller_test.go
+++ b/pkg/controller/controller_test.go
@@ -418,7 +418,7 @@ func TestCreateDriverReturnsInvalidCapacityDuringProvision(t *testing.T) {
 
 	pluginCaps, controllerCaps := provisionCapabilities()
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test",
-		5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, nil, nil, false, defaultfsType, nil, true)
+		5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, nil, nil, false, defaultfsType, nil, true, false)
 
 	// Requested PVC with requestedBytes storage
 	deletePolicy := v1.PersistentVolumeReclaimDelete
@@ -2327,7 +2327,7 @@ func runFSTypeProvisionTest(t *testing.T, k string, tc provisioningFSTypeTestcas
 		myDefaultfsType = ""
 	}
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-		nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, true, csitrans.New(), nil, nil, nil, nil, nil, false, myDefaultfsType, nil, false)
+		nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, true, csitrans.New(), nil, nil, nil, nil, nil, false, myDefaultfsType, nil, false, false)
 	out := &csi.CreateVolumeResponse{
 		Volume: &csi.Volume{
 			CapacityBytes: requestedBytes,
@@ -2507,7 +2507,7 @@ func runProvisionTest(t *testing.T, tc provisioningTestcase, requestedBytes int6
 	}
 	mycontrollerPublishReadOnly := tc.controllerPublishReadOnly
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-		nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, true, csitrans.New(), scInformer.Lister(), csiNodeInformer.Lister(), nodeInformer.Lister(), nil, nil, tc.withExtraMetadata, defaultfsType, nodeDeployment, mycontrollerPublishReadOnly)
+		nil, provisionDriverName, pluginCaps, controllerCaps, supportsMigrationFromInTreePluginName, false, true, csitrans.New(), scInformer.Lister(), csiNodeInformer.Lister(), nodeInformer.Lister(), nil, nil, tc.withExtraMetadata, defaultfsType, nodeDeployment, mycontrollerPublishReadOnly, false)
 
 	// Adding objects to the informer ensures that they are consistent with
 	// the fake storage without having to start the informers.
@@ -2614,6 +2614,7 @@ func newContent(name, className, snapshotHandle, volumeUID, volumeName, boundToS
 				SnapshotHandle: &snapshotHandle,
 			},
 			VolumeSnapshotClassName: &className,
+			SourceVolumeMode:        &volumeModeFileSystem,
 		},
 	}
 
@@ -2674,6 +2675,7 @@ func TestProvisionFromSnapshot(t *testing.T) {
 		nilReadyToUse                     bool
 		nilContentStatus                  bool
 		nilSnapshotHandle                 bool
+		allowVolumeModeChange             bool
 	}
 	testcases := map[string]testcase{
 		"provision with volume snapshot data source": {
@@ -3202,6 +3204,88 @@ func TestProvisionFromSnapshot(t *testing.T) {
 			nilReadyToUse: true,
 			expectErr:     true,
 		},
+		"allow source volume mode conversion": {
+			volOpts: controller.ProvisionOptions{
+				StorageClass: &storagev1.StorageClass{
+					Parameters:  map[string]string{},
+					Provisioner: "test-driver",
+				},
+				PVName: "test-name",
+				PVC: &v1.PersistentVolumeClaim{
+					ObjectMeta: metav1.ObjectMeta{
+						UID:         "testid",
+						Annotations: driverNameAnnotation,
+					},
+					Spec: v1.PersistentVolumeClaimSpec{
+						StorageClassName: &snapClassName,
+						Resources: v1.ResourceRequirements{
+							Requests: v1.ResourceList{
+								v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)),
+							},
+						},
+						AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
+						DataSource: &v1.TypedLocalObjectReference{
+							Name:     snapName,
+							Kind:     "VolumeSnapshot",
+							APIGroup: &apiGrp,
+						},
+						VolumeMode: &volumeModeBlock,
+					},
+				},
+			},
+			expectedPVSpec: &pvSpec{
+				Name:          "test-testi",
+				ReclaimPolicy: v1.PersistentVolumeReclaimDelete,
+				AccessModes:   []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
+				Capacity: v1.ResourceList{
+					v1.ResourceName(v1.ResourceStorage): bytesToQuantity(requestedBytes),
+				},
+				CSIPVS: &v1.CSIPersistentVolumeSource{
+					Driver:       "test-driver",
+					VolumeHandle: "test-volume-id",
+					VolumeAttributes: map[string]string{
+						"storage.kubernetes.io/csiProvisionerIdentity": "test-provisioner",
+					},
+				},
+			},
+			allowVolumeModeChange: true,
+			snapshotStatusReady:   true,
+			expectCSICall:         true,
+			expectErr:             false,
+		},
+		"fail source volume mode conversion": {
+			volOpts: controller.ProvisionOptions{
+				StorageClass: &storagev1.StorageClass{
+					Parameters:  map[string]string{},
+					Provisioner: "test-driver",
+				},
+				PVName: "test-name",
+				PVC: &v1.PersistentVolumeClaim{
+					ObjectMeta: metav1.ObjectMeta{
+						UID:         "testid",
+						Annotations: driverNameAnnotation,
+					},
+					Spec: v1.PersistentVolumeClaimSpec{
+						StorageClassName: &snapClassName,
+						Resources: v1.ResourceRequirements{
+							Requests: v1.ResourceList{
+								v1.ResourceName(v1.ResourceStorage): resource.MustParse(strconv.FormatInt(requestedBytes, 10)),
+							},
+						},
+						AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce},
+						DataSource: &v1.TypedLocalObjectReference{
+							Name:     snapName,
+							Kind:     "VolumeSnapshot",
+							APIGroup: &apiGrp,
+						},
+						VolumeMode: &volumeModeBlock,
+					},
+				},
+			},
+			allowVolumeModeChange: false,
+			snapshotStatusReady:   true,
+			expectErr:             true,
+		},
 	}
 
 	tmpdir := tempDir(t)
@@ -3249,12 +3333,17 @@ func TestProvisionFromSnapshot(t *testing.T) {
 			if tc.nilSnapshotHandle {
 				content.Status.SnapshotHandle = nil
 			}
+			if tc.allowVolumeModeChange {
+				content.Annotations = map[string]string{
+					annAllowVolumeModeChange: "true",
+				}
+			}
 			return true, content, nil
 		})
 
 		pluginCaps, controllerCaps := provisionFromSnapshotCapabilities()
 		csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-			client, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, nil, nil, false, defaultfsType, nil, true)
+			client, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, nil, nil, false, defaultfsType, nil, true, true)
 
 		out := &csi.CreateVolumeResponse{
 			Volume: &csi.Volume{
@@ -3434,7 +3523,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, true, csitrans.New(), scLister, csiNodeLister, nodeLister, claimLister, vaLister, false, defaultfsType, nil, true)
+				csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), scLister, csiNodeLister, nodeLister, claimLister, vaLister, false, defaultfsType, nil, true, false)
 
 			pv, _, err := csiProvisioner.Provision(context.Background(), controller.ProvisionOptions{
 				StorageClass: &storagev1.StorageClass{},
@@ -3528,7 +3617,7 @@ func TestProvisionErrorHandling(t *testing.T) {
 					defer close(stopChan)
 
 					csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5,
-						csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), scLister, csiNodeLister, nodeLister, claimLister, vaLister, false, defaultfsType, nil, true)
+						csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), scLister, csiNodeLister, nodeLister, claimLister, vaLister, false, defaultfsType, nil, true, false)
 
 					options := controller.ProvisionOptions{
 						StorageClass: &storagev1.StorageClass{},
@@ -3601,7 +3690,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, true, csitrans.New(), nil, nil, nil, nil, nil, false, defaultfsType, nil, true)
+		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, nil, nil, false, defaultfsType, nil, true, false)
 
 	out := &csi.CreateVolumeResponse{
 		Volume: &csi.Volume{
@@ -4299,7 +4388,7 @@ func runDeleteTest(t *testing.T, k string, tc deleteTestcase) {
 	pluginCaps, controllerCaps := provisionCapabilities()
 	scLister, _, _, _, vaLister, _ := listers(clientSet)
 	csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5,
-		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), scLister, nil, nil, nil, vaLister, false, defaultfsType, nodeDeployment, true)
+		csiConn.conn, nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), scLister, nil, nil, nil, vaLister, false, defaultfsType, nodeDeployment, true, false)
 
 	err = csiProvisioner.Delete(context.Background(), tc.persistentVolume)
 	if tc.expectErr && err == nil {
@@ -4721,7 +4810,7 @@ func TestProvisionFromPVC(t *testing.T) {
 
 			// Phase: execute the test
 			csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner", "test", 5, csiConn.conn,
-				nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, claimLister, nil, false, defaultfsType, nil, true)
+				nil, driverName, pluginCaps, controllerCaps, "", false, true, csitrans.New(), nil, nil, nil, claimLister, nil, false, defaultfsType, nil, true, false)
 
 			pv, _, err = csiProvisioner.Provision(context.Background(), tc.volOpts)
 			if tc.expectErr && err == nil {
@@ -4851,7 +4940,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, true, mockTranslator, nil, nil, nil, nil, nil, false, defaultfsType, nil, true)
+				inTreePluginName, false, true, mockTranslator, nil, nil, nil, nil, nil, false, defaultfsType, nil, true, false)
 
 			// Set up return values (AnyTimes to avoid overfitting on implementation)
 
@@ -5027,7 +5116,7 @@ func TestDeleteMigration(t *testing.T) {
 			defer close(stopCh)
 			csiProvisioner := NewCSIProvisioner(clientSet, 5*time.Second, "test-provisioner",
 				"test", 5, csiConn.conn, nil, driverName, pluginCaps, controllerCaps, inTreePluginName,
-				false, true, mockTranslator, scLister, nil, nil, nil, vaLister, false, defaultfsType, nil, true)
+				false, true, mockTranslator, scLister, nil, nil, nil, vaLister, false, defaultfsType, nil, true, false)
 
 			// Set mock return values (AnyTimes to avoid overfitting on implementation details)
 			mockTranslator.EXPECT().IsPVMigratable(gomock.Any()).Return(tc.expectTranslation).AnyTimes()