diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go index ae0f01f3257..952f6d7eb6c 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go @@ -72,6 +72,13 @@ type ClientConfig interface { ConfigAccess() ConfigAccess } +// OverridingClientConfig is used to enable overrriding the raw KubeConfig +type OverridingClientConfig interface { + ClientConfig + // MergedRawConfig return the RawConfig merged with all overrides. + MergedRawConfig() (clientcmdapi.Config, error) +} + type PersistAuthProviderConfigForUser func(user string) restclient.AuthProviderConfigPersister type promptedCredentials struct { @@ -91,22 +98,22 @@ type DirectClientConfig struct { } // NewDefaultClientConfig creates a DirectClientConfig using the config.CurrentContext as the context name -func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) ClientConfig { +func NewDefaultClientConfig(config clientcmdapi.Config, overrides *ConfigOverrides) OverridingClientConfig { return &DirectClientConfig{config, config.CurrentContext, overrides, nil, NewDefaultClientConfigLoadingRules(), promptedCredentials{}} } // NewNonInteractiveClientConfig creates a DirectClientConfig using the passed context name and does not have a fallback reader for auth information -func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, configAccess ConfigAccess) ClientConfig { +func NewNonInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, configAccess ConfigAccess) OverridingClientConfig { return &DirectClientConfig{config, contextName, overrides, nil, configAccess, promptedCredentials{}} } // NewInteractiveClientConfig creates a DirectClientConfig using the passed context name and a reader in case auth information is not provided via files or flags -func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader, configAccess ConfigAccess) ClientConfig { +func NewInteractiveClientConfig(config clientcmdapi.Config, contextName string, overrides *ConfigOverrides, fallbackReader io.Reader, configAccess ConfigAccess) OverridingClientConfig { return &DirectClientConfig{config, contextName, overrides, fallbackReader, configAccess, promptedCredentials{}} } // NewClientConfigFromBytes takes your kubeconfig and gives you back a ClientConfig -func NewClientConfigFromBytes(configBytes []byte) (ClientConfig, error) { +func NewClientConfigFromBytes(configBytes []byte) (OverridingClientConfig, error) { config, err := Load(configBytes) if err != nil { return nil, err @@ -129,6 +136,40 @@ func (config *DirectClientConfig) RawConfig() (clientcmdapi.Config, error) { return config.config, nil } +// MergedRawConfig returns the raw kube config merged with the overrides +func (config *DirectClientConfig) MergedRawConfig() (clientcmdapi.Config, error) { + if err := config.ConfirmUsable(); err != nil { + return clientcmdapi.Config{}, err + } + merged := config.config.DeepCopy() + + // set the AuthInfo merged with overrides in the merged config + mergedAuthInfo, err := config.getAuthInfo() + if err != nil { + return clientcmdapi.Config{}, err + } + mergedAuthInfoName, _ := config.getAuthInfoName() + merged.AuthInfos[mergedAuthInfoName] = &mergedAuthInfo + + // set the Context merged with overrides in the merged config + mergedContext, err := config.getContext() + if err != nil { + return clientcmdapi.Config{}, err + } + mergedContextName, _ := config.getContextName() + merged.Contexts[mergedContextName] = &mergedContext + merged.CurrentContext = mergedContextName + + // set the Cluster merged with overrides in the merged config + configClusterInfo, err := config.getCluster() + if err != nil { + return clientcmdapi.Config{}, err + } + configClusterName, _ := config.getClusterName() + merged.Clusters[configClusterName] = &configClusterInfo + return *merged, nil +} + // ClientConfig implements ClientConfig func (config *DirectClientConfig) ClientConfig() (*restclient.Config, error) { // check that getAuthInfo, getContext, and getCluster do not return an error. diff --git a/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go b/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go index 64efc901623..17d1b7b20ad 100644 --- a/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go +++ b/staging/src/k8s.io/client-go/tools/clientcmd/client_config_test.go @@ -1013,3 +1013,68 @@ func TestCleanANSIEscapeCodes(t *testing.T) { }) } } + +func TestMergeRawConfigDoOverride(t *testing.T) { + const ( + server = "https://anything.com:8080" + token = "the-token" + modifiedServer = "http://localhost:8081" + modifiedToken = "modified-token" + ) + config := createValidTestConfig() + + // add another context which to modify with overrides + config.Clusters["modify"] = &clientcmdapi.Cluster{ + Server: server, + } + config.AuthInfos["modify"] = &clientcmdapi.AuthInfo{ + Token: token, + } + config.Contexts["modify"] = &clientcmdapi.Context{ + Cluster: "modify", + AuthInfo: "modify", + Namespace: "modify", + } + + // create overrides for the modify context + overrides := &ConfigOverrides{ + ClusterInfo: clientcmdapi.Cluster{ + Server: modifiedServer, + }, + Context: clientcmdapi.Context{ + Namespace: "foobar", + Cluster: "modify", + AuthInfo: "modify", + }, + AuthInfo: clientcmdapi.AuthInfo{ + Token: modifiedToken, + }, + CurrentContext: "modify", + } + + cut := NewDefaultClientConfig(*config, overrides) + act, err := cut.MergedRawConfig() + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // ensure overrides were applied to "modify" + actContext := act.CurrentContext + if actContext != "modify" { + t.Errorf("Expected context %v, got %v", "modify", actContext) + } + if act.Clusters[actContext].Server != "http://localhost:8081" { + t.Errorf("Expected server %v, got %v", "http://localhost:8081", act.Clusters[actContext].Server) + } + if act.Contexts[actContext].Namespace != "foobar" { + t.Errorf("Expected namespace %v, got %v", "foobar", act.Contexts[actContext].Namespace) + } + + // ensure context "clean" was not touched + if act.Clusters["clean"].Server != config.Clusters["clean"].Server { + t.Errorf("Expected server %v, got %v", config.Clusters["clean"].Server, act.Clusters["clean"].Server) + } + if act.Contexts["clean"].Namespace != config.Contexts["clean"].Namespace { + t.Errorf("Expected namespace %v, got %v", config.Contexts["clean"].Namespace, act.Contexts["clean"].Namespace) + } +}