diff --git a/cmd/skaffold/app/cmd/runner.go b/cmd/skaffold/app/cmd/runner.go index 29cac410ad3..8fee8654653 100644 --- a/cmd/skaffold/app/cmd/runner.go +++ b/cmd/skaffold/app/cmd/runner.go @@ -72,7 +72,7 @@ func createNewRunner(opts config.SkaffoldOptions) (runner.Runner, *latest.Skaffo return nil, nil, errors.Wrap(err, "applying profiles") } - kubectx.UseKubeContext(opts.KubeContext, config.Deploy.KubeContext) + kubectx.ConfigureKubeConfig(opts.KubeConfig, opts.KubeContext, config.Deploy.KubeContext) if err := defaults.Set(config); err != nil { return nil, nil, errors.Wrap(err, "setting default values") diff --git a/pkg/skaffold/kubernetes/context/context.go b/pkg/skaffold/kubernetes/context/context.go index 219f2c3a2cf..465bd4ce95d 100644 --- a/pkg/skaffold/kubernetes/context/context.go +++ b/pkg/skaffold/kubernetes/context/context.go @@ -33,23 +33,26 @@ var ( ) var ( - kubeConfigOnce sync.Once - kubeConfig clientcmd.ClientConfig - kubeContextOnce sync.Once - kubeContext string + kubeConfigOnce sync.Once + kubeConfig clientcmd.ClientConfig + + configureOnce sync.Once + kubeContext string + kubeConfigFile string ) -// UseKubeContext sets an override for the current context in the k8s config. +// ConfigureKubeConfig sets an override for the current context in the k8s config. // When given, the firstCliValue always takes precedence over the yamlValue. // Changing the kube-context of a running Skaffold process is not supported, so // after the first call, the kube-context will be locked. -func UseKubeContext(cliValue, yamlValue string) { - newKubeContext := yamlValue - if cliValue != "" { - newKubeContext = cliValue +func ConfigureKubeConfig(cliKubeConfig, cliKubeContext, yamlKubeContext string) { + newKubeContext := yamlKubeContext + if cliKubeContext != "" { + newKubeContext = cliKubeContext } - kubeContextOnce.Do(func() { + configureOnce.Do(func() { kubeContext = newKubeContext + kubeConfigFile = cliKubeConfig if kubeContext != "" { logrus.Infof("Activated kube-context %q", kubeContext) } @@ -60,15 +63,15 @@ func UseKubeContext(cliValue, yamlValue string) { } // GetRestClientConfig returns a REST client config for API calls against the Kubernetes API. -// If UseKubeContext was called before, the CurrentContext will be overridden. +// If ConfigureKubeConfig was called before, the CurrentContext will be overridden. // The kubeconfig used will be cached for the life of the skaffold process after the first call. // If the CurrentContext is empty and the resulting config is empty, this method attempts to // create a RESTClient with an in-cluster config. func GetRestClientConfig() (*restclient.Config, error) { - return getRestClientConfig(kubeContext) + return getRestClientConfig(kubeContext, kubeConfigFile) } -func getRestClientConfig(kctx string) (*restclient.Config, error) { +func getRestClientConfig(kctx string, kcfg string) (*restclient.Config, error) { logrus.Debugf("getting client config for kubeContext: `%s`", kctx) rawConfig, err := getRawKubeConfig() if err != nil { @@ -76,7 +79,7 @@ func getRestClientConfig(kctx string) (*restclient.Config, error) { } clientConfig := clientcmd.NewNonInteractiveClientConfig(rawConfig, kctx, &clientcmd.ConfigOverrides{CurrentContext: kctx}, nil) restConfig, err := clientConfig.ClientConfig() - if kctx == "" && clientcmd.IsEmptyConfig(err) { + if kctx == "" && kcfg == "" && clientcmd.IsEmptyConfig(err) { logrus.Debug("no kube-context set and no kubeConfig found, attempting in-cluster config") restConfig, err := restclient.InClusterConfig() return restConfig, errors.Wrap(err, "error creating REST client config in-cluster") @@ -85,7 +88,7 @@ func getRestClientConfig(kctx string) (*restclient.Config, error) { return restConfig, errors.Wrapf(err, "error creating REST client config for kubeContext '%s'", kctx) } -// getCurrentConfig retrieves the kubeconfig file. If UseKubeContext was called before, the CurrentContext will be overridden. +// getCurrentConfig retrieves the kubeconfig file. If ConfigureKubeConfig was called before, the CurrentContext will be overridden. // The result will be cached after the first call. func getCurrentConfig() (clientcmdapi.Config, error) { cfg, err := getRawKubeConfig() @@ -101,6 +104,7 @@ func getCurrentConfig() (clientcmdapi.Config, error) { func getRawKubeConfig() (clientcmdapi.Config, error) { kubeConfigOnce.Do(func() { loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + loadingRules.ExplicitPath = kubeConfigFile kubeConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{ CurrentContext: kubeContext, }) diff --git a/pkg/skaffold/kubernetes/context/context_test.go b/pkg/skaffold/kubernetes/context/context_test.go index 90f609a617a..2778c8cd9f4 100644 --- a/pkg/skaffold/kubernetes/context/context_test.go +++ b/pkg/skaffold/kubernetes/context/context_test.go @@ -66,9 +66,13 @@ clusters: contexts: - context: cluster: cluster-bar - user: user1 - name: cluster-bar -current-context: cluster-foo + user: user-bar + name: context-bar +- context: + cluster: cluster-bar + user: user-baz + name: context-baz +current-context: context-baz users: - name: user1 user: @@ -96,6 +100,17 @@ func TestCurrentContext(t *testing.T) { t.CheckDeepEqual(clusterBarContext, config.CurrentContext) }) + testutil.Run(t, "kubeconfig CLI flag takes precedence", func(t *testutil.T) { + resetKubeConfig(t, validKubeConfig) + kubeConfig := t.TempFile("config", []byte(changedKubeConfig)) + + kubeConfigFile = kubeConfig + config, err := CurrentConfig() + + t.CheckNoError(err) + t.CheckDeepEqual("context-baz", config.CurrentContext) + }) + testutil.Run(t, "invalid context", func(t *testutil.T) { resetKubeConfig(t, "invalid") @@ -172,7 +187,7 @@ func TestGetRestClientConfig(t *testing.T) { t.SetEnvs(map[string]string{"KUBECONFIG": "non-valid"}) resetConfig() - _, err := getRestClientConfig("") + _, err := getRestClientConfig("", "") if err == nil { t.Errorf("expected error outside the cluster") @@ -258,7 +273,7 @@ func TestUseKubeContext(t *testing.T) { testutil.Run(t, test.name, func(t *testutil.T) { kubeContext = "" for _, inv := range test.invocations { - UseKubeContext(inv.cliValue, inv.yamlValue) + ConfigureKubeConfig("", inv.cliValue, inv.yamlValue) } t.CheckDeepEqual(test.expected, kubeContext) @@ -270,12 +285,13 @@ func TestUseKubeContext(t *testing.T) { // resetConfig is used by tests func resetConfig() { kubeConfigOnce = sync.Once{} - kubeContextOnce = sync.Once{} + configureOnce = sync.Once{} } func resetKubeConfig(t *testutil.T, content string) { kubeConfig := t.TempFile("config", []byte(content)) t.SetEnvs(map[string]string{"KUBECONFIG": kubeConfig}) kubeContext = "" + kubeConfigFile = "" resetConfig() }