From 668618c22b025c0b5a64d074b514a2c04a250977 Mon Sep 17 00:00:00 2001 From: bwplotka Date: Tue, 11 Jun 2024 10:46:06 +0100 Subject: [PATCH] fix(export): add support in onGCE + no conn to metadata case Fixes b/344740239 (edge case with GKE Metadata Server and GKE sandbox). * Added debug logging. * Updated metadata deps to get https://github.com/googleapis/google-cloud-go/pull/9733 & use timeout-ed context * Moved risky logic from FromFlags, see code comment why. * Added regession test. ### Alternatives Everything we do in FromFlags or constructor is within readines period. We could consider moving potentially "slow" things on slow network or metadata srv to exporter.Run. This could be questionable as for GMP to work we at end need export functionality to work, so delaying that information or making it surface in separation to readiness might not be helpful. Signed-off-by: bwplotka --- go.mod | 3 +- go.sum | 5 +- pkg/export/setup/setup.go | 136 +++++++++--- pkg/export/setup/setup_test.go | 86 ++++++++ vendor/cloud.google.com/go/compute/LICENSE | 202 ------------------ .../go/compute/internal/version.go | 18 -- .../go/compute/metadata/CHANGES.md | 7 + .../go/compute/metadata/metadata.go | 104 ++++++--- .../go/compute/metadata/retry.go | 2 +- .../go/compute/metadata/tidyfix.go | 23 -- vendor/modules.txt | 5 +- 11 files changed, 271 insertions(+), 320 deletions(-) create mode 100644 pkg/export/setup/setup_test.go delete mode 100644 vendor/cloud.google.com/go/compute/LICENSE delete mode 100644 vendor/cloud.google.com/go/compute/internal/version.go delete mode 100644 vendor/cloud.google.com/go/compute/metadata/tidyfix.go diff --git a/go.mod b/go.mod index 6163d00842..4a41d593e4 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/GoogleCloudPlatform/prometheus-engine go 1.22.3 require ( - cloud.google.com/go/compute/metadata v0.2.3 + cloud.google.com/go/compute/metadata v0.3.0 cloud.google.com/go/monitoring v1.18.0 github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 github.com/alecthomas/kingpin/v2 v2.4.0 @@ -47,7 +47,6 @@ require ( ) require ( - cloud.google.com/go/compute v1.23.3 // indirect github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 // indirect diff --git a/go.sum b/go.sum index 055a2d86ac..a0e4d39001 100644 --- a/go.sum +++ b/go.sum @@ -174,13 +174,12 @@ cloud.google.com/go/compute v1.18.0/go.mod h1:1X7yHxec2Ga+Ss6jPyjxRxpu2uu7PLgsOV cloud.google.com/go/compute v1.19.0/go.mod h1:rikpw2y+UMidAe9tISo04EHNOIf42RLYF/q8Bs93scU= cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= +cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= diff --git a/pkg/export/setup/setup.go b/pkg/export/setup/setup.go index 6e9f2e1258..971438504b 100644 --- a/pkg/export/setup/setup.go +++ b/pkg/export/setup/setup.go @@ -21,12 +21,15 @@ import ( "fmt" "os" "strconv" + "strings" + "time" "cloud.google.com/go/compute/metadata" "github.com/GoogleCloudPlatform/prometheus-engine/pkg/export" "github.com/GoogleCloudPlatform/prometheus-engine/pkg/lease" kingpin "github.com/alecthomas/kingpin/v2" "github.com/go-kit/log" + "github.com/go-kit/log/level" "github.com/google/shlex" "github.com/prometheus/client_golang/prometheus" "k8s.io/client-go/rest" @@ -90,34 +93,13 @@ func Global() *export.Exporter { // FromFlags returns a constructor for a new exporter that is configured through flags that are // registered with the given application. The constructor must be called after the flags // have been parsed. +// +// NOTE(bwplotka): This method should only setup flags, no extra logic should be done here +// as we don't have a logger ready, and nothing was logged for the binary yet. +// Potential risky logic can be moved to the returned function we return here. +// See b/344740239 on how hard is to debug regressions here. func FromFlags(a *kingpin.Application, userAgentProduct string) func(context.Context, log.Logger, prometheus.Registerer) (*export.Exporter, error) { var opts export.ExporterOpts - env := UAEnvUnspecified - - // Default target fields if we can detect them in GCP. - if metadata.OnGCE() { - env = UAEnvGCE - opts.ProjectID, _ = metadata.ProjectID() - opts.Cluster, _ = metadata.InstanceAttributeValue("cluster-name") - if opts.Cluster != "" { - env = UAEnvGKE - } - // These attributes are set for GKE nodes. For the location, we first check - // the cluster location, which may be a zone or a region. We must always use that value - // to avoid collisions with other clusters, as the same cluster name may be reused - // in different locations. - // In particular, we cannot set the location to the node's zone for a regional cluster, - // even though this would provide more accuracy, as there may also be a zonal cluster - // with the same name. - // We only fallback to the node zone as the location if no cluster location exists to - // default for deployments on GCE. - if loc, _ := metadata.InstanceAttributeValue("cluster-location"); loc != "" { - opts.Location = loc - } else { - opts.Location, _ = metadata.Zone() - } - } - opts.UserAgentEnv = env opts.UserAgentProduct = userAgentProduct a.Flag("export.disable", "Disable exporting to GCM."). @@ -132,20 +114,17 @@ func FromFlags(a *kingpin.Application, userAgentProduct string) func(context.Con a.Flag("export.credentials-file", "Credentials file for authentication with the GCM API."). Default("").StringVar(&opts.CredentialsFile) - a.Flag("export.label.project-id", fmt.Sprintf("Default project ID set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyProjectID)). - Default(opts.ProjectID).StringVar(&opts.ProjectID) + a.Flag("export.label.project-id", fmt.Sprintf("Default project ID set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyProjectID)).StringVar(&opts.ProjectID) a.Flag("export.user-agent-mode", fmt.Sprintf("Mode for user agent used for requests against the GCM API. Valid values are %q, %q, %q, %q or %q.", UAModeGKE, UAModeKubectl, UAModeAVMW, UAModeABM, UAModeUnspecified)). - Default("unspecified").EnumVar(&opts.UserAgentMode, UAModeUnspecified, UAModeGKE, UAModeKubectl, UAModeAVMW, UAModeABM) + Default(UAModeUnspecified).EnumVar(&opts.UserAgentMode, UAModeUnspecified, UAModeGKE, UAModeKubectl, UAModeAVMW, UAModeABM) // The location and cluster flag should probably not be used. On the other hand, they make it easy // to populate these important values in the monitored resource without interfering with existing // Prometheus configuration. - a.Flag("export.label.location", fmt.Sprintf("The default location set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyLocation)). - Default(opts.Location).StringVar(&opts.Location) + a.Flag("export.label.location", fmt.Sprintf("The default location set for all exported data. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyLocation)).StringVar(&opts.Location) - a.Flag("export.label.cluster", fmt.Sprintf("The default cluster set for all scraped targets. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyCluster)). - Default(opts.Cluster).StringVar(&opts.Cluster) + a.Flag("export.label.cluster", fmt.Sprintf("The default cluster set for all scraped targets. Prefer setting the external label %q in the Prometheus configuration if not using the auto-discovered default.", export.KeyCluster)).StringVar(&opts.Cluster) a.Flag("export.match", `A Prometheus time series matcher. Can be repeated. Every time series must match at least one of the matchers to be exported. This flag can be used equivalently to the match[] parameter of the Prometheus federation endpoint to selectively export data. (Example: --export.match='{job="prometheus"}' --export.match='{__name__=~"job:.*"})`). Default("").SetValue(&opts.Matchers) @@ -184,7 +163,28 @@ func FromFlags(a *kingpin.Application, userAgentProduct string) func(context.Con kubeName := a.Flag("export.ha.kube.name", "Name for the HA locking resource. Must be identical across replicas. May be set through the KUBE_NAME environment variable."). Default("").OverrideDefaultFromEnvar("KUBE_NAME").String() + // NOTE(bwplotka): This function will be likely performed within "getting ready" period, so before readiness is + // set to ready. Typical readiness can be as fast as 30s, so make sure this code timeouts faster than that. return func(ctx context.Context, logger log.Logger, metrics prometheus.Registerer) (*export.Exporter, error) { + level.Debug(logger).Log("msg", "started constructing the GCM export logic") + + // NOTE: OnGCE() actually means "onGCP", or really any place with + // certain IP (of metadata server) or DNS entry available. For example on GKE. + if metadata.OnGCE() { + // When, potentially, on GCE we attempt to populate some, unspecified option entries + // like project ID, cluster, location, zone and user agent from GCP metadata server. + // + // This will be used to get *some* data, if not specified override by flags, to + // use if labels or external label settings does not have those set. Those will + // be used as crucial labels for export to work against GCM's Prometheus target. + // + // NOTE: Set a 10s hard limit due to readiness and liveliness probes during this stage. + mctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + tryPopulateUnspecifiedFromMetadata(mctx, logger, &opts) + cancel() + level.Debug(logger).Log("msg", "best-effort on-GCP metadata gathering finished") + } + switch *haBackend { case HABackendNone: case HABackendKubernetes: @@ -230,3 +230,73 @@ func loadKubeConfig(kubeconfigPath string) (*rest.Config, error) { func ExtraArgs() ([]string, error) { return shlex.Split(os.Getenv(ExtraArgsEnvvar)) } + +// tryPopulateUnspecifiedFromMetadata assumes we are in GCP, with unknown state +// of the metadata server. This is best-effort, so when the metadata server +// is not accessible on GCP, because it was disabled (404 errors), or not accessible +// (connection refused, slow network, e.g. sandbox + metadata disabled) +// it's a noop. Make sure to pass context with a timeout. +func tryPopulateUnspecifiedFromMetadata(ctx context.Context, logger log.Logger, opts *export.ExporterOpts) { + const ( + projectIDPath = "project/project-id" + clusterNamePath = "instance/attributes/cluster-name" + clusterLocationPath = "instance/attributes/cluster-location" + zonePath = "instance/zone" + ) + + env := UAEnvGCE // This also means GKE with metadata server disabled. + + c := metadata.NewClient(nil) + // Mimick metadata.InstanceAttributeValue("cluster-name") but with context. + gkeClusterName, err := c.GetWithContext(ctx, clusterNamePath) + if err != nil { + level.Debug(logger).Log("msg", "fetching entry from GCP metadata server failed; skipping", "key", clusterNamePath, "err", err) + } else if gkeClusterName != "" { + env = UAEnvGKE + if opts.Cluster == "" { + opts.Cluster = gkeClusterName + } + } + + if opts.ProjectID == "" { + // Mimick metadata.ProjectID() but with context. + projectID, err := c.GetWithContext(ctx, projectIDPath) + if err != nil { + level.Debug(logger).Log("msg", "fetching entry from GCP metadata server failed; skipping", "key", projectIDPath, "err", err) + } else { + opts.ProjectID = strings.TrimSpace(projectID) + } + } + if opts.Location == "" { + // These attributes are set for GKE nodes. For the location, we first check + // the cluster location, which may be a zone or a region. We must always use that value + // to avoid collisions with other clusters, as the same cluster name may be reused + // in different locations. + // In particular, we cannot set the location to the node's zone for a regional cluster, + // even though this would provide more accuracy, as there may also be a zonal cluster + // with the same name. + // + // We only fallback to the node zone as the location if no cluster location exists to + // default for deployments on GCE. + + // Mimick metadata.InstanceAttributeValue("cluster-location") but with context. + loc, err := c.GetWithContext(ctx, clusterLocationPath) + if err != nil { + level.Debug(logger).Log("msg", "fetching entry from GCP metadata server failed; falling back to zone", "key", clusterLocationPath, "err", err) + zone, err := c.GetWithContext(ctx, zonePath) + if err != nil { + level.Debug(logger).Log("msg", "fetching entry from GCP metadata server failed; skipping", "key", zonePath, "err", err) + } else { + zone = strings.TrimSpace(zone) + // zone is of the form "projects//zones/". + opts.Location = zone[strings.LastIndex(zone, "/")+1:] + } + } else { + opts.Location = loc + } + } + if opts.UserAgentEnv == UAEnvUnspecified { + // We acknowledge that, if user set unspecified on purpose, this will override. + opts.UserAgentEnv = env + } +} diff --git a/pkg/export/setup/setup_test.go b/pkg/export/setup/setup_test.go new file mode 100644 index 0000000000..d94c2f2d04 --- /dev/null +++ b/pkg/export/setup/setup_test.go @@ -0,0 +1,86 @@ +package setup + +import ( + "context" + "net/http" + "net/http/httptest" + "os" + "sync" + "testing" + "time" + + "cloud.google.com/go/compute/metadata" + "github.com/GoogleCloudPlatform/prometheus-engine/pkg/export" + "github.com/alecthomas/kingpin/v2" + "github.com/go-kit/log" + "github.com/google/go-cmp/cmp" +) + +func TestFromFlags_NotOnGCE(t *testing.T) { + // Asserting there is actually no GCE underneath. + if metadata.OnGCE() { + t.Fatal("This test assumes we don't run on GCP") + } + + fake := kingpin.New("test", "test") + newExport := FromFlags(fake, "product") + // Our readines is default (3 * 10s), so ensure FromFlags is never longer than 30s. + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Fake app invocation. + if _, err := fake.Parse(nil); err != nil { + t.Fatal(err) + } + + // Make sure constructor works too. + if _, err := newExport(ctx, log.NewLogfmtLogger(os.Stderr), nil); err != nil { + t.Fatal(err) + } +} + +// Regression test for b/344740239. We ensure that even stuck metadata servers +// calls will timeout correctly (we propagate context properly). +func TestTryPopulateUnspecifiedFromMetadata(t *testing.T) { + // Asserting there is actually no GCE underneath. + if metadata.OnGCE() { + t.Fatal("This test assumes we don't run on GCP") + } + + var wg sync.WaitGroup + wg.Add(1) + + // Setup fake HTTP server taking forever. + s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + wg.Wait() + })) + t.Cleanup(func() { + wg.Done() + s.Close() + _ = os.Setenv("GCE_METADATA_HOST", "") + }) + + // Main "readiness" like timeout that we have to be faster than. + testCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + + // Inject metadata URL for our server (does not matter if 404). + if err := os.Setenv("GCE_METADATA_HOST", s.Listener.Addr().String()); err != nil { + t.Fatal(err) + } + + // We expect this to finish sooner. + ctx, cancel2 := context.WithTimeout(context.Background(), 1*time.Second) + defer cancel2() + opts := export.ExporterOpts{} + tryPopulateUnspecifiedFromMetadata(ctx, log.NewLogfmtLogger(os.Stderr), &opts) + if diff := cmp.Diff(export.ExporterOpts{}, opts); diff != "" { + t.Fatal("expected no options populated", diff) + } + // We expect to finish the test, before testCtx is cancelled. + // TODO(bwplotka): Assert we are not exiting faster because metadata cannot access our HTTP test server. + // I checked manually for inverse to fail (e.g. setting in ctx longer than textCtx). + if testCtx.Err() != nil { + t.Fatal("tryPopulateUnspecifiedFromMetadata took 30s to complete, it should timeout after 1s") + } +} diff --git a/vendor/cloud.google.com/go/compute/LICENSE b/vendor/cloud.google.com/go/compute/LICENSE deleted file mode 100644 index d645695673..0000000000 --- a/vendor/cloud.google.com/go/compute/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/vendor/cloud.google.com/go/compute/internal/version.go b/vendor/cloud.google.com/go/compute/internal/version.go deleted file mode 100644 index 540ad16ac4..0000000000 --- a/vendor/cloud.google.com/go/compute/internal/version.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package internal - -// Version is the current tagged release of the library. -const Version = "1.23.3" diff --git a/vendor/cloud.google.com/go/compute/metadata/CHANGES.md b/vendor/cloud.google.com/go/compute/metadata/CHANGES.md index 06b957349a..967e060747 100644 --- a/vendor/cloud.google.com/go/compute/metadata/CHANGES.md +++ b/vendor/cloud.google.com/go/compute/metadata/CHANGES.md @@ -1,5 +1,12 @@ # Changes +## [0.3.0](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.3...compute/metadata/v0.3.0) (2024-04-15) + + +### Features + +* **compute/metadata:** Add context aware functions ([#9733](https://github.com/googleapis/google-cloud-go/issues/9733)) ([e4eb5b4](https://github.com/googleapis/google-cloud-go/commit/e4eb5b46ee2aec9d2fc18300bfd66015e25a0510)) + ## [0.2.3](https://github.com/googleapis/google-cloud-go/compare/compute/metadata/v0.2.2...compute/metadata/v0.2.3) (2022-12-15) diff --git a/vendor/cloud.google.com/go/compute/metadata/metadata.go b/vendor/cloud.google.com/go/compute/metadata/metadata.go index c17faa142a..f67e3c7eea 100644 --- a/vendor/cloud.google.com/go/compute/metadata/metadata.go +++ b/vendor/cloud.google.com/go/compute/metadata/metadata.go @@ -23,7 +23,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net" "net/http" "net/url" @@ -95,9 +95,9 @@ func (c *cachedValue) get(cl *Client) (v string, err error) { return c.v, nil } if c.trim { - v, err = cl.getTrimmed(c.k) + v, err = cl.getTrimmed(context.Background(), c.k) } else { - v, err = cl.Get(c.k) + v, err = cl.GetWithContext(context.Background(), c.k) } if err == nil { c.v = v @@ -197,18 +197,32 @@ func systemInfoSuggestsGCE() bool { // We don't have any non-Linux clues available, at least yet. return false } - slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name") + slurp, _ := os.ReadFile("/sys/class/dmi/id/product_name") name := strings.TrimSpace(string(slurp)) return name == "Google" || name == "Google Compute Engine" } -// Subscribe calls Client.Subscribe on the default client. +// Subscribe calls Client.SubscribeWithContext on the default client. func Subscribe(suffix string, fn func(v string, ok bool) error) error { - return defaultClient.Subscribe(suffix, fn) + return defaultClient.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) }) } -// Get calls Client.Get on the default client. -func Get(suffix string) (string, error) { return defaultClient.Get(suffix) } +// 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) +} + +// 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) +} + +// GetWithContext calls Client.GetWithContext on the default client. +func GetWithContext(ctx context.Context, suffix string) (string, error) { + return defaultClient.GetWithContext(ctx, suffix) +} // ProjectID returns the current instance's project ID string. func ProjectID() (string, error) { return defaultClient.ProjectID() } @@ -288,8 +302,7 @@ func NewClient(c *http.Client) *Client { // getETag returns a value from the metadata service as well as the associated ETag. // This func is otherwise equivalent to Get. -func (c *Client) getETag(suffix string) (value, etag string, err error) { - ctx := context.TODO() +func (c *Client) getETag(ctx context.Context, suffix string) (value, etag string, err error) { // Using a fixed IP makes it very difficult to spoof the metadata service in // a container, which is an important use-case for local testing of cloud // deployments. To enable spoofing of the metadata service, the environment @@ -306,7 +319,7 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) { } suffix = strings.TrimLeft(suffix, "/") u := "http://" + host + "/computeMetadata/v1/" + suffix - req, err := http.NewRequest("GET", u, nil) + req, err := http.NewRequestWithContext(ctx, "GET", u, nil) if err != nil { return "", "", err } @@ -336,7 +349,7 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) { if res.StatusCode == http.StatusNotFound { return "", "", NotDefinedError(suffix) } - all, err := ioutil.ReadAll(res.Body) + all, err := io.ReadAll(res.Body) if err != nil { return "", "", err } @@ -354,19 +367,33 @@ func (c *Client) getETag(suffix string) (value, etag string, err error) { // // If the requested metadata is not defined, the returned error will // be of type NotDefinedError. +// +// Deprecated: Please use the context aware variant [Client.GetWithContext]. func (c *Client) Get(suffix string) (string, error) { - val, _, err := c.getETag(suffix) + return c.GetWithContext(context.Background(), suffix) +} + +// GetWithContext returns a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// +// If the GCE_METADATA_HOST environment variable is not defined, a default of +// 169.254.169.254 will be used instead. +// +// If the requested metadata is not defined, the returned error will +// be of type NotDefinedError. +func (c *Client) GetWithContext(ctx context.Context, suffix string) (string, error) { + val, _, err := c.getETag(ctx, suffix) return val, err } -func (c *Client) getTrimmed(suffix string) (s string, err error) { - s, err = c.Get(suffix) +func (c *Client) getTrimmed(ctx context.Context, suffix string) (s string, err error) { + s, err = c.GetWithContext(ctx, suffix) s = strings.TrimSpace(s) return } func (c *Client) lines(suffix string) ([]string, error) { - j, err := c.Get(suffix) + j, err := c.GetWithContext(context.Background(), suffix) if err != nil { return nil, err } @@ -388,7 +415,7 @@ func (c *Client) InstanceID() (string, error) { return instID.get(c) } // InternalIP returns the instance's primary internal IP address. func (c *Client) InternalIP() (string, error) { - return c.getTrimmed("instance/network-interfaces/0/ip") + return c.getTrimmed(context.Background(), "instance/network-interfaces/0/ip") } // Email returns the email address associated with the service account. @@ -398,25 +425,25 @@ func (c *Client) Email(serviceAccount string) (string, error) { if serviceAccount == "" { serviceAccount = "default" } - return c.getTrimmed("instance/service-accounts/" + serviceAccount + "/email") + return c.getTrimmed(context.Background(), "instance/service-accounts/"+serviceAccount+"/email") } // ExternalIP returns the instance's primary external (public) IP address. func (c *Client) ExternalIP() (string, error) { - return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip") + return c.getTrimmed(context.Background(), "instance/network-interfaces/0/access-configs/0/external-ip") } // Hostname returns the instance's hostname. This will be of the form // ".c..internal". func (c *Client) Hostname() (string, error) { - return c.getTrimmed("instance/hostname") + return c.getTrimmed(context.Background(), "instance/hostname") } // InstanceTags returns the list of user-defined instance tags, // assigned when initially creating a GCE instance. func (c *Client) InstanceTags() ([]string, error) { var s []string - j, err := c.Get("instance/tags") + j, err := c.GetWithContext(context.Background(), "instance/tags") if err != nil { return nil, err } @@ -428,12 +455,12 @@ func (c *Client) InstanceTags() ([]string, error) { // InstanceName returns the current VM's instance ID string. func (c *Client) InstanceName() (string, error) { - return c.getTrimmed("instance/name") + return c.getTrimmed(context.Background(), "instance/name") } // Zone returns the current VM's zone, such as "us-central1-b". func (c *Client) Zone() (string, error) { - zone, err := c.getTrimmed("instance/zone") + zone, err := c.getTrimmed(context.Background(), "instance/zone") // zone is of the form "projects//zones/". if err != nil { return "", err @@ -460,7 +487,7 @@ func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project // InstanceAttributeValue may return ("", nil) if the attribute was // defined to be the empty string. func (c *Client) InstanceAttributeValue(attr string) (string, error) { - return c.Get("instance/attributes/" + attr) + return c.GetWithContext(context.Background(), "instance/attributes/"+attr) } // ProjectAttributeValue returns the value of the provided @@ -472,7 +499,7 @@ func (c *Client) InstanceAttributeValue(attr string) (string, error) { // ProjectAttributeValue may return ("", nil) if the attribute was // defined to be the empty string. func (c *Client) ProjectAttributeValue(attr string) (string, error) { - return c.Get("project/attributes/" + attr) + return c.GetWithContext(context.Background(), "project/attributes/"+attr) } // Scopes returns the service account scopes for the given account. @@ -489,21 +516,30 @@ func (c *Client) Scopes(serviceAccount string) ([]string, error) { // The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". // The suffix may contain query parameters. // -// Subscribe calls fn with the latest metadata value indicated by the provided -// suffix. If the metadata value is deleted, fn is called with the empty string -// and ok false. Subscribe blocks until fn returns a non-nil error or the value -// is deleted. Subscribe returns the error value returned from the last call to -// fn, which may be nil when ok == false. +// Deprecated: Please use the context aware variant [Client.SubscribeWithContext]. func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error { + return c.SubscribeWithContext(context.Background(), suffix, func(ctx context.Context, v string, ok bool) error { return fn(v, ok) }) +} + +// SubscribeWithContext subscribes to a value from the metadata service. +// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/". +// The suffix may contain query parameters. +// +// SubscribeWithContext calls fn with the latest metadata value indicated by the +// provided suffix. If the metadata value is deleted, fn is called with the +// empty string and ok false. Subscribe blocks until fn returns a non-nil error +// or the value is deleted. Subscribe returns the error value returned from the +// last call to fn, which may be nil when ok == false. +func (c *Client) SubscribeWithContext(ctx context.Context, suffix string, fn func(ctx context.Context, v string, ok bool) error) error { const failedSubscribeSleep = time.Second * 5 // First check to see if the metadata value exists at all. - val, lastETag, err := c.getETag(suffix) + val, lastETag, err := c.getETag(ctx, suffix) if err != nil { return err } - if err := fn(val, true); err != nil { + if err := fn(ctx, val, true); err != nil { return err } @@ -514,7 +550,7 @@ func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) erro suffix += "?wait_for_change=true&last_etag=" } for { - val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag)) + val, etag, err := c.getETag(ctx, suffix+url.QueryEscape(lastETag)) if err != nil { if _, deleted := err.(NotDefinedError); !deleted { time.Sleep(failedSubscribeSleep) @@ -524,7 +560,7 @@ func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) erro } lastETag = etag - if err := fn(val, ok); err != nil || !ok { + if err := fn(ctx, val, ok); err != nil || !ok { return err } } diff --git a/vendor/cloud.google.com/go/compute/metadata/retry.go b/vendor/cloud.google.com/go/compute/metadata/retry.go index 0f18f3cda1..3d4bc75ddf 100644 --- a/vendor/cloud.google.com/go/compute/metadata/retry.go +++ b/vendor/cloud.google.com/go/compute/metadata/retry.go @@ -27,7 +27,7 @@ const ( ) var ( - syscallRetryable = func(err error) bool { return false } + syscallRetryable = func(error) bool { return false } ) // defaultBackoff is basically equivalent to gax.Backoff without the need for diff --git a/vendor/cloud.google.com/go/compute/metadata/tidyfix.go b/vendor/cloud.google.com/go/compute/metadata/tidyfix.go deleted file mode 100644 index 4cef485008..0000000000 --- a/vendor/cloud.google.com/go/compute/metadata/tidyfix.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// This file, and the {{.RootMod}} import, won't actually become part of -// the resultant binary. -//go:build modhack -// +build modhack - -package metadata - -// Necessary for safely adding multi-module repo. See: https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository -import _ "cloud.google.com/go/compute/internal" diff --git a/vendor/modules.txt b/vendor/modules.txt index 43ce08b2b2..1a29fecdac 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,4 @@ -# cloud.google.com/go/compute v1.23.3 -## explicit; go 1.19 -cloud.google.com/go/compute/internal -# cloud.google.com/go/compute/metadata v0.2.3 +# cloud.google.com/go/compute/metadata v0.3.0 ## explicit; go 1.19 cloud.google.com/go/compute/metadata # cloud.google.com/go/monitoring v1.18.0