// clientauth.Info object contain both user identification and server identification. We want different precedence orders for // both, so we have to split the objects and merge them separately // we want this order of precedence for user identifcation // 1. configAuthInfo minus auth-path (the final result of command line flags and merged .kubeconfig files) // 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) // 3. if there is not enough information to idenfity the user, load try the ~/.kubernetes_auth file // 4. if there is not enough information to identify the user, prompt if possible func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fallbackReader io.Reader) (*client.Config, error) { mergedConfig := &client.Config{} // blindly overwrite existing values based on precedence if len(configAuthInfo.Token) > 0 { mergedConfig.BearerToken = configAuthInfo.Token } if len(configAuthInfo.ClientCertificate) > 0 || len(configAuthInfo.ClientCertificateData) > 0 { mergedConfig.CertFile = configAuthInfo.ClientCertificate mergedConfig.CertData = configAuthInfo.ClientCertificateData mergedConfig.KeyFile = configAuthInfo.ClientKey mergedConfig.KeyData = configAuthInfo.ClientKeyData } if len(configAuthInfo.Username) > 0 || len(configAuthInfo.Password) > 0 { mergedConfig.Username = configAuthInfo.Username mergedConfig.Password = configAuthInfo.Password } // if there still isn't enough information to authenticate the user, try prompting if !canIdentifyUser(*mergedConfig) && (fallbackReader != nil) { prompter := NewPromptingAuthLoader(fallbackReader) promptedAuthInfo := prompter.Prompt() promptedConfig := makeUserIdentificationConfig(*promptedAuthInfo) previouslyMergedConfig := mergedConfig mergedConfig = &client.Config{} mergo.Merge(mergedConfig, promptedConfig) mergo.Merge(mergedConfig, previouslyMergedConfig) } return mergedConfig, nil }
func (config DirectClientConfig) getAuthInfo() clientcmdapi.AuthInfo { authInfos := config.config.AuthInfos authInfoName := config.getAuthInfoName() var mergedAuthInfo clientcmdapi.AuthInfo if configAuthInfo, exists := authInfos[authInfoName]; exists { mergo.Merge(&mergedAuthInfo, configAuthInfo) } mergo.Merge(&mergedAuthInfo, config.overrides.AuthInfo) return mergedAuthInfo }
func (config DirectClientConfig) getContext() clientcmdapi.Context { contexts := config.config.Contexts contextName := config.getContextName() var mergedContext clientcmdapi.Context if configContext, exists := contexts[contextName]; exists { mergo.Merge(&mergedContext, configContext) } mergo.Merge(&mergedContext, config.overrides.Context) return mergedContext }
// 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) } } // 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) } // 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, errors.NewAggregate(errlist) }
func (config DirectClientConfig) getCluster() clientcmdapi.Cluster { clusterInfos := config.config.Clusters clusterInfoName := config.getClusterName() var mergedClusterInfo clientcmdapi.Cluster mergo.Merge(&mergedClusterInfo, DefaultCluster) mergo.Merge(&mergedClusterInfo, EnvVarCluster) if configClusterInfo, exists := clusterInfos[clusterInfoName]; exists { mergo.Merge(&mergedClusterInfo, configClusterInfo) } mergo.Merge(&mergedClusterInfo, config.overrides.ClusterInfo) return mergedClusterInfo }
// clientauth.Info object contain both user identification and server identification. We want different precedence orders for // both, so we have to split the objects and merge them separately // we want this order of precedence for the server identification // 1. configClusterInfo (the final result of command line flags and merged .kubeconfig files) // 2. configAuthInfo.auth-path (this file can contain information that conflicts with #1, and we want #1 to win the priority) // 3. load the ~/.kubernetes_auth file as a default func getServerIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, configClusterInfo clientcmdapi.Cluster) (*client.Config, error) { mergedConfig := &client.Config{} // configClusterInfo holds the information identify the server provided by .kubeconfig configClientConfig := &client.Config{} configClientConfig.CAFile = configClusterInfo.CertificateAuthority configClientConfig.CAData = configClusterInfo.CertificateAuthorityData configClientConfig.Insecure = configClusterInfo.InsecureSkipTLSVerify mergo.Merge(mergedConfig, configClientConfig) return mergedConfig, nil }
// ClientConfig implements ClientConfig func (config DirectClientConfig) ClientConfig() (*client.Config, error) { if err := config.ConfirmUsable(); err != nil { return nil, err } configAuthInfo := config.getAuthInfo() configClusterInfo := config.getCluster() clientConfig := &client.Config{} clientConfig.Host = configClusterInfo.Server if u, err := url.ParseRequestURI(clientConfig.Host); err == nil && u.Opaque == "" && len(u.Path) > 1 { clientConfig.Prefix = u.Path u.Path = "" u.RawQuery = "" u.Fragment = "" clientConfig.Host = u.String() } clientConfig.Version = configClusterInfo.APIVersion // only try to read the auth information if we are secure if client.IsConfigTransportTLS(*clientConfig) { var err error // mergo is a first write wins for map value and a last writing wins for interface values userAuthPartialConfig, err := getUserIdentificationPartialConfig(configAuthInfo, config.fallbackReader) if err != nil { return nil, err } mergo.Merge(clientConfig, userAuthPartialConfig) serverAuthPartialConfig, err := getServerIdentificationPartialConfig(configAuthInfo, configClusterInfo) if err != nil { return nil, err } mergo.Merge(clientConfig, serverAuthPartialConfig) } return clientConfig, nil }
func mergeConfigWithFile(startingConfig *clientcmdapi.Config, filename string) error { if len(filename) == 0 { // no work to do return nil } config, err := LoadFromFile(filename) if os.IsNotExist(err) { return nil } if err != nil { return fmt.Errorf("Error loading config file \"%s\": %v", filename, err) } mergo.Merge(startingConfig, config) return nil }