@@ -179,9 +179,8 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
179
179
if err != nil {
180
180
return nil , status .Error (codes .Internal , fmt .Sprintf ("CreateVolume disk %v had error checking ready status: %v" , volKey , err ))
181
181
}
182
-
183
182
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 ))
185
184
}
186
185
187
186
// If there is no validation error, immediately return success
@@ -190,10 +189,10 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
190
189
}
191
190
192
191
snapshotID := ""
192
+ volumeContentSourceVolumeID := ""
193
193
content := req .GetVolumeContentSource ()
194
194
if content != nil {
195
195
if content .GetSnapshot () != nil {
196
- // TODO(#161): Add support for Volume Source (cloning) introduced in CSI v1.0.0
197
196
snapshotID = content .GetSnapshot ().GetSnapshotId ()
198
197
199
198
// Verify that snapshot exists
@@ -204,24 +203,54 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
204
203
return nil , status .Errorf (codes .NotFound , "CreateVolume source snapshot %s does not exist" , snapshotID )
205
204
}
206
205
}
207
- }
208
206
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
+ }
209
238
// Create the disk
210
239
var disk * gce.CloudDisk
211
240
switch params .ReplicationType {
212
241
case replicationTypeNone :
213
242
if len (zones ) != 1 {
214
243
return nil , status .Error (codes .Internal , fmt .Sprintf ("CreateVolume failed to get a single zone for creating zonal disk, instead got: %v" , zones ))
215
244
}
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 )
217
246
if err != nil {
218
247
return nil , status .Error (codes .Internal , fmt .Sprintf ("CreateVolume failed to create single zonal disk %#v: %v" , name , err ))
219
248
}
220
249
case replicationTypeRegionalPD :
221
250
if len (zones ) != 2 {
222
251
return nil , status .Errorf (codes .Internal , fmt .Sprintf ("CreateVolume failed to get a 2 zones for creating regional disk, instead got: %v" , zones ))
223
252
}
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 )
225
254
if err != nil {
226
255
return nil , status .Error (codes .Internal , fmt .Sprintf ("CreateVolume failed to create regional disk %#v: %v" , name , err ))
227
256
}
@@ -1053,15 +1082,28 @@ func generateCreateVolumeResponse(disk *gce.CloudDisk, zones []string) *csi.Crea
1053
1082
},
1054
1083
}
1055
1084
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
+ },
1061
1094
},
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
+ }
1063
1105
}
1064
- createResp .Volume .ContentSource = source
1106
+ createResp .Volume .ContentSource = contentSource
1065
1107
}
1066
1108
return createResp
1067
1109
}
@@ -1072,7 +1114,7 @@ func cleanSelfLink(selfLink string) string {
1072
1114
return strings .TrimPrefix (temp , gce .GCEComputeAlphaAPIEndpoint )
1073
1115
}
1074
1116
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 ) {
1076
1118
project := cloudProvider .GetDefaultProject ()
1077
1119
region , err := common .GetRegionFromZones (zones )
1078
1120
if err != nil {
@@ -1085,7 +1127,7 @@ func createRegionalDisk(ctx context.Context, cloudProvider gce.GCECompute, name
1085
1127
fullyQualifiedReplicaZones , cloudProvider .GetReplicaZoneURI (project , replicaZone ))
1086
1128
}
1087
1129
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 )
1089
1131
if err != nil {
1090
1132
return nil , fmt .Errorf ("failed to insert regional disk: %v" , err )
1091
1133
}
@@ -1102,13 +1144,13 @@ func createRegionalDisk(ctx context.Context, cloudProvider gce.GCECompute, name
1102
1144
return disk , nil
1103
1145
}
1104
1146
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 ) {
1106
1148
project := cloudProvider .GetDefaultProject ()
1107
1149
if len (zones ) != 1 {
1108
1150
return nil , fmt .Errorf ("got wrong number of zones for zonal create volume: %v" , len (zones ))
1109
1151
}
1110
1152
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 )
1112
1154
if err != nil {
1113
1155
return nil , fmt .Errorf ("failed to insert zonal disk: %v" , err )
1114
1156
}
0 commit comments