mirror of
				https://github.com/k3s-io/kubernetes.git
				synced 2025-11-03 23:40:03 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			442 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			442 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
Copyright 2014 The Kubernetes Authors.
 | 
						|
 | 
						|
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 gcp
 | 
						|
 | 
						|
import (
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"net/http"
 | 
						|
	"net/http/httptest"
 | 
						|
	"net/url"
 | 
						|
	"os"
 | 
						|
	"reflect"
 | 
						|
	"strings"
 | 
						|
	"testing"
 | 
						|
 | 
						|
	utilnet "k8s.io/apimachinery/pkg/util/net"
 | 
						|
	"k8s.io/kubernetes/pkg/credentialprovider"
 | 
						|
)
 | 
						|
 | 
						|
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"
 | 
						|
	username := "foo"
 | 
						|
	password := "bar"
 | 
						|
	auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
 | 
						|
	sampleDockerConfig := fmt.Sprintf(`{
 | 
						|
   "https://%s": {
 | 
						|
     "email": %q,
 | 
						|
     "auth": %q
 | 
						|
   }
 | 
						|
}`, 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.
 | 
						|
		if probeEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
		} else if strings.HasSuffix(dockerConfigKey, r.URL.Path) {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			fmt.Fprintln(w, sampleDockerConfig)
 | 
						|
		} else {
 | 
						|
			http.Error(w, "", http.StatusNotFound)
 | 
						|
		}
 | 
						|
	}))
 | 
						|
	defer server.Close()
 | 
						|
 | 
						|
	// Make a transport that reroutes all traffic to the example server
 | 
						|
	transport := utilnet.SetTransportDefaults(&http.Transport{
 | 
						|
		Proxy: func(req *http.Request) (*url.URL, error) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	keyring := &credentialprovider.BasicDockerKeyring{}
 | 
						|
	provider := &dockerConfigKeyProvider{
 | 
						|
		metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
	}
 | 
						|
 | 
						|
	if !provider.Enabled() {
 | 
						|
		t.Errorf("Provider is unexpectedly disabled")
 | 
						|
	}
 | 
						|
 | 
						|
	keyring.Add(provider.Provide(""))
 | 
						|
 | 
						|
	creds, ok := keyring.Lookup(registryURL)
 | 
						|
	if !ok {
 | 
						|
		t.Errorf("Didn't find expected URL: %s", registryURL)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(creds) > 1 {
 | 
						|
		t.Errorf("Got more hits than expected: %s", creds)
 | 
						|
	}
 | 
						|
	val := creds[0]
 | 
						|
 | 
						|
	if username != val.Username {
 | 
						|
		t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username)
 | 
						|
	}
 | 
						|
	if password != val.Password {
 | 
						|
		t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
 | 
						|
	}
 | 
						|
	if email != val.Email {
 | 
						|
		t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestDockerKeyringFromGoogleDockerConfigMetadataUrl(t *testing.T) {
 | 
						|
	registryURL := "hello.kubernetes.io"
 | 
						|
	email := "foo@bar.baz"
 | 
						|
	username := "foo"
 | 
						|
	password := "bar"
 | 
						|
	auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password)))
 | 
						|
	sampleDockerConfig := fmt.Sprintf(`{
 | 
						|
   "https://%s": {
 | 
						|
     "email": %q,
 | 
						|
     "auth": %q
 | 
						|
   }
 | 
						|
}`, 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) {
 | 
						|
		// Only serve the URL key and the value endpoint
 | 
						|
		if probeEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
		} else if valueEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			fmt.Fprintln(w, sampleDockerConfig)
 | 
						|
		} else if strings.HasSuffix(dockerConfigURLKey, r.URL.Path) {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/text")
 | 
						|
			fmt.Fprint(w, "http://foo.bar.com"+valueEndpoint)
 | 
						|
		} else {
 | 
						|
			http.Error(w, "", http.StatusNotFound)
 | 
						|
		}
 | 
						|
	}))
 | 
						|
	defer server.Close()
 | 
						|
 | 
						|
	// Make a transport that reroutes all traffic to the example server
 | 
						|
	transport := utilnet.SetTransportDefaults(&http.Transport{
 | 
						|
		Proxy: func(req *http.Request) (*url.URL, error) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	keyring := &credentialprovider.BasicDockerKeyring{}
 | 
						|
	provider := &dockerConfigURLKeyProvider{
 | 
						|
		metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
	}
 | 
						|
 | 
						|
	if !provider.Enabled() {
 | 
						|
		t.Errorf("Provider is unexpectedly disabled")
 | 
						|
	}
 | 
						|
 | 
						|
	keyring.Add(provider.Provide(""))
 | 
						|
 | 
						|
	creds, ok := keyring.Lookup(registryURL)
 | 
						|
	if !ok {
 | 
						|
		t.Errorf("Didn't find expected URL: %s", registryURL)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(creds) > 1 {
 | 
						|
		t.Errorf("Got more hits than expected: %s", creds)
 | 
						|
	}
 | 
						|
	val := creds[0]
 | 
						|
 | 
						|
	if username != val.Username {
 | 
						|
		t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username)
 | 
						|
	}
 | 
						|
	if password != val.Password {
 | 
						|
		t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password)
 | 
						|
	}
 | 
						|
	if email != val.Email {
 | 
						|
		t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestContainerRegistryBasics(t *testing.T) {
 | 
						|
	registryURL := "container.cloud.google.com"
 | 
						|
	email := "1234@project.gserviceaccount.com"
 | 
						|
	token := &tokenBlob{AccessToken: "ya26.lots-of-indiscernible-garbage"}
 | 
						|
 | 
						|
	const (
 | 
						|
		serviceAccountsEndpoint = "/computeMetadata/v1/instance/service-accounts/"
 | 
						|
		defaultEndpoint         = "/computeMetadata/v1/instance/service-accounts/default/"
 | 
						|
		scopeEndpoint           = defaultEndpoint + "scopes"
 | 
						|
		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 {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			fmt.Fprintf(w, `["%s.read_write"]`, storageScopePrefix)
 | 
						|
		} else if emailEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			fmt.Fprint(w, email)
 | 
						|
		} else if tokenEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			bytes, err := json.Marshal(token)
 | 
						|
			if err != nil {
 | 
						|
				t.Fatalf("unexpected error: %v", err)
 | 
						|
			}
 | 
						|
			fmt.Fprintln(w, string(bytes))
 | 
						|
		} else if serviceAccountsEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			fmt.Fprintln(w, "default/\ncustom")
 | 
						|
		} else {
 | 
						|
			http.Error(w, "", http.StatusNotFound)
 | 
						|
		}
 | 
						|
	}))
 | 
						|
	defer server.Close()
 | 
						|
 | 
						|
	// Make a transport that reroutes all traffic to the example server
 | 
						|
	transport := utilnet.SetTransportDefaults(&http.Transport{
 | 
						|
		Proxy: func(req *http.Request) (*url.URL, error) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	keyring := &credentialprovider.BasicDockerKeyring{}
 | 
						|
	provider := &containerRegistryProvider{
 | 
						|
		metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
	}
 | 
						|
 | 
						|
	if !provider.Enabled() {
 | 
						|
		t.Errorf("Provider is unexpectedly disabled")
 | 
						|
	}
 | 
						|
 | 
						|
	keyring.Add(provider.Provide(""))
 | 
						|
 | 
						|
	creds, ok := keyring.Lookup(registryURL)
 | 
						|
	if !ok {
 | 
						|
		t.Errorf("Didn't find expected URL: %s", registryURL)
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if len(creds) > 1 {
 | 
						|
		t.Errorf("Got more hits than expected: %s", creds)
 | 
						|
	}
 | 
						|
	val := creds[0]
 | 
						|
 | 
						|
	if "_token" != val.Username {
 | 
						|
		t.Errorf("Unexpected username value, want: %s, got: %s", "_token", val.Username)
 | 
						|
	}
 | 
						|
	if token.AccessToken != val.Password {
 | 
						|
		t.Errorf("Unexpected password value, want: %s, got: %s", token.AccessToken, val.Password)
 | 
						|
	}
 | 
						|
	if email != val.Email {
 | 
						|
		t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestContainerRegistryNoServiceAccount(t *testing.T) {
 | 
						|
	const (
 | 
						|
		serviceAccountsEndpoint = "/computeMetadata/v1/instance/service-accounts/"
 | 
						|
	)
 | 
						|
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		// Only serve the URL key and the value endpoint
 | 
						|
		if serviceAccountsEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			bytes, err := json.Marshal([]string{})
 | 
						|
			if err != nil {
 | 
						|
				t.Fatalf("unexpected error: %v", err)
 | 
						|
			}
 | 
						|
			fmt.Fprintln(w, string(bytes))
 | 
						|
		} else {
 | 
						|
			http.Error(w, "", http.StatusNotFound)
 | 
						|
		}
 | 
						|
	}))
 | 
						|
	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) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	provider := &containerRegistryProvider{
 | 
						|
		metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
	}
 | 
						|
 | 
						|
	if provider.Enabled() {
 | 
						|
		t.Errorf("Provider is unexpectedly enabled")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestContainerRegistryNoStorageScope(t *testing.T) {
 | 
						|
	const (
 | 
						|
		serviceAccountsEndpoint = "/computeMetadata/v1/instance/service-accounts/"
 | 
						|
		defaultEndpoint         = "/computeMetadata/v1/instance/service-accounts/default/"
 | 
						|
		scopeEndpoint           = defaultEndpoint + "scopes"
 | 
						|
	)
 | 
						|
	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 {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			fmt.Fprint(w, `["https://www.googleapis.com/auth/compute.read_write"]`)
 | 
						|
		} else if serviceAccountsEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			fmt.Fprintln(w, "default/\ncustom")
 | 
						|
		} else {
 | 
						|
			http.Error(w, "", http.StatusNotFound)
 | 
						|
		}
 | 
						|
	}))
 | 
						|
	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) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	provider := &containerRegistryProvider{
 | 
						|
		metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
	}
 | 
						|
 | 
						|
	if provider.Enabled() {
 | 
						|
		t.Errorf("Provider is unexpectedly enabled")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestComputePlatformScopeSubstitutesStorageScope(t *testing.T) {
 | 
						|
	const (
 | 
						|
		serviceAccountsEndpoint = "/computeMetadata/v1/instance/service-accounts/"
 | 
						|
		defaultEndpoint         = "/computeMetadata/v1/instance/service-accounts/default/"
 | 
						|
		scopeEndpoint           = defaultEndpoint + "scopes"
 | 
						|
	)
 | 
						|
	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 {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			fmt.Fprint(w, `["https://www.googleapis.com/auth/compute.read_write","https://www.googleapis.com/auth/cloud-platform.read-only"]`)
 | 
						|
		} else if serviceAccountsEndpoint == r.URL.Path {
 | 
						|
			w.WriteHeader(http.StatusOK)
 | 
						|
			w.Header().Set("Content-Type", "application/json")
 | 
						|
			fmt.Fprintln(w, "default/\ncustom")
 | 
						|
		} else {
 | 
						|
			http.Error(w, "", http.StatusNotFound)
 | 
						|
		}
 | 
						|
	}))
 | 
						|
	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) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	provider := &containerRegistryProvider{
 | 
						|
		metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
	}
 | 
						|
 | 
						|
	if !provider.Enabled() {
 | 
						|
		t.Errorf("Provider is unexpectedly disabled")
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func TestAllProvidersNoMetadata(t *testing.T) {
 | 
						|
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | 
						|
		http.Error(w, "", http.StatusNotFound)
 | 
						|
	}))
 | 
						|
	defer server.Close()
 | 
						|
 | 
						|
	// Make a transport that reroutes all traffic to the example server
 | 
						|
	transport := utilnet.SetTransportDefaults(&http.Transport{
 | 
						|
		Proxy: func(req *http.Request) (*url.URL, error) {
 | 
						|
			return url.Parse(server.URL + req.URL.Path)
 | 
						|
		},
 | 
						|
	})
 | 
						|
 | 
						|
	providers := []credentialprovider.DockerConfigProvider{
 | 
						|
		&dockerConfigKeyProvider{
 | 
						|
			metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
		},
 | 
						|
		&dockerConfigURLKeyProvider{
 | 
						|
			metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
		},
 | 
						|
		&containerRegistryProvider{
 | 
						|
			metadataProvider{Client: &http.Client{Transport: transport}},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	for _, provider := range providers {
 | 
						|
		if provider.Enabled() {
 | 
						|
			t.Errorf("Provider %s is unexpectedly enabled", reflect.TypeOf(provider).String())
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 |