@@ -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"
@@ -130,6 +130,17 @@ const (
130
130
// but 500 is a good proxy (gives ~8KB of data per ListVolumesResponse#Entry)
131
131
// See https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h#L503)
132
132
maxListVolumesResponseEntries = 500
133
+
134
+ // Keys in the volume context.
135
+ contextForceAttach = "force-attach"
136
+
137
+ resourceApiScheme = "https"
138
+ resourceApiService = "compute"
139
+ resourceProject = "projects"
140
+ )
141
+
142
+ var (
143
+ validResourceApiVersions = map [string ]bool {"v1" : true , "alpha" : true , "beta" : true }
133
144
)
134
145
135
146
func isDiskReady (disk * gce.CloudDisk ) (bool , error ) {
@@ -306,7 +317,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
306
317
307
318
// If there is no validation error, immediately return success
308
319
klog .V (4 ).Infof ("CreateVolume succeeded for disk %v, it already exists and was compatible" , volKey )
309
- return generateCreateVolumeResponse (existingDisk , zones ), nil
320
+ return generateCreateVolumeResponse (existingDisk , zones )
310
321
}
311
322
312
323
snapshotID := ""
@@ -421,7 +432,7 @@ func (gceCS *GCEControllerServer) CreateVolume(ctx context.Context, req *csi.Cre
421
432
}
422
433
423
434
klog .V (4 ).Infof ("CreateVolume succeeded for disk %v" , volKey )
424
- return generateCreateVolumeResponse (disk , zones ), nil
435
+ return generateCreateVolumeResponse (disk , zones )
425
436
426
437
}
427
438
@@ -856,13 +867,23 @@ func (gceCS *GCEControllerServer) ListVolumes(ctx context.Context, req *csi.List
856
867
entries := []* csi.ListVolumesResponse_Entry {}
857
868
for i := 0 ; i + offset < len (gceCS .disks ) && i < maxEntries ; i ++ {
858
869
d := gceCS .disks [i + offset ]
870
+ diskRsrc , err := getResourceId (d .SelfLink )
871
+ if err != nil {
872
+ klog .Warningf ("Bad ListVolumes disk resource %s, skipped: %v (%+v)" , d .SelfLink , err , d )
873
+ continue
874
+ }
859
875
users := []string {}
860
876
for _ , u := range d .Users {
861
- users = append (users , cleanSelfLink (u ))
877
+ rsrc , err := getResourceId (u )
878
+ if err != nil {
879
+ klog .Warningf ("Bad ListVolumes user %s, skipped: %v" , u , err )
880
+ } else {
881
+ users = append (users , rsrc )
882
+ }
862
883
}
863
884
entries = append (entries , & csi.ListVolumesResponse_Entry {
864
885
Volume : & csi.Volume {
865
- VolumeId : cleanSelfLink ( d . SelfLink ) ,
886
+ VolumeId : diskRsrc ,
866
887
},
867
888
Status : & csi.ListVolumesResponse_VolumeStatus {
868
889
PublishedNodeIds : users ,
@@ -975,6 +996,10 @@ func (gceCS *GCEControllerServer) createPDSnapshot(ctx context.Context, project
975
996
return nil , common .LoggedError ("Failed to create snapshot: " , err )
976
997
}
977
998
}
999
+ snapshotId , err := getResourceId (snapshot .SelfLink )
1000
+ if err != nil {
1001
+ return nil , common .LoggedError (fmt .Sprintf ("Cannot extract resource id from snapshot %s" , snapshot .SelfLink ), err )
1002
+ }
978
1003
979
1004
err = gceCS .validateExistingSnapshot (snapshot , volKey )
980
1005
if err != nil {
@@ -993,7 +1018,7 @@ func (gceCS *GCEControllerServer) createPDSnapshot(ctx context.Context, project
993
1018
994
1019
return & csi.Snapshot {
995
1020
SizeBytes : common .GbToBytes (snapshot .DiskSizeGb ),
996
- SnapshotId : cleanSelfLink ( snapshot . SelfLink ) ,
1021
+ SnapshotId : snapshotId ,
997
1022
SourceVolumeId : volumeID ,
998
1023
CreationTime : timestamp ,
999
1024
ReadyToUse : ready ,
@@ -1022,6 +1047,10 @@ func (gceCS *GCEControllerServer) createImage(ctx context.Context, project strin
1022
1047
return nil , common .LoggedError ("Failed to create image: " , err )
1023
1048
}
1024
1049
}
1050
+ imageId , err := getResourceId (image .SelfLink )
1051
+ if err != nil {
1052
+ return nil , common .LoggedError (fmt .Sprintf ("Cannot extract resource id from snapshot %s" , image .SelfLink ), err )
1053
+ }
1025
1054
1026
1055
err = gceCS .validateExistingImage (image , volKey )
1027
1056
if err != nil {
@@ -1040,7 +1069,7 @@ func (gceCS *GCEControllerServer) createImage(ctx context.Context, project strin
1040
1069
1041
1070
return & csi.Snapshot {
1042
1071
SizeBytes : common .GbToBytes (image .DiskSizeGb ),
1043
- SnapshotId : cleanSelfLink ( image . SelfLink ) ,
1072
+ SnapshotId : imageId ,
1044
1073
SourceVolumeId : volumeID ,
1045
1074
CreationTime : timestamp ,
1046
1075
ReadyToUse : ready ,
@@ -1052,9 +1081,13 @@ func (gceCS *GCEControllerServer) validateExistingImage(image *compute.Image, vo
1052
1081
return fmt .Errorf ("disk does not exist" )
1053
1082
}
1054
1083
1055
- _ , sourceKey , err := common . VolumeIDToKey ( cleanSelfLink ( image .SourceDisk ) )
1084
+ sourceId , err := getResourceId ( image .SourceDisk )
1056
1085
if err != nil {
1057
- return fmt .Errorf ("fail to get source disk key %s, %w" , image .SourceDisk , err )
1086
+ return fmt .Errorf ("failed to get source id from %s: %w" , image .SourceDisk , err )
1087
+ }
1088
+ _ , sourceKey , err := common .VolumeIDToKey (sourceId )
1089
+ if err != nil {
1090
+ return fmt .Errorf ("failed to get source disk key %s: %w" , image .SourceDisk , err )
1058
1091
}
1059
1092
1060
1093
if sourceKey .String () != volKey .String () {
@@ -1103,7 +1136,11 @@ func (gceCS *GCEControllerServer) validateExistingSnapshot(snapshot *compute.Sna
1103
1136
return fmt .Errorf ("disk does not exist" )
1104
1137
}
1105
1138
1106
- _ , sourceKey , err := common .VolumeIDToKey (cleanSelfLink (snapshot .SourceDisk ))
1139
+ sourceId , err := getResourceId (snapshot .SourceDisk )
1140
+ if err != nil {
1141
+ return fmt .Errorf ("failed to get source id from %s: %w" , snapshot .SourceDisk , err )
1142
+ }
1143
+ _ , sourceKey , err := common .VolumeIDToKey (sourceId )
1107
1144
if err != nil {
1108
1145
return fmt .Errorf ("fail to get source disk key %s, %w" , snapshot .SourceDisk , err )
1109
1146
}
@@ -1146,7 +1183,7 @@ func (gceCS *GCEControllerServer) DeleteSnapshot(ctx context.Context, req *csi.D
1146
1183
if err != nil {
1147
1184
// Cannot get snapshot ID from the passing request
1148
1185
// This is a success according to the spec
1149
- klog .Warningf ("Snapshot id does not have the correct format %s" , snapshotID )
1186
+ klog .Warningf ("Snapshot id does not have the correct format %s: %v " , snapshotID , err )
1150
1187
return & csi.DeleteSnapshotResponse {}, nil
1151
1188
}
1152
1189
@@ -1337,7 +1374,7 @@ func (gceCS *GCEControllerServer) getSnapshotByID(ctx context.Context, snapshotI
1337
1374
return & csi.ListSnapshotsResponse {}, nil
1338
1375
}
1339
1376
}
1340
- e , err := generateImageEntry (image )
1377
+ e , err := generateDiskImageEntry (image )
1341
1378
if err != nil {
1342
1379
return nil , fmt .Errorf ("failed to generate image entry: %w" , err )
1343
1380
}
@@ -1359,6 +1396,15 @@ func generateDiskSnapshotEntry(snapshot *compute.Snapshot) (*csi.ListSnapshotsRe
1359
1396
return nil , fmt .Errorf ("Failed to covert creation timestamp: %w" , err )
1360
1397
}
1361
1398
1399
+ snapshotId , err := getResourceId (snapshot .SelfLink )
1400
+ if err != nil {
1401
+ return nil , fmt .Errorf ("failed to get snapshot id from %s: %w" , snapshot .SelfLink , err )
1402
+ }
1403
+ sourceId , err := getResourceId (snapshot .SourceDisk )
1404
+ if err != nil {
1405
+ return nil , fmt .Errorf ("failed to get source id from %s: %w" , snapshot .SourceDisk , err )
1406
+ }
1407
+
1362
1408
// We ignore the error intentionally here since we are just listing snapshots
1363
1409
// TODO: If the snapshot is in "FAILED" state we need to think through what this
1364
1410
// should actually look like.
@@ -1367,8 +1413,8 @@ func generateDiskSnapshotEntry(snapshot *compute.Snapshot) (*csi.ListSnapshotsRe
1367
1413
entry := & csi.ListSnapshotsResponse_Entry {
1368
1414
Snapshot : & csi.Snapshot {
1369
1415
SizeBytes : common .GbToBytes (snapshot .DiskSizeGb ),
1370
- SnapshotId : cleanSelfLink ( snapshot . SelfLink ) ,
1371
- SourceVolumeId : cleanSelfLink ( snapshot . SourceDisk ) ,
1416
+ SnapshotId : snapshotId ,
1417
+ SourceVolumeId : sourceId ,
1372
1418
CreationTime : tp ,
1373
1419
ReadyToUse : ready ,
1374
1420
},
@@ -1384,35 +1430,23 @@ func generateDiskImageEntry(image *compute.Image) (*csi.ListSnapshotsResponse_En
1384
1430
return nil , fmt .Errorf ("failed to covert creation timestamp: %w" , err )
1385
1431
}
1386
1432
1387
- ready , _ := isImageReady (image .Status )
1388
-
1389
- entry := & csi.ListSnapshotsResponse_Entry {
1390
- Snapshot : & csi.Snapshot {
1391
- SizeBytes : common .GbToBytes (image .DiskSizeGb ),
1392
- SnapshotId : cleanSelfLink (image .SelfLink ),
1393
- SourceVolumeId : cleanSelfLink (image .SourceDisk ),
1394
- CreationTime : tp ,
1395
- ReadyToUse : ready ,
1396
- },
1433
+ imageId , err := getResourceId (image .SelfLink )
1434
+ if err != nil {
1435
+ return nil , fmt .Errorf ("cannot get image id from %s: %w" , image .SelfLink , err )
1397
1436
}
1398
- return entry , nil
1399
- }
1400
-
1401
- func generateImageEntry (image * compute.Image ) (* csi.ListSnapshotsResponse_Entry , error ) {
1402
- timestamp , err := parseTimestamp (image .CreationTimestamp )
1437
+ sourceId , err := getResourceId (image .SourceDisk )
1403
1438
if err != nil {
1404
- return nil , fmt .Errorf ("Failed to covert creation timestamp : %w" , err )
1439
+ return nil , fmt .Errorf ("cannot get source id from %s : %w" , image . SourceDisk , err )
1405
1440
}
1406
1441
1407
- // ignore the error intentionally here since we are just listing images
1408
1442
ready , _ := isImageReady (image .Status )
1409
1443
1410
1444
entry := & csi.ListSnapshotsResponse_Entry {
1411
1445
Snapshot : & csi.Snapshot {
1412
1446
SizeBytes : common .GbToBytes (image .DiskSizeGb ),
1413
- SnapshotId : cleanSelfLink ( image . SelfLink ) ,
1414
- SourceVolumeId : cleanSelfLink ( image . SourceDisk ) ,
1415
- CreationTime : timestamp ,
1447
+ SnapshotId : imageId ,
1448
+ SourceVolumeId : sourceId ,
1449
+ CreationTime : tp ,
1416
1450
ReadyToUse : ready ,
1417
1451
},
1418
1452
}
@@ -1650,7 +1684,12 @@ func getDefaultZonesInRegion(ctx context.Context, gceCS *GCEControllerServer, ex
1650
1684
return ret , nil
1651
1685
}
1652
1686
1653
- func generateCreateVolumeResponse (disk * gce.CloudDisk , zones []string ) * csi.CreateVolumeResponse {
1687
+ func generateCreateVolumeResponse (disk * gce.CloudDisk , zones []string ) (* csi.CreateVolumeResponse , error ) {
1688
+ volumeId , err := getResourceId (disk .GetSelfLink ())
1689
+ if err != nil {
1690
+ return nil , fmt .Errorf ("cannot get volume id from %s: %w" , disk .GetSelfLink (), err )
1691
+ }
1692
+
1654
1693
tops := []* csi.Topology {}
1655
1694
for _ , zone := range zones {
1656
1695
tops = append (tops , & csi.Topology {
@@ -1661,7 +1700,7 @@ func generateCreateVolumeResponse(disk *gce.CloudDisk, zones []string) *csi.Crea
1661
1700
createResp := & csi.CreateVolumeResponse {
1662
1701
Volume : & csi.Volume {
1663
1702
CapacityBytes : realDiskSizeBytes ,
1664
- VolumeId : cleanSelfLink ( disk . GetSelfLink ()) ,
1703
+ VolumeId : volumeId ,
1665
1704
VolumeContext : nil ,
1666
1705
AccessibleTopology : tops ,
1667
1706
},
@@ -1700,12 +1739,36 @@ func generateCreateVolumeResponse(disk *gce.CloudDisk, zones []string) *csi.Crea
1700
1739
}
1701
1740
createResp .Volume .ContentSource = contentSource
1702
1741
}
1703
- return createResp
1742
+ return createResp , nil
1704
1743
}
1705
1744
1706
- func cleanSelfLink (selfLink string ) string {
1707
- r , _ := regexp .Compile ("https:\\ /\\ /www.*apis.com\\ /.*(v1|beta|alpha)\\ /" )
1708
- return r .ReplaceAllString (selfLink , "" )
1745
+ func getResourceId (resourceLink string ) (string , error ) {
1746
+ url , err := neturl .Parse (resourceLink )
1747
+ if err != nil {
1748
+ return "" , fmt .Errorf ("Could not parse resource %s: %w" , resourceLink , err )
1749
+ }
1750
+ if url .Scheme != resourceApiScheme {
1751
+ return "" , fmt .Errorf ("Unexpected API scheme for resource %s" , resourceLink )
1752
+ }
1753
+
1754
+ // Note that the resource host can basically be anything, if we are running in
1755
+ // a distributed cloud or trusted partner environment.
1756
+
1757
+ // The path should be /compute/VERSION/project/....
1758
+ elts := strings .Split (url .Path , "/" )
1759
+ if len (elts ) < 4 {
1760
+ return "" , fmt .Errorf ("Short resource path %s" , resourceLink )
1761
+ }
1762
+ if elts [1 ] != resourceApiService {
1763
+ return "" , fmt .Errorf ("Bad resource service %s in %s" , elts [1 ], resourceLink )
1764
+ }
1765
+ if _ , ok := validResourceApiVersions [elts [2 ]]; ! ok {
1766
+ return "" , fmt .Errorf ("Bad version %s in %s" , elts [2 ], resourceLink )
1767
+ }
1768
+ if elts [3 ] != resourceProject {
1769
+ return "" , fmt .Errorf ("Expected %v to start with %s in resource %s" , elts [3 :], resourceProject , resourceLink )
1770
+ }
1771
+ return strings .Join (elts [3 :], "/" ), nil
1709
1772
}
1710
1773
1711
1774
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