github.com/someshkoli/terratest@v0.41.1/modules/k8s/config.go (about) 1 package k8s 2 3 import ( 4 "errors" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "sort" 9 10 gwErrors "github.com/gruntwork-io/go-commons/errors" 11 homedir "github.com/mitchellh/go-homedir" 12 restclient "k8s.io/client-go/rest" 13 "k8s.io/client-go/tools/clientcmd" 14 "k8s.io/client-go/tools/clientcmd/api" 15 16 "github.com/gruntwork-io/terratest/modules/environment" 17 "github.com/gruntwork-io/terratest/modules/files" 18 "github.com/gruntwork-io/terratest/modules/logger" 19 "github.com/gruntwork-io/terratest/modules/testing" 20 ) 21 22 // LoadConfigFromPath will load a ClientConfig object from a file path that points to a location on disk containing a 23 // kubectl config. 24 func LoadConfigFromPath(path string) clientcmd.ClientConfig { 25 config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 26 &clientcmd.ClientConfigLoadingRules{ExplicitPath: path}, 27 &clientcmd.ConfigOverrides{}) 28 return config 29 } 30 31 // LoadApiClientConfigE will load a ClientConfig object from a file path that points to a location on disk containing a 32 // kubectl config, with the requested context loaded. 33 func LoadApiClientConfigE(configPath string, contextName string) (*restclient.Config, error) { 34 overrides := clientcmd.ConfigOverrides{} 35 if contextName != "" { 36 overrides.CurrentContext = contextName 37 } 38 config := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( 39 &clientcmd.ClientConfigLoadingRules{ExplicitPath: configPath}, 40 &overrides) 41 return config.ClientConfig() 42 } 43 44 // DeleteConfigContextE will remove the context specified at the provided name, and remove any clusters and authinfos 45 // that are orphaned as a result of it. The config path is either specified in the environment variable KUBECONFIG or at 46 // the user's home directory under `.kube/config`. 47 func DeleteConfigContextE(t testing.TestingT, contextName string) error { 48 kubeConfigPath, err := GetKubeConfigPathE(t) 49 if err != nil { 50 return err 51 } 52 53 return DeleteConfigContextWithPathE(t, kubeConfigPath, contextName) 54 } 55 56 // DeleteConfigContextWithPathE will remove the context specified at the provided name, and remove any clusters and 57 // authinfos that are orphaned as a result of it. 58 func DeleteConfigContextWithPathE(t testing.TestingT, kubeConfigPath string, contextName string) error { 59 logger.Logf(t, "Removing kubectl config context %s from config at path %s", contextName, kubeConfigPath) 60 61 // Load config and get data structure representing config info 62 config := LoadConfigFromPath(kubeConfigPath) 63 rawConfig, err := config.RawConfig() 64 if err != nil { 65 return err 66 } 67 68 // Check if the context we want to delete actually exists, and if so, delete it. 69 _, ok := rawConfig.Contexts[contextName] 70 if !ok { 71 logger.Logf(t, "WARNING: Could not find context %s from config at path %s", contextName, kubeConfigPath) 72 return nil 73 } 74 delete(rawConfig.Contexts, contextName) 75 76 // If the removing context is the current context, be sure to set a new one 77 if contextName == rawConfig.CurrentContext { 78 if err := setNewContext(&rawConfig); err != nil { 79 return err 80 } 81 } 82 83 // Finally, clean up orphaned clusters and authinfos and then save config 84 RemoveOrphanedClusterAndAuthInfoConfig(&rawConfig) 85 if err := clientcmd.ModifyConfig(config.ConfigAccess(), rawConfig, false); err != nil { 86 return err 87 } 88 89 logger.Logf( 90 t, 91 "Removed context %s from config at path %s and any orphaned clusters and authinfos", 92 contextName, 93 kubeConfigPath) 94 return nil 95 } 96 97 // setNewContext will pick the alphebetically first available context from the list of contexts in the config to use as 98 // the new current context 99 func setNewContext(config *api.Config) error { 100 // Sort contextNames and pick the first one 101 var contextNames []string 102 for name := range config.Contexts { 103 contextNames = append(contextNames, name) 104 } 105 sort.Strings(contextNames) 106 if len(contextNames) > 0 { 107 config.CurrentContext = contextNames[0] 108 } else { 109 return errors.New("There are no available contexts remaining") 110 } 111 return nil 112 } 113 114 // RemoveOrphanedClusterAndAuthInfoConfig will remove all configurations related to clusters and users that have no 115 // contexts associated with it 116 func RemoveOrphanedClusterAndAuthInfoConfig(config *api.Config) { 117 newAuthInfos := map[string]*api.AuthInfo{} 118 newClusters := map[string]*api.Cluster{} 119 for _, context := range config.Contexts { 120 newClusters[context.Cluster] = config.Clusters[context.Cluster] 121 newAuthInfos[context.AuthInfo] = config.AuthInfos[context.AuthInfo] 122 } 123 config.AuthInfos = newAuthInfos 124 config.Clusters = newClusters 125 } 126 127 // GetKubeConfigPathE determines which file path to use as the kubectl config path 128 func GetKubeConfigPathE(t testing.TestingT) (string, error) { 129 kubeConfigPath := environment.GetFirstNonEmptyEnvVarOrEmptyString(t, []string{"KUBECONFIG"}) 130 if kubeConfigPath == "" { 131 configPath, err := KubeConfigPathFromHomeDirE() 132 if err != nil { 133 return "", err 134 } 135 kubeConfigPath = configPath 136 } 137 return kubeConfigPath, nil 138 } 139 140 // KubeConfigPathFromHomeDirE returns a string to the default Kubernetes config path in the home directory. This will 141 // error if the home directory can not be determined. 142 func KubeConfigPathFromHomeDirE() (string, error) { 143 home, err := homedir.Dir() 144 if err != nil { 145 return "", err 146 } 147 configPath := filepath.Join(home, ".kube", "config") 148 return configPath, err 149 } 150 151 // CopyHomeKubeConfigToTemp will copy the kubeconfig in the home directory to a temp file. This will fail the test if 152 // there are any errors. 153 func CopyHomeKubeConfigToTemp(t testing.TestingT) string { 154 path, err := CopyHomeKubeConfigToTempE(t) 155 if err != nil { 156 if path != "" { 157 os.Remove(path) 158 } 159 t.Fatal(err) 160 } 161 return path 162 } 163 164 // CopyHomeKubeConfigToTempE will copy the kubeconfig in the home directory to a temp file. 165 func CopyHomeKubeConfigToTempE(t testing.TestingT) (string, error) { 166 configPath, err := KubeConfigPathFromHomeDirE() 167 if err != nil { 168 return "", err 169 } 170 tmpConfig, err := ioutil.TempFile("", "") 171 if err != nil { 172 return "", gwErrors.WithStackTrace(err) 173 } 174 defer tmpConfig.Close() 175 err = files.CopyFile(configPath, tmpConfig.Name()) 176 return tmpConfig.Name(), err 177 } 178 179 // UpsertConfigContext will update or insert a new context to the provided config, binding the provided cluster to the 180 // provided user. 181 func UpsertConfigContext(config *api.Config, contextName string, clusterName string, userName string) { 182 config.Contexts[contextName] = &api.Context{Cluster: clusterName, AuthInfo: userName} 183 }