github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/cloud/compute/metadata/metadata.go (about)

     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
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net"
    27  	"net/http"
    28  	"strings"
    29  	"sync"
    30  	"time"
    31  
    32  	"google.golang.org/cloud/internal"
    33  )
    34  
    35  type cachedValue struct {
    36  	k    string
    37  	trim bool
    38  	mu   sync.Mutex
    39  	v    string
    40  }
    41  
    42  var (
    43  	projID  = &cachedValue{k: "project/project-id", trim: true}
    44  	projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
    45  	instID  = &cachedValue{k: "instance/id", trim: true}
    46  )
    47  
    48  var metaClient = &http.Client{
    49  	Transport: &internal.UATransport{
    50  		Base: &http.Transport{
    51  			Dial: (&net.Dialer{
    52  				Timeout:   750 * time.Millisecond,
    53  				KeepAlive: 30 * time.Second,
    54  			}).Dial,
    55  			ResponseHeaderTimeout: 750 * time.Millisecond,
    56  		},
    57  	},
    58  }
    59  
    60  // Get returns a value from the metadata service.
    61  // The suffix is appended to "http://metadata/computeMetadata/v1/".
    62  func Get(suffix string) (string, error) {
    63  	// Using 169.254.169.254 instead of "metadata" here because Go
    64  	// binaries built with the "netgo" tag and without cgo won't
    65  	// know the search suffix for "metadata" is
    66  	// ".google.internal", and this IP address is documented as
    67  	// being stable anyway.
    68  	url := "http://169.254.169.254/computeMetadata/v1/" + suffix
    69  	req, _ := http.NewRequest("GET", url, nil)
    70  	req.Header.Set("Metadata-Flavor", "Google")
    71  	res, err := metaClient.Do(req)
    72  	if err != nil {
    73  		return "", err
    74  	}
    75  	defer res.Body.Close()
    76  	if res.StatusCode != 200 {
    77  		return "", fmt.Errorf("status code %d trying to fetch %s", res.StatusCode, url)
    78  	}
    79  	all, err := ioutil.ReadAll(res.Body)
    80  	if err != nil {
    81  		return "", err
    82  	}
    83  	return string(all), nil
    84  }
    85  
    86  func getTrimmed(suffix string) (s string, err error) {
    87  	s, err = Get(suffix)
    88  	s = strings.TrimSpace(s)
    89  	return
    90  }
    91  
    92  func (c *cachedValue) get() (v string, err error) {
    93  	defer c.mu.Unlock()
    94  	c.mu.Lock()
    95  	if c.v != "" {
    96  		return c.v, nil
    97  	}
    98  	if c.trim {
    99  		v, err = getTrimmed(c.k)
   100  	} else {
   101  		v, err = Get(c.k)
   102  	}
   103  	if err == nil {
   104  		c.v = v
   105  	}
   106  	return
   107  }
   108  
   109  var onGCE struct {
   110  	sync.Mutex
   111  	set bool
   112  	v   bool
   113  }
   114  
   115  // OnGCE reports whether this process is running on Google Compute Engine.
   116  func OnGCE() bool {
   117  	defer onGCE.Unlock()
   118  	onGCE.Lock()
   119  	if onGCE.set {
   120  		return onGCE.v
   121  	}
   122  	onGCE.set = true
   123  
   124  	// We use the DNS name of the metadata service here instead of the IP address
   125  	// because we expect that to fail faster in the not-on-GCE case.
   126  	res, err := metaClient.Get("http://metadata.google.internal")
   127  	if err != nil {
   128  		return false
   129  	}
   130  	onGCE.v = res.Header.Get("Metadata-Flavor") == "Google"
   131  	return onGCE.v
   132  }
   133  
   134  // ProjectID returns the current instance's project ID string.
   135  func ProjectID() (string, error) { return projID.get() }
   136  
   137  // NumericProjectID returns the current instance's numeric project ID.
   138  func NumericProjectID() (string, error) { return projNum.get() }
   139  
   140  // InternalIP returns the instance's primary internal IP address.
   141  func InternalIP() (string, error) {
   142  	return getTrimmed("instance/network-interfaces/0/ip")
   143  }
   144  
   145  // ExternalIP returns the instance's primary external (public) IP address.
   146  func ExternalIP() (string, error) {
   147  	return getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
   148  }
   149  
   150  // Hostname returns the instance's hostname. This will probably be of
   151  // the form "INSTANCENAME.c.PROJECT.internal" but that isn't
   152  // guaranteed.
   153  //
   154  // TODO: what is this defined to be? Docs say "The host name of the
   155  // instance."
   156  func Hostname() (string, error) {
   157  	return getTrimmed("network-interfaces/0/ip")
   158  }
   159  
   160  // InstanceTags returns the list of user-defined instance tags,
   161  // assigned when initially creating a GCE instance.
   162  func InstanceTags() ([]string, error) {
   163  	var s []string
   164  	j, err := Get("instance/tags")
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
   169  		return nil, err
   170  	}
   171  	return s, nil
   172  }
   173  
   174  // InstanceID returns the current VM's numeric instance ID.
   175  func InstanceID() (string, error) {
   176  	return instID.get()
   177  }
   178  
   179  // InstanceAttributes returns the list of user-defined attributes,
   180  // assigned when initially creating a GCE VM instance. The value of an
   181  // attribute can be obtained with InstanceAttributeValue.
   182  func InstanceAttributes() ([]string, error) { return lines("instance/attributes/") }
   183  
   184  // ProjectAttributes returns the list of user-defined attributes
   185  // applying to the project as a whole, not just this VM.  The value of
   186  // an attribute can be obtained with ProjectAttributeValue.
   187  func ProjectAttributes() ([]string, error) { return lines("project/attributes/") }
   188  
   189  func lines(suffix string) ([]string, error) {
   190  	j, err := Get(suffix)
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	s := strings.Split(strings.TrimSpace(j), "\n")
   195  	for i := range s {
   196  		s[i] = strings.TrimSpace(s[i])
   197  	}
   198  	return s, nil
   199  }
   200  
   201  // InstanceAttributeValue returns the value of the provided VM
   202  // instance attribute.
   203  func InstanceAttributeValue(attr string) (string, error) {
   204  	return Get("instance/attributes/" + attr)
   205  }
   206  
   207  // ProjectAttributeValue returns the value of the provided
   208  // project attribute.
   209  func ProjectAttributeValue(attr string) (string, error) {
   210  	return Get("project/attributes/" + attr)
   211  }
   212  
   213  // Scopes returns the service account scopes for the given account.
   214  // The account may be empty or the string "default" to use the instance's
   215  // main account.
   216  func Scopes(serviceAccount string) ([]string, error) {
   217  	if serviceAccount == "" {
   218  		serviceAccount = "default"
   219  	}
   220  	return lines("instance/service-accounts/" + serviceAccount + "/scopes")
   221  }