@@ -19,7 +19,7 @@ import (
19
19
"errors"
20
20
"fmt"
21
21
"math/rand"
22
- "regexp "
22
+ neturl "net/url "
23
23
"sort"
24
24
"strings"
25
25
"time"
@@ -138,6 +138,14 @@ const (
138
138
139
139
// Keys in the volume context.
140
140
contextForceAttach = "force-attach"
141
+
142
+ resourceApiScheme = "https"
143
+ resourceApiService = "compute"
144
+ resourceProject = "projects"
145
+ )
146
+
147
+ var (
148
+ validResourceApiVersions = map [string ]bool {"v1" : true , "alpha" : true , "beta" : true }
141
149
)
142
150
143
151
func isDiskReady (disk * gce.CloudDisk ) (bool , error ) {
@@ -319,7 +327,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
319
327
320
328
// If there is no validation error, immediately return success
321
329
klog .V (4 ).Infof ("CreateVolume succeeded for disk %v, it already exists and was compatible" , volKey )
322
- return generateCreateVolumeResponse (existingDisk , zones , params ), nil
330
+ return generateCreateVolumeResponse (existingDisk , zones , params )
323
331
}
324
332
325
333
snapshotID := ""
@@ -434,7 +442,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
434
442
}
435
443
436
444
klog .V (4 ).Infof ("CreateVolume succeeded for disk %v" , volKey )
437
- return generateCreateVolumeResponse (disk , zones , params ), nil
445
+ return generateCreateVolumeResponse (disk , zones , params )
438
446
439
447
}
440
448
@@ -869,13 +877,23 @@ func (gceCS *GCEControllerServer) ListVolumes(ctx context.Context, req *csi.List
869
877
entries := []* csi.ListVolumesResponse_Entry {}
870
878
for i := 0 ; i + offset < len (gceCS .disks ) && i < maxEntries ; i ++ {
871
879
d := gceCS .disks [i + offset ]
880
+ diskRsrc , err := getResourceId (d .SelfLink )
881
+ if err != nil {
882
+ klog .Warningf ("Bad ListVolumes disk resource %s, skipped: %v (%+v)" , d .SelfLink , err , d )
883
+ continue
884
+ }
872
885
users := []string {}
873
886
for _ , u := range d .Users {
874
- users = append (users , cleanSelfLink (u ))
887
+ rsrc , err := getResourceId (u )
888
+ if err != nil {
889
+ klog .Warningf ("Bad ListVolumes user %s, skipped: %v" , u , err )
890
+ } else {
891
+ users = append (users , rsrc )
892
+ }
875
893
}
876
894
entries = append (entries , & csi.ListVolumesResponse_Entry {
877
895
Volume : & csi.Volume {
878
- VolumeId : cleanSelfLink ( d . SelfLink ) ,
896
+ VolumeId : diskRsrc ,
879
897
},
880
898
Status : & csi.ListVolumesResponse_VolumeStatus {
881
899
PublishedNodeIds : users ,
@@ -988,6 +1006,10 @@ func (gceCS *GCEControllerServer) createPDSnapshot(ctx context.Context, project
988
1006
return nil , common .LoggedError ("Failed to create snapshot: " , err )
989
1007
}
990
1008
}
1009
+ snapshotId , err := getResourceId (snapshot .SelfLink )
1010
+ if err != nil {
1011
+ return nil , common .LoggedError (fmt .Sprintf ("Cannot extract resource id from snapshot %s" , snapshot .SelfLink ), err )
1012
+ }
991
1013
992
1014
err = gceCS .validateExistingSnapshot (snapshot , volKey )
993
1015
if err != nil {
@@ -1006,7 +1028,7 @@ func (gceCS *GCEControllerServer) createPDSnapshot(ctx context.Context, project
1006
1028
1007
1029
return & csi.Snapshot {
1008
1030
SizeBytes : common .GbToBytes (snapshot .DiskSizeGb ),
1009
- SnapshotId : cleanSelfLink ( snapshot . SelfLink ) ,
1031
+ SnapshotId : snapshotId ,
1010
1032
SourceVolumeId : volumeID ,
1011
1033
CreationTime : timestamp ,
1012
1034
ReadyToUse : ready ,
@@ -1035,6 +1057,10 @@ func (gceCS *GCEControllerServer) createImage(ctx context.Context, project strin
1035
1057
return nil , common .LoggedError ("Failed to create image: " , err )
1036
1058
}
1037
1059
}
1060
+ imageId , err := getResourceId (image .SelfLink )
1061
+ if err != nil {
1062
+ return nil , common .LoggedError (fmt .Sprintf ("Cannot extract resource id from snapshot %s" , image .SelfLink ), err )
1063
+ }
1038
1064
1039
1065
err = gceCS .validateExistingImage (image , volKey )
1040
1066
if err != nil {
@@ -1053,7 +1079,7 @@ func (gceCS *GCEControllerServer) createImage(ctx context.Context, project strin
1053
1079
1054
1080
return & csi.Snapshot {
1055
1081
SizeBytes : common .GbToBytes (image .DiskSizeGb ),
1056
- SnapshotId : cleanSelfLink ( image . SelfLink ) ,
1082
+ SnapshotId : imageId ,
1057
1083
SourceVolumeId : volumeID ,
1058
1084
CreationTime : timestamp ,
1059
1085
ReadyToUse : ready ,
@@ -1065,9 +1091,13 @@ func (gceCS *GCEControllerServer) validateExistingImage(image *compute.Image, vo
1065
1091
return fmt .Errorf ("disk does not exist" )
1066
1092
}
1067
1093
1068
- _ , sourceKey , err := common .VolumeIDToKey (cleanSelfLink (image .SourceDisk ))
1094
+ sourceId , err := getResourceId (image .SourceDisk )
1095
+ if err != nil {
1096
+ return fmt .Errorf ("failed to get source id from %s: %w" , image .SourceDisk , err )
1097
+ }
1098
+ _ , sourceKey , err := common .VolumeIDToKey (sourceId )
1069
1099
if err != nil {
1070
- return fmt .Errorf ("fail to get source disk key %s, %w" , image .SourceDisk , err )
1100
+ return fmt .Errorf ("failed to get source disk key %s: %w" , image .SourceDisk , err )
1071
1101
}
1072
1102
1073
1103
if sourceKey .String () != volKey .String () {
@@ -1116,7 +1146,11 @@ func (gceCS *GCEControllerServer) validateExistingSnapshot(snapshot *compute.Sna
1116
1146
return fmt .Errorf ("disk does not exist" )
1117
1147
}
1118
1148
1119
- _ , sourceKey , err := common .VolumeIDToKey (cleanSelfLink (snapshot .SourceDisk ))
1149
+ sourceId , err := getResourceId (snapshot .SourceDisk )
1150
+ if err != nil {
1151
+ return fmt .Errorf ("failed to get source id from %s: %w" , snapshot .SourceDisk , err )
1152
+ }
1153
+ _ , sourceKey , err := common .VolumeIDToKey (sourceId )
1120
1154
if err != nil {
1121
1155
return fmt .Errorf ("fail to get source disk key %s, %w" , snapshot .SourceDisk , err )
1122
1156
}
@@ -1159,7 +1193,7 @@ func (gceCS *GCEControllerServer) DeleteSnapshot(ctx context.Context, req *csi.D
1159
1193
if err != nil {
1160
1194
// Cannot get snapshot ID from the passing request
1161
1195
// This is a success according to the spec
1162
- klog .Warningf ("Snapshot id does not have the correct format %s" , snapshotID )
1196
+ klog .Warningf ("Snapshot id does not have the correct format %s: %v " , snapshotID , err )
1163
1197
return & csi.DeleteSnapshotResponse {}, nil
1164
1198
}
1165
1199
@@ -1350,7 +1384,7 @@ func (gceCS *GCEControllerServer) getSnapshotByID(ctx context.Context, snapshotI
1350
1384
return & csi.ListSnapshotsResponse {}, nil
1351
1385
}
1352
1386
}
1353
- e , err := generateImageEntry (image )
1387
+ e , err := generateDiskImageEntry (image )
1354
1388
if err != nil {
1355
1389
return nil , fmt .Errorf ("failed to generate image entry: %w" , err )
1356
1390
}
@@ -1372,6 +1406,15 @@ func generateDiskSnapshotEntry(snapshot *compute.Snapshot) (*csi.ListSnapshotsRe
1372
1406
return nil , fmt .Errorf ("Failed to covert creation timestamp: %w" , err )
1373
1407
}
1374
1408
1409
+ snapshotId , err := getResourceId (snapshot .SelfLink )
1410
+ if err != nil {
1411
+ return nil , fmt .Errorf ("failed to get snapshot id from %s: %w" , snapshot .SelfLink , err )
1412
+ }
1413
+ sourceId , err := getResourceId (snapshot .SourceDisk )
1414
+ if err != nil {
1415
+ return nil , fmt .Errorf ("failed to get source id from %s: %w" , snapshot .SourceDisk , err )
1416
+ }
1417
+
1375
1418
// We ignore the error intentionally here since we are just listing snapshots
1376
1419
// TODO: If the snapshot is in "FAILED" state we need to think through what this
1377
1420
// should actually look like.
@@ -1380,8 +1423,8 @@ func generateDiskSnapshotEntry(snapshot *compute.Snapshot) (*csi.ListSnapshotsRe
1380
1423
entry := & csi.ListSnapshotsResponse_Entry {
1381
1424
Snapshot : & csi.Snapshot {
1382
1425
SizeBytes : common .GbToBytes (snapshot .DiskSizeGb ),
1383
- SnapshotId : cleanSelfLink ( snapshot . SelfLink ) ,
1384
- SourceVolumeId : cleanSelfLink ( snapshot . SourceDisk ) ,
1426
+ SnapshotId : snapshotId ,
1427
+ SourceVolumeId : sourceId ,
1385
1428
CreationTime : tp ,
1386
1429
ReadyToUse : ready ,
1387
1430
},
@@ -1397,35 +1440,23 @@ func generateDiskImageEntry(image *compute.Image) (*csi.ListSnapshotsResponse_En
1397
1440
return nil , fmt .Errorf ("failed to covert creation timestamp: %w" , err )
1398
1441
}
1399
1442
1400
- ready , _ := isImageReady (image .Status )
1401
-
1402
- entry := & csi.ListSnapshotsResponse_Entry {
1403
- Snapshot : & csi.Snapshot {
1404
- SizeBytes : common .GbToBytes (image .DiskSizeGb ),
1405
- SnapshotId : cleanSelfLink (image .SelfLink ),
1406
- SourceVolumeId : cleanSelfLink (image .SourceDisk ),
1407
- CreationTime : tp ,
1408
- ReadyToUse : ready ,
1409
- },
1443
+ imageId , err := getResourceId (image .SelfLink )
1444
+ if err != nil {
1445
+ return nil , fmt .Errorf ("cannot get image id from %s: %w" , image .SelfLink , err )
1410
1446
}
1411
- return entry , nil
1412
- }
1413
-
1414
- func generateImageEntry (image * compute.Image ) (* csi.ListSnapshotsResponse_Entry , error ) {
1415
- timestamp , err := parseTimestamp (image .CreationTimestamp )
1447
+ sourceId , err := getResourceId (image .SourceDisk )
1416
1448
if err != nil {
1417
- return nil , fmt .Errorf ("Failed to covert creation timestamp : %w" , err )
1449
+ return nil , fmt .Errorf ("cannot get source id from %s : %w" , image . SourceDisk , err )
1418
1450
}
1419
1451
1420
- // ignore the error intentionally here since we are just listing images
1421
1452
ready , _ := isImageReady (image .Status )
1422
1453
1423
1454
entry := & csi.ListSnapshotsResponse_Entry {
1424
1455
Snapshot : & csi.Snapshot {
1425
1456
SizeBytes : common .GbToBytes (image .DiskSizeGb ),
1426
- SnapshotId : cleanSelfLink ( image . SelfLink ) ,
1427
- SourceVolumeId : cleanSelfLink ( image . SourceDisk ) ,
1428
- CreationTime : timestamp ,
1457
+ SnapshotId : imageId ,
1458
+ SourceVolumeId : sourceId ,
1459
+ CreationTime : tp ,
1429
1460
ReadyToUse : ready ,
1430
1461
},
1431
1462
}
@@ -1691,7 +1722,12 @@ func extractVolumeContext(context map[string]string) (*PDCSIContext, error) {
1691
1722
return info , nil
1692
1723
}
1693
1724
1694
- func generateCreateVolumeResponse (disk * gce.CloudDisk , zones []string , params common.DiskParameters ) * csi.CreateVolumeResponse {
1725
+ func generateCreateVolumeResponse (disk * gce.CloudDisk , zones []string , params common.DiskParameters ) (* csi.CreateVolumeResponse , error ) {
1726
+ volumeId , err := getResourceId (disk .GetSelfLink ())
1727
+ if err != nil {
1728
+ return nil , fmt .Errorf ("cannot get volume id from %s: %w" , disk .GetSelfLink (), err )
1729
+ }
1730
+
1695
1731
tops := []* csi.Topology {}
1696
1732
for _ , zone := range zones {
1697
1733
tops = append (tops , & csi.Topology {
@@ -1702,7 +1738,7 @@ func generateCreateVolumeResponse(disk *gce.CloudDisk, zones []string, params co
1702
1738
createResp := & csi.CreateVolumeResponse {
1703
1739
Volume : & csi.Volume {
1704
1740
CapacityBytes : realDiskSizeBytes ,
1705
- VolumeId : cleanSelfLink ( disk . GetSelfLink ()) ,
1741
+ VolumeId : volumeId ,
1706
1742
VolumeContext : paramsToVolumeContext (params ),
1707
1743
AccessibleTopology : tops ,
1708
1744
},
@@ -1741,12 +1777,36 @@ func generateCreateVolumeResponse(disk *gce.CloudDisk, zones []string, params co
1741
1777
}
1742
1778
createResp .Volume .ContentSource = contentSource
1743
1779
}
1744
- return createResp
1780
+ return createResp , nil
1745
1781
}
1746
1782
1747
- func cleanSelfLink (selfLink string ) string {
1748
- r , _ := regexp .Compile ("https:\\ /\\ /www.*apis.com\\ /.*(v1|beta|alpha)\\ /" )
1749
- return r .ReplaceAllString (selfLink , "" )
1783
+ func getResourceId (resourceLink string ) (string , error ) {
1784
+ url , err := neturl .Parse (resourceLink )
1785
+ if err != nil {
1786
+ return "" , fmt .Errorf ("Could not parse resource %s: %w" , resourceLink , err )
1787
+ }
1788
+ if url .Scheme != resourceApiScheme {
1789
+ return "" , fmt .Errorf ("Unexpected API scheme for resource %s" , resourceLink )
1790
+ }
1791
+
1792
+ // Note that the resource host can basically be anything, if we are running in
1793
+ // a distributed cloud or trusted partner environment.
1794
+
1795
+ // The path should be /compute/VERSION/project/....
1796
+ elts := strings .Split (url .Path , "/" )
1797
+ if len (elts ) < 4 {
1798
+ return "" , fmt .Errorf ("Short resource path %s" , resourceLink )
1799
+ }
1800
+ if elts [1 ] != resourceApiService {
1801
+ return "" , fmt .Errorf ("Bad resource service %s in %s" , elts [1 ], resourceLink )
1802
+ }
1803
+ if _ , ok := validResourceApiVersions [elts [2 ]]; ! ok {
1804
+ return "" , fmt .Errorf ("Bad version %s in %s" , elts [2 ], resourceLink )
1805
+ }
1806
+ if elts [3 ] != resourceProject {
1807
+ return "" , fmt .Errorf ("Expected %v to start with %s in resource %s" , elts [3 :], resourceProject , resourceLink )
1808
+ }
1809
+ return strings .Join (elts [3 :], "/" ), nil
1750
1810
}
1751
1811
1752
1812
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 ) {
0 commit comments