func TestNewEmptyAuth(t *testing.T) { expectedConfig := *clientcmdapi.NewConfig() expectedConfig.AuthInfos["the-user-name"] = clientcmdapi.NewAuthInfo() test := configCommandTest{ args: []string{"set-credentials", "the-user-name"}, startingConfig: *clientcmdapi.NewConfig(), expectedConfig: expectedConfig, } test.run(t) }
func TestNewEmptyContext(t *testing.T) { expectedConfig := *clientcmdapi.NewConfig() expectedConfig.Contexts["new-context"] = clientcmdapi.NewContext() test := configCommandTest{ args: []string{"set-context", "new-context"}, startingConfig: *clientcmdapi.NewConfig(), expectedConfig: expectedConfig, } test.run(t) }
// 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...) } // first merge all of our maps mapConfig := clientcmdapi.NewConfig() for _, file := range kubeConfigFiles { if err := mergeConfigWithFile(mapConfig, file); err != nil { errlist = append(errlist, err) } if rules.ResolvePaths() { if err := ResolveLocalPaths(file, mapConfig); err != nil { errlist = append(errlist, err) } } } // 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(kubeConfigFiles) - 1; i >= 0; i-- { file := kubeConfigFiles[i] mergeConfigWithFile(nonMapConfig, file) if rules.ResolvePaths() { ResolveLocalPaths(file, nonMapConfig) } } // 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) return config, errors.NewAggregate(errlist) }
func TestSetIntoNewConfig(t *testing.T) { expectedConfig := *clientcmdapi.NewConfig() context := clientcmdapi.NewContext() context.AuthInfo = "fake-user" expectedConfig.Contexts["new-context"] = context test := configCommandTest{ args: []string{"set", "contexts.new-context.user", "fake-user"}, startingConfig: *clientcmdapi.NewConfig(), expectedConfig: expectedConfig, } test.run(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", APIVersion: latest.Version, 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{}) 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) }
// getFactoryFromCluster returns an OpenShift's Factory // using the config that is made available when we are running in a cluster // (using environment variables and token secret file) // or an error if those are not available (meaning we are not running in a cluster) func getFactoryFromCluster() (*clientcmd.Factory, error) { clusterConfig, err := k8client.InClusterConfig() if err != nil { return nil, err } // keep only what we need to initialize a factory overrides := &kclientcmd.ConfigOverrides{ ClusterInfo: kclientcmdapi.Cluster{ Server: clusterConfig.Host, APIVersion: clusterConfig.Version, }, AuthInfo: kclientcmdapi.AuthInfo{ Token: clusterConfig.BearerToken, }, Context: kclientcmdapi.Context{}, } if len(clusterConfig.TLSClientConfig.CAFile) > 0 { // FIXME "x509: cannot validate certificate for x.x.x.x because it doesn't contain any IP SANs" // overrides.ClusterInfo.CertificateAuthority = clusterConfig.TLSClientConfig.CAFile overrides.ClusterInfo.InsecureSkipTLSVerify = true } else { overrides.ClusterInfo.InsecureSkipTLSVerify = true } config := kclientcmd.NewDefaultClientConfig(*kclientcmdapi.NewConfig(), overrides) factory := clientcmd.NewFactory(config) return factory, nil }
func TestBasicAuthData(t *testing.T) { username := "******" password := "******" config := clientcmdapi.NewConfig() config.Clusters["clean"] = &clientcmdapi.Cluster{ Server: "https://localhost:8443", APIVersion: latest.Version, } 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{}) 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 TestValidateEmptyConfig(t *testing.T) { config := clientcmdapi.NewConfig() test := configValidationTest{ config: config, } test.testConfig(t) }
func TestNewFactoryNoFlagBindings(t *testing.T) { clientConfig := clientcmd.NewDefaultClientConfig(*clientcmdapi.NewConfig(), &clientcmd.ConfigOverrides{}) factory := NewFactory(clientConfig) if factory.flags.HasFlags() { t.Errorf("Expected zero flags, but got %v", factory.flags) } }
func (o *LoginOptions) Complete(f *osclientcmd.Factory, cmd *cobra.Command, args []string) error { kubeconfig, err := f.OpenShiftClientConfig.RawConfig() o.StartingKubeConfig = &kubeconfig if err != nil { if !os.IsNotExist(err) { return err } // build a valid object to use if we failed on a non-existent file o.StartingKubeConfig = kclientcmdapi.NewConfig() } addr := flagtypes.Addr{Value: "localhost:8443", DefaultScheme: "https", DefaultPort: 8443, AllowPrefix: true}.Default() if serverFlag := kcmdutil.GetFlagString(cmd, "server"); len(serverFlag) > 0 { if err := addr.Set(serverFlag); err != nil { return err } o.Server = addr.String() } else if len(args) == 1 { if err := addr.Set(args[0]); err != nil { return err } o.Server = addr.String() } else if len(o.Server) == 0 { if defaultContext, defaultContextExists := o.StartingKubeConfig.Contexts[o.StartingKubeConfig.CurrentContext]; defaultContextExists { if cluster, exists := o.StartingKubeConfig.Clusters[defaultContext.Cluster]; exists { o.Server = cluster.Server } } } o.CertFile = kcmdutil.GetFlagString(cmd, "client-certificate") o.KeyFile = kcmdutil.GetFlagString(cmd, "client-key") o.APIVersion = kcmdutil.GetFlagString(cmd, "api-version") // if the API version isn't explicitly passed, use the API version from the default context (same rules as the server above) if len(o.APIVersion) == 0 { if defaultContext, defaultContextExists := o.StartingKubeConfig.Contexts[o.StartingKubeConfig.CurrentContext]; defaultContextExists { if cluster, exists := o.StartingKubeConfig.Clusters[defaultContext.Cluster]; exists { o.APIVersion = cluster.APIVersion } } } o.CAFile = kcmdutil.GetFlagString(cmd, "certificate-authority") o.InsecureTLS = kcmdutil.GetFlagBool(cmd, "insecure-skip-tls-verify") o.Token = kcmdutil.GetFlagString(cmd, "token") o.DefaultNamespace, _, _ = f.OpenShiftClientConfig.Namespace() o.PathOptions = config.NewPathOptions(cmd) return nil }
func TestConfirmUsableMissingConfig(t *testing.T) { config := clientcmdapi.NewConfig() test := configValidationTest{ config: config, expectedErrorSubstring: []string{"context was not found for"}, } test.testConfirmUsable("not-here", t) }
func TestConfirmUsableEmptyConfig(t *testing.T) { config := clientcmdapi.NewConfig() test := configValidationTest{ config: config, expectedErrorSubstring: []string{"no context chosen"}, } test.testConfirmUsable("", 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) }
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 TestValidateEmptyClusterInfo(t *testing.T) { config := clientcmdapi.NewConfig() config.Clusters["empty"] = &clientcmdapi.Cluster{} test := configValidationTest{ config: config, expectedErrorSubstring: []string{"no server found for"}, } test.testCluster("empty", 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 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) }
// getConfigFromFileOrDie tries to read a kubeconfig file and if it can't, it calls exit. One exception, missing files result in empty configs, not an exit func getConfigFromFileOrDie(filename string) *clientcmdapi.Config { config, err := clientcmd.LoadFromFile(filename) if err != nil && !os.IsNotExist(err) { glog.FatalDepth(1, err) } if config == nil { return clientcmdapi.NewConfig() } return config }
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) }
// 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 } if err := clientcmdlatest.Codec.DecodeInto(data, config); err != nil { return nil, err } return config, nil }
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) } }
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 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 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) }
// CreateConfig takes a clientCfg and builds a config (kubeconfig style) from it. func CreateConfig(namespace string, clientCfg *client.Config) (*clientcmdapi.Config, error) { clusterNick, err := GetClusterNicknameFromConfig(clientCfg) if err != nil { return nil, err } userNick, err := GetUserNicknameFromConfig(clientCfg) if err != nil { return nil, err } contextNick, err := GetContextNicknameFromConfig(namespace, clientCfg) if err != nil { return nil, err } config := clientcmdapi.NewConfig() credentials := clientcmdapi.NewAuthInfo() credentials.Token = clientCfg.BearerToken credentials.ClientCertificate = clientCfg.TLSClientConfig.CertFile if len(credentials.ClientCertificate) == 0 { credentials.ClientCertificateData = clientCfg.TLSClientConfig.CertData } credentials.ClientKey = clientCfg.TLSClientConfig.KeyFile if len(credentials.ClientKey) == 0 { credentials.ClientKeyData = clientCfg.TLSClientConfig.KeyData } config.AuthInfos[userNick] = credentials cluster := clientcmdapi.NewCluster() cluster.Server = clientCfg.Host cluster.CertificateAuthority = clientCfg.CAFile if len(cluster.CertificateAuthority) == 0 { cluster.CertificateAuthorityData = clientCfg.CAData } cluster.InsecureSkipTLSVerify = clientCfg.Insecure cluster.APIVersion = clientCfg.Version config.Clusters[clusterNick] = cluster context := clientcmdapi.NewContext() context.Cluster = clusterNick context.AuthInfo = userNick context.Namespace = namespace config.Contexts[contextNick] = context config.CurrentContext = contextNick return config, nil }
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 (o *PathOptions) GetStartingConfig() (*clientcmdapi.Config, error) { // don't mutate the original loadingRules := *o.LoadingRules loadingRules.Precedence = o.GetLoadingPrecedence() clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(&loadingRules, &clientcmd.ConfigOverrides{}) rawConfig, err := clientConfig.RawConfig() if os.IsNotExist(err) { return clientcmdapi.NewConfig(), nil } if err != nil { return nil, err } return &rawConfig, nil }
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) }
// ValidateToken validates that the given token is valid on the given server // // It returns a client config if successful, or an error func ValidateToken(server string, token string) (*kclient.Config, error) { opts := &cmd.LoginOptions{ Server: server, Token: token, InsecureTLS: true, APIVersion: api.Version, StartingKubeConfig: kclientcmdapi.NewConfig(), PathOptions: kcmdconfig.NewDefaultPathOptions(), Out: ioutil.Discard, } // check the token if err := opts.GatherInfo(); err != nil { return nil, err } return opts.Config, nil }