@@ -28,6 +28,7 @@ import (
28
28
"github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta"
29
29
csi "github.com/container-storage-interface/spec/lib/go/csi"
30
30
compute "google.golang.org/api/compute/v1"
31
+ "google.golang.org/api/googleapi"
31
32
"google.golang.org/grpc/codes"
32
33
"google.golang.org/grpc/status"
33
34
"google.golang.org/protobuf/types/known/timestamppb"
@@ -105,6 +106,8 @@ type GCEControllerServer struct {
105
106
enableStoragePools bool
106
107
107
108
multiZoneVolumeHandleConfig MultiZoneVolumeHandleConfig
109
+
110
+ listVolumesConfig ListVolumesConfig
108
111
}
109
112
110
113
type MultiZoneVolumeHandleConfig struct {
@@ -124,6 +127,21 @@ type MultiZoneVolumeHandleConfig struct {
124
127
Enable bool
125
128
}
126
129
130
+ type ListVolumesConfig struct {
131
+ UseInstancesAPIForPublishedNodes bool
132
+ }
133
+
134
+ func (c ListVolumesConfig ) listDisksFields () []googleapi.Field {
135
+ if c .UseInstancesAPIForPublishedNodes {
136
+ // If we are using the instances.list API in ListVolumes,
137
+ // don't include the users field in the response, as an optimization.
138
+ // We rely on instances.list items.disks for attachment pairings.
139
+ return listDisksFieldsWithoutUsers
140
+ }
141
+
142
+ return listDisksFieldsWithUsers
143
+ }
144
+
127
145
type csiErrorBackoffId string
128
146
129
147
type csiErrorBackoff struct {
@@ -175,10 +193,28 @@ const (
175
193
resourceApiScheme = "https"
176
194
resourceApiService = "compute"
177
195
resourceProject = "projects"
196
+
197
+ listDisksUsersField = googleapi .Field ("items/users" )
178
198
)
179
199
180
200
var (
181
201
validResourceApiVersions = map [string ]bool {"v1" : true , "alpha" : true , "beta" : true , "staging_v1" : true , "staging_beta" : true , "staging_alpha" : true }
202
+
203
+ // By default GCE returns a lot of data for each instance. Request only a subset of the fields.
204
+ listInstancesFields = []googleapi.Field {
205
+ "items/disks/deviceName" ,
206
+ "items/disks/source" ,
207
+ "items/selfLink" ,
208
+ "nextPageToken" ,
209
+ }
210
+
211
+ // By default GCE returns a lot of data for each disk. Request only a subset of the fields.
212
+ listDisksFieldsWithoutUsers = []googleapi.Field {
213
+ "items/labels" ,
214
+ "items/selfLink" ,
215
+ "nextPageToken" ,
216
+ }
217
+ listDisksFieldsWithUsers = append (listDisksFieldsWithoutUsers , "items/users" )
182
218
)
183
219
184
220
func isDiskReady (disk * gce.CloudDisk ) (bool , error ) {
@@ -935,6 +971,22 @@ func generateFailedValidationMessage(format string, a ...interface{}) *csi.Valid
935
971
}
936
972
}
937
973
974
+ func (gceCS * GCEControllerServer ) listVolumeEntries (ctx context.Context ) ([]* csi.ListVolumesResponse_Entry , error ) {
975
+ diskList , _ , err := gceCS .CloudProvider .ListDisks (ctx , gceCS .listVolumesConfig .listDisksFields ())
976
+ if err != nil {
977
+ return nil , err
978
+ }
979
+
980
+ var instanceList []* compute.Instance = nil
981
+ if gceCS .listVolumesConfig .UseInstancesAPIForPublishedNodes {
982
+ instanceList , _ , err = gceCS .CloudProvider .ListInstances (ctx , listInstancesFields )
983
+ if err != nil {
984
+ return nil , err
985
+ }
986
+ }
987
+ return gceCS .disksAndInstancesToVolumeEntries (diskList , instanceList ), nil
988
+ }
989
+
938
990
func (gceCS * GCEControllerServer ) ListVolumes (ctx context.Context , req * csi.ListVolumesRequest ) (* csi.ListVolumesResponse , error ) {
939
991
// https://cloud.google.com/compute/docs/reference/beta/disks/list
940
992
if req .MaxEntries < 0 {
@@ -944,19 +996,15 @@ func (gceCS *GCEControllerServer) ListVolumes(ctx context.Context, req *csi.List
944
996
945
997
offsetLow := 0
946
998
var ok bool
947
- var volumeEntries []* csi.ListVolumesResponse_Entry
948
999
if req .StartingToken == "" {
949
- diskList , _ , err := gceCS .CloudProvider . ListDisks (ctx )
1000
+ volumeEntries , err := gceCS .listVolumeEntries (ctx )
950
1001
if err != nil {
951
1002
if gce .IsGCEInvalidError (err ) {
952
1003
return nil , status .Errorf (codes .Aborted , "ListVolumes error with invalid request: %v" , err .Error ())
953
1004
}
954
- return nil , common .LoggedError ("Failed to list disk : " , err )
1005
+ return nil , common .LoggedError ("Failed to list volumes : " , err )
955
1006
}
956
- volumeEntries = gceCS .disksToVolumeEntries (diskList )
957
- }
958
1007
959
- if req .StartingToken == "" {
960
1008
gceCS .volumeEntries = volumeEntries
961
1009
gceCS .volumeEntriesSeen = map [string ]int {}
962
1010
} else {
@@ -1008,47 +1056,62 @@ func isMultiZoneDisk(diskRsrc string, diskLabels map[string]string) (string, boo
1008
1056
return multiZoneVolumeId , true
1009
1057
}
1010
1058
1011
- // disksToVolumeEntries converts a list of disks to a list of CSI ListVolumeResponse entries
1059
+ // disksAndInstancesToVolumeEntries converts a list of disks and instances to a list
1060
+ // of CSI ListVolumeResponse entries.
1012
1061
// It appends "multi-zone" volumeHandles at the end. These are volumeHandles which
1013
1062
// map to multiple volumeHandles in different zones
1014
- func (gceCS * GCEControllerServer ) disksToVolumeEntries (disks []* compute.Disk ) []* csi.ListVolumesResponse_Entry {
1015
- multiZoneNodesByVolumeId := map [string ][]string {}
1016
- entries := [] * csi. ListVolumesResponse_Entry {}
1063
+ func (gceCS * GCEControllerServer ) disksAndInstancesToVolumeEntries (disks []* compute.Disk , instances [] * compute. Instance ) []* csi.ListVolumesResponse_Entry {
1064
+ nodesByVolumeId := map [string ][]string {}
1065
+ multiZoneVolumeIdsByVolumeId := map [ string ] string {}
1017
1066
for _ , d := range disks {
1018
- diskRsrc , err := getResourceId (d .SelfLink )
1067
+ volumeId , err := getResourceId (d .SelfLink )
1019
1068
if err != nil {
1020
1069
klog .Warningf ("Bad ListVolumes disk resource %s, skipped: %v (%+v)" , d .SelfLink , err , d )
1021
1070
continue
1022
1071
}
1023
- users := []string {}
1072
+
1073
+ instanceIds := make ([]string , len (d .Users ))
1024
1074
for _ , u := range d .Users {
1025
- rsrc , err := getResourceId (u )
1075
+ instanceId , err := getResourceId (u )
1026
1076
if err != nil {
1027
1077
klog .Warningf ("Bad ListVolumes user %s, skipped: %v" , u , err )
1028
1078
} else {
1029
- users = append (users , rsrc )
1079
+ instanceIds = append (instanceIds , instanceId )
1030
1080
}
1031
1081
}
1032
1082
1083
+ nodesByVolumeId [volumeId ] = instanceIds
1084
+
1033
1085
if gceCS .multiZoneVolumeHandleConfig .Enable {
1034
- if multiZoneVolumeId , isMultiZone := isMultiZoneDisk (diskRsrc , d .Labels ); isMultiZone {
1035
- _ , ok := multiZoneNodesByVolumeId [multiZoneVolumeId ]
1036
- if ! ok {
1037
- multiZoneNodesByVolumeId [multiZoneVolumeId ] = []string {}
1038
- }
1039
- multiZoneNodesByVolumeId [multiZoneVolumeId ] = append (multiZoneNodesByVolumeId [multiZoneVolumeId ], users ... )
1086
+ if multiZoneVolumeId , isMultiZone := isMultiZoneDisk (volumeId , d .Labels ); isMultiZone {
1087
+ multiZoneVolumeIdsByVolumeId [volumeId ] = multiZoneVolumeId
1088
+ nodesByVolumeId [multiZoneVolumeId ] = append (nodesByVolumeId [multiZoneVolumeId ], instanceIds ... )
1040
1089
}
1041
1090
}
1042
- entries = append (entries , & csi.ListVolumesResponse_Entry {
1043
- Volume : & csi.Volume {
1044
- VolumeId : diskRsrc ,
1045
- },
1046
- Status : & csi.ListVolumesResponse_VolumeStatus {
1047
- PublishedNodeIds : users ,
1048
- },
1049
- })
1050
1091
}
1051
- for volumeId , nodeIds := range multiZoneNodesByVolumeId {
1092
+
1093
+ entries := []* csi.ListVolumesResponse_Entry {}
1094
+ for _ , instance := range instances {
1095
+ instanceId , err := getResourceId (instance .SelfLink )
1096
+ if err != nil {
1097
+ klog .Warningf ("Bad ListVolumes instance resource %s, skipped: %v (%+v)" , instance .SelfLink , err , instance )
1098
+ continue
1099
+ }
1100
+ for _ , disk := range instance .Disks {
1101
+ volumeId , err := getResourceId (disk .Source )
1102
+ if err != nil {
1103
+ klog .Warningf ("Bad ListVolumes instance disk source %s, skipped: %v (%+v)" , disk .Source , err , instance )
1104
+ continue
1105
+ }
1106
+
1107
+ nodesByVolumeId [volumeId ] = append (nodesByVolumeId [volumeId ], instanceId )
1108
+ if multiZoneVolumeId , isMultiZone := multiZoneVolumeIdsByVolumeId [volumeId ]; isMultiZone {
1109
+ nodesByVolumeId [multiZoneVolumeId ] = append (nodesByVolumeId [multiZoneVolumeId ], instanceId )
1110
+ }
1111
+ }
1112
+ }
1113
+
1114
+ for volumeId , nodeIds := range nodesByVolumeId {
1052
1115
entries = append (entries , & csi.ListVolumesResponse_Entry {
1053
1116
Volume : & csi.Volume {
1054
1117
VolumeId : volumeId ,
0 commit comments