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