Skip to content

Commit eeead67

Browse files
authored
Merge pull request #854 from amacaskill/volume-cloning-feature
Volume cloning feature
2 parents bc88549 + f0d15be commit eeead67

File tree

8 files changed

+249
-25
lines changed

8 files changed

+249
-25
lines changed

pkg/gce-cloud-provider/compute/cloud-disk.go

+11
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,17 @@ func (d *CloudDisk) GetSnapshotId() string {
177177
}
178178
}
179179

180+
func (d *CloudDisk) GetSourceDiskId() string {
181+
switch {
182+
case d.disk != nil:
183+
return d.disk.SourceDiskId
184+
case d.betaDisk != nil:
185+
return d.betaDisk.SourceDiskId
186+
default:
187+
return ""
188+
}
189+
}
190+
180191
func (d *CloudDisk) GetKMSKeyName() string {
181192
switch {
182193
case d.disk != nil:

pkg/gce-cloud-provider/compute/fake-gce.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ func (cloud *FakeCloudProvider) ValidateExistingDisk(ctx context.Context, resp *
220220
return ValidateDiskParameters(resp, params)
221221
}
222222

223-
func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, project string, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string, multiWriter bool) error {
223+
func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, project string, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string, volumeContentSourceVolumeID string, multiWriter bool) error {
224224
if disk, ok := cloud.disks[volKey.Name]; ok {
225225
err := cloud.ValidateExistingDisk(ctx, disk, params,
226226
int64(capacityRange.GetRequiredBytes()),
@@ -237,6 +237,7 @@ func (cloud *FakeCloudProvider) InsertDisk(ctx context.Context, project string,
237237
Description: "Disk created by GCE-PD CSI Driver",
238238
Type: cloud.GetDiskTypeURI(project, volKey, params.DiskType),
239239
SourceSnapshotId: snapshotID,
240+
SourceDiskId: volumeContentSourceVolumeID,
240241
Status: cloud.mockDiskStatus,
241242
Labels: params.Labels,
242243
}

pkg/gce-cloud-provider/compute/gce-compute.go

+12-4
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type GCECompute interface {
5656
GetDisk(ctx context.Context, project string, volumeKey *meta.Key, gceAPIVersion GCEAPIVersion) (*CloudDisk, error)
5757
RepairUnderspecifiedVolumeKey(ctx context.Context, project string, volumeKey *meta.Key) (string, *meta.Key, error)
5858
ValidateExistingDisk(ctx context.Context, disk *CloudDisk, params common.DiskParameters, reqBytes, limBytes int64, multiWriter bool) error
59-
InsertDisk(ctx context.Context, project string, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string, multiWriter bool) error
59+
InsertDisk(ctx context.Context, project string, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string, volumeContentSourceVolumeID string, multiWriter bool) error
6060
DeleteDisk(ctx context.Context, project string, volumeKey *meta.Key) error
6161
AttachDisk(ctx context.Context, project string, volKey *meta.Key, readWrite, diskType, instanceZone, instanceName string) error
6262
DetachDisk(ctx context.Context, project, deviceName, instanceZone, instanceName string) error
@@ -315,7 +315,7 @@ func ValidateDiskParameters(disk *CloudDisk, params common.DiskParameters) error
315315
return nil
316316
}
317317

318-
func (cloud *CloudProvider) InsertDisk(ctx context.Context, project string, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string, multiWriter bool) error {
318+
func (cloud *CloudProvider) InsertDisk(ctx context.Context, project string, volKey *meta.Key, params common.DiskParameters, capBytes int64, capacityRange *csi.CapacityRange, replicaZones []string, snapshotID string, volumeContentSourceVolumeID string, multiWriter bool) error {
319319
klog.V(5).Infof("Inserting disk %v", volKey)
320320

321321
description, err := encodeDiskTags(params.Tags)
@@ -328,12 +328,12 @@ func (cloud *CloudProvider) InsertDisk(ctx context.Context, project string, volK
328328
if description == "" {
329329
description = "Disk created by GCE-PD CSI Driver"
330330
}
331-
return cloud.insertZonalDisk(ctx, project, volKey, params, capBytes, capacityRange, snapshotID, description, multiWriter)
331+
return cloud.insertZonalDisk(ctx, project, volKey, params, capBytes, capacityRange, snapshotID, volumeContentSourceVolumeID, description, multiWriter)
332332
case meta.Regional:
333333
if description == "" {
334334
description = "Regional disk created by GCE-PD CSI Driver"
335335
}
336-
return cloud.insertRegionalDisk(ctx, project, volKey, params, capBytes, capacityRange, replicaZones, snapshotID, description, multiWriter)
336+
return cloud.insertRegionalDisk(ctx, project, volKey, params, capBytes, capacityRange, replicaZones, snapshotID, volumeContentSourceVolumeID, description, multiWriter)
337337
default:
338338
return fmt.Errorf("could not insert disk, key was neither zonal nor regional, instead got: %v", volKey.String())
339339
}
@@ -377,6 +377,7 @@ func (cloud *CloudProvider) insertRegionalDisk(
377377
capacityRange *csi.CapacityRange,
378378
replicaZones []string,
379379
snapshotID string,
380+
volumeContentSourceVolumeID string,
380381
description string,
381382
multiWriter bool) error {
382383
var (
@@ -399,6 +400,9 @@ func (cloud *CloudProvider) insertRegionalDisk(
399400
if snapshotID != "" {
400401
diskToCreate.SourceSnapshot = snapshotID
401402
}
403+
if volumeContentSourceVolumeID != "" {
404+
diskToCreate.SourceDisk = volumeContentSourceVolumeID
405+
}
402406
if len(replicaZones) != 0 {
403407
diskToCreate.ReplicaZones = replicaZones
404408
}
@@ -472,6 +476,7 @@ func (cloud *CloudProvider) insertZonalDisk(
472476
capBytes int64,
473477
capacityRange *csi.CapacityRange,
474478
snapshotID string,
479+
volumeContentSourceVolumeID string,
475480
description string,
476481
multiWriter bool) error {
477482
var (
@@ -495,6 +500,9 @@ func (cloud *CloudProvider) insertZonalDisk(
495500
if snapshotID != "" {
496501
diskToCreate.SourceSnapshot = snapshotID
497502
}
503+
if volumeContentSourceVolumeID != "" {
504+
diskToCreate.SourceDisk = volumeContentSourceVolumeID
505+
}
498506

499507
if params.DiskEncryptionKMSKey != "" {
500508
diskToCreate.DiskEncryptionKey = &computev1.CustomerEncryptionKey{

pkg/gce-pd-csi-driver/controller.go

+59-17
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,8 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
179179
if err != nil {
180180
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume disk %v had error checking ready status: %v", volKey, err))
181181
}
182-
183182
if !ready {
184-
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume disk %v is not ready", volKey))
183+
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume existing disk %v is not ready", volKey))
185184
}
186185

187186
// If there is no validation error, immediately return success
@@ -190,10 +189,10 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
190189
}
191190

192191
snapshotID := ""
192+
volumeContentSourceVolumeID := ""
193193
content := req.GetVolumeContentSource()
194194
if content != nil {
195195
if content.GetSnapshot() != nil {
196-
// TODO(#161): Add support for Volume Source (cloning) introduced in CSI v1.0.0
197196
snapshotID = content.GetSnapshot().GetSnapshotId()
198197

199198
// Verify that snapshot exists
@@ -204,24 +203,54 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
204203
return nil, status.Errorf(codes.NotFound, "CreateVolume source snapshot %s does not exist", snapshotID)
205204
}
206205
}
207-
}
208206

207+
if content.GetVolume() != nil {
208+
volumeContentSourceVolumeID = content.GetVolume().GetVolumeId()
209+
// Verify that the source VolumeID is in the correct format.
210+
project, sourceVolKey, err := common.VolumeIDToKey(volumeContentSourceVolumeID)
211+
if err != nil {
212+
return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("CreateVolume source volume id is invalid: %v", err))
213+
}
214+
215+
// Verify that the volume in VolumeContentSource exists.
216+
diskFromSourceVolume, err := gceCS.CloudProvider.GetDisk(ctx, project, sourceVolKey, gceAPIVersion)
217+
if err != nil {
218+
if gce.IsGCEError(err, "notFound") {
219+
return nil, status.Errorf(codes.NotFound, "CreateVolume source volume %s does not exist", volumeContentSourceVolumeID)
220+
} else {
221+
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume unknown get disk error when validating: %v", err))
222+
}
223+
}
224+
// Verify the zone, region, and disk type of the clone must be the same as that of the source disk.
225+
if err := gce.ValidateDiskParameters(diskFromSourceVolume, params); err != nil {
226+
return nil, status.Errorf(codes.InvalidArgument, `CreateVolume source volume parameters do not match CreateVolumeRequest Parameters: %v`, err)
227+
}
228+
// Verify the source disk is ready.
229+
ready, err := isDiskReady(diskFromSourceVolume)
230+
if err != nil {
231+
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume disk from source volume %v had error checking ready status: %v", sourceVolKey, err))
232+
}
233+
if !ready {
234+
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume disk from source volume %v is not ready", sourceVolKey))
235+
}
236+
}
237+
}
209238
// Create the disk
210239
var disk *gce.CloudDisk
211240
switch params.ReplicationType {
212241
case replicationTypeNone:
213242
if len(zones) != 1 {
214243
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume failed to get a single zone for creating zonal disk, instead got: %v", zones))
215244
}
216-
disk, err = createSingleZoneDisk(ctx, gceCS.CloudProvider, name, zones, params, capacityRange, capBytes, snapshotID, multiWriter)
245+
disk, err = createSingleZoneDisk(ctx, gceCS.CloudProvider, name, zones, params, capacityRange, capBytes, snapshotID, volumeContentSourceVolumeID, multiWriter)
217246
if err != nil {
218247
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume failed to create single zonal disk %#v: %v", name, err))
219248
}
220249
case replicationTypeRegionalPD:
221250
if len(zones) != 2 {
222251
return nil, status.Errorf(codes.Internal, fmt.Sprintf("CreateVolume failed to get a 2 zones for creating regional disk, instead got: %v", zones))
223252
}
224-
disk, err = createRegionalDisk(ctx, gceCS.CloudProvider, name, zones, params, capacityRange, capBytes, snapshotID, multiWriter)
253+
disk, err = createRegionalDisk(ctx, gceCS.CloudProvider, name, zones, params, capacityRange, capBytes, snapshotID, volumeContentSourceVolumeID, multiWriter)
225254
if err != nil {
226255
return nil, status.Error(codes.Internal, fmt.Sprintf("CreateVolume failed to create regional disk %#v: %v", name, err))
227256
}
@@ -1053,15 +1082,28 @@ func generateCreateVolumeResponse(disk *gce.CloudDisk, zones []string) *csi.Crea
10531082
},
10541083
}
10551084
snapshotID := disk.GetSnapshotId()
1056-
if snapshotID != "" {
1057-
source := &csi.VolumeContentSource{
1058-
Type: &csi.VolumeContentSource_Snapshot{
1059-
Snapshot: &csi.VolumeContentSource_SnapshotSource{
1060-
SnapshotId: snapshotID,
1085+
diskID := disk.GetSourceDiskId()
1086+
if diskID != "" || snapshotID != "" {
1087+
contentSource := &csi.VolumeContentSource{}
1088+
if snapshotID != "" {
1089+
contentSource = &csi.VolumeContentSource{
1090+
Type: &csi.VolumeContentSource_Snapshot{
1091+
Snapshot: &csi.VolumeContentSource_SnapshotSource{
1092+
SnapshotId: snapshotID,
1093+
},
10611094
},
1062-
},
1095+
}
1096+
}
1097+
if diskID != "" {
1098+
contentSource = &csi.VolumeContentSource{
1099+
Type: &csi.VolumeContentSource_Volume{
1100+
Volume: &csi.VolumeContentSource_VolumeSource{
1101+
VolumeId: diskID,
1102+
},
1103+
},
1104+
}
10631105
}
1064-
createResp.Volume.ContentSource = source
1106+
createResp.Volume.ContentSource = contentSource
10651107
}
10661108
return createResp
10671109
}
@@ -1072,7 +1114,7 @@ func cleanSelfLink(selfLink string) string {
10721114
return strings.TrimPrefix(temp, gce.GCEComputeAlphaAPIEndpoint)
10731115
}
10741116

1075-
func createRegionalDisk(ctx context.Context, cloudProvider gce.GCECompute, name string, zones []string, params common.DiskParameters, capacityRange *csi.CapacityRange, capBytes int64, snapshotID string, multiWriter bool) (*gce.CloudDisk, error) {
1117+
func createRegionalDisk(ctx context.Context, cloudProvider gce.GCECompute, name string, zones []string, params common.DiskParameters, capacityRange *csi.CapacityRange, capBytes int64, snapshotID string, volumeContentSourceVolumeID string, multiWriter bool) (*gce.CloudDisk, error) {
10761118
project := cloudProvider.GetDefaultProject()
10771119
region, err := common.GetRegionFromZones(zones)
10781120
if err != nil {
@@ -1085,7 +1127,7 @@ func createRegionalDisk(ctx context.Context, cloudProvider gce.GCECompute, name
10851127
fullyQualifiedReplicaZones, cloudProvider.GetReplicaZoneURI(project, replicaZone))
10861128
}
10871129

1088-
err = cloudProvider.InsertDisk(ctx, project, meta.RegionalKey(name, region), params, capBytes, capacityRange, fullyQualifiedReplicaZones, snapshotID, multiWriter)
1130+
err = cloudProvider.InsertDisk(ctx, project, meta.RegionalKey(name, region), params, capBytes, capacityRange, fullyQualifiedReplicaZones, snapshotID, volumeContentSourceVolumeID, multiWriter)
10891131
if err != nil {
10901132
return nil, fmt.Errorf("failed to insert regional disk: %v", err)
10911133
}
@@ -1102,13 +1144,13 @@ func createRegionalDisk(ctx context.Context, cloudProvider gce.GCECompute, name
11021144
return disk, nil
11031145
}
11041146

1105-
func createSingleZoneDisk(ctx context.Context, cloudProvider gce.GCECompute, name string, zones []string, params common.DiskParameters, capacityRange *csi.CapacityRange, capBytes int64, snapshotID string, multiWriter bool) (*gce.CloudDisk, error) {
1147+
func createSingleZoneDisk(ctx context.Context, cloudProvider gce.GCECompute, name string, zones []string, params common.DiskParameters, capacityRange *csi.CapacityRange, capBytes int64, snapshotID string, volumeContentSourceVolumeID string, multiWriter bool) (*gce.CloudDisk, error) {
11061148
project := cloudProvider.GetDefaultProject()
11071149
if len(zones) != 1 {
11081150
return nil, fmt.Errorf("got wrong number of zones for zonal create volume: %v", len(zones))
11091151
}
11101152
diskZone := zones[0]
1111-
err := cloudProvider.InsertDisk(ctx, project, meta.ZonalKey(name, diskZone), params, capBytes, capacityRange, nil, snapshotID, multiWriter)
1153+
err := cloudProvider.InsertDisk(ctx, project, meta.ZonalKey(name, diskZone), params, capBytes, capacityRange, nil, snapshotID, volumeContentSourceVolumeID, multiWriter)
11121154
if err != nil {
11131155
return nil, fmt.Errorf("failed to insert zonal disk: %v", err)
11141156
}

0 commit comments

Comments
 (0)