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://cloud.google.com/compute/docs/metadata/overview.
20package metadata // import "cloud.google.com/go/compute/metadata"
21
22import (
23 "context"
24 "encoding/json"
25 "fmt"
26 "io"
27 "log/slog"
28 "net"
29 "net/http"
30 "net/url"
31 "os"
32 "strings"
33 "sync"
34 "time"
35)
36
37const (
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
51type cachedValue struct {
52 k string
53 trim bool
54 mu sync.Mutex
55 v string
56}
57
58var (
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
64var defaultClient = &Client{
65 hc: newDefaultHTTPClient(),
66 logger: slog.New(noOpHandler{}),
67}
68
69func newDefaultHTTPClient() *http.Client {
70 return &http.Client{
71 Transport: &http.Transport{
72 Dial: (&net.Dialer{
73 Timeout: 2 * time.Second,
74 KeepAlive: 30 * time.Second,
75 }).Dial,
76 IdleConnTimeout: 60 * time.Second,
77 },
78 Timeout: 5 * time.Second,
79 }
80}
81
82// NotDefinedError is returned when requested metadata is not defined.
83//
84// The underlying string is the suffix after "/computeMetadata/v1/".
85//
86// This error is not returned if the value is defined to be the empty
87// string.
88type NotDefinedError string
89
90func (suffix NotDefinedError) Error() string {
91 return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
92}
93
94func (c *cachedValue) get(ctx context.Context, cl *Client) (v string, err error) {
95 defer c.mu.Unlock()
96 c.mu.Lock()
97 if c.v != "" {
98 return c.v, nil
99 }
100 if c.trim {
101 v, err = cl.getTrimmed(ctx, c.k)
102 } else {
103 v, err = cl.GetWithContext(ctx, c.k)
104 }
105 if err == nil {
106 c.v = v
107 }
108 return
109}
110
111var (
112 onGCEOnce sync.Once
113 onGCE bool
114)
115
116// OnGCE reports whether this process is running on Google Compute Platforms.
117// NOTE: True returned from `OnGCE` does not guarantee that the metadata server
118// is accessible from this process and have all the metadata defined.
119func OnGCE() bool {
120 onGCEOnce.Do(initOnGCE)
121 return onGCE
122}
123
124func initOnGCE() {
125 onGCE = testOnGCE()
126}
127
128func 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 := newDefaultHTTPClient().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 resolver := &net.Resolver{}
155 addrs, err := resolver.LookupHost(ctx, "metadata.google.internal.")
156 if err != nil || len(addrs) == 0 {
157 resc <- false
158 return
159 }
160 resc <- strsContains(addrs, metadataIP)
161 }()
162
163 tryHarder := systemInfoSuggestsGCE()
164 if tryHarder {
165 res := <-resc
166 if res {
167 // The first strategy succeeded, so let's use it.
168 return true
169 }
170 // Wait for either the DNS or metadata server probe to
171 // contradict the other one and say we are running on
172 // GCE. Give it a lot of time to do so, since the system
173 // info already suggests we're running on a GCE BIOS.
174 timer := time.NewTimer(5 * time.Second)
175 defer timer.Stop()
176 select {
177 case res = <-resc:
178 return res
179 case <-timer.C:
180 // Too slow. Who knows what this system is.
181 return false
182 }
183 }
184
185 // There's no hint from the system info that we're running on
186 // GCE, so use the first probe's result as truth, whether it's
187 // true or false. The goal here is to optimize for speed for
188 // users who are NOT running on GCE. We can't assume that
189 // either a DNS lookup or an HTTP request to a blackholed IP
190 // address is fast. Worst case this should return when the
191 // metaClient's Transport.ResponseHeaderTimeout or
192 // Transport.Dial.Timeout fires (in two seconds).
193 return <-resc
194}
195
196// Subscribe calls Client.SubscribeWithContext on the default client.
197//
198// Deprecated: Please use the context aware variant [SubscribeWithContext].
199func Subscribe(suffix string, fn func(v string, ok bool) error) error {
200 return defaultClient.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) })
201}
202
203// SubscribeWithContext calls Client.SubscribeWithContext on the default client.
204func SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error {
205 return defaultClient.SubscribeWithContext(ctx, suffix, fn)
206}
207
208// Get calls Client.GetWithContext on the default client.
209//
210// Deprecated: Please use the context aware variant [GetWithContext].
211func Get(suffix string) (string, error) {
212 return defaultClient.GetWithContext(context.Background(), suffix)
213}
214
215// GetWithContext calls Client.GetWithContext on the default client.
216func GetWithContext(ctx context.Context, suffix string) (string, error) {
217 return defaultClient.GetWithContext(ctx, suffix)
218}
219
220// ProjectID returns the current instance's project ID string.
221//
222// Deprecated: Please use the context aware variant [ProjectIDWithContext].
223func ProjectID() (string, error) {
224 return defaultClient.ProjectIDWithContext(context.Background())
225}
226
227// ProjectIDWithContext returns the current instance's project ID string.
228func ProjectIDWithContext(ctx context.Context) (string, error) {
229 return defaultClient.ProjectIDWithContext(ctx)
230}
231
232// NumericProjectID returns the current instance's numeric project ID.
233//
234// Deprecated: Please use the context aware variant [NumericProjectIDWithContext].
235func NumericProjectID() (string, error) {
236 return defaultClient.NumericProjectIDWithContext(context.Background())
237}
238
239// NumericProjectIDWithContext returns the current instance's numeric project ID.
240func NumericProjectIDWithContext(ctx context.Context) (string, error) {
241 return defaultClient.NumericProjectIDWithContext(ctx)
242}
243
244// InternalIP returns the instance's primary internal IP address.
245//
246// Deprecated: Please use the context aware variant [InternalIPWithContext].
247func InternalIP() (string, error) {
248 return defaultClient.InternalIPWithContext(context.Background())
249}
250
251// InternalIPWithContext returns the instance's primary internal IP address.
252func InternalIPWithContext(ctx context.Context) (string, error) {
253 return defaultClient.InternalIPWithContext(ctx)
254}
255
256// ExternalIP returns the instance's primary external (public) IP address.
257//
258// Deprecated: Please use the context aware variant [ExternalIPWithContext].
259func ExternalIP() (string, error) {
260 return defaultClient.ExternalIPWithContext(context.Background())
261}
262
263// ExternalIPWithContext returns the instance's primary external (public) IP address.
264func ExternalIPWithContext(ctx context.Context) (string, error) {
265 return defaultClient.ExternalIPWithContext(ctx)
266}
267
268// Email calls Client.EmailWithContext on the default client.
269//
270// Deprecated: Please use the context aware variant [EmailWithContext].
271func Email(serviceAccount string) (string, error) {
272 return defaultClient.EmailWithContext(context.Background(), serviceAccount)
273}
274
275// EmailWithContext calls Client.EmailWithContext on the default client.
276func EmailWithContext(ctx context.Context, serviceAccount string) (string, error) {
277 return defaultClient.EmailWithContext(ctx, serviceAccount)
278}
279
280// Hostname returns the instance's hostname. This will be of the form
281// "<instanceID>.c.<projID>.internal".
282//
283// Deprecated: Please use the context aware variant [HostnameWithContext].
284func Hostname() (string, error) {
285 return defaultClient.HostnameWithContext(context.Background())
286}
287
288// HostnameWithContext returns the instance's hostname. This will be of the form
289// "<instanceID>.c.<projID>.internal".
290func HostnameWithContext(ctx context.Context) (string, error) {
291 return defaultClient.HostnameWithContext(ctx)
292}
293
294// InstanceTags returns the list of user-defined instance tags,
295// assigned when initially creating a GCE instance.
296//
297// Deprecated: Please use the context aware variant [InstanceTagsWithContext].
298func InstanceTags() ([]string, error) {
299 return defaultClient.InstanceTagsWithContext(context.Background())
300}
301
302// InstanceTagsWithContext returns the list of user-defined instance tags,
303// assigned when initially creating a GCE instance.
304func InstanceTagsWithContext(ctx context.Context) ([]string, error) {
305 return defaultClient.InstanceTagsWithContext(ctx)
306}
307
308// InstanceID returns the current VM's numeric instance ID.
309//
310// Deprecated: Please use the context aware variant [InstanceIDWithContext].
311func InstanceID() (string, error) {
312 return defaultClient.InstanceIDWithContext(context.Background())
313}
314
315// InstanceIDWithContext returns the current VM's numeric instance ID.
316func InstanceIDWithContext(ctx context.Context) (string, error) {
317 return defaultClient.InstanceIDWithContext(ctx)
318}
319
320// InstanceName returns the current VM's instance ID string.
321//
322// Deprecated: Please use the context aware variant [InstanceNameWithContext].
323func InstanceName() (string, error) {
324 return defaultClient.InstanceNameWithContext(context.Background())
325}
326
327// InstanceNameWithContext returns the current VM's instance ID string.
328func InstanceNameWithContext(ctx context.Context) (string, error) {
329 return defaultClient.InstanceNameWithContext(ctx)
330}
331
332// Zone returns the current VM's zone, such as "us-central1-b".
333//
334// Deprecated: Please use the context aware variant [ZoneWithContext].
335func Zone() (string, error) {
336 return defaultClient.ZoneWithContext(context.Background())
337}
338
339// ZoneWithContext returns the current VM's zone, such as "us-central1-b".
340func ZoneWithContext(ctx context.Context) (string, error) {
341 return defaultClient.ZoneWithContext(ctx)
342}
343
344// InstanceAttributes calls Client.InstanceAttributesWithContext on the default client.
345//
346// Deprecated: Please use the context aware variant [InstanceAttributesWithContext.
347func InstanceAttributes() ([]string, error) {
348 return defaultClient.InstanceAttributesWithContext(context.Background())
349}
350
351// InstanceAttributesWithContext calls Client.ProjectAttributesWithContext on the default client.
352func InstanceAttributesWithContext(ctx context.Context) ([]string, error) {
353 return defaultClient.InstanceAttributesWithContext(ctx)
354}
355
356// ProjectAttributes calls Client.ProjectAttributesWithContext on the default client.
357//
358// Deprecated: Please use the context aware variant [ProjectAttributesWithContext].
359func ProjectAttributes() ([]string, error) {
360 return defaultClient.ProjectAttributesWithContext(context.Background())
361}
362
363// ProjectAttributesWithContext calls Client.ProjectAttributesWithContext on the default client.
364func ProjectAttributesWithContext(ctx context.Context) ([]string, error) {
365 return defaultClient.ProjectAttributesWithContext(ctx)
366}
367
368// InstanceAttributeValue calls Client.InstanceAttributeValueWithContext on the default client.
369//
370// Deprecated: Please use the context aware variant [InstanceAttributeValueWithContext].
371func InstanceAttributeValue(attr string) (string, error) {
372 return defaultClient.InstanceAttributeValueWithContext(context.Background(), attr)
373}
374
375// InstanceAttributeValueWithContext calls Client.InstanceAttributeValueWithContext on the default client.
376func InstanceAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
377 return defaultClient.InstanceAttributeValueWithContext(ctx, attr)
378}
379
380// ProjectAttributeValue calls Client.ProjectAttributeValueWithContext on the default client.
381//
382// Deprecated: Please use the context aware variant [ProjectAttributeValueWithContext].
383func ProjectAttributeValue(attr string) (string, error) {
384 return defaultClient.ProjectAttributeValueWithContext(context.Background(), attr)
385}
386
387// ProjectAttributeValueWithContext calls Client.ProjectAttributeValueWithContext on the default client.
388func ProjectAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
389 return defaultClient.ProjectAttributeValueWithContext(ctx, attr)
390}
391
392// Scopes calls Client.ScopesWithContext on the default client.
393//
394// Deprecated: Please use the context aware variant [ScopesWithContext].
395func Scopes(serviceAccount string) ([]string, error) {
396 return defaultClient.ScopesWithContext(context.Background(), serviceAccount)
397}
398
399// ScopesWithContext calls Client.ScopesWithContext on the default client.
400func ScopesWithContext(ctx context.Context, serviceAccount string) ([]string, error) {
401 return defaultClient.ScopesWithContext(ctx, serviceAccount)
402}
403
404func strsContains(ss []string, s string) bool {
405 for _, v := range ss {
406 if v == s {
407 return true
408 }
409 }
410 return false
411}
412
413// A Client provides metadata.
414type Client struct {
415 hc *http.Client
416 logger *slog.Logger
417}
418
419// Options for configuring a [Client].
420type Options struct {
421 // Client is the HTTP client used to make requests. Optional.
422 Client *http.Client
423 // Logger is used to log information about HTTP request and responses.
424 // If not provided, nothing will be logged. Optional.
425 Logger *slog.Logger
426}
427
428// NewClient returns a Client that can be used to fetch metadata.
429// Returns the client that uses the specified http.Client for HTTP requests.
430// If nil is specified, returns the default client.
431func NewClient(c *http.Client) *Client {
432 return NewWithOptions(&Options{
433 Client: c,
434 })
435}
436
437// NewWithOptions returns a Client that is configured with the provided Options.
438func NewWithOptions(opts *Options) *Client {
439 if opts == nil {
440 return defaultClient
441 }
442 client := opts.Client
443 if client == nil {
444 client = newDefaultHTTPClient()
445 }
446 logger := opts.Logger
447 if logger == nil {
448 logger = slog.New(noOpHandler{})
449 }
450 return &Client{hc: client, logger: logger}
451}
452
453// getETag returns a value from the metadata service as well as the associated ETag.
454// This func is otherwise equivalent to Get.
455func (c *Client) getETag(ctx context.Context, suffix string) (value, etag string, err error) {
456 // Using a fixed IP makes it very difficult to spoof the metadata service in
457 // a container, which is an important use-case for local testing of cloud
458 // deployments. To enable spoofing of the metadata service, the environment
459 // variable GCE_METADATA_HOST is first inspected to decide where metadata
460 // requests shall go.
461 host := os.Getenv(metadataHostEnv)
462 if host == "" {
463 // Using 169.254.169.254 instead of "metadata" here because Go
464 // binaries built with the "netgo" tag and without cgo won't
465 // know the search suffix for "metadata" is
466 // ".google.internal", and this IP address is documented as
467 // being stable anyway.
468 host = metadataIP
469 }
470 suffix = strings.TrimLeft(suffix, "/")
471 u := "http://" + host + "/computeMetadata/v1/" + suffix
472 req, err := http.NewRequestWithContext(ctx, "GET", u, nil)
473 if err != nil {
474 return "", "", err
475 }
476 req.Header.Set("Metadata-Flavor", "Google")
477 req.Header.Set("User-Agent", userAgent)
478 var res *http.Response
479 var reqErr error
480 var body []byte
481 retryer := newRetryer()
482 for {
483 c.logger.DebugContext(ctx, "metadata request", "request", httpRequest(req, nil))
484 res, reqErr = c.hc.Do(req)
485 var code int
486 if res != nil {
487 code = res.StatusCode
488 body, err = io.ReadAll(res.Body)
489 if err != nil {
490 res.Body.Close()
491 return "", "", err
492 }
493 c.logger.DebugContext(ctx, "metadata response", "response", httpResponse(res, body))
494 res.Body.Close()
495 }
496 if delay, shouldRetry := retryer.Retry(code, reqErr); shouldRetry {
497 if res != nil && res.Body != nil {
498 res.Body.Close()
499 }
500 if err := sleep(ctx, delay); err != nil {
501 return "", "", err
502 }
503 continue
504 }
505 break
506 }
507 if reqErr != nil {
508 return "", "", reqErr
509 }
510 if res.StatusCode == http.StatusNotFound {
511 return "", "", NotDefinedError(suffix)
512 }
513 if res.StatusCode != 200 {
514 return "", "", &Error{Code: res.StatusCode, Message: string(body)}
515 }
516 return string(body), res.Header.Get("Etag"), nil
517}
518
519// Get returns a value from the metadata service.
520// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
521//
522// If the GCE_METADATA_HOST environment variable is not defined, a default of
523// 169.254.169.254 will be used instead.
524//
525// If the requested metadata is not defined, the returned error will
526// be of type NotDefinedError.
527//
528// Deprecated: Please use the context aware variant [Client.GetWithContext].
529func (c *Client) Get(suffix string) (string, error) {
530 return c.GetWithContext(context.Background(), suffix)
531}
532
533// GetWithContext returns a value from the metadata service.
534// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
535//
536// If the GCE_METADATA_HOST environment variable is not defined, a default of
537// 169.254.169.254 will be used instead.
538//
539// If the requested metadata is not defined, the returned error will
540// be of type NotDefinedError.
541//
542// NOTE: Without an extra deadline in the context this call can take in the
543// worst case, with internal backoff retries, up to 15 seconds (e.g. when server
544// is responding slowly). Pass context with additional timeouts when needed.
545func (c *Client) GetWithContext(ctx context.Context, suffix string) (string, error) {
546 val, _, err := c.getETag(ctx, suffix)
547 return val, err
548}
549
550func (c *Client) getTrimmed(ctx context.Context, suffix string) (s string, err error) {
551 s, err = c.GetWithContext(ctx, suffix)
552 s = strings.TrimSpace(s)
553 return
554}
555
556func (c *Client) lines(ctx context.Context, suffix string) ([]string, error) {
557 j, err := c.GetWithContext(ctx, suffix)
558 if err != nil {
559 return nil, err
560 }
561 s := strings.Split(strings.TrimSpace(j), "\n")
562 for i := range s {
563 s[i] = strings.TrimSpace(s[i])
564 }
565 return s, nil
566}
567
568// ProjectID returns the current instance's project ID string.
569//
570// Deprecated: Please use the context aware variant [Client.ProjectIDWithContext].
571func (c *Client) ProjectID() (string, error) { return c.ProjectIDWithContext(context.Background()) }
572
573// ProjectIDWithContext returns the current instance's project ID string.
574func (c *Client) ProjectIDWithContext(ctx context.Context) (string, error) { return projID.get(ctx, c) }
575
576// NumericProjectID returns the current instance's numeric project ID.
577//
578// Deprecated: Please use the context aware variant [Client.NumericProjectIDWithContext].
579func (c *Client) NumericProjectID() (string, error) {
580 return c.NumericProjectIDWithContext(context.Background())
581}
582
583// NumericProjectIDWithContext returns the current instance's numeric project ID.
584func (c *Client) NumericProjectIDWithContext(ctx context.Context) (string, error) {
585 return projNum.get(ctx, c)
586}
587
588// InstanceID returns the current VM's numeric instance ID.
589//
590// Deprecated: Please use the context aware variant [Client.InstanceIDWithContext].
591func (c *Client) InstanceID() (string, error) {
592 return c.InstanceIDWithContext(context.Background())
593}
594
595// InstanceIDWithContext returns the current VM's numeric instance ID.
596func (c *Client) InstanceIDWithContext(ctx context.Context) (string, error) {
597 return instID.get(ctx, c)
598}
599
600// InternalIP returns the instance's primary internal IP address.
601//
602// Deprecated: Please use the context aware variant [Client.InternalIPWithContext].
603func (c *Client) InternalIP() (string, error) {
604 return c.InternalIPWithContext(context.Background())
605}
606
607// InternalIPWithContext returns the instance's primary internal IP address.
608func (c *Client) InternalIPWithContext(ctx context.Context) (string, error) {
609 return c.getTrimmed(ctx, "instance/network-interfaces/0/ip")
610}
611
612// Email returns the email address associated with the service account.
613//
614// Deprecated: Please use the context aware variant [Client.EmailWithContext].
615func (c *Client) Email(serviceAccount string) (string, error) {
616 return c.EmailWithContext(context.Background(), serviceAccount)
617}
618
619// EmailWithContext returns the email address associated with the service account.
620// The serviceAccount parameter default value (empty string or "default" value)
621// will use the instance's main account.
622func (c *Client) EmailWithContext(ctx context.Context, serviceAccount string) (string, error) {
623 if serviceAccount == "" {
624 serviceAccount = "default"
625 }
626 return c.getTrimmed(ctx, "instance/service-accounts/"+serviceAccount+"/email")
627}
628
629// ExternalIP returns the instance's primary external (public) IP address.
630//
631// Deprecated: Please use the context aware variant [Client.ExternalIPWithContext].
632func (c *Client) ExternalIP() (string, error) {
633 return c.ExternalIPWithContext(context.Background())
634}
635
636// ExternalIPWithContext returns the instance's primary external (public) IP address.
637func (c *Client) ExternalIPWithContext(ctx context.Context) (string, error) {
638 return c.getTrimmed(ctx, "instance/network-interfaces/0/access-configs/0/external-ip")
639}
640
641// Hostname returns the instance's hostname. This will be of the form
642// "<instanceID>.c.<projID>.internal".
643//
644// Deprecated: Please use the context aware variant [Client.HostnameWithContext].
645func (c *Client) Hostname() (string, error) {
646 return c.HostnameWithContext(context.Background())
647}
648
649// HostnameWithContext returns the instance's hostname. This will be of the form
650// "<instanceID>.c.<projID>.internal".
651func (c *Client) HostnameWithContext(ctx context.Context) (string, error) {
652 return c.getTrimmed(ctx, "instance/hostname")
653}
654
655// InstanceTags returns the list of user-defined instance tags.
656//
657// Deprecated: Please use the context aware variant [Client.InstanceTagsWithContext].
658func (c *Client) InstanceTags() ([]string, error) {
659 return c.InstanceTagsWithContext(context.Background())
660}
661
662// InstanceTagsWithContext returns the list of user-defined instance tags,
663// assigned when initially creating a GCE instance.
664func (c *Client) InstanceTagsWithContext(ctx context.Context) ([]string, error) {
665 var s []string
666 j, err := c.GetWithContext(ctx, "instance/tags")
667 if err != nil {
668 return nil, err
669 }
670 if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
671 return nil, err
672 }
673 return s, nil
674}
675
676// InstanceName returns the current VM's instance ID string.
677//
678// Deprecated: Please use the context aware variant [Client.InstanceNameWithContext].
679func (c *Client) InstanceName() (string, error) {
680 return c.InstanceNameWithContext(context.Background())
681}
682
683// InstanceNameWithContext returns the current VM's instance ID string.
684func (c *Client) InstanceNameWithContext(ctx context.Context) (string, error) {
685 return c.getTrimmed(ctx, "instance/name")
686}
687
688// Zone returns the current VM's zone, such as "us-central1-b".
689//
690// Deprecated: Please use the context aware variant [Client.ZoneWithContext].
691func (c *Client) Zone() (string, error) {
692 return c.ZoneWithContext(context.Background())
693}
694
695// ZoneWithContext returns the current VM's zone, such as "us-central1-b".
696func (c *Client) ZoneWithContext(ctx context.Context) (string, error) {
697 zone, err := c.getTrimmed(ctx, "instance/zone")
698 // zone is of the form "projects/<projNum>/zones/<zoneName>".
699 if err != nil {
700 return "", err
701 }
702 return zone[strings.LastIndex(zone, "/")+1:], nil
703}
704
705// InstanceAttributes returns the list of user-defined attributes,
706// assigned when initially creating a GCE VM instance. The value of an
707// attribute can be obtained with InstanceAttributeValue.
708//
709// Deprecated: Please use the context aware variant [Client.InstanceAttributesWithContext].
710func (c *Client) InstanceAttributes() ([]string, error) {
711 return c.InstanceAttributesWithContext(context.Background())
712}
713
714// InstanceAttributesWithContext returns the list of user-defined attributes,
715// assigned when initially creating a GCE VM instance. The value of an
716// attribute can be obtained with InstanceAttributeValue.
717func (c *Client) InstanceAttributesWithContext(ctx context.Context) ([]string, error) {
718 return c.lines(ctx, "instance/attributes/")
719}
720
721// ProjectAttributes returns the list of user-defined attributes
722// applying to the project as a whole, not just this VM. The value of
723// an attribute can be obtained with ProjectAttributeValue.
724//
725// Deprecated: Please use the context aware variant [Client.ProjectAttributesWithContext].
726func (c *Client) ProjectAttributes() ([]string, error) {
727 return c.ProjectAttributesWithContext(context.Background())
728}
729
730// ProjectAttributesWithContext returns the list of user-defined attributes
731// applying to the project as a whole, not just this VM. The value of
732// an attribute can be obtained with ProjectAttributeValue.
733func (c *Client) ProjectAttributesWithContext(ctx context.Context) ([]string, error) {
734 return c.lines(ctx, "project/attributes/")
735}
736
737// InstanceAttributeValue returns the value of the provided VM
738// instance attribute.
739//
740// If the requested attribute is not defined, the returned error will
741// be of type NotDefinedError.
742//
743// InstanceAttributeValue may return ("", nil) if the attribute was
744// defined to be the empty string.
745//
746// Deprecated: Please use the context aware variant [Client.InstanceAttributeValueWithContext].
747func (c *Client) InstanceAttributeValue(attr string) (string, error) {
748 return c.InstanceAttributeValueWithContext(context.Background(), attr)
749}
750
751// InstanceAttributeValueWithContext returns the value of the provided VM
752// instance attribute.
753//
754// If the requested attribute is not defined, the returned error will
755// be of type NotDefinedError.
756//
757// InstanceAttributeValue may return ("", nil) if the attribute was
758// defined to be the empty string.
759func (c *Client) InstanceAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
760 return c.GetWithContext(ctx, "instance/attributes/"+attr)
761}
762
763// ProjectAttributeValue returns the value of the provided
764// project attribute.
765//
766// If the requested attribute is not defined, the returned error will
767// be of type NotDefinedError.
768//
769// ProjectAttributeValue may return ("", nil) if the attribute was
770// defined to be the empty string.
771//
772// Deprecated: Please use the context aware variant [Client.ProjectAttributeValueWithContext].
773func (c *Client) ProjectAttributeValue(attr string) (string, error) {
774 return c.ProjectAttributeValueWithContext(context.Background(), attr)
775}
776
777// ProjectAttributeValueWithContext returns the value of the provided
778// project attribute.
779//
780// If the requested attribute is not defined, the returned error will
781// be of type NotDefinedError.
782//
783// ProjectAttributeValue may return ("", nil) if the attribute was
784// defined to be the empty string.
785func (c *Client) ProjectAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
786 return c.GetWithContext(ctx, "project/attributes/"+attr)
787}
788
789// Scopes returns the service account scopes for the given account.
790// The account may be empty or the string "default" to use the instance's
791// main account.
792//
793// Deprecated: Please use the context aware variant [Client.ScopesWithContext].
794func (c *Client) Scopes(serviceAccount string) ([]string, error) {
795 return c.ScopesWithContext(context.Background(), serviceAccount)
796}
797
798// ScopesWithContext returns the service account scopes for the given account.
799// The account may be empty or the string "default" to use the instance's
800// main account.
801func (c *Client) ScopesWithContext(ctx context.Context, serviceAccount string) ([]string, error) {
802 if serviceAccount == "" {
803 serviceAccount = "default"
804 }
805 return c.lines(ctx, "instance/service-accounts/"+serviceAccount+"/scopes")
806}
807
808// Subscribe subscribes to a value from the metadata service.
809// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
810// The suffix may contain query parameters.
811//
812// Deprecated: Please use the context aware variant [Client.SubscribeWithContext].
813func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
814 return c.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) })
815}
816
817// SubscribeWithContext subscribes to a value from the metadata service.
818// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
819// The suffix may contain query parameters.
820//
821// SubscribeWithContext calls fn with the latest metadata value indicated by the
822// provided suffix. If the metadata value is deleted, fn is called with the
823// empty string and ok false. Subscribe blocks until fn returns a non-nil error
824// or the value is deleted. Subscribe returns the error value returned from the
825// last call to fn, which may be nil when ok == false.
826func (c *Client) SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error {
827 const failedSubscribeSleep = time.Second * 5
828
829 // First check to see if the metadata value exists at all.
830 val, lastETag, err := c.getETag(ctx, suffix)
831 if err != nil {
832 return err
833 }
834
835 if err := fn(ctx, val, true); err != nil {
836 return err
837 }
838
839 ok := true
840 if strings.ContainsRune(suffix, '?') {
841 suffix += "&wait_for_change=true&last_etag="
842 } else {
843 suffix += "?wait_for_change=true&last_etag="
844 }
845 for {
846 val, etag, err := c.getETag(ctx, suffix+url.QueryEscape(lastETag))
847 if err != nil {
848 if _, deleted := err.(NotDefinedError); !deleted {
849 time.Sleep(failedSubscribeSleep)
850 continue // Retry on other errors.
851 }
852 ok = false
853 }
854 lastETag = etag
855
856 if err := fn(ctx, val, ok); err != nil || !ok {
857 return err
858 }
859 }
860}
861
862// Error contains an error response from the server.
863type Error struct {
864 // Code is the HTTP response status code.
865 Code int
866 // Message is the server response message.
867 Message string
868}
869
870func (e *Error) Error() string {
871 return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
872}