func TestBasicAuthData(t *testing.T) { username := "******" password := "******" config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "https://localhost:8443", } config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ Username: username, Password: password, } config.Contexts["clean"] = &clientcmdapi.Context{ Cluster: "clean", AuthInfo: "clean", } config.CurrentContext = "clean" clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil) clientConfig, err := clientBuilder.ClientConfig() if err != nil { t.Fatalf("Unexpected error: %v", err) } // Make sure basic auth data gets into config matchStringArg(username, clientConfig.Username, t) matchStringArg(password, clientConfig.Password, t) }
func TestCertificateData(t *testing.T) { caData := []byte("ca-data") certData := []byte("cert-data") keyData := []byte("key-data") config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "https://localhost:8443", CertificateAuthorityData: caData, } config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ ClientCertificateData: certData, ClientKeyData: keyData, } config.Contexts["clean"] = &clientcmdapi.Context{ Cluster: "clean", AuthInfo: "clean", } config.CurrentContext = "clean" clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil) clientConfig, err := clientBuilder.ClientConfig() if err != nil { t.Fatalf("Unexpected error: %v", err) } // Make sure cert data gets into config (will override file paths) matchByteArg(caData, clientConfig.TLSClientConfig.CAData, t) matchByteArg(certData, clientConfig.TLSClientConfig.CertData, t) matchByteArg(keyData, clientConfig.TLSClientConfig.KeyData, t) }
func TestBasicTokenFile(t *testing.T) { token := "exampletoken" f, err := ioutil.TempFile("", "tokenfile") if err != nil { t.Errorf("Unexpected error: %v", err) return } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), []byte(token), 0644); err != nil { t.Errorf("Unexpected error: %v", err) return } config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "https://localhost:8443", } config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ TokenFile: f.Name(), } config.Contexts["clean"] = &clientcmdapi.Context{ Cluster: "clean", AuthInfo: "clean", } config.CurrentContext = "clean" clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil) clientConfig, err := clientBuilder.ClientConfig() if err != nil { t.Fatalf("Unexpected error: %v", err) } matchStringArg(token, clientConfig.BearerToken, t) }
func TestValidateEmptyConfig(t *testing.T) { config := clientcmdapi.NewConfig() test := configValidationTest{ config: config, expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"}, } test.testConfig(t) }
func TestConfirmUsableMissingConfig(t *testing.T) { config := clientcmdapi.NewConfig() test := configValidationTest{ config: config, expectedErrorSubstring: []string{"invalid configuration: no configuration has been provided"}, } test.testConfirmUsable("not-here", t) }
func TestValidateMissingCurrentContextConfig(t *testing.T) { config := clientcmdapi.NewConfig() config.CurrentContext = "anything" test := configValidationTest{ config: config, expectedErrorSubstring: []string{"context was not found for specified "}, } test.testConfig(t) }
// getConfigFromFile tries to read a kubeconfig file and if it can't, returns an error. One exception, missing files result in empty configs, not an error. func getConfigFromFile(filename string) (*clientcmdapi.Config, error) { config, err := LoadFromFile(filename) if err != nil && !os.IsNotExist(err) { return nil, err } if config == nil { config = clientcmdapi.NewConfig() } return config, nil }
func TestValidateEmptyAuthInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.AuthInfos["error"] = &clientcmdapi.AuthInfo{} test := configValidationTest{ config: config, } test.testAuthInfo("error", t) test.testConfig(t) }
func TestIsEmptyConfig(t *testing.T) { config := clientcmdapi.NewConfig() err := Validate(*config) if !IsEmptyConfig(err) { t.Errorf("Expected context not found, but got %v", err) } if !IsConfigurationInvalid(err) { t.Errorf("Expected configuration invalid, but got %v", err) } }
func TestValidateEmptyClusterInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.Clusters["empty"] = &clientcmdapi.Cluster{} test := configValidationTest{ config: config, expectedErrorSubstring: []string{"cluster has no server defined"}, } test.testCluster("empty", t) test.testConfig(t) }
// Load takes a byte slice and deserializes the contents into Config object. // Encapsulates deserialization without assuming the source is a file. func Load(data []byte) (*clientcmdapi.Config, error) { config := clientcmdapi.NewConfig() // if there's no data in a file, return the default object instead of failing (DecodeInto reject empty input) if len(data) == 0 { return config, nil } decoded, _, err := clientcmdlatest.Codec.Decode(data, &unversioned.GroupVersionKind{Version: clientcmdlatest.Version, Kind: "Config"}, config) if err != nil { return nil, err } return decoded.(*clientcmdapi.Config), nil }
func TestValidateCleanTokenAuthInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ Token: "any-value", } test := configValidationTest{ config: config, } test.testAuthInfo("clean", t) test.testConfig(t) }
func TestIsContextNotFound(t *testing.T) { config := clientcmdapi.NewConfig() config.CurrentContext = "anything" err := Validate(*config) if !IsContextNotFound(err) { t.Errorf("Expected context not found, but got %v", err) } if !IsConfigurationInvalid(err) { t.Errorf("Expected configuration invalid, but got %v", err) } }
// GetStartingConfig implements ConfigAccess func (rules *ClientConfigLoadingRules) GetStartingConfig() (*clientcmdapi.Config, error) { clientConfig := NewNonInteractiveDeferredLoadingClientConfig(rules, &ConfigOverrides{}) rawConfig, err := clientConfig.RawConfig() if os.IsNotExist(err) { return clientcmdapi.NewConfig(), nil } if err != nil { return nil, err } return &rawConfig, nil }
func TestValidateCleanClusterInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "anything", } test := configValidationTest{ config: config, } test.testCluster("clean", t) test.testConfig(t) }
func TestValidateEmptyContext(t *testing.T) { config := clientcmdapi.NewConfig() config.CurrentContext = "anything" config.Contexts["anything"] = &clientcmdapi.Context{} test := configValidationTest{ config: config, expectedErrorSubstring: []string{"user was not specified for context \"anything\"", "cluster was not specified for context \"anything\""}, } test.testContext("anything", t) test.testConfig(t) }
func TestValidateMissingReferencesConfig(t *testing.T) { config := clientcmdapi.NewConfig() config.CurrentContext = "anything" config.Contexts["anything"] = &clientcmdapi.Context{Cluster: "missing", AuthInfo: "missing"} test := configValidationTest{ config: config, expectedErrorSubstring: []string{"user \"missing\" was not found for context \"anything\"", "cluster \"missing\" was not found for context \"anything\""}, } test.testContext("anything", t) test.testConfig(t) }
func TestValidateCertFilesNotFoundAuthInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ ClientCertificate: "missing", ClientKey: "missing", } test := configValidationTest{ config: config, expectedErrorSubstring: []string{"unable to read client-cert", "unable to read client-key"}, } test.testAuthInfo("error", t) test.testConfig(t) }
func TestValidateMissingCAFileClusterInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.Clusters["missing ca"] = &clientcmdapi.Cluster{ Server: "anything", CertificateAuthority: "missing", } test := configValidationTest{ config: config, expectedErrorSubstring: []string{"unable to read certificate-authority"}, } test.testCluster("missing ca", t) test.testConfig(t) }
func TestValidateMultipleMethodsAuthInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ Token: "token", Username: "******", } test := configValidationTest{ config: config, expectedErrorSubstring: []string{"more than one authentication method", "token", "basicAuth"}, } test.testAuthInfo("error", t) test.testConfig(t) }
func TestValidateCleanWithCAClusterInfo(t *testing.T) { tempFile, _ := ioutil.TempFile("", "") defer os.Remove(tempFile.Name()) config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "anything", CertificateAuthority: tempFile.Name(), } test := configValidationTest{ config: config, } test.testCluster("clean", t) test.testConfig(t) }
func TestValidateCleanCertFilesAuthInfo(t *testing.T) { tempFile, _ := ioutil.TempFile("", "") defer os.Remove(tempFile.Name()) config := clientcmdapi.NewConfig() config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ ClientCertificate: tempFile.Name(), ClientKey: tempFile.Name(), } test := configValidationTest{ config: config, } test.testAuthInfo("clean", t) test.testConfig(t) }
func (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) { // don't mutate the original loadingRules := *o.LoadingRules loadingRules.Precedence = o.GetLoadingPrecedence() clientConfig := NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &ConfigOverrides{}) rawConfig, err := clientConfig.RawConfig() if os.IsNotExist(err) { return clientcmdapi.NewConfig(), nil } if err != nil { return nil, err } return &rawConfig, nil }
func TestValidateCertDataOverridesFiles(t *testing.T) { tempFile, _ := ioutil.TempFile("", "") defer os.Remove(tempFile.Name()) config := clientcmdapi.NewConfig() config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ ClientCertificate: tempFile.Name(), ClientCertificateData: []byte("certdata"), ClientKey: tempFile.Name(), ClientKeyData: []byte("keydata"), } test := configValidationTest{ config: config, expectedErrorSubstring: []string{"client-cert-data and client-cert are both specified", "client-key-data and client-key are both specified"}, } test.testAuthInfo("clean", t) test.testConfig(t) }
func createValidTestConfig() *clientcmdapi.Config { const ( server = "https://anything.com:8080" token = "the-token" ) config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: server, } config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ Token: token, } config.Contexts["clean"] = &clientcmdapi.Context{ Cluster: "clean", AuthInfo: "clean", } config.CurrentContext = "clean" return config }
func TestConfirmUsableBadInfoConfig(t *testing.T) { config := clientcmdapi.NewConfig() config.Clusters["missing ca"] = &clientcmdapi.Cluster{ Server: "anything", CertificateAuthority: "missing", } config.AuthInfos["error"] = &clientcmdapi.AuthInfo{ Username: "******", Token: "here", } config.Contexts["first"] = &clientcmdapi.Context{ Cluster: "missing ca", AuthInfo: "error", } test := configValidationTest{ config: config, expectedErrorSubstring: []string{"unable to read certificate-authority"}, } test.testConfirmUsable("first", t) }
"k8s.io/client-go/1.4/pkg/api" "k8s.io/client-go/1.4/rest" clientauth "k8s.io/client-go/1.4/tools/auth" clientcmdapi "k8s.io/client-go/1.4/tools/clientcmd/api" ) var ( // DefaultCluster is the cluster config used when no other config is specified // TODO: eventually apiserver should start on 443 and be secure by default DefaultCluster = clientcmdapi.Cluster{Server: "http://localhost:8080"} // EnvVarCluster allows overriding the DefaultCluster using an envvar for the server name EnvVarCluster = clientcmdapi.Cluster{Server: os.Getenv("KUBERNETES_MASTER")} DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{}, nil, NewDefaultClientConfigLoadingRules()} ) // ClientConfig is used to make it easy to get an api server client type ClientConfig interface { // RawConfig returns the merged result of all overrides RawConfig() (clientcmdapi.Config, error) // ClientConfig returns a complete client config ClientConfig() (*rest.Config, error) // Namespace returns the namespace resulting from the merged // result of all overrides and a boolean indicating if it was // overridden Namespace() (string, bool, error) // ConfigAccess returns the rules for loading/persisting the config. ConfigAccess() ConfigAccess }
"github.com/golang/glog" "github.com/imdario/mergo" "k8s.io/client-go/1.4/pkg/api" "k8s.io/client-go/1.4/rest" clientauth "k8s.io/client-go/1.4/tools/auth" clientcmdapi "k8s.io/client-go/1.4/tools/clientcmd/api" ) var ( // ClusterDefaults has the same behavior as the old EnvVar and DefaultCluster fields // DEPRECATED will be replaced ClusterDefaults = clientcmdapi.Cluster{Server: getDefaultServer()} // DefaultClientConfig represents the legacy behavior of this package for defaulting // DEPRECATED will be replace DefaultClientConfig = DirectClientConfig{*clientcmdapi.NewConfig(), "", &ConfigOverrides{ ClusterDefaults: ClusterDefaults, }, nil, NewDefaultClientConfigLoadingRules()} ) // getDefaultServer returns a default setting for DefaultClientConfig // DEPRECATED func getDefaultServer() string { if server := os.Getenv("KUBERNETES_MASTER"); len(server) > 0 { return server } return "http://localhost:8080" } // ClientConfig is used to make it easy to get an api server client type ClientConfig interface {
func TestInClusterConfig(t *testing.T) { default1 := &DirectClientConfig{ config: *createValidTestConfig(), contextName: "clean", overrides: &ConfigOverrides{}, } invalidDefaultConfig := clientcmdapi.NewConfig() invalidDefaultConfig.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "http://localhost:8080", } invalidDefaultConfig.Contexts["other"] = &clientcmdapi.Context{ Cluster: "clean", } invalidDefaultConfig.CurrentContext = "clean" defaultInvalid := &DirectClientConfig{ config: *invalidDefaultConfig, overrides: &ConfigOverrides{}, } if _, err := defaultInvalid.ClientConfig(); err == nil || !IsConfigurationInvalid(err) { t.Fatal(err) } config1, err := default1.ClientConfig() if err != nil { t.Fatal(err) } config2 := &rest.Config{Host: "config2"} err1 := fmt.Errorf("unique error") testCases := map[string]struct { clientConfig *testClientConfig icc *testICC defaultConfig *DirectClientConfig checkedICC bool result *rest.Config err error }{ "in-cluster checked on other error": { clientConfig: &testClientConfig{err: ErrEmptyConfig}, icc: &testICC{}, checkedICC: true, result: nil, err: ErrEmptyConfig, }, "in-cluster not checked on non-empty error": { clientConfig: &testClientConfig{err: ErrEmptyCluster}, icc: &testICC{}, checkedICC: false, result: nil, err: ErrEmptyCluster, }, "in-cluster checked when config is default": { defaultConfig: default1, clientConfig: &testClientConfig{config: config1}, icc: &testICC{}, checkedICC: true, result: config1, err: nil, }, "in-cluster not checked when default config is invalid": { defaultConfig: defaultInvalid, clientConfig: &testClientConfig{config: config1}, icc: &testICC{}, checkedICC: false, result: config1, err: nil, }, "in-cluster not checked when config is not equal to default": { defaultConfig: default1, clientConfig: &testClientConfig{config: config2}, icc: &testICC{}, checkedICC: false, result: config2, err: nil, }, "in-cluster checked when config is not equal to default and error is empty": { clientConfig: &testClientConfig{config: config2, err: ErrEmptyConfig}, icc: &testICC{}, checkedICC: true, result: config2, err: ErrEmptyConfig, }, "in-cluster error returned when config is empty": { clientConfig: &testClientConfig{err: ErrEmptyConfig}, icc: &testICC{ possible: true, testClientConfig: testClientConfig{ err: err1, }, }, checkedICC: true, result: nil, err: err1, }, "in-cluster config returned when config is empty": { clientConfig: &testClientConfig{err: ErrEmptyConfig}, icc: &testICC{ possible: true, testClientConfig: testClientConfig{ config: config2, }, }, checkedICC: true, result: config2, err: nil, }, "in-cluster not checked when standard default is invalid": { defaultConfig: &DefaultClientConfig, clientConfig: &testClientConfig{config: config2}, icc: &testICC{}, checkedICC: false, result: config2, err: nil, }, } for name, test := range testCases { c := &DeferredLoadingClientConfig{icc: test.icc} c.loader = &ClientConfigLoadingRules{DefaultClientConfig: test.defaultConfig} c.clientConfig = test.clientConfig cfg, err := c.ClientConfig() if test.icc.called != test.checkedICC { t.Errorf("%s: unexpected in-cluster-config call %t", name, test.icc.called) } if err != test.err || cfg != test.result { t.Errorf("%s: unexpected result: %v %#v", name, err, cfg) } } }
// Load starts by running the MigrationRules and then // takes the loading rules and returns a Config object based on following rules. // if the ExplicitPath, return the unmerged explicit file // Otherwise, return a merged config based on the Precedence slice // A missing ExplicitPath file produces an error. Empty filenames or other missing files are ignored. // Read errors or files with non-deserializable content produce errors. // The first file to set a particular map key wins and map key's value is never changed. // BUT, if you set a struct value that is NOT contained inside of map, the value WILL be changed. // This results in some odd looking logic to merge in one direction, merge in the other, and then merge the two. // It also means that if two files specify a "red-user", only values from the first file's red-user are used. Even // non-conflicting entries from the second file's "red-user" are discarded. // Relative paths inside of the .kubeconfig files are resolved against the .kubeconfig file's parent folder // and only absolute file paths are returned. func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { if err := rules.Migrate(); err != nil { return nil, err } errlist := []error{} kubeConfigFiles := []string{} // Make sure a file we were explicitly told to use exists if len(rules.ExplicitPath) > 0 { if _, err := os.Stat(rules.ExplicitPath); os.IsNotExist(err) { return nil, err } kubeConfigFiles = append(kubeConfigFiles, rules.ExplicitPath) } else { kubeConfigFiles = append(kubeConfigFiles, rules.Precedence...) } kubeconfigs := []*clientcmdapi.Config{} // read and cache the config files so that we only look at them once for _, filename := range kubeConfigFiles { if len(filename) == 0 { // no work to do continue } config, err := LoadFromFile(filename) if os.IsNotExist(err) { // skip missing files continue } if err != nil { errlist = append(errlist, fmt.Errorf("Error loading config file \"%s\": %v", filename, err)) continue } kubeconfigs = append(kubeconfigs, config) } // first merge all of our maps mapConfig := clientcmdapi.NewConfig() for _, kubeconfig := range kubeconfigs { mergo.Merge(mapConfig, kubeconfig) } // merge all of the struct values in the reverse order so that priority is given correctly // errors are not added to the list the second time nonMapConfig := clientcmdapi.NewConfig() for i := len(kubeconfigs) - 1; i >= 0; i-- { kubeconfig := kubeconfigs[i] mergo.Merge(nonMapConfig, kubeconfig) } // since values are overwritten, but maps values are not, we can merge the non-map config on top of the map config and // get the values we expect. config := clientcmdapi.NewConfig() mergo.Merge(config, mapConfig) mergo.Merge(config, nonMapConfig) if rules.ResolvePaths() { if err := ResolveLocalPaths(config); err != nil { errlist = append(errlist, err) } } return config, utilerrors.NewAggregate(errlist) }