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