diff options
author | Mat Schaffer <mat@schaffer.me> | 2019-06-10 08:40:12 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-06-10 08:40:12 +0900 |
commit | 3cd2d4f34aefe6090b287b2b4c26049819414e46 (patch) | |
tree | cfbf065f78e205230c63659a556574a4414755ed /vendor/cloud.google.com/go/compute | |
parent | 844b5a68d8af4791755b8f0ad293cc99f5959183 (diff) | |
parent | 03539d597d869600a38d13fd2328fb48fd63aa7d (diff) | |
download | terraform-provider-statuscake-3cd2d4f34aefe6090b287b2b4c26049819414e46.tar.gz terraform-provider-statuscake-3cd2d4f34aefe6090b287b2b4c26049819414e46.tar.zst terraform-provider-statuscake-3cd2d4f34aefe6090b287b2b4c26049819414e46.zip |
Merge pull request #32 from ndench/0.12-compatibility
Upgrade to 0.12
Diffstat (limited to 'vendor/cloud.google.com/go/compute')
-rw-r--r-- | vendor/cloud.google.com/go/compute/metadata/metadata.go | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata.go b/vendor/cloud.google.com/go/compute/metadata/metadata.go new file mode 100644 index 0000000..125b703 --- /dev/null +++ b/vendor/cloud.google.com/go/compute/metadata/metadata.go | |||
@@ -0,0 +1,513 @@ | |||
1 | // Copyright 2014 Google LLC | ||
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | // Package metadata provides access to Google Compute Engine (GCE) | ||
16 | // metadata and API service accounts. | ||
17 | // | ||
18 | // This package is a wrapper around the GCE metadata service, | ||
19 | // as documented at https://developers.google.com/compute/docs/metadata. | ||
20 | package metadata // import "cloud.google.com/go/compute/metadata" | ||
21 | |||
22 | import ( | ||
23 | "context" | ||
24 | "encoding/json" | ||
25 | "fmt" | ||
26 | "io/ioutil" | ||
27 | "net" | ||
28 | "net/http" | ||
29 | "net/url" | ||
30 | "os" | ||
31 | "runtime" | ||
32 | "strings" | ||
33 | "sync" | ||
34 | "time" | ||
35 | ) | ||
36 | |||
37 | const ( | ||
38 | // metadataIP is the documented metadata server IP address. | ||
39 | metadataIP = "169.254.169.254" | ||
40 | |||
41 | // metadataHostEnv is the environment variable specifying the | ||
42 | // GCE metadata hostname. If empty, the default value of | ||
43 | // metadataIP ("169.254.169.254") is used instead. | ||
44 | // This is variable name is not defined by any spec, as far as | ||
45 | // I know; it was made up for the Go package. | ||
46 | metadataHostEnv = "GCE_METADATA_HOST" | ||
47 | |||
48 | userAgent = "gcloud-golang/0.1" | ||
49 | ) | ||
50 | |||
51 | type cachedValue struct { | ||
52 | k string | ||
53 | trim bool | ||
54 | mu sync.Mutex | ||
55 | v string | ||
56 | } | ||
57 | |||
58 | var ( | ||
59 | projID = &cachedValue{k: "project/project-id", trim: true} | ||
60 | projNum = &cachedValue{k: "project/numeric-project-id", trim: true} | ||
61 | instID = &cachedValue{k: "instance/id", trim: true} | ||
62 | ) | ||
63 | |||
64 | var ( | ||
65 | defaultClient = &Client{hc: &http.Client{ | ||
66 | Transport: &http.Transport{ | ||
67 | Dial: (&net.Dialer{ | ||
68 | Timeout: 2 * time.Second, | ||
69 | KeepAlive: 30 * time.Second, | ||
70 | }).Dial, | ||
71 | ResponseHeaderTimeout: 2 * time.Second, | ||
72 | }, | ||
73 | }} | ||
74 | subscribeClient = &Client{hc: &http.Client{ | ||
75 | Transport: &http.Transport{ | ||
76 | Dial: (&net.Dialer{ | ||
77 | Timeout: 2 * time.Second, | ||
78 | KeepAlive: 30 * time.Second, | ||
79 | }).Dial, | ||
80 | }, | ||
81 | }} | ||
82 | ) | ||
83 | |||
84 | // NotDefinedError is returned when requested metadata is not defined. | ||
85 | // | ||
86 | // The underlying string is the suffix after "/computeMetadata/v1/". | ||
87 | // | ||
88 | // This error is not returned if the value is defined to be the empty | ||
89 | // string. | ||
90 | type NotDefinedError string | ||
91 | |||
92 | func (suffix NotDefinedError) Error() string { | ||
93 | return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix)) | ||
94 | } | ||
95 | |||
96 | func (c *cachedValue) get(cl *Client) (v string, err error) { | ||
97 | defer c.mu.Unlock() | ||
98 | c.mu.Lock() | ||
99 | if c.v != "" { | ||
100 | return c.v, nil | ||
101 | } | ||
102 | if c.trim { | ||
103 | v, err = cl.getTrimmed(c.k) | ||
104 | } else { | ||
105 | v, err = cl.Get(c.k) | ||
106 | } | ||
107 | if err == nil { | ||
108 | c.v = v | ||
109 | } | ||
110 | return | ||
111 | } | ||
112 | |||
113 | var ( | ||
114 | onGCEOnce sync.Once | ||
115 | onGCE bool | ||
116 | ) | ||
117 | |||
118 | // OnGCE reports whether this process is running on Google Compute Engine. | ||
119 | func OnGCE() bool { | ||
120 | onGCEOnce.Do(initOnGCE) | ||
121 | return onGCE | ||
122 | } | ||
123 | |||
124 | func initOnGCE() { | ||
125 | onGCE = testOnGCE() | ||
126 | } | ||
127 | |||
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 := defaultClient.hc.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 | addrs, err := net.LookupHost("metadata.google.internal") | ||
155 | if err != nil || len(addrs) == 0 { | ||
156 | resc <- false | ||
157 | return | ||
158 | } | ||
159 | resc <- strsContains(addrs, metadataIP) | ||
160 | }() | ||
161 | |||
162 | tryHarder := systemInfoSuggestsGCE() | ||
163 | if tryHarder { | ||
164 | res := <-resc | ||
165 | if res { | ||
166 | // The first strategy succeeded, so let's use it. | ||
167 | return true | ||
168 | } | ||
169 | // Wait for either the DNS or metadata server probe to | ||
170 | // contradict the other one and say we are running on | ||
171 | // GCE. Give it a lot of time to do so, since the system | ||
172 | // info already suggests we're running on a GCE BIOS. | ||
173 | timer := time.NewTimer(5 * time.Second) | ||
174 | defer timer.Stop() | ||
175 | select { | ||
176 | case res = <-resc: | ||
177 | return res | ||
178 | case <-timer.C: | ||
179 | // Too slow. Who knows what this system is. | ||
180 | return false | ||
181 | } | ||
182 | } | ||
183 | |||
184 | // There's no hint from the system info that we're running on | ||
185 | // GCE, so use the first probe's result as truth, whether it's | ||
186 | // true or false. The goal here is to optimize for speed for | ||
187 | // users who are NOT running on GCE. We can't assume that | ||
188 | // either a DNS lookup or an HTTP request to a blackholed IP | ||
189 | // address is fast. Worst case this should return when the | ||
190 | // metaClient's Transport.ResponseHeaderTimeout or | ||
191 | // Transport.Dial.Timeout fires (in two seconds). | ||
192 | return <-resc | ||
193 | } | ||
194 | |||
195 | // systemInfoSuggestsGCE reports whether the local system (without | ||
196 | // doing network requests) suggests that we're running on GCE. If this | ||
197 | // returns true, testOnGCE tries a bit harder to reach its metadata | ||
198 | // server. | ||
199 | func systemInfoSuggestsGCE() bool { | ||
200 | if runtime.GOOS != "linux" { | ||
201 | // We don't have any non-Linux clues available, at least yet. | ||
202 | return false | ||
203 | } | ||
204 | slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name") | ||
205 | name := strings.TrimSpace(string(slurp)) | ||
206 | return name == "Google" || name == "Google Compute Engine" | ||
207 | } | ||
208 | |||
209 | // Subscribe calls Client.Subscribe on a client designed for subscribing (one with no | ||
210 | // ResponseHeaderTimeout). | ||
211 | func Subscribe(suffix string, fn func(v string, ok bool) error) error { | ||
212 | return subscribeClient.Subscribe(suffix, fn) | ||
213 | } | ||
214 | |||
215 | // Get calls Client.Get on the default client. | ||
216 | func Get(suffix string) (string, error) { return defaultClient.Get(suffix) } | ||
217 | |||
218 | // ProjectID returns the current instance's project ID string. | ||
219 | func ProjectID() (string, error) { return defaultClient.ProjectID() } | ||
220 | |||
221 | // NumericProjectID returns the current instance's numeric project ID. | ||
222 | func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() } | ||
223 | |||
224 | // InternalIP returns the instance's primary internal IP address. | ||
225 | func InternalIP() (string, error) { return defaultClient.InternalIP() } | ||
226 | |||
227 | // ExternalIP returns the instance's primary external (public) IP address. | ||
228 | func ExternalIP() (string, error) { return defaultClient.ExternalIP() } | ||
229 | |||
230 | // Hostname returns the instance's hostname. This will be of the form | ||
231 | // "<instanceID>.c.<projID>.internal". | ||
232 | func Hostname() (string, error) { return defaultClient.Hostname() } | ||
233 | |||
234 | // InstanceTags returns the list of user-defined instance tags, | ||
235 | // assigned when initially creating a GCE instance. | ||
236 | func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() } | ||
237 | |||
238 | // InstanceID returns the current VM's numeric instance ID. | ||
239 | func InstanceID() (string, error) { return defaultClient.InstanceID() } | ||
240 | |||
241 | // InstanceName returns the current VM's instance ID string. | ||
242 | func InstanceName() (string, error) { return defaultClient.InstanceName() } | ||
243 | |||
244 | // Zone returns the current VM's zone, such as "us-central1-b". | ||
245 | func Zone() (string, error) { return defaultClient.Zone() } | ||
246 | |||
247 | // InstanceAttributes calls Client.InstanceAttributes on the default client. | ||
248 | func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() } | ||
249 | |||
250 | // ProjectAttributes calls Client.ProjectAttributes on the default client. | ||
251 | func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() } | ||
252 | |||
253 | // InstanceAttributeValue calls Client.InstanceAttributeValue on the default client. | ||
254 | func InstanceAttributeValue(attr string) (string, error) { | ||
255 | return defaultClient.InstanceAttributeValue(attr) | ||
256 | } | ||
257 | |||
258 | // ProjectAttributeValue calls Client.ProjectAttributeValue on the default client. | ||
259 | func ProjectAttributeValue(attr string) (string, error) { | ||
260 | return defaultClient.ProjectAttributeValue(attr) | ||
261 | } | ||
262 | |||
263 | // Scopes calls Client.Scopes on the default client. | ||
264 | func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) } | ||
265 | |||
266 | func strsContains(ss []string, s string) bool { | ||
267 | for _, v := range ss { | ||
268 | if v == s { | ||
269 | return true | ||
270 | } | ||
271 | } | ||
272 | return false | ||
273 | } | ||
274 | |||
275 | // A Client provides metadata. | ||
276 | type Client struct { | ||
277 | hc *http.Client | ||
278 | } | ||
279 | |||
280 | // NewClient returns a Client that can be used to fetch metadata. All HTTP requests | ||
281 | // will use the given http.Client instead of the default client. | ||
282 | func NewClient(c *http.Client) *Client { | ||
283 | return &Client{hc: c} | ||
284 | } | ||
285 | |||
286 | // getETag returns a value from the metadata service as well as the associated ETag. | ||
287 | // This func is otherwise equivalent to Get. | ||
288 | func (c *Client) getETag(suffix string) (value, etag string, err error) { | ||
289 | // Using a fixed IP makes it very difficult to spoof the metadata service in | ||
290 | // a container, which is an important use-case for local testing of cloud | ||
291 | // deployments. To enable spoofing of the metadata service, the environment | ||
292 | // variable GCE_METADATA_HOST is first inspected to decide where metadata | ||
293 | // requests shall go. | ||
294 | host := os.Getenv(metadataHostEnv) | ||
295 | if host == "" { | ||
296 | // Using 169.254.169.254 instead of "metadata" here because Go | ||
297 | // binaries built with the "netgo" tag and without cgo won't | ||
298 | // know the search suffix for "metadata" is | ||
299 | // ".google.internal", and this IP address is documented as | ||
300 | // being stable anyway. | ||
301 | host = metadataIP | ||
302 | } | ||
303 | u := "http://" + host + "/computeMetadata/v1/" + suffix | ||
304 | req, _ := http.NewRequest("GET", u, nil) | ||
305 | req.Header.Set("Metadata-Flavor", "Google") | ||
306 | req.Header.Set("User-Agent", userAgent) | ||
307 | res, err := c.hc.Do(req) | ||
308 | if err != nil { | ||
309 | return "", "", err | ||
310 | } | ||
311 | defer res.Body.Close() | ||
312 | if res.StatusCode == http.StatusNotFound { | ||
313 | return "", "", NotDefinedError(suffix) | ||
314 | } | ||
315 | all, err := ioutil.ReadAll(res.Body) | ||
316 | if err != nil { | ||
317 | return "", "", err | ||
318 | } | ||
319 | if res.StatusCode != 200 { | ||
320 | return "", "", &Error{Code: res.StatusCode, Message: string(all)} | ||
321 | } | ||
322 | return string(all), res.Header.Get("Etag"), nil | ||
323 | } | ||
324 | |||
325 | // Get returns a value from the metadata service. | ||
326 | // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". | ||
327 | // | ||
328 | // If the GCE_METADATA_HOST environment variable is not defined, a default of | ||
329 | // 169.254.169.254 will be used instead. | ||
330 | // | ||
331 | // If the requested metadata is not defined, the returned error will | ||
332 | // be of type NotDefinedError. | ||
333 | func (c *Client) Get(suffix string) (string, error) { | ||
334 | val, _, err := c.getETag(suffix) | ||
335 | return val, err | ||
336 | } | ||
337 | |||
338 | func (c *Client) getTrimmed(suffix string) (s string, err error) { | ||
339 | s, err = c.Get(suffix) | ||
340 | s = strings.TrimSpace(s) | ||
341 | return | ||
342 | } | ||
343 | |||
344 | func (c *Client) lines(suffix string) ([]string, error) { | ||
345 | j, err := c.Get(suffix) | ||
346 | if err != nil { | ||
347 | return nil, err | ||
348 | } | ||
349 | s := strings.Split(strings.TrimSpace(j), "\n") | ||
350 | for i := range s { | ||
351 | s[i] = strings.TrimSpace(s[i]) | ||
352 | } | ||
353 | return s, nil | ||
354 | } | ||
355 | |||
356 | // ProjectID returns the current instance's project ID string. | ||
357 | func (c *Client) ProjectID() (string, error) { return projID.get(c) } | ||
358 | |||
359 | // NumericProjectID returns the current instance's numeric project ID. | ||
360 | func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) } | ||
361 | |||
362 | // InstanceID returns the current VM's numeric instance ID. | ||
363 | func (c *Client) InstanceID() (string, error) { return instID.get(c) } | ||
364 | |||
365 | // InternalIP returns the instance's primary internal IP address. | ||
366 | func (c *Client) InternalIP() (string, error) { | ||
367 | return c.getTrimmed("instance/network-interfaces/0/ip") | ||
368 | } | ||
369 | |||
370 | // ExternalIP returns the instance's primary external (public) IP address. | ||
371 | func (c *Client) ExternalIP() (string, error) { | ||
372 | return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") | ||
373 | } | ||
374 | |||
375 | // Hostname returns the instance's hostname. This will be of the form | ||
376 | // "<instanceID>.c.<projID>.internal". | ||
377 | func (c *Client) Hostname() (string, error) { | ||
378 | return c.getTrimmed("instance/hostname") | ||
379 | } | ||
380 | |||
381 | // InstanceTags returns the list of user-defined instance tags, | ||
382 | // assigned when initially creating a GCE instance. | ||
383 | func (c *Client) InstanceTags() ([]string, error) { | ||
384 | var s []string | ||
385 | j, err := c.Get("instance/tags") | ||
386 | if err != nil { | ||
387 | return nil, err | ||
388 | } | ||
389 | if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil { | ||
390 | return nil, err | ||
391 | } | ||
392 | return s, nil | ||
393 | } | ||
394 | |||
395 | // InstanceName returns the current VM's instance ID string. | ||
396 | func (c *Client) InstanceName() (string, error) { | ||
397 | host, err := c.Hostname() | ||
398 | if err != nil { | ||
399 | return "", err | ||
400 | } | ||
401 | return strings.Split(host, ".")[0], nil | ||
402 | } | ||
403 | |||
404 | // Zone returns the current VM's zone, such as "us-central1-b". | ||
405 | func (c *Client) Zone() (string, error) { | ||
406 | zone, err := c.getTrimmed("instance/zone") | ||
407 | // zone is of the form "projects/<projNum>/zones/<zoneName>". | ||
408 | if err != nil { | ||
409 | return "", err | ||
410 | } | ||
411 | return zone[strings.LastIndex(zone, "/")+1:], nil | ||
412 | } | ||
413 | |||
414 | // InstanceAttributes returns the list of user-defined attributes, | ||
415 | // assigned when initially creating a GCE VM instance. The value of an | ||
416 | // attribute can be obtained with InstanceAttributeValue. | ||
417 | func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") } | ||
418 | |||
419 | // ProjectAttributes returns the list of user-defined attributes | ||
420 | // applying to the project as a whole, not just this VM. The value of | ||
421 | // an attribute can be obtained with ProjectAttributeValue. | ||
422 | func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") } | ||
423 | |||
424 | // InstanceAttributeValue returns the value of the provided VM | ||
425 | // instance attribute. | ||
426 | // | ||
427 | // If the requested attribute is not defined, the returned error will | ||
428 | // be of type NotDefinedError. | ||
429 | // | ||
430 | // InstanceAttributeValue may return ("", nil) if the attribute was | ||
431 | // defined to be the empty string. | ||
432 | func (c *Client) InstanceAttributeValue(attr string) (string, error) { | ||
433 | return c.Get("instance/attributes/" + attr) | ||
434 | } | ||
435 | |||
436 | // ProjectAttributeValue returns the value of the provided | ||
437 | // project attribute. | ||
438 | // | ||
439 | // If the requested attribute is not defined, the returned error will | ||
440 | // be of type NotDefinedError. | ||
441 | // | ||
442 | // ProjectAttributeValue may return ("", nil) if the attribute was | ||
443 | // defined to be the empty string. | ||
444 | func (c *Client) ProjectAttributeValue(attr string) (string, error) { | ||
445 | return c.Get("project/attributes/" + attr) | ||
446 | } | ||
447 | |||
448 | // Scopes returns the service account scopes for the given account. | ||
449 | // The account may be empty or the string "default" to use the instance's | ||
450 | // main account. | ||
451 | func (c *Client) Scopes(serviceAccount string) ([]string, error) { | ||
452 | if serviceAccount == "" { | ||
453 | serviceAccount = "default" | ||
454 | } | ||
455 | return c.lines("instance/service-accounts/" + serviceAccount + "/scopes") | ||
456 | } | ||
457 | |||
458 | // Subscribe subscribes to a value from the metadata service. | ||
459 | // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". | ||
460 | // The suffix may contain query parameters. | ||
461 | // | ||
462 | // Subscribe calls fn with the latest metadata value indicated by the provided | ||
463 | // suffix. If the metadata value is deleted, fn is called with the empty string | ||
464 | // and ok false. Subscribe blocks until fn returns a non-nil error or the value | ||
465 | // is deleted. Subscribe returns the error value returned from the last call to | ||
466 | // fn, which may be nil when ok == false. | ||
467 | func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { | ||
468 | const failedSubscribeSleep = time.Second * 5 | ||
469 | |||
470 | // First check to see if the metadata value exists at all. | ||
471 | val, lastETag, err := c.getETag(suffix) | ||
472 | if err != nil { | ||
473 | return err | ||
474 | } | ||
475 | |||
476 | if err := fn(val, true); err != nil { | ||
477 | return err | ||
478 | } | ||
479 | |||
480 | ok := true | ||
481 | if strings.ContainsRune(suffix, '?') { | ||
482 | suffix += "&wait_for_change=true&last_etag=" | ||
483 | } else { | ||
484 | suffix += "?wait_for_change=true&last_etag=" | ||
485 | } | ||
486 | for { | ||
487 | val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) | ||
488 | if err != nil { | ||
489 | if _, deleted := err.(NotDefinedError); !deleted { | ||
490 | time.Sleep(failedSubscribeSleep) | ||
491 | continue // Retry on other errors. | ||
492 | } | ||
493 | ok = false | ||
494 | } | ||
495 | lastETag = etag | ||
496 | |||
497 | if err := fn(val, ok); err != nil || !ok { | ||
498 | return err | ||
499 | } | ||
500 | } | ||
501 | } | ||
502 | |||
503 | // Error contains an error response from the server. | ||
504 | type Error struct { | ||
505 | // Code is the HTTP response status code. | ||
506 | Code int | ||
507 | // Message is the server response message. | ||
508 | Message string | ||
509 | } | ||
510 | |||
511 | func (e *Error) Error() string { | ||
512 | return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message) | ||
513 | } | ||