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 }