Tizen_4.0 base
[platform/upstream/docker-engine.git] / vendor / cloud.google.com / go / compute / metadata / metadata.go
1 // Copyright 2014 Google Inc. All Rights Reserved.
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         "encoding/json"
24         "fmt"
25         "io/ioutil"
26         "net"
27         "net/http"
28         "net/url"
29         "os"
30         "runtime"
31         "strings"
32         "sync"
33         "time"
34
35         "golang.org/x/net/context"
36         "golang.org/x/net/context/ctxhttp"
37
38         "cloud.google.com/go/internal"
39 )
40
41 const (
42         // metadataIP is the documented metadata server IP address.
43         metadataIP = "169.254.169.254"
44
45         // metadataHostEnv is the environment variable specifying the
46         // GCE metadata hostname.  If empty, the default value of
47         // metadataIP ("169.254.169.254") is used instead.
48         // This is variable name is not defined by any spec, as far as
49         // I know; it was made up for the Go package.
50         metadataHostEnv = "GCE_METADATA_HOST"
51 )
52
53 type cachedValue struct {
54         k    string
55         trim bool
56         mu   sync.Mutex
57         v    string
58 }
59
60 var (
61         projID  = &cachedValue{k: "project/project-id", trim: true}
62         projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
63         instID  = &cachedValue{k: "instance/id", trim: true}
64 )
65
66 var (
67         metaClient = &http.Client{
68                 Transport: &internal.Transport{
69                         Base: &http.Transport{
70                                 Dial: (&net.Dialer{
71                                         Timeout:   2 * time.Second,
72                                         KeepAlive: 30 * time.Second,
73                                 }).Dial,
74                                 ResponseHeaderTimeout: 2 * time.Second,
75                         },
76                 },
77         }
78         subscribeClient = &http.Client{
79                 Transport: &internal.Transport{
80                         Base: &http.Transport{
81                                 Dial: (&net.Dialer{
82                                         Timeout:   2 * time.Second,
83                                         KeepAlive: 30 * time.Second,
84                                 }).Dial,
85                         },
86                 },
87         }
88 )
89
90 // NotDefinedError is returned when requested metadata is not defined.
91 //
92 // The underlying string is the suffix after "/computeMetadata/v1/".
93 //
94 // This error is not returned if the value is defined to be the empty
95 // string.
96 type NotDefinedError string
97
98 func (suffix NotDefinedError) Error() string {
99         return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
100 }
101
102 // Get returns a value from the metadata service.
103 // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
104 //
105 // If the GCE_METADATA_HOST environment variable is not defined, a default of
106 // 169.254.169.254 will be used instead.
107 //
108 // If the requested metadata is not defined, the returned error will
109 // be of type NotDefinedError.
110 func Get(suffix string) (string, error) {
111         val, _, err := getETag(metaClient, suffix)
112         return val, err
113 }
114
115 // getETag returns a value from the metadata service as well as the associated
116 // ETag using the provided client. This func is otherwise equivalent to Get.
117 func getETag(client *http.Client, suffix string) (value, etag string, err error) {
118         // Using a fixed IP makes it very difficult to spoof the metadata service in
119         // a container, which is an important use-case for local testing of cloud
120         // deployments. To enable spoofing of the metadata service, the environment
121         // variable GCE_METADATA_HOST is first inspected to decide where metadata
122         // requests shall go.
123         host := os.Getenv(metadataHostEnv)
124         if host == "" {
125                 // Using 169.254.169.254 instead of "metadata" here because Go
126                 // binaries built with the "netgo" tag and without cgo won't
127                 // know the search suffix for "metadata" is
128                 // ".google.internal", and this IP address is documented as
129                 // being stable anyway.
130                 host = metadataIP
131         }
132         url := "http://" + host + "/computeMetadata/v1/" + suffix
133         req, _ := http.NewRequest("GET", url, nil)
134         req.Header.Set("Metadata-Flavor", "Google")
135         res, err := client.Do(req)
136         if err != nil {
137                 return "", "", err
138         }
139         defer res.Body.Close()
140         if res.StatusCode == http.StatusNotFound {
141                 return "", "", NotDefinedError(suffix)
142         }
143         if res.StatusCode != 200 {
144                 return "", "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
145         }
146         all, err := ioutil.ReadAll(res.Body)
147         if err != nil {
148                 return "", "", err
149         }
150         return string(all), res.Header.Get("Etag"), nil
151 }
152
153 func getTrimmed(suffix string) (s string, err error) {
154         s, err = Get(suffix)
155         s = strings.TrimSpace(s)
156         return
157 }
158
159 func (c *cachedValue) get() (v string, err error) {
160         defer c.mu.Unlock()
161         c.mu.Lock()
162         if c.v != "" {
163                 return c.v, nil
164         }
165         if c.trim {
166                 v, err = getTrimmed(c.k)
167         } else {
168                 v, err = Get(c.k)
169         }
170         if err == nil {
171                 c.v = v
172         }
173         return
174 }
175
176 var (
177         onGCEOnce sync.Once
178         onGCE     bool
179 )
180
181 // OnGCE reports whether this process is running on Google Compute Engine.
182 func OnGCE() bool {
183         onGCEOnce.Do(initOnGCE)
184         return onGCE
185 }
186
187 func initOnGCE() {
188         onGCE = testOnGCE()
189 }
190
191 func testOnGCE() bool {
192         // The user explicitly said they're on GCE, so trust them.
193         if os.Getenv(metadataHostEnv) != "" {
194                 return true
195         }
196
197         ctx, cancel := context.WithCancel(context.Background())
198         defer cancel()
199
200         resc := make(chan bool, 2)
201
202         // Try two strategies in parallel.
203         // See https://github.com/GoogleCloudPlatform/google-cloud-go/issues/194
204         go func() {
205                 res, err := ctxhttp.Get(ctx, metaClient, "http://"+metadataIP)
206                 if err != nil {
207                         resc <- false
208                         return
209                 }
210                 defer res.Body.Close()
211                 resc <- res.Header.Get("Metadata-Flavor") == "Google"
212         }()
213
214         go func() {
215                 addrs, err := net.LookupHost("metadata.google.internal")
216                 if err != nil || len(addrs) == 0 {
217                         resc <- false
218                         return
219                 }
220                 resc <- strsContains(addrs, metadataIP)
221         }()
222
223         tryHarder := systemInfoSuggestsGCE()
224         if tryHarder {
225                 res := <-resc
226                 if res {
227                         // The first strategy succeeded, so let's use it.
228                         return true
229                 }
230                 // Wait for either the DNS or metadata server probe to
231                 // contradict the other one and say we are running on
232                 // GCE. Give it a lot of time to do so, since the system
233                 // info already suggests we're running on a GCE BIOS.
234                 timer := time.NewTimer(5 * time.Second)
235                 defer timer.Stop()
236                 select {
237                 case res = <-resc:
238                         return res
239                 case <-timer.C:
240                         // Too slow. Who knows what this system is.
241                         return false
242                 }
243         }
244
245         // There's no hint from the system info that we're running on
246         // GCE, so use the first probe's result as truth, whether it's
247         // true or false. The goal here is to optimize for speed for
248         // users who are NOT running on GCE. We can't assume that
249         // either a DNS lookup or an HTTP request to a blackholed IP
250         // address is fast. Worst case this should return when the
251         // metaClient's Transport.ResponseHeaderTimeout or
252         // Transport.Dial.Timeout fires (in two seconds).
253         return <-resc
254 }
255
256 // systemInfoSuggestsGCE reports whether the local system (without
257 // doing network requests) suggests that we're running on GCE. If this
258 // returns true, testOnGCE tries a bit harder to reach its metadata
259 // server.
260 func systemInfoSuggestsGCE() bool {
261         if runtime.GOOS != "linux" {
262                 // We don't have any non-Linux clues available, at least yet.
263                 return false
264         }
265         slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
266         name := strings.TrimSpace(string(slurp))
267         return name == "Google" || name == "Google Compute Engine"
268 }
269
270 // Subscribe subscribes to a value from the metadata service.
271 // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
272 // The suffix may contain query parameters.
273 //
274 // Subscribe calls fn with the latest metadata value indicated by the provided
275 // suffix. If the metadata value is deleted, fn is called with the empty string
276 // and ok false. Subscribe blocks until fn returns a non-nil error or the value
277 // is deleted. Subscribe returns the error value returned from the last call to
278 // fn, which may be nil when ok == false.
279 func Subscribe(suffix string, fn func(v string, ok bool) error) error {
280         const failedSubscribeSleep = time.Second * 5
281
282         // First check to see if the metadata value exists at all.
283         val, lastETag, err := getETag(subscribeClient, suffix)
284         if err != nil {
285                 return err
286         }
287
288         if err := fn(val, true); err != nil {
289                 return err
290         }
291
292         ok := true
293         if strings.ContainsRune(suffix, '?') {
294                 suffix += "&wait_for_change=true&last_etag="
295         } else {
296                 suffix += "?wait_for_change=true&last_etag="
297         }
298         for {
299                 val, etag, err := getETag(subscribeClient, suffix+url.QueryEscape(lastETag))
300                 if err != nil {
301                         if _, deleted := err.(NotDefinedError); !deleted {
302                                 time.Sleep(failedSubscribeSleep)
303                                 continue // Retry on other errors.
304                         }
305                         ok = false
306                 }
307                 lastETag = etag
308
309                 if err := fn(val, ok); err != nil || !ok {
310                         return err
311                 }
312         }
313 }
314
315 // ProjectID returns the current instance's project ID string.
316 func ProjectID() (string, error) { return projID.get() }
317
318 // NumericProjectID returns the current instance's numeric project ID.
319 func NumericProjectID() (string, error) { return projNum.get() }
320
321 // InternalIP returns the instance's primary internal IP address.
322 func InternalIP() (string, error) {
323         return getTrimmed("instance/network-interfaces/0/ip")
324 }
325
326 // ExternalIP returns the instance's primary external (public) IP address.
327 func ExternalIP() (string, error) {
328         return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
329 }
330
331 // Hostname returns the instance's hostname. This will be of the form
332 // "<instanceID>.c.<projID>.internal".
333 func Hostname() (string, error) {
334         return getTrimmed("instance/hostname")
335 }
336
337 // InstanceTags returns the list of user-defined instance tags,
338 // assigned when initially creating a GCE instance.
339 func InstanceTags() ([]string, error) {
340         var s []string
341         j, err := Get("instance/tags")
342         if err != nil {
343                 return nil, err
344         }
345         if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
346                 return nil, err
347         }
348         return s, nil
349 }
350
351 // InstanceID returns the current VM's numeric instance ID.
352 func InstanceID() (string, error) {
353         return instID.get()
354 }
355
356 // InstanceName returns the current VM's instance ID string.
357 func InstanceName() (string, error) {
358         host, err := Hostname()
359         if err != nil {
360                 return "", err
361         }
362         return strings.Split(host, ".")[0], nil
363 }
364
365 // Zone returns the current VM's zone, such as "us-central1-b".
366 func Zone() (string, error) {
367         zone, err := getTrimmed("instance/zone")
368         // zone is of the form "projects/<projNum>/zones/<zoneName>".
369         if err != nil {
370                 return "", err
371         }
372         return zone[strings.LastIndex(zone, "/")+1:], nil
373 }
374
375 // InstanceAttributes returns the list of user-defined attributes,
376 // assigned when initially creating a GCE VM instance. The value of an
377 // attribute can be obtained with InstanceAttributeValue.
378 func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
379
380 // ProjectAttributes returns the list of user-defined attributes
381 // applying to the project as a whole, not just this VM.  The value of
382 // an attribute can be obtained with ProjectAttributeValue.
383 func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
384
385 func lines(suffix string) ([]string, error) {
386         j, err := Get(suffix)
387         if err != nil {
388                 return nil, err
389         }
390         s := strings.Split(strings.TrimSpace(j), "\n")
391         for i := range s {
392                 s[i] = strings.TrimSpace(s[i])
393         }
394         return s, nil
395 }
396
397 // InstanceAttributeValue returns the value of the provided VM
398 // instance attribute.
399 //
400 // If the requested attribute is not defined, the returned error will
401 // be of type NotDefinedError.
402 //
403 // InstanceAttributeValue may return ("", nil) if the attribute was
404 // defined to be the empty string.
405 func InstanceAttributeValue(attr string) (string, error) {
406         return Get("instance/attributes/" + attr)
407 }
408
409 // ProjectAttributeValue returns the value of the provided
410 // project attribute.
411 //
412 // If the requested attribute is not defined, the returned error will
413 // be of type NotDefinedError.
414 //
415 // ProjectAttributeValue may return ("", nil) if the attribute was
416 // defined to be the empty string.
417 func ProjectAttributeValue(attr string) (string, error) {
418         return Get("project/attributes/" + attr)
419 }
420
421 // Scopes returns the service account scopes for the given account.
422 // The account may be empty or the string "default" to use the instance's
423 // main account.
424 func Scopes(serviceAccount string) ([]string, error) {
425         if serviceAccount == "" {
426                 serviceAccount = "default"
427         }
428         return lines("instance/service-accounts/" + serviceAccount + "/scopes")
429 }
430
431 func strsContains(ss []string, s string) bool {
432         for _, v := range ss {
433                 if v == s {
434                         return true
435                 }
436         }
437         return false
438 }