Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(compute/metadata): add logging #10864

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion compute/metadata/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module cloud.google.com/go/compute/metadata

go 1.21

require golang.org/x/sys v0.25.0
require (
github.com/googleapis/gax-go/v2 v2.13.0
golang.org/x/sys v0.25.0
)
2 changes: 2 additions & 0 deletions compute/metadata/go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
118 changes: 72 additions & 46 deletions compute/metadata/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ import (
"encoding/json"
"fmt"
"io"
"log/slog"
"net"
"net/http"
"net/url"
"os"
"strings"
"sync"
"time"

"github.com/googleapis/gax-go/v2/clog"
)

const (
Expand Down Expand Up @@ -60,7 +63,20 @@ var (
instID = &cachedValue{k: "instance/id", trim: true}
)

var defaultClient = &Client{hc: newDefaultHTTPClient()}
var (
once sync.Once
defaultMetadataClient *Client
)

func defaultClient() *Client {
once.Do(func() {
defaultMetadataClient = &Client{
hc: newDefaultHTTPClient(),
logger: clog.New(),
}
})
return defaultMetadataClient
}

func newDefaultHTTPClient() *http.Client {
return &http.Client{
Expand Down Expand Up @@ -193,208 +209,208 @@ func testOnGCE() bool {
//
// Deprecated: Please use the context aware variant [SubscribeWithContext].
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return defaultClient.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) })
return defaultClient().SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) })
}

// SubscribeWithContext calls Client.SubscribeWithContext on the default client.
func SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error {
return defaultClient.SubscribeWithContext(ctx, suffix, fn)
return defaultClient().SubscribeWithContext(ctx, suffix, fn)
}

// Get calls Client.GetWithContext on the default client.
//
// Deprecated: Please use the context aware variant [GetWithContext].
func Get(suffix string) (string, error) {
return defaultClient.GetWithContext(context.Background(), suffix)
return defaultClient().GetWithContext(context.Background(), suffix)
}

// GetWithContext calls Client.GetWithContext on the default client.
func GetWithContext(ctx context.Context, suffix string) (string, error) {
return defaultClient.GetWithContext(ctx, suffix)
return defaultClient().GetWithContext(ctx, suffix)
}

// ProjectID returns the current instance's project ID string.
//
// Deprecated: Please use the context aware variant [ProjectIDWithContext].
func ProjectID() (string, error) {
return defaultClient.ProjectIDWithContext(context.Background())
return defaultClient().ProjectIDWithContext(context.Background())
}

// ProjectIDWithContext returns the current instance's project ID string.
func ProjectIDWithContext(ctx context.Context) (string, error) {
return defaultClient.ProjectIDWithContext(ctx)
return defaultClient().ProjectIDWithContext(ctx)
}

// NumericProjectID returns the current instance's numeric project ID.
//
// Deprecated: Please use the context aware variant [NumericProjectIDWithContext].
func NumericProjectID() (string, error) {
return defaultClient.NumericProjectIDWithContext(context.Background())
return defaultClient().NumericProjectIDWithContext(context.Background())
}

// NumericProjectIDWithContext returns the current instance's numeric project ID.
func NumericProjectIDWithContext(ctx context.Context) (string, error) {
return defaultClient.NumericProjectIDWithContext(ctx)
return defaultClient().NumericProjectIDWithContext(ctx)
}

// InternalIP returns the instance's primary internal IP address.
//
// Deprecated: Please use the context aware variant [InternalIPWithContext].
func InternalIP() (string, error) {
return defaultClient.InternalIPWithContext(context.Background())
return defaultClient().InternalIPWithContext(context.Background())
}

// InternalIPWithContext returns the instance's primary internal IP address.
func InternalIPWithContext(ctx context.Context) (string, error) {
return defaultClient.InternalIPWithContext(ctx)
return defaultClient().InternalIPWithContext(ctx)
}

// ExternalIP returns the instance's primary external (public) IP address.
//
// Deprecated: Please use the context aware variant [ExternalIPWithContext].
func ExternalIP() (string, error) {
return defaultClient.ExternalIPWithContext(context.Background())
return defaultClient().ExternalIPWithContext(context.Background())
}

// ExternalIPWithContext returns the instance's primary external (public) IP address.
func ExternalIPWithContext(ctx context.Context) (string, error) {
return defaultClient.ExternalIPWithContext(ctx)
return defaultClient().ExternalIPWithContext(ctx)
}

// Email calls Client.EmailWithContext on the default client.
//
// Deprecated: Please use the context aware variant [EmailWithContext].
func Email(serviceAccount string) (string, error) {
return defaultClient.EmailWithContext(context.Background(), serviceAccount)
return defaultClient().EmailWithContext(context.Background(), serviceAccount)
}

// EmailWithContext calls Client.EmailWithContext on the default client.
func EmailWithContext(ctx context.Context, serviceAccount string) (string, error) {
return defaultClient.EmailWithContext(ctx, serviceAccount)
return defaultClient().EmailWithContext(ctx, serviceAccount)
}

// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
//
// Deprecated: Please use the context aware variant [HostnameWithContext].
func Hostname() (string, error) {
return defaultClient.HostnameWithContext(context.Background())
return defaultClient().HostnameWithContext(context.Background())
}

// HostnameWithContext returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func HostnameWithContext(ctx context.Context) (string, error) {
return defaultClient.HostnameWithContext(ctx)
return defaultClient().HostnameWithContext(ctx)
}

// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
//
// Deprecated: Please use the context aware variant [InstanceTagsWithContext].
func InstanceTags() ([]string, error) {
return defaultClient.InstanceTagsWithContext(context.Background())
return defaultClient().InstanceTagsWithContext(context.Background())
}

// InstanceTagsWithContext returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTagsWithContext(ctx context.Context) ([]string, error) {
return defaultClient.InstanceTagsWithContext(ctx)
return defaultClient().InstanceTagsWithContext(ctx)
}

// InstanceID returns the current VM's numeric instance ID.
//
// Deprecated: Please use the context aware variant [InstanceIDWithContext].
func InstanceID() (string, error) {
return defaultClient.InstanceIDWithContext(context.Background())
return defaultClient().InstanceIDWithContext(context.Background())
}

// InstanceIDWithContext returns the current VM's numeric instance ID.
func InstanceIDWithContext(ctx context.Context) (string, error) {
return defaultClient.InstanceIDWithContext(ctx)
return defaultClient().InstanceIDWithContext(ctx)
}

// InstanceName returns the current VM's instance ID string.
//
// Deprecated: Please use the context aware variant [InstanceNameWithContext].
func InstanceName() (string, error) {
return defaultClient.InstanceNameWithContext(context.Background())
return defaultClient().InstanceNameWithContext(context.Background())
}

// InstanceNameWithContext returns the current VM's instance ID string.
func InstanceNameWithContext(ctx context.Context) (string, error) {
return defaultClient.InstanceNameWithContext(ctx)
return defaultClient().InstanceNameWithContext(ctx)
}

// Zone returns the current VM's zone, such as "us-central1-b".
//
// Deprecated: Please use the context aware variant [ZoneWithContext].
func Zone() (string, error) {
return defaultClient.ZoneWithContext(context.Background())
return defaultClient().ZoneWithContext(context.Background())
}

// ZoneWithContext returns the current VM's zone, such as "us-central1-b".
func ZoneWithContext(ctx context.Context) (string, error) {
return defaultClient.ZoneWithContext(ctx)
return defaultClient().ZoneWithContext(ctx)
}

// InstanceAttributes calls Client.InstanceAttributesWithContext on the default client.
//
// Deprecated: Please use the context aware variant [InstanceAttributesWithContext.
func InstanceAttributes() ([]string, error) {
return defaultClient.InstanceAttributesWithContext(context.Background())
return defaultClient().InstanceAttributesWithContext(context.Background())
}

// InstanceAttributesWithContext calls Client.ProjectAttributesWithContext on the default client.
func InstanceAttributesWithContext(ctx context.Context) ([]string, error) {
return defaultClient.InstanceAttributesWithContext(ctx)
return defaultClient().InstanceAttributesWithContext(ctx)
}

// ProjectAttributes calls Client.ProjectAttributesWithContext on the default client.
//
// Deprecated: Please use the context aware variant [ProjectAttributesWithContext].
func ProjectAttributes() ([]string, error) {
return defaultClient.ProjectAttributesWithContext(context.Background())
return defaultClient().ProjectAttributesWithContext(context.Background())
}

// ProjectAttributesWithContext calls Client.ProjectAttributesWithContext on the default client.
func ProjectAttributesWithContext(ctx context.Context) ([]string, error) {
return defaultClient.ProjectAttributesWithContext(ctx)
return defaultClient().ProjectAttributesWithContext(ctx)
}

// InstanceAttributeValue calls Client.InstanceAttributeValueWithContext on the default client.
//
// Deprecated: Please use the context aware variant [InstanceAttributeValueWithContext].
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValueWithContext(context.Background(), attr)
return defaultClient().InstanceAttributeValueWithContext(context.Background(), attr)
}

// InstanceAttributeValueWithContext calls Client.InstanceAttributeValueWithContext on the default client.
func InstanceAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
return defaultClient.InstanceAttributeValueWithContext(ctx, attr)
return defaultClient().InstanceAttributeValueWithContext(ctx, attr)
}

// ProjectAttributeValue calls Client.ProjectAttributeValueWithContext on the default client.
//
// Deprecated: Please use the context aware variant [ProjectAttributeValueWithContext].
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValueWithContext(context.Background(), attr)
return defaultClient().ProjectAttributeValueWithContext(context.Background(), attr)
}

// ProjectAttributeValueWithContext calls Client.ProjectAttributeValueWithContext on the default client.
func ProjectAttributeValueWithContext(ctx context.Context, attr string) (string, error) {
return defaultClient.ProjectAttributeValueWithContext(ctx, attr)
return defaultClient().ProjectAttributeValueWithContext(ctx, attr)
}

// Scopes calls Client.ScopesWithContext on the default client.
//
// Deprecated: Please use the context aware variant [ScopesWithContext].
func Scopes(serviceAccount string) ([]string, error) {
return defaultClient.ScopesWithContext(context.Background(), serviceAccount)
return defaultClient().ScopesWithContext(context.Background(), serviceAccount)
}

// ScopesWithContext calls Client.ScopesWithContext on the default client.
func ScopesWithContext(ctx context.Context, serviceAccount string) ([]string, error) {
return defaultClient.ScopesWithContext(ctx, serviceAccount)
return defaultClient().ScopesWithContext(ctx, serviceAccount)
}

func strsContains(ss []string, s string) bool {
Expand All @@ -408,17 +424,21 @@ func strsContains(ss []string, s string) bool {

// A Client provides metadata.
type Client struct {
hc *http.Client
hc *http.Client
logger *slog.Logger
}

// NewClient returns a Client that can be used to fetch metadata.
// Returns the client that uses the specified http.Client for HTTP requests.
// If nil is specified, returns the default client.
// If nil is specified, returns a default client.
func NewClient(c *http.Client) *Client {
if c == nil {
return defaultClient
return defaultClient()
}
return &Client{
hc: c,
logger: clog.New(),
}
return &Client{hc: c}
}

// getETag returns a value from the metadata service as well as the associated ETag.
Expand Down Expand Up @@ -448,13 +468,24 @@ func (c *Client) getETag(ctx context.Context, suffix string) (value, etag string
req.Header.Set("User-Agent", userAgent)
var res *http.Response
var reqErr error
var body []byte
retryer := newRetryer()
for {
c.logger.Log(ctx, clog.DynamicLevel(), "metadata request", "request", clog.HTTPRequest(req, nil))
res, reqErr = c.hc.Do(req)
var code int
if res != nil {
code = res.StatusCode
}
if res != nil {
body, err = io.ReadAll(res.Body)
if err != nil {
res.Body.Close()
return "", "", err
}
c.logger.Log(ctx, clog.DynamicLevel(), "metadata response", "response", clog.HTTPResponse(res, body))
res.Body.Close()
}
if delay, shouldRetry := retryer.Retry(code, reqErr); shouldRetry {
if err := sleep(ctx, delay); err != nil {
return "", "", err
Expand All @@ -466,18 +497,13 @@ func (c *Client) getETag(ctx context.Context, suffix string) (value, etag string
if reqErr != nil {
return "", "", reqErr
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
all, err := io.ReadAll(res.Body)
if err != nil {
return "", "", err
}
if res.StatusCode != 200 {
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
return "", "", &Error{Code: res.StatusCode, Message: string(body)}
}
return string(all), res.Header.Get("Etag"), nil
return string(body), res.Header.Get("Etag"), nil
}

// Get returns a value from the metadata service.
Expand Down
2 changes: 0 additions & 2 deletions compute/metadata/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"bytes"
"context"
"io"
"log"
"net/http"
"os"
"strings"
Expand Down Expand Up @@ -70,7 +69,6 @@ func TestGetFailsOnBadURL(t *testing.T) {
c := NewClient(http.DefaultClient)
t.Setenv(metadataHostEnv, "host:-1")
_, err := c.GetWithContext(ctx, "suffix")
log.Printf("%v", err)
if err == nil {
t.Errorf("got %v, want non-nil error", err)
}
Expand Down
3 changes: 3 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ go.opentelemetry.io/otel/metric v1.23.1/go.mod h1:mpG2QPlAfnK8yNhNJAxDZruU9Y1/Hu
go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw=
go.opentelemetry.io/otel/trace v1.23.1/go.mod h1:4IpnpJFwr1mo/6HL8XIPJaE9y0+u1KcVmuW7dwFSVrI=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
Expand All @@ -81,4 +82,6 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20231120223509-83a465c02
google.golang.org/genproto/googleapis/bytestream v0.0.0-20240102182953-50ed04b92917/go.mod h1:O9TvT7A9NLgdqqF0JJXJ+axpaoYiEb8txGmkvy+AvLc=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20240513163218-0867130af1f8/go.mod h1:RCpt0+3mpEDPldc32vXBM8ADXlFL95T8Chxx0nv0/zE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230725213213-b022f6e96895/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM=
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
Loading