@@ -37,9 +37,8 @@ import (
37
37
)
38
38
39
39
const (
40
- defaultOperationWaitTimeout = 20 * time .Second
41
- defaultOperationPollInterval = 100 * time .Millisecond
42
- defaultOperationDeletionPollInterval = 1 * time .Second
40
+ defaultOperationWaitTimeout = 20 * time .Second
41
+ defaultOperationPollInterval = 100 * time .Millisecond
43
42
// ErrorCodeQuotaExceeded is an error code used in InstanceErrorInfo if quota exceeded error occurs.
44
43
ErrorCodeQuotaExceeded = "QUOTA_EXCEEDED"
45
44
@@ -116,6 +115,11 @@ type AutoscalingGceClient interface {
116
115
ResizeMig (GceRef , int64 ) error
117
116
DeleteInstances (migRef GceRef , instances []GceRef ) error
118
117
CreateInstances (GceRef , string , int64 , []string ) error
118
+
119
+ // WaitForOperation can be used to poll GCE operations until completion/timeout using WAIT calls.
120
+ // Calling this is normally not needed when interacting with the client, other methods should call it internally.
121
+ // Can be used to extend the interface with more methods outside of this package.
122
+ WaitForOperation (operationName , operationType , project , zone string ) error
119
123
}
120
124
121
125
type autoscalingGceClientV1 struct {
@@ -124,39 +128,36 @@ type autoscalingGceClientV1 struct {
124
128
projectId string
125
129
domainUrl string
126
130
127
- // These can be overridden, e.g. for testing.
128
- operationWaitTimeout time.Duration
129
- operationPollInterval time.Duration
130
- operationDeletionPollInterval time.Duration
131
+ // Can be overridden, e.g. for testing.
132
+ operationWaitTimeout time.Duration
133
+ operationPollInterval time.Duration
131
134
}
132
135
133
136
// NewAutoscalingGceClientV1WithTimeout creates a new client with custom timeouts
134
137
// for communicating with GCE v1 API
135
- func NewAutoscalingGceClientV1WithTimeout (client * http.Client , projectId string , userAgent string ,
136
- waitTimeout , pollInterval , deletionPollInterval time.Duration ) (* autoscalingGceClientV1 , error ) {
138
+ func NewAutoscalingGceClientV1WithTimeout (client * http.Client , projectId string , userAgent string , waitTimeout time.Duration , pollInterval time.Duration ) (* autoscalingGceClientV1 , error ) {
137
139
gceService , err := gce .New (client )
138
140
if err != nil {
139
141
return nil , err
140
142
}
141
143
gceService .UserAgent = userAgent
144
+
142
145
return & autoscalingGceClientV1 {
143
- projectId : projectId ,
144
- gceService : gceService ,
145
- operationWaitTimeout : waitTimeout ,
146
- operationPollInterval : pollInterval ,
147
- operationDeletionPollInterval : deletionPollInterval ,
146
+ projectId : projectId ,
147
+ gceService : gceService ,
148
+ operationWaitTimeout : waitTimeout ,
149
+ operationPollInterval : pollInterval ,
148
150
}, nil
149
151
}
150
152
151
153
// NewAutoscalingGceClientV1 creates a new client for communicating with GCE v1 API.
152
154
func NewAutoscalingGceClientV1 (client * http.Client , projectId string , userAgent string ) (* autoscalingGceClientV1 , error ) {
153
- return NewAutoscalingGceClientV1WithTimeout (client , projectId , userAgent , defaultOperationWaitTimeout , defaultOperationPollInterval , defaultOperationDeletionPollInterval )
155
+ return NewAutoscalingGceClientV1WithTimeout (client , projectId , userAgent , defaultOperationWaitTimeout , defaultOperationPollInterval )
154
156
}
155
157
156
158
// NewCustomAutoscalingGceClientV1 creates a new client using custom server url and timeouts
157
159
// for communicating with GCE v1 API.
158
- func NewCustomAutoscalingGceClientV1 (client * http.Client , projectId , serverUrl , userAgent , domainUrl string ,
159
- waitTimeout , pollInterval time.Duration , deletionPollInterval time.Duration ) (* autoscalingGceClientV1 , error ) {
160
+ func NewCustomAutoscalingGceClientV1 (client * http.Client , projectId , serverUrl , userAgent , domainUrl string , waitTimeout time.Duration , pollInterval time.Duration ) (* autoscalingGceClientV1 , error ) {
160
161
gceService , err := gce .New (client )
161
162
if err != nil {
162
163
return nil , err
@@ -165,12 +166,11 @@ func NewCustomAutoscalingGceClientV1(client *http.Client, projectId, serverUrl,
165
166
gceService .UserAgent = userAgent
166
167
167
168
return & autoscalingGceClientV1 {
168
- projectId : projectId ,
169
- gceService : gceService ,
170
- domainUrl : domainUrl ,
171
- operationWaitTimeout : waitTimeout ,
172
- operationPollInterval : pollInterval ,
173
- operationDeletionPollInterval : deletionPollInterval ,
169
+ projectId : projectId ,
170
+ gceService : gceService ,
171
+ domainUrl : domainUrl ,
172
+ operationWaitTimeout : waitTimeout ,
173
+ operationPollInterval : pollInterval ,
174
174
}, nil
175
175
}
176
176
@@ -255,7 +255,7 @@ func (client *autoscalingGceClientV1) ResizeMig(migRef GceRef, size int64) error
255
255
if err != nil {
256
256
return err
257
257
}
258
- return client .waitForOp (op , migRef .Project , migRef .Zone , false )
258
+ return client .WaitForOperation (op . Name , op . OperationType , migRef .Project , migRef .Zone )
259
259
}
260
260
261
261
func (client * autoscalingGceClientV1 ) CreateInstances (migRef GceRef , baseName string , delta int64 , existingInstanceProviderIds []string ) error {
@@ -272,7 +272,7 @@ func (client *autoscalingGceClientV1) CreateInstances(migRef GceRef, baseName st
272
272
if err != nil {
273
273
return err
274
274
}
275
- return client .waitForOp (op , migRef .Project , migRef .Zone , false )
275
+ return client .WaitForOperation (op . Name , op . OperationType , migRef .Project , migRef .Zone )
276
276
}
277
277
278
278
func instanceIdsToNamesMap (instanceProviderIds []string ) map [string ]bool {
@@ -289,32 +289,37 @@ func instanceIdsToNamesMap(instanceProviderIds []string) map[string]bool {
289
289
return instanceNames
290
290
}
291
291
292
- func (client * autoscalingGceClientV1 ) waitForOp (operation * gce.Operation , project , zone string , isDeletion bool ) error {
293
- pollInterval := client .operationPollInterval
294
- if isDeletion {
295
- pollInterval = client .operationDeletionPollInterval
296
- }
297
- for start := time .Now (); time .Since (start ) < client .operationWaitTimeout ; time .Sleep (pollInterval ) {
298
- klog .V (4 ).Infof ("Waiting for operation %s %s %s" , project , zone , operation .Name )
299
- registerRequest ("zone_operations" , "get" )
300
- if op , err := client .gceService .ZoneOperations .Get (project , zone , operation .Name ).Do (); err == nil {
301
- klog .V (4 ).Infof ("Operation %s %s %s status: %s" , project , zone , operation .Name , op .Status )
302
- if op .Status == "DONE" {
303
- if op .Error != nil {
304
- errBytes , err := op .Error .MarshalJSON ()
305
- if err != nil {
306
- errBytes = []byte (fmt .Sprintf ("operation failed, but error couldn't be recovered: %v" , err ))
307
- }
308
- return fmt .Errorf ("error while getting operation %s on %s: %s" , operation .Name , operation .TargetLink , errBytes )
309
- }
292
+ // WaitForOperation can be used to poll GCE operations until completion/timeout using WAIT calls.
293
+ // Calling this is normally not needed when interacting with the client, other methods should call it internally.
294
+ // Can be used to extend the interface with more methods outside of this package.
295
+ func (client * autoscalingGceClientV1 ) WaitForOperation (operationName , operationType , project , zone string ) error {
296
+ ctx , cancel := context .WithTimeout (context .Background (), client .operationWaitTimeout )
297
+ defer cancel ()
298
+
299
+ for {
300
+ klog .V (4 ).Infof ("Waiting for operation %s/%s (%s/%s)" , operationType , operationName , project , zone )
301
+ registerRequest ("zone_operations" , "wait" )
302
+ op , err := client .gceService .ZoneOperations .Wait (project , zone , operationName ).Context (ctx ).Do ()
303
+ if err != nil {
304
+ return fmt .Errorf ("error while waiting for operation %s/%s: %w" , operationType , operationName , err )
305
+ }
310
306
311
- return nil
307
+ klog .V (4 ).Infof ("Operation %s/%s (%s/%s) status: %s" , operationType , operationName , project , zone , op .Status )
308
+ if op .Status == "DONE" {
309
+ if op .Error != nil {
310
+ errBytes , err := op .Error .MarshalJSON ()
311
+ if err != nil {
312
+ errBytes = []byte (fmt .Sprintf ("operation failed, but error couldn't be recovered: %v" , err ))
313
+ }
314
+ return fmt .Errorf ("error while waiting for operation %s/%s: %s" , operationType , operationName , errBytes )
312
315
}
313
- } else {
314
- klog . Warningf ( "Error while getting operation %s on %s: %v" , operation . Name , operation . TargetLink , err )
316
+
317
+ return nil
315
318
}
319
+
320
+ // NOTE: sleep in order not to overload server, as potentially response may be returned immediately
321
+ time .Sleep (client .operationPollInterval )
316
322
}
317
- return fmt .Errorf ("timeout while waiting for operation %s on %s to complete." , operation .Name , operation .TargetLink )
318
323
}
319
324
320
325
func (client * autoscalingGceClientV1 ) DeleteInstances (migRef GceRef , instances []GceRef ) error {
@@ -330,7 +335,7 @@ func (client *autoscalingGceClientV1) DeleteInstances(migRef GceRef, instances [
330
335
if err != nil {
331
336
return err
332
337
}
333
- return client .waitForOp (op , migRef .Project , migRef .Zone , true )
338
+ return client .WaitForOperation (op . Name , op . OperationType , migRef .Project , migRef .Zone )
334
339
}
335
340
336
341
func (client * autoscalingGceClientV1 ) FetchMigInstances (migRef GceRef ) ([]GceInstance , error ) {
0 commit comments