Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare cli-go to accept --kubeconfig setting #3107

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/skaffold/app/cmd/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions pkg/skaffold/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type SkaffoldOptions struct {
CacheFile string
Trigger string
KubeContext string
KubeConfig string
WatchPollInterval int
DefaultRepo string
CustomLabels []string
Expand Down
34 changes: 19 additions & 15 deletions pkg/skaffold/kubernetes/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand All @@ -60,23 +63,23 @@ 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 {
return nil, err
}
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")
Expand All @@ -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()
Expand All @@ -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,
})
Expand Down
28 changes: 22 additions & 6 deletions pkg/skaffold/kubernetes/context/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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")

Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand All @@ -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()
}