Skip to content

[release-1.33] feat: support nfs volume clone and snapshot restore #2713

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions pkg/azurefile/azurefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -1115,9 +1115,6 @@ func (d *Driver) ResizeFileShare(ctx context.Context, subsID, resourceGroup, acc

// copyFileShare copies a fileshare, if dstAccountName is empty, then copy in the same account
func (d *Driver) copyFileShare(ctx context.Context, req *csi.CreateVolumeRequest, dstAccountName string, dstAccountSasToken string, authAzcopyEnv []string, secretNamespace string, shareOptions *ShareOptions, accountOptions *storage.AccountOptions, storageEndpointSuffix string) error {
if shareOptions.Protocol == armstorage.EnabledProtocolsNFS {
return fmt.Errorf("protocol nfs is not supported for volume cloning")
}
var sourceVolumeID string
if req.GetVolumeContentSource() != nil && req.GetVolumeContentSource().GetVolume() != nil {
sourceVolumeID = req.GetVolumeContentSource().GetVolume().GetVolumeId()
Expand Down Expand Up @@ -1149,7 +1146,7 @@ func (d *Driver) copyFileShare(ctx context.Context, req *csi.CreateVolumeRequest
srcPath := fmt.Sprintf("https://%s.file.%s/%s%s", srcAccountName, storageEndpointSuffix, srcFileShareName, srcAccountSasToken)
dstPath := fmt.Sprintf("https://%s.file.%s/%s%s", dstAccountName, storageEndpointSuffix, dstFileShareName, dstAccountSasToken)

return d.copyFileShareByAzcopy(ctx, srcFileShareName, dstFileShareName, srcPath, dstPath, "", srcAccountName, dstAccountName, srcAccountSasToken, authAzcopyEnv, accountOptions)
return d.copyFileShareByAzcopy(ctx, srcFileShareName, dstFileShareName, srcPath, dstPath, "", srcAccountName, dstAccountName, srcAccountSasToken, authAzcopyEnv, shareOptions, accountOptions)
}

// GetTotalAccountQuota returns the total quota in GB of all file shares in the storage account and the number of file shares
Expand Down
11 changes: 6 additions & 5 deletions pkg/azurefile/controllerserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -1128,9 +1128,6 @@ func (d *Driver) ListSnapshots(_ context.Context, _ *csi.ListSnapshotsRequest) (

// restoreSnapshot restores from a snapshot
func (d *Driver) restoreSnapshot(ctx context.Context, req *csi.CreateVolumeRequest, dstAccountName, dstAccountSasToken string, authAzcopyEnv []string, secretNamespace string, shareOptions *ShareOptions, accountOptions *storage.AccountOptions, storageEndpointSuffix string) error {
if shareOptions.Protocol == armstorage.EnabledProtocolsNFS {
return fmt.Errorf("protocol nfs is not supported for snapshot restore")
}
var sourceSnapshotID string
if req.GetVolumeContentSource() != nil && req.GetVolumeContentSource().GetSnapshot() != nil {
sourceSnapshotID = req.GetVolumeContentSource().GetSnapshot().GetSnapshotId()
Expand Down Expand Up @@ -1171,10 +1168,10 @@ func (d *Driver) restoreSnapshot(ctx context.Context, req *csi.CreateVolumeReque
dstPath := fmt.Sprintf("https://%s.file.%s/%s%s", dstAccountName, storageEndpointSuffix, dstFileShareName, dstAccountSasToken)

srcFileShareSnapshotName := fmt.Sprintf("%s(snapshot: %s)", srcFileShareName, snapshot)
return d.copyFileShareByAzcopy(ctx, srcFileShareSnapshotName, dstFileShareName, srcPath, dstPath, snapshot, srcAccountName, dstAccountName, srcAccountSasToken, authAzcopyEnv, accountOptions)
return d.copyFileShareByAzcopy(ctx, srcFileShareSnapshotName, dstFileShareName, srcPath, dstPath, snapshot, srcAccountName, dstAccountName, srcAccountSasToken, authAzcopyEnv, shareOptions, accountOptions)
}

func (d *Driver) copyFileShareByAzcopy(ctx context.Context, srcFileShareName, dstFileShareName, srcPath, dstPath, snapshot, srcAccountName, dstAccountName, accountSASToken string, authAzcopyEnv []string, accountOptions *storage.AccountOptions) error {
func (d *Driver) copyFileShareByAzcopy(ctx context.Context, srcFileShareName, dstFileShareName, srcPath, dstPath, snapshot, srcAccountName, dstAccountName, accountSASToken string, authAzcopyEnv []string, shareOptions *ShareOptions, accountOptions *storage.AccountOptions) error {
azcopyCopyOptions := azcopyCloneVolumeOptions
srcPathAuth := srcPath
if snapshot != "" {
Expand All @@ -1186,6 +1183,10 @@ func (d *Driver) copyFileShareByAzcopy(ctx context.Context, srcFileShareName, ds
}
}

if shareOptions.Protocol == armstorage.EnabledProtocolsNFS {
azcopyCopyOptions = append(azcopyCopyOptions, "--from-to=FileNFSFileNFS")
}

jobState, percent, err := d.azcopy.GetAzcopyJob(dstFileShareName, authAzcopyEnv)
klog.V(2).Infof("azcopy job status: %s, copy percent: %s%%, error: %v", jobState, percent, err)

Expand Down
54 changes: 0 additions & 54 deletions pkg/azurefile/controllerserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1430,33 +1430,6 @@ var _ = ginkgo.Describe("TestCopyVolume", func() {
ginkgo.AfterEach(func() {
ctrl.Finish()
})
ginkgo.When("restore volume from volumeSnapshot nfs is not supported", func() {
ginkgo.It("should fail", func(ctx context.Context) {
allParam := map[string]string{}

volumeSnapshotSource := &csi.VolumeContentSource_SnapshotSource{
SnapshotId: "unit-test",
}
volumeContentSourceSnapshotSource := &csi.VolumeContentSource_Snapshot{
Snapshot: volumeSnapshotSource,
}
volumecontensource := csi.VolumeContentSource{
Type: volumeContentSourceSnapshotSource,
}

req := &csi.CreateVolumeRequest{
Name: "random-vol-name-valid-request",
VolumeCapabilities: stdVolCap,
CapacityRange: lessThanPremCapRange,
Parameters: allParam,
VolumeContentSource: &volumecontensource,
}

expectedErr := fmt.Errorf("protocol nfs is not supported for snapshot restore")
err := d.copyVolume(ctx, req, "", "", []string{}, "", &ShareOptions{Protocol: armstorage.EnabledProtocolsNFS}, nil, "core.windows.net")
gomega.Expect(err).To(gomega.Equal(expectedErr))
})
})
ginkgo.When("restore volume from volumeSnapshot not found", func() {
ginkgo.It("should fail", func(ctx context.Context) {
allParam := map[string]string{}
Expand Down Expand Up @@ -1511,33 +1484,6 @@ var _ = ginkgo.Describe("TestCopyVolume", func() {
gomega.Expect(err).To(gomega.Equal(expectedErr))
})
})
ginkgo.When("copy volume nfs is not supported", func() {
ginkgo.It("should fail", func(ctx context.Context) {
allParam := map[string]string{}

volumeSource := &csi.VolumeContentSource_VolumeSource{
VolumeId: "unit-test",
}
volumeContentSourceVolumeSource := &csi.VolumeContentSource_Volume{
Volume: volumeSource,
}
volumecontensource := csi.VolumeContentSource{
Type: volumeContentSourceVolumeSource,
}

req := &csi.CreateVolumeRequest{
Name: "random-vol-name-valid-request",
VolumeCapabilities: stdVolCap,
CapacityRange: lessThanPremCapRange,
Parameters: allParam,
VolumeContentSource: &volumecontensource,
}

expectedErr := fmt.Errorf("protocol nfs is not supported for volume cloning")
err := d.copyVolume(ctx, req, "", "", []string{}, "", &ShareOptions{Protocol: armstorage.EnabledProtocolsNFS}, nil, "core.windows.net")
gomega.Expect(err).To(gomega.Equal(expectedErr))
})
})
ginkgo.When("copy volume from volume not found", func() {
ginkgo.It("should fail", func(ctx context.Context) {
allParam := map[string]string{}
Expand Down
38 changes: 36 additions & 2 deletions test/e2e/dynamic_provisioning_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,39 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
test.Run(ctx, cs, snapshotrcs, ns)
})

ginkgo.It("should create a pod, write and read to it, take a nfs volume snapshot, and create another pod from the snapshot [file.csi.azure.com]", func(ctx ginkgo.SpecContext) {
skipIfUsingInTreeVolumePlugin()
skipIfTestingInWindowsCluster()

pod := testsuites.PodDetails{
Cmd: "echo 'hello world' > /mnt/test-1/data && grep 'hello world' /mnt/test-1/data",
Volumes: []testsuites.VolumeDetails{
{
ClaimSize: "100Gi",
VolumeMount: testsuites.VolumeMountDetails{
NameGenerate: "test-volume-",
MountPathGenerate: "/mnt/test-",
},
},
},
}
podWithSnapshot := testsuites.PodDetails{
Cmd: "grep 'hello world' /mnt/test-1/data",
}
test := testsuites.DynamicallyProvisionedVolumeSnapshotTest{
CSIDriver: testDriver,
Pod: pod,
ShouldOverwrite: false,
ShouldRestore: true,
PodWithSnapshot: podWithSnapshot,
StorageClassParameters: map[string]string{
"skuName": "Premium_LRS",
"protocol": "nfs",
},
}
test.Run(ctx, cs, snapshotrcs, ns)
})

ginkgo.It("should create a pod, write to its pv, take a volume snapshot, overwrite data in original pv, create another pod from the snapshot, use another storage class, and read unaltered original data from original pv[file.csi.azure.com]", func(ctx ginkgo.SpecContext) {
skipIfUsingInTreeVolumePlugin()
skipIfTestingInWindowsCluster()
Expand Down Expand Up @@ -1740,7 +1773,7 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
test.Run(ctx, cs, ns)
})

ginkgo.It("should clone a large size volume from an existing volume [file.csi.azure.com]", func(ctx ginkgo.SpecContext) {
ginkgo.It("should clone a large size volume from an existing nfs volume [file.csi.azure.com]", func(ctx ginkgo.SpecContext) {
skipIfTestingInWindowsCluster()
skipIfTestingInMigrationCluster()
skipIfUsingInTreeVolumePlugin()
Expand All @@ -1765,7 +1798,8 @@ var _ = ginkgo.Describe("Dynamic Provisioning", func() {
Pod: pod,
PodWithClonedVolume: podWithClonedVolume,
StorageClassParameters: map[string]string{
"skuName": "Standard_LRS",
"skuName": "Premium_LRS",
"protocol": "nfs",
},
}
test.Run(ctx, cs, ns)
Expand Down
Loading