From ee9cded79a8a5a597efcef7404bc32a003f8ea37 Mon Sep 17 00:00:00 2001 From: Vishnu kannan Date: Tue, 12 Jul 2016 17:39:38 -0700 Subject: [PATCH] do not query the metadata server to find out if running on GCE. Retry docker registry fetches on GCP Signed-off-by: Vishnu kannan --- pkg/credentialprovider/gcp/metadata.go | 42 +++++++++++++++++--- pkg/credentialprovider/gcp/metadata_test.go | 43 +++++++++++++++++++++ 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/pkg/credentialprovider/gcp/metadata.go b/pkg/credentialprovider/gcp/metadata.go index 98e09659063..7496ae917c0 100644 --- a/pkg/credentialprovider/gcp/metadata.go +++ b/pkg/credentialprovider/gcp/metadata.go @@ -18,6 +18,7 @@ package gcp_credentials import ( "encoding/json" + "io/ioutil" "net/http" "strings" "time" @@ -36,8 +37,12 @@ const ( metadataEmail = metadataUrl + "instance/service-accounts/default/email" storageScopePrefix = "https://www.googleapis.com/auth/devstorage" cloudPlatformScopePrefix = "https://www.googleapis.com/auth/cloud-platform" + googleProductName = "Google" ) +// A variable to enable testing +var gceProductNameFile = "/sys/class/dmi/id/product_name" + // For these urls, the parts of the host name can be glob, for example '*.gcr.io" will match // "foo.gcr.io" and "bar.gcr.io". var containerRegistryUrls = []string{"container.cloud.google.com", "gcr.io", "*.gcr.io"} @@ -98,10 +103,19 @@ func init() { }) } +// Returns true if it finds a local GCE VM +func onGCEVM() bool { + data, err := ioutil.ReadFile(gceProductNameFile) + if err != nil { + glog.V(2).Infof("Error while reading product_name: %v", err) + return false + } + return strings.Contains(string(data), googleProductName) +} + // Enabled implements DockerConfigProvider for all of the Google implementations. func (g *metadataProvider) Enabled() bool { - _, err := credentialprovider.ReadUrl(metadataUrl, g.Client, metadataHeader) - return err == nil + return onGCEVM() } // LazyProvide implements DockerConfigProvider. Should never be called. @@ -151,12 +165,30 @@ func (g *dockerConfigUrlKeyProvider) Provide() credentialprovider.DockerConfig { // Enabled implements a special metadata-based check, which verifies the // storage scope is available on the GCE VM. func (g *containerRegistryProvider) Enabled() bool { - value, err := credentialprovider.ReadUrl(metadataScopes+"?alt=json", g.Client, metadataHeader) - if err != nil { + if !onGCEVM() { return false } + // Given that we are on GCE, we should keep retrying until the metadata server responds. + var ( + value []byte + err error + backoff = time.Millisecond + ) + const maxBackoff = time.Minute + for { + value, err = credentialprovider.ReadUrl(metadataScopes+"?alt=json", g.Client, metadataHeader) + if err == nil { + break + } + glog.Errorf("failed to Get %q: %v", metadataScopes+"?alt=json", err) + time.Sleep(backoff) + backoff = backoff * 2 + if backoff > maxBackoff { + backoff = maxBackoff + } + } var scopes []string - if err := json.Unmarshal([]byte(value), &scopes); err != nil { + if err := json.Unmarshal(value, &scopes); err != nil { return false } diff --git a/pkg/credentialprovider/gcp/metadata_test.go b/pkg/credentialprovider/gcp/metadata_test.go index 9f738531c11..832da7285d9 100644 --- a/pkg/credentialprovider/gcp/metadata_test.go +++ b/pkg/credentialprovider/gcp/metadata_test.go @@ -20,9 +20,11 @@ import ( "encoding/base64" "encoding/json" "fmt" + "io/ioutil" "net/http" "net/http/httptest" "net/url" + "os" "reflect" "strings" "testing" @@ -31,6 +33,14 @@ import ( utilnet "k8s.io/kubernetes/pkg/util/net" ) +func createProductNameFile() (string, error) { + file, err := ioutil.TempFile("", "") + if err != nil { + return "", fmt.Errorf("failed to create temporary test file: %v", err) + } + return file.Name(), ioutil.WriteFile(file.Name(), []byte("Google"), 0600) +} + func TestDockerKeyringFromGoogleDockerConfigMetadata(t *testing.T) { registryUrl := "hello.kubernetes.io" email := "foo@bar.baz" @@ -44,6 +54,12 @@ func TestDockerKeyringFromGoogleDockerConfigMetadata(t *testing.T) { } }`, registryUrl, email, auth) + var err error + gceProductNameFile, err = createProductNameFile() + if err != nil { + t.Errorf("failed to create gce product name file: %v", err) + } + defer os.Remove(gceProductNameFile) const probeEndpoint = "/computeMetadata/v1/" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Only serve the one metadata key. @@ -111,6 +127,12 @@ func TestDockerKeyringFromGoogleDockerConfigMetadataUrl(t *testing.T) { } }`, registryUrl, email, auth) + var err error + gceProductNameFile, err = createProductNameFile() + if err != nil { + t.Errorf("failed to create gce product name file: %v", err) + } + defer os.Remove(gceProductNameFile) const probeEndpoint = "/computeMetadata/v1/" const valueEndpoint = "/my/value" server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -181,6 +203,13 @@ func TestContainerRegistryBasics(t *testing.T) { emailEndpoint = defaultEndpoint + "email" tokenEndpoint = defaultEndpoint + "token" ) + var err error + gceProductNameFile, err = createProductNameFile() + if err != nil { + t.Errorf("failed to create gce product name file: %v", err) + } + defer os.Remove(gceProductNameFile) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Only serve the URL key and the value endpoint if scopeEndpoint == r.URL.Path { @@ -260,6 +289,13 @@ func TestContainerRegistryNoStorageScope(t *testing.T) { })) defer server.Close() + var err error + gceProductNameFile, err = createProductNameFile() + if err != nil { + t.Errorf("failed to create gce product name file: %v", err) + } + defer os.Remove(gceProductNameFile) + // Make a transport that reroutes all traffic to the example server transport := utilnet.SetTransportDefaults(&http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) { @@ -293,6 +329,13 @@ func TestComputePlatformScopeSubstitutesStorageScope(t *testing.T) { })) defer server.Close() + var err error + gceProductNameFile, err = createProductNameFile() + if err != nil { + t.Errorf("failed to create gce product name file: %v", err) + } + defer os.Remove(gceProductNameFile) + // Make a transport that reroutes all traffic to the example server transport := utilnet.SetTransportDefaults(&http.Transport{ Proxy: func(req *http.Request) (*url.URL, error) {