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