diff --git a/pkg/client/unversioned/clientcmd/api/types.go b/pkg/client/unversioned/clientcmd/api/types.go index 95b5289f460..9fd52e44c86 100644 --- a/pkg/client/unversioned/clientcmd/api/types.go +++ b/pkg/client/unversioned/clientcmd/api/types.go @@ -88,6 +88,8 @@ type AuthInfo struct { ClientKeyData []byte `json:"client-key-data,omitempty"` // Token is the bearer token for authentication to the kubernetes cluster. Token string `json:"token,omitempty"` + // TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence. + TokenFile string `json:"tokenFile,omitempty"` // Impersonate is the username to act-as. Impersonate string `json:"act-as,omitempty"` // Username is the username for basic authentication to the kubernetes cluster. diff --git a/pkg/client/unversioned/clientcmd/api/v1/types.go b/pkg/client/unversioned/clientcmd/api/v1/types.go index 77bce80a3bb..bcb82aa9829 100644 --- a/pkg/client/unversioned/clientcmd/api/v1/types.go +++ b/pkg/client/unversioned/clientcmd/api/v1/types.go @@ -82,6 +82,8 @@ type AuthInfo struct { ClientKeyData []byte `json:"client-key-data,omitempty"` // Token is the bearer token for authentication to the kubernetes cluster. Token string `json:"token,omitempty"` + // TokenFile is a pointer to a file that contains a bearer token (as described above). If both Token and TokenFile are present, Token takes precedence. + TokenFile string `json:"tokenFile,omitempty"` // Impersonate is the username to imperonate. The name matches the flag. Impersonate string `json:"as,omitempty"` // Username is the username for basic authentication to the kubernetes cluster. diff --git a/pkg/client/unversioned/clientcmd/client_config.go b/pkg/client/unversioned/clientcmd/client_config.go index 47b14e215ac..3be1d77c66b 100644 --- a/pkg/client/unversioned/clientcmd/client_config.go +++ b/pkg/client/unversioned/clientcmd/client_config.go @@ -167,6 +167,12 @@ func getUserIdentificationPartialConfig(configAuthInfo clientcmdapi.AuthInfo, fa // blindly overwrite existing values based on precedence if len(configAuthInfo.Token) > 0 { mergedConfig.BearerToken = configAuthInfo.Token + } else if len(configAuthInfo.TokenFile) > 0 { + tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile) + if err != nil { + return nil, err + } + mergedConfig.BearerToken = string(tokenBytes) } if len(configAuthInfo.Impersonate) > 0 { mergedConfig.Impersonate = configAuthInfo.Impersonate diff --git a/pkg/client/unversioned/clientcmd/client_config_test.go b/pkg/client/unversioned/clientcmd/client_config_test.go index 3c2aba7bd3b..e819e72fe07 100644 --- a/pkg/client/unversioned/clientcmd/client_config_test.go +++ b/pkg/client/unversioned/clientcmd/client_config_test.go @@ -17,6 +17,8 @@ limitations under the License. package clientcmd import ( + "io/ioutil" + "os" "reflect" "testing" @@ -186,6 +188,80 @@ func TestBasicAuthData(t *testing.T) { matchStringArg(password, clientConfig.Password, 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 TestPrecedenceTokenFile(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", + } + expectedToken := "expected" + config.AuthInfos["clean"] = &clientcmdapi.AuthInfo{ + Token: expectedToken, + 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(expectedToken, clientConfig.BearerToken, t) +} + func TestCreateClean(t *testing.T) { config := createValidTestConfig() clientBuilder := NewNonInteractiveClientConfig(*config, "clean", &ConfigOverrides{}, nil) diff --git a/pkg/client/unversioned/clientcmd/loader.go b/pkg/client/unversioned/clientcmd/loader.go index 10094062279..4e509878901 100644 --- a/pkg/client/unversioned/clientcmd/loader.go +++ b/pkg/client/unversioned/clientcmd/loader.go @@ -215,7 +215,6 @@ func (rules *ClientConfigLoadingRules) Load() (*clientcmdapi.Config, error) { errlist = append(errlist, err) } } - return config, utilerrors.NewAggregate(errlist) } @@ -530,7 +529,7 @@ func GetClusterFileReferences(cluster *clientcmdapi.Cluster) []*string { } func GetAuthInfoFileReferences(authInfo *clientcmdapi.AuthInfo) []*string { - return []*string{&authInfo.ClientCertificate, &authInfo.ClientKey} + return []*string{&authInfo.ClientCertificate, &authInfo.ClientKey, &authInfo.TokenFile} } // ResolvePaths updates the given refs to be absolute paths, relative to the given base directory