metadata.go raw

   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