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}