@@ -117,80 +117,18 @@ var (
117
117
// NOTE: True returned from `OnGCE` does not guarantee that the metadata server
118
118
// is accessible from this process and have all the metadata defined.
119
119
func OnGCE () bool {
120
- onGCEOnce .Do (initOnGCE )
121
- return onGCE
122
- }
123
-
124
- func initOnGCE () {
125
- onGCE = testOnGCE ()
120
+ return OnGCEWithContext (context .Background ())
126
121
}
127
122
128
- func testOnGCE () bool {
129
- // The user explicitly said they're on GCE, so trust them.
130
- if os .Getenv (metadataHostEnv ) != "" {
131
- return true
132
- }
133
-
134
- ctx , cancel := context .WithCancel (context .Background ())
135
- defer cancel ()
136
-
137
- resc := make (chan bool , 2 )
138
-
139
- // Try two strategies in parallel.
140
- // See https://github.com/googleapis/google-cloud-go/issues/194
141
- go func () {
142
- req , _ := http .NewRequest ("GET" , "http://" + metadataIP , nil )
143
- req .Header .Set ("User-Agent" , userAgent )
144
- res , err := newDefaultHTTPClient ().Do (req .WithContext (ctx ))
145
- if err != nil {
146
- resc <- false
147
- return
148
- }
149
- defer res .Body .Close ()
150
- resc <- res .Header .Get ("Metadata-Flavor" ) == "Google"
151
- }()
152
-
153
- go func () {
154
- resolver := & net.Resolver {}
155
- addrs , err := resolver .LookupHost (ctx , "metadata.google.internal." )
156
- if err != nil || len (addrs ) == 0 {
157
- resc <- false
158
- return
159
- }
160
- resc <- strsContains (addrs , metadataIP )
161
- }()
162
-
163
- tryHarder := systemInfoSuggestsGCE ()
164
- if tryHarder {
165
- res := <- resc
166
- if res {
167
- // The first strategy succeeded, so let's use it.
168
- return true
169
- }
170
- // Wait for either the DNS or metadata server probe to
171
- // contradict the other one and say we are running on
172
- // GCE. Give it a lot of time to do so, since the system
173
- // info already suggests we're running on a GCE BIOS.
174
- timer := time .NewTimer (5 * time .Second )
175
- defer timer .Stop ()
176
- select {
177
- case res = <- resc :
178
- return res
179
- case <- timer .C :
180
- // Too slow. Who knows what this system is.
181
- return false
182
- }
183
- }
184
-
185
- // There's no hint from the system info that we're running on
186
- // GCE, so use the first probe's result as truth, whether it's
187
- // true or false. The goal here is to optimize for speed for
188
- // users who are NOT running on GCE. We can't assume that
189
- // either a DNS lookup or an HTTP request to a blackholed IP
190
- // address is fast. Worst case this should return when the
191
- // metaClient's Transport.ResponseHeaderTimeout or
192
- // Transport.Dial.Timeout fires (in two seconds).
193
- return <- resc
123
+ // OnGCEWithContext reports whether this process is running on Google Compute Platforms.
124
+ // This function's return value is memoized for better performance.
125
+ // NOTE: True returned from `OnGCEWithContext` does not guarantee that the metadata server
126
+ // is accessible from this process and have all the metadata defined.
127
+ func OnGCEWithContext (ctx context.Context ) bool {
128
+ onGCEOnce .Do (func () {
129
+ onGCE = defaultClient .OnGCEWithContext (ctx )
130
+ })
131
+ return onGCE
194
132
}
195
133
196
134
// Subscribe calls Client.SubscribeWithContext on the default client.
@@ -450,6 +388,84 @@ func NewWithOptions(opts *Options) *Client {
450
388
return & Client {hc : client , logger : logger }
451
389
}
452
390
391
+ // NOTE: metadataRequestStrategy is assigned to a variable for test stubbing purposes.
392
+ var metadataRequestStrategy = func (ctx context.Context , httpClient * http.Client , resc chan bool ) {
393
+ req , _ := http .NewRequest ("GET" , "http://" + metadataIP , nil )
394
+ req .Header .Set ("User-Agent" , userAgent )
395
+ res , err := httpClient .Do (req .WithContext (ctx ))
396
+ if err != nil {
397
+ resc <- false
398
+ return
399
+ }
400
+ defer res .Body .Close ()
401
+ resc <- res .Header .Get ("Metadata-Flavor" ) == "Google"
402
+ }
403
+
404
+ // NOTE: dnsRequestStrategy is assigned to a variable for test stubbing purposes.
405
+ var dnsRequestStrategy = func (ctx context.Context , resc chan bool ) {
406
+ resolver := & net.Resolver {}
407
+ addrs , err := resolver .LookupHost (ctx , "metadata.google.internal." )
408
+ if err != nil || len (addrs ) == 0 {
409
+ resc <- false
410
+ return
411
+ }
412
+ resc <- strsContains (addrs , metadataIP )
413
+ }
414
+
415
+ // OnGCEWithContext reports whether this process is running on Google Compute Platforms.
416
+ // NOTE: True returned from `OnGCEWithContext` does not guarantee that the metadata server
417
+ // is accessible from this process and have all the metadata defined.
418
+ func (c * Client ) OnGCEWithContext (ctx context.Context ) bool {
419
+ // The user explicitly said they're on GCE, so trust them.
420
+ if os .Getenv (metadataHostEnv ) != "" {
421
+ return true
422
+ }
423
+
424
+ ctx , cancel := context .WithCancel (ctx )
425
+ defer cancel ()
426
+
427
+ resc := make (chan bool , 2 )
428
+
429
+ // Try two strategies in parallel.
430
+ // See https://github.com/googleapis/google-cloud-go/issues/194
431
+ go metadataRequestStrategy (ctx , c .hc , resc )
432
+ go dnsRequestStrategy (ctx , resc )
433
+
434
+ tryHarder := systemInfoSuggestsGCE ()
435
+ if tryHarder {
436
+ res := <- resc
437
+ if res {
438
+ // The first strategy succeeded, so let's use it.
439
+ return true
440
+ }
441
+
442
+ // Wait for either the DNS or metadata server probe to
443
+ // contradict the other one and say we are running on
444
+ // GCE. Give it a lot of time to do so, since the system
445
+ // info already suggests we're running on a GCE BIOS.
446
+ // Ensure cancellations from the calling context are respected.
447
+ waitContext , cancelWait := context .WithTimeout (ctx , 5 * time .Second )
448
+ defer cancelWait ()
449
+ select {
450
+ case res = <- resc :
451
+ return res
452
+ case <- waitContext .Done ():
453
+ // Too slow. Who knows what this system is.
454
+ return false
455
+ }
456
+ }
457
+
458
+ // There's no hint from the system info that we're running on
459
+ // GCE, so use the first probe's result as truth, whether it's
460
+ // true or false. The goal here is to optimize for speed for
461
+ // users who are NOT running on GCE. We can't assume that
462
+ // either a DNS lookup or an HTTP request to a blackholed IP
463
+ // address is fast. Worst case this should return when the
464
+ // metaClient's Transport.ResponseHeaderTimeout or
465
+ // Transport.Dial.Timeout fires (in two seconds).
466
+ return <- resc
467
+ }
468
+
453
469
// getETag returns a value from the metadata service as well as the associated ETag.
454
470
// This func is otherwise equivalent to Get.
455
471
func (c * Client ) getETag (ctx context.Context , suffix string ) (value , etag string , err error ) {
0 commit comments