github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/kubernetes/context/context.go (about) 1 /* 2 Copyright 2019 The Skaffold Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package context 18 19 import ( 20 "context" 21 "fmt" 22 "sync" 23 24 restclient "k8s.io/client-go/rest" 25 "k8s.io/client-go/tools/clientcmd" 26 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 27 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 29 ) 30 31 // For testing 32 var ( 33 CurrentConfig = getCurrentConfig 34 ) 35 36 var ( 37 kubeConfigOnce sync.Once 38 kubeConfig clientcmd.ClientConfig 39 40 configureOnce sync.Once 41 kubeContext string 42 kubeConfigFile string 43 ) 44 45 // ConfigureKubeConfig sets an override for the current context in the k8s config. 46 // When given, the firstCliValue always takes precedence over the yamlValue. 47 // Changing the kube-context of a running Skaffold process is not supported, so 48 // after the first call, the kube-context will be locked. 49 func ConfigureKubeConfig(cliKubeConfig, cliKubeContext string) { 50 configureOnce.Do(func() { 51 kubeContext = cliKubeContext 52 kubeConfigFile = cliKubeConfig 53 if kubeContext != "" { 54 log.Entry(context.TODO()).Infof("Activated kube-context %q", kubeContext) 55 } 56 }) 57 } 58 59 // GetDefaultRestClientConfig returns a REST client config for API calls against the Kubernetes API. 60 // If ConfigureKubeConfig was called before, the CurrentContext will be overridden. 61 // The kubeconfig used will be cached for the life of the skaffold process after the first call. 62 // If the CurrentContext is empty and the resulting config is empty, this method attempts to 63 // create a RESTClient with an in-cluster config. 64 func GetDefaultRestClientConfig() (*restclient.Config, error) { 65 return getRestClientConfig(kubeContext, kubeConfigFile) 66 } 67 68 // GetRestClientConfig returns a REST client config for API calls against the Kubernetes API for the given context. 69 func GetRestClientConfig(kubeContext string) (*restclient.Config, error) { 70 return getRestClientConfig(kubeContext, kubeConfigFile) 71 } 72 73 // GetClusterInfo returns the Cluster information for the given kubeContext 74 func GetClusterInfo(kctx string) (*clientcmdapi.Cluster, error) { 75 rawConfig, err := getCurrentConfig() 76 if err != nil { 77 return nil, err 78 } 79 c, found := rawConfig.Clusters[kctx] 80 if !found { 81 return nil, fmt.Errorf("failed to get cluster info for kubeContext: `%s`", kctx) 82 } 83 return c, nil 84 } 85 86 func getRestClientConfig(kctx string, kcfg string) (*restclient.Config, error) { 87 log.Entry(context.TODO()).Debugf("getting client config for kubeContext: `%s`", kctx) 88 89 rawConfig, err := getCurrentConfig() 90 if err != nil { 91 return nil, err 92 } 93 94 clientConfig := clientcmd.NewNonInteractiveClientConfig(rawConfig, kctx, &clientcmd.ConfigOverrides{CurrentContext: kctx}, clientcmd.NewDefaultClientConfigLoadingRules()) 95 restConfig, err := clientConfig.ClientConfig() 96 if kctx == "" && kcfg == "" && clientcmd.IsEmptyConfig(err) { 97 log.Entry(context.TODO()).Debug("no kube-context set and no kubeConfig found, attempting in-cluster config") 98 restConfig, err := restclient.InClusterConfig() 99 if err != nil { 100 return restConfig, fmt.Errorf("error creating REST client config in-cluster: %w", err) 101 } 102 103 return restConfig, nil 104 } 105 if err != nil { 106 return restConfig, fmt.Errorf("error creating REST client config for kubeContext %q: %w", kctx, err) 107 } 108 109 return restConfig, nil 110 } 111 112 // getCurrentConfig retrieves and caches the raw kubeConfig. The cache ensures that Skaffold always works with the identical kubeconfig, 113 // even if it was changed on disk. 114 func getCurrentConfig() (clientcmdapi.Config, error) { 115 kubeConfigOnce.Do(func() { 116 loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() 117 loadingRules.ExplicitPath = kubeConfigFile 118 kubeConfig = clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{ 119 CurrentContext: kubeContext, 120 }) 121 }) 122 123 cfg, err := kubeConfig.RawConfig() 124 if kubeContext != "" { 125 // RawConfig does not respect the override in kubeConfig 126 cfg.CurrentContext = kubeContext 127 } 128 return cfg, err 129 }