Change:
Found in openshift, but I'm pretty sure the codepaths are the same. That means the token is getting ignored and in-cluster config is being used in preference to flags.
Is a similar issue?
Yep, kind of regression.
Sigh.
Yes, most likely the same cause.
I'm on it.
Unit test :)
Probably will want to cherrypick this into 1.5.
WIP in

Discussion:
} // inClusterClientConfig makes a config that will work from within a kubernetes cluster container environment. <del> type inClusterClientConfig struct{} </del> <ins> // Can take options overrides for flags explicitly provided to the command inside the cluster container. type inClusterClientConfig struct { overrides               *ConfigOverrides inClusterConfigProvider func() (*restclient.Config, error) } </ins> <del> var _ ClientConfig = inClusterClientConfig{} </del> <ins> var _ ClientConfig = &inClusterClientConfig{} </ins> <del> func (inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) { </del> <ins> func (config *inClusterClientConfig) RawConfig() (clientcmdapi.Config, error) { </ins> return clientcmdapi.Config{}, fmt.Errorf("inCluster environment config doesn't support multiple clusters") } <del> func (inClusterClientConfig) ClientConfig() (*restclient.Config, error) { return restclient.InClusterConfig() </del> <ins> func (config *inClusterClientConfig) ClientConfig() (*restclient.Config, error) { if config.inClusterConfigProvider == nil { config.inClusterConfigProvider = restclient.InClusterConfig } icc, err := config.inClusterConfigProvider() if err != nil { return nil, err } // in-cluster configs only takes a host, token, or CA file // if any of them were individually provided, ovewrite anything else if config.overrides != nil { if server := config.overrides.ClusterInfo.Server; len(server) > 0 { icc.Host = server } if token := config.overrides.AuthInfo.Token; len(token) > 0 { icc.BearerToken = token } if certificateAuthorityFile := config.overrides.ClusterInfo.CertificateAuthority; len(certificateAuthorityFile) > 0 { icc.TLSClientConfig.CAFile = certificateAuthorityFile } } return icc, err </ins> } <del> func (inClusterClientConfig) Namespace() (string, bool, error) { </del> <ins> func (config *inClusterClientConfig) Namespace() (string, bool, error) { </ins> // This way assumes you've set the POD_NAMESPACE environment variable using the downward API. // This check has to be done first for backwards compatibility with the way InClusterConfig was originally set up if ns := os.Getenv("POD_NAMESPACE"); ns != "" {

return "default", false, nil } <del> func (inClusterClientConfig) ConfigAccess() ConfigAccess { </del> <ins> func (config *inClusterClientConfig) ConfigAccess() ConfigAccess { </ins> return NewDefaultClientConfigLoadingRules() } // Possible returns true if loading an inside-kubernetes-cluster is possible. <del> func (inClusterClientConfig) Possible() bool { </del> <ins> func (config *inClusterClientConfig) Possible() bool { </ins> fi, err := os.Stat("/var/run/secrets/kubernetes.io/serviceaccount/token") return os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "" &&

"testing" "github.com/imdario/mergo" <ins> "k8s.io/kubernetes/pkg/client/restclient" </ins> clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api" )

} } <ins> func TestInClusterClientConfigPrecedence(t *testing.T) { tt := []struct { overrides *ConfigOverrides }{ { overrides: &ConfigOverrides{ ClusterInfo: clientcmdapi.Cluster{ Server: "https://host-from-overrides.com", }, }, }, { overrides: &ConfigOverrides{ AuthInfo: clientcmdapi.AuthInfo{ Token: "https://host-from-overrides.com", }, }, }, { overrides: &ConfigOverrides{ ClusterInfo: clientcmdapi.Cluster{ CertificateAuthority: "/path/to/ca-from-overrides.crt", }, }, }, { overrides: &ConfigOverrides{ ClusterInfo: clientcmdapi.Cluster{ Server: "https://host-from-overrides.com", }, AuthInfo: clientcmdapi.AuthInfo{ Token: "https://host-from-overrides.com", }, }, }, { overrides: &ConfigOverrides{ ClusterInfo: clientcmdapi.Cluster{ Server:               "https://host-from-overrides.com", CertificateAuthority: "/path/to/ca-from-overrides.crt", }, }, }, { overrides: &ConfigOverrides{ ClusterInfo: clientcmdapi.Cluster{ CertificateAuthority: "/path/to/ca-from-overrides.crt", }, AuthInfo: clientcmdapi.AuthInfo{ Token: "https://host-from-overrides.com", }, }, }, { overrides: &ConfigOverrides{ ClusterInfo: clientcmdapi.Cluster{ Server:               "https://host-from-overrides.com", CertificateAuthority: "/path/to/ca-from-overrides.crt", }, AuthInfo: clientcmdapi.AuthInfo{ Token: "https://host-from-overrides.com", }, }, }, { overrides: &ConfigOverrides{}, }, } for _, tc := range tt { expectedServer := "https://host-from-cluster.com" expectedToken := "token-from-cluster" expectedCAFile := "/path/to/ca-from-cluster.crt" icc := &inClusterClientConfig{ inClusterConfigProvider: func() (*restclient.Config, error) { return &restclient.Config{ Host:        expectedServer, BearerToken: expectedToken, TLSClientConfig: restclient.TLSClientConfig{ CAFile: expectedCAFile, }, }, nil }, overrides: tc.overrides, } clientConfig, err := icc.ClientConfig() if err != nil { t.Fatalf("Unxpected error: %v", err) } if overridenServer := tc.overrides.ClusterInfo.Server; len(overridenServer) > 0 { expectedServer = overridenServer } if overridenToken := tc.overrides.AuthInfo.Token; len(overridenToken) > 0 { expectedToken = overridenToken } if overridenCAFile := tc.overrides.ClusterInfo.CertificateAuthority; len(overridenCAFile) > 0 { expectedCAFile = overridenCAFile } if clientConfig.Host != expectedServer { t.Errorf("Expected server %v, got %v", expectedServer, clientConfig.Host) } if clientConfig.BearerToken != expectedToken { t.Errorf("Expected token %v, got %v", expectedToken, clientConfig.BearerToken) } if clientConfig.TLSClientConfig.CAFile != expectedCAFile { t.Errorf("Expected Certificate Authority %v, got %v", expectedCAFile, clientConfig.TLSClientConfig.CAFile) } } } </ins> func matchBoolArg(expected, got bool, t *testing.T) { if expected != got { t.Errorf("Expected %v, got %v", expected, got)

// NewNonInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name func NewNonInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides) ClientConfig { <del> return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: inClusterClientConfig{}} </del> <ins> return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}} </ins> } // NewInteractiveDeferredLoadingClientConfig creates a ConfigClientClientConfig using the passed context name and the fallback auth reader func NewInteractiveDeferredLoadingClientConfig(loader ClientConfigLoader, overrides *ConfigOverrides, fallbackReader io.Reader) ClientConfig { <del> return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: inClusterClientConfig{}, fallbackReader: fallbackReader} </del> <ins> return &DeferredLoadingClientConfig{loader: loader, overrides: overrides, icc: &inClusterClientConfig{overrides: overrides}, fallbackReader: fallbackReader} </ins> } func (config *DeferredLoadingClientConfig) createClientConfig() (ClientConfig, error) {

FlagTimeout      = "request-timeout" ) <ins> // RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { return ConfigOverrideFlags{ AuthOverrideFlags:    RecommendedAuthOverrideFlags(prefix), ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix), ContextOverrideFlags: RecommendedContextOverrideFlags(prefix), CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"}, Timeout:        FlagInfo{prefix + FlagTimeout, "", "0", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."}, } } </ins> // RecommendedAuthOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing func RecommendedAuthOverrideFlags(prefix string) AuthOverrideFlags { return AuthOverrideFlags{

} } <del> // RecommendedConfigOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing func RecommendedConfigOverrideFlags(prefix string) ConfigOverrideFlags { return ConfigOverrideFlags{ AuthOverrideFlags:    RecommendedAuthOverrideFlags(prefix), ClusterOverrideFlags: RecommendedClusterOverrideFlags(prefix), ContextOverrideFlags: RecommendedContextOverrideFlags(prefix), CurrentContext: FlagInfo{prefix + FlagContext, "", "", "The name of the kubeconfig context to use"}, Timeout:        FlagInfo{prefix + FlagTimeout, "", "0", "The length of time to wait before giving up on a single server request. Non-zero values should contain a corresponding time unit (e.g. 1s, 2m, 3h). A value of zero means don't timeout requests."}, } } </del> // RecommendedContextOverrideFlags is a convenience method to return recommended flag names prefixed with a string of your choosing func RecommendedContextOverrideFlags(prefix string) ContextOverrideFlags { return ContextOverrideFlags{

} } <ins> // BindOverrideFlags is a convenience method to bind the specified flags to their associated variables func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNames ConfigOverrideFlags) { BindAuthInfoFlags(&overrides.AuthInfo, flags, flagNames.AuthOverrideFlags) BindClusterFlags(&overrides.ClusterInfo, flags, flagNames.ClusterOverrideFlags) BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags) flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext) flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout) } </ins> // BindAuthInfoFlags is a convenience method to bind the specified flags to their associated variables func BindAuthInfoFlags(authInfo *clientcmdapi.AuthInfo, flags *pflag.FlagSet, flagNames AuthOverrideFlags) { flagNames.ClientCertificate.BindStringFlag(flags, &authInfo.ClientCertificate)

flagNames.InsecureSkipTLSVerify.BindBoolFlag(flags, &clusterInfo.InsecureSkipTLSVerify) } <del> // BindOverrideFlags is a convenience method to bind the specified flags to their associated variables func BindOverrideFlags(overrides *ConfigOverrides, flags *pflag.FlagSet, flagNames ConfigOverrideFlags) { BindAuthInfoFlags(&overrides.AuthInfo, flags, flagNames.AuthOverrideFlags) BindClusterFlags(&overrides.ClusterInfo, flags, flagNames.ClusterOverrideFlags) BindContextFlags(&overrides.Context, flags, flagNames.ContextOverrideFlags) flagNames.CurrentContext.BindStringFlag(flags, &overrides.CurrentContext) flagNames.Timeout.BindStringFlag(flags, &overrides.Timeout) } </del> // BindFlags is a convenience method to bind the specified flags to their associated variables func BindContextFlags(contextInfo *clientcmdapi.Context, flags *pflag.FlagSet, flagNames ContextOverrideFlags) { flagNames.ClusterName.BindStringFlag(flags, &contextInfo.Cluster)

framework.Failf("Container port output missing expected value. Wanted:'%s', got: %s", nginxDefaultOutput, body) } }) <ins> It("should handle in-cluster config", func() { By("overriding icc with values provided by flags") kubectlPath := framework.TestContext.KubectlPath inClusterHost := strings.TrimSpace(framework.RunHostCmdOrDie(ns, simplePodName, "printenv KUBERNETES_SERVICE_HOST")) inClusterPort := strings.TrimSpace(framework.RunHostCmdOrDie(ns, simplePodName, "printenv KUBERNETES_SERVICE_PORT")) framework.RunKubectlOrDie("cp", kubectlPath, ns+"/"+simplePodName+":/") By("getting pods with in-cluster configs") execOutput := framework.RunHostCmdOrDie(ns, simplePodName, "/kubectl get pods") if matched, err := regexp.MatchString("nginx +1/1 +Running", execOutput); err != nil || !matched { framework.Failf("Unexpected kubectl exec output: ", execOutput) } By("trying to use kubectl with invalid token") _, err := framework.RunHostCmd(ns, simplePodName, "/kubectl get pods --token=invalid --v=7 2>&1") framework.Logf("got err %v", err) Expect(err).To(HaveOccurred()) Expect(err).To(ContainSubstring("User "system:anonymous" cannot list pods in the namespace")) Expect(err).To(ContainSubstring("Using in-cluster namespace")) Expect(err).To(ContainSubstring("Using in-cluster configuration")) Expect(err).To(ContainSubstring("Authorization: Bearer invalid")) Expect(err).To(ContainSubstring("Response Status: 403 Forbidden")) By("trying to use kubectl with invalid server") _, err = framework.RunHostCmd(ns, simplePodName, "/kubectl get pods --server=invalid --v=6 2>&1") framework.Logf("got err %v", err) Expect(err).To(HaveOccurred()) Expect(err).To(ContainSubstring("Unable to connect to the server")) Expect(err).To(ContainSubstring("GET http://invalid/api")) By("trying to use kubectl with invalid namespace") output, _ := framework.RunHostCmd(ns, simplePodName, "/kubectl get pods --namespace=invalid --v=6 2>&1") Expect(output).To(ContainSubstring("No resources found")) Expect(output).ToNot(ContainSubstring("Using in-cluster namespace")) Expect(output).To(ContainSubstring("Using in-cluster configuration")) if matched, _ := regexp.MatchString(fmt.Sprintf("GET http[s]?://%s:%s/api/v1/namespaces/invalid/pods", inClusterHost, inClusterPort), output); !matched { framework.Failf("Unexpected kubectl exec output: ", output) } }) </ins> }) framework.KubeDescribe("Kubectl api-versions", func() {