Code refactoring for bpa operator
[icn.git] / cmd / bpa-operator / vendor / cloud.google.com / go / compute / metadata / metadata.go
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 }