do not query the metadata server to find out if running on GCE. Retry docker registry fetches on GCP

Signed-off-by: Vishnu kannan <vishnuk@google.com>
This commit is contained in:
Vishnu kannan 2016-07-12 17:39:38 -07:00
parent 6193335bd9
commit ee9cded79a
2 changed files with 80 additions and 5 deletions

View File

@ -18,6 +18,7 @@ package gcp_credentials
import ( import (
"encoding/json" "encoding/json"
"io/ioutil"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -36,8 +37,12 @@ const (
metadataEmail = metadataUrl + "instance/service-accounts/default/email" metadataEmail = metadataUrl + "instance/service-accounts/default/email"
storageScopePrefix = "https://www.googleapis.com/auth/devstorage" storageScopePrefix = "https://www.googleapis.com/auth/devstorage"
cloudPlatformScopePrefix = "https://www.googleapis.com/auth/cloud-platform" 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 // 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". // "foo.gcr.io" and "bar.gcr.io".
var containerRegistryUrls = []string{"container.cloud.google.com", "gcr.io", "*.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. // Enabled implements DockerConfigProvider for all of the Google implementations.
func (g *metadataProvider) Enabled() bool { func (g *metadataProvider) Enabled() bool {
_, err := credentialprovider.ReadUrl(metadataUrl, g.Client, metadataHeader) return onGCEVM()
return err == nil
} }
// LazyProvide implements DockerConfigProvider. Should never be called. // 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 // Enabled implements a special metadata-based check, which verifies the
// storage scope is available on the GCE VM. // storage scope is available on the GCE VM.
func (g *containerRegistryProvider) Enabled() bool { func (g *containerRegistryProvider) Enabled() bool {
value, err := credentialprovider.ReadUrl(metadataScopes+"?alt=json", g.Client, metadataHeader) if !onGCEVM() {
if err != nil {
return false 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 var scopes []string
if err := json.Unmarshal([]byte(value), &scopes); err != nil { if err := json.Unmarshal(value, &scopes); err != nil {
return false return false
} }

View File

@ -20,9 +20,11 @@ import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -31,6 +33,14 @@ import (
utilnet "k8s.io/kubernetes/pkg/util/net" 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) { func TestDockerKeyringFromGoogleDockerConfigMetadata(t *testing.T) {
registryUrl := "hello.kubernetes.io" registryUrl := "hello.kubernetes.io"
email := "foo@bar.baz" email := "foo@bar.baz"
@ -44,6 +54,12 @@ func TestDockerKeyringFromGoogleDockerConfigMetadata(t *testing.T) {
} }
}`, registryUrl, email, auth) }`, 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 probeEndpoint = "/computeMetadata/v1/"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Only serve the one metadata key. // Only serve the one metadata key.
@ -111,6 +127,12 @@ func TestDockerKeyringFromGoogleDockerConfigMetadataUrl(t *testing.T) {
} }
}`, registryUrl, email, auth) }`, 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 probeEndpoint = "/computeMetadata/v1/"
const valueEndpoint = "/my/value" const valueEndpoint = "/my/value"
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@ -181,6 +203,13 @@ func TestContainerRegistryBasics(t *testing.T) {
emailEndpoint = defaultEndpoint + "email" emailEndpoint = defaultEndpoint + "email"
tokenEndpoint = defaultEndpoint + "token" 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) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Only serve the URL key and the value endpoint // Only serve the URL key and the value endpoint
if scopeEndpoint == r.URL.Path { if scopeEndpoint == r.URL.Path {
@ -260,6 +289,13 @@ func TestContainerRegistryNoStorageScope(t *testing.T) {
})) }))
defer server.Close() 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 // Make a transport that reroutes all traffic to the example server
transport := utilnet.SetTransportDefaults(&http.Transport{ transport := utilnet.SetTransportDefaults(&http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) { Proxy: func(req *http.Request) (*url.URL, error) {
@ -293,6 +329,13 @@ func TestComputePlatformScopeSubstitutesStorageScope(t *testing.T) {
})) }))
defer server.Close() 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 // Make a transport that reroutes all traffic to the example server
transport := utilnet.SetTransportDefaults(&http.Transport{ transport := utilnet.SetTransportDefaults(&http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) { Proxy: func(req *http.Request) (*url.URL, error) {