github.com/GoogleCloudPlatform/terraformer@v0.8.18/providers/kubernetes/kubernetes_provider.go (about) 1 // Copyright 2018 The Terraformer Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package kubernetes 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "log" 21 "os" 22 "path/filepath" 23 "runtime" 24 "time" 25 26 restclient "k8s.io/client-go/rest" 27 "k8s.io/client-go/tools/clientcmd" 28 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 29 30 "github.com/GoogleCloudPlatform/terraformer/terraformutils" 31 "github.com/GoogleCloudPlatform/terraformer/terraformutils/providerwrapper" 32 "github.com/zclconf/go-cty/cty" 33 34 "github.com/pkg/errors" 35 "k8s.io/apimachinery/pkg/runtime/schema" 36 "k8s.io/apimachinery/pkg/util/sets" 37 "k8s.io/client-go/discovery" 38 _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // GKE support 39 ) 40 41 type KubernetesProvider struct { //nolint 42 terraformutils.Provider 43 verbose string 44 } 45 46 func (p KubernetesProvider) GetResourceConnections() map[string]map[string][]string { 47 return map[string]map[string][]string{} 48 } 49 50 func (p KubernetesProvider) GetProviderData(arg ...string) map[string]interface{} { 51 return map[string]interface{}{} 52 } 53 54 func (p *KubernetesProvider) Init(args []string) error { 55 p.verbose = args[0] 56 return nil 57 } 58 59 func (p *KubernetesProvider) GetName() string { 60 return "kubernetes" 61 } 62 63 func (p *KubernetesProvider) InitService(serviceName string, verbose bool) error { 64 var isSupported bool 65 if _, isSupported = p.GetSupportedService()[serviceName]; !isSupported { 66 return errors.New("kubernetes: " + serviceName + " not supported resource") 67 } 68 p.Service = p.GetSupportedService()[serviceName] 69 p.Service.SetName(serviceName) 70 p.Service.SetVerbose(verbose) 71 p.Service.SetProviderName(p.GetName()) 72 return nil 73 } 74 75 // GetSupportService return map of supported resource for Kubernetes 76 func (p *KubernetesProvider) GetSupportedService() map[string]terraformutils.ServiceGenerator { 77 resources := make(map[string]terraformutils.ServiceGenerator) 78 79 config, _, err := initClientAndConfig() 80 if err != nil { 81 return resources 82 } 83 84 dc, err := discovery.NewDiscoveryClientForConfig(config) 85 if err != nil { 86 log.Println(err) 87 return resources 88 } 89 90 lists, err := dc.ServerPreferredResources() 91 if err != nil { 92 log.Println(err) 93 return resources 94 } 95 provider, err := providerwrapper.NewProviderWrapper("kubernetes", cty.Value{}, p.verbose == "true") 96 if err != nil { 97 log.Println(err) 98 return resources 99 } 100 resp := provider.GetSchema() 101 for _, list := range lists { 102 if len(list.APIResources) == 0 { 103 continue 104 } 105 106 gv, err := schema.ParseGroupVersion(list.GroupVersion) 107 if err != nil { 108 continue 109 } 110 111 for _, resource := range list.APIResources { 112 if len(resource.Verbs) == 0 { 113 continue 114 } 115 116 // filter to resources that support list 117 if len(resource.Verbs) > 0 && !sets.NewString(resource.Verbs...).Has("list") { 118 continue 119 } 120 121 // filter to resource that are supported by terraform kubernetes provider 122 if _, ok := resp.ResourceTypes[extractTfResourceName(resource.Kind)]; !ok { 123 continue 124 } 125 126 resources[resource.Name] = &Kind{ 127 Group: gv.Group, 128 Version: gv.Version, 129 Name: resource.Kind, 130 Namespaced: resource.Namespaced, 131 } 132 } 133 } 134 return resources 135 } 136 137 // InitClientAndConfig uses the KUBECONFIG environment variable to create 138 // a new rest client and config object based on the existing kubectl config 139 // and options passed from the plugin framework via environment variables 140 func initClientAndConfig() (*restclient.Config, clientcmd.ClientConfig, error) { //nolint 141 // resolve kubeconfig location, prioritizing the --config global flag, 142 // then the value of the KUBECONFIG env var (if any), and defaulting 143 // to ~/.kube/config as a last resort. 144 home := os.Getenv("HOME") 145 if runtime.GOOS == "windows" { 146 home = os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") 147 if home == "" { 148 home = os.Getenv("USERPROFILE") 149 } 150 } 151 kubeconfig := filepath.Join(home, ".kube", "config") 152 153 kubeconfigEnv := os.Getenv("KUBECONFIG") 154 if len(kubeconfigEnv) > 0 { 155 kubeconfig = kubeconfigEnv 156 } 157 158 configFile := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_CONFIG") 159 kubeConfigFile := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_KUBECONFIG") 160 if len(configFile) > 0 { 161 kubeconfig = configFile 162 } else if len(kubeConfigFile) > 0 { 163 kubeconfig = kubeConfigFile 164 } 165 166 if len(kubeconfig) == 0 { 167 return nil, nil, fmt.Errorf("error initializing config. The KUBECONFIG environment variable must be defined") 168 } 169 170 config, err := configFromPath(kubeconfig) 171 if err != nil { 172 return nil, nil, fmt.Errorf("error obtaining kubectl config: %v", err) 173 } 174 client, err := config.ClientConfig() 175 if err != nil { 176 return nil, nil, fmt.Errorf("the provided credentials %q could not be used: %v", kubeconfig, err) 177 } 178 179 err = applyGlobalOptionsToConfig(client) 180 if err != nil { 181 return nil, nil, fmt.Errorf("error processing global plugin options: %v", err) 182 } 183 184 return client, config, nil 185 } 186 187 func configFromPath(path string) (clientcmd.ClientConfig, error) { 188 rules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: path} 189 credentials, err := rules.Load() 190 if err != nil { 191 return nil, fmt.Errorf("the provided credentials %q could not be loaded: %v", path, err) 192 } 193 194 overrides := &clientcmd.ConfigOverrides{ 195 Context: clientcmdapi.Context{ 196 Namespace: os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_NAMESPACE"), 197 }, 198 } 199 200 var cfg clientcmd.ClientConfig 201 context := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_CONTEXT") 202 if len(context) > 0 { 203 rules := clientcmd.NewDefaultClientConfigLoadingRules() 204 cfg = clientcmd.NewNonInteractiveClientConfig(*credentials, context, overrides, rules) 205 } else { 206 cfg = clientcmd.NewDefaultClientConfig(*credentials, overrides) 207 } 208 209 return cfg, nil 210 } 211 212 func applyGlobalOptionsToConfig(config *restclient.Config) error { 213 // impersonation config 214 impersonateUser := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_AS") 215 if len(impersonateUser) > 0 { 216 config.Impersonate.UserName = impersonateUser 217 } 218 219 impersonateGroup := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_AS_GROUP") 220 if len(impersonateGroup) > 0 { 221 impersonateGroupJSON := []string{} 222 err := json.Unmarshal([]byte(impersonateGroup), &impersonateGroupJSON) 223 if err != nil { 224 return errors.New(fmt.Sprintf("error parsing global option %q: %v", "--as-group", err)) 225 } 226 if len(impersonateGroupJSON) > 0 { 227 config.Impersonate.Groups = impersonateGroupJSON 228 } 229 } 230 231 // tls config 232 caFile := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_CERTIFICATE_AUTHORITY") 233 if len(caFile) > 0 { 234 config.TLSClientConfig.CAFile = caFile 235 } 236 237 clientCertFile := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_CLIENT_CERTIFICATE") 238 if len(clientCertFile) > 0 { 239 config.TLSClientConfig.CertFile = clientCertFile 240 } 241 242 clientKey := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_CLIENT_KEY") 243 if len(clientKey) > 0 { 244 config.TLSClientConfig.KeyFile = clientKey 245 } 246 247 // user / misc request config 248 requestTimeout := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_REQUEST_TIMEOUT") 249 if len(requestTimeout) > 0 { 250 t, err := time.ParseDuration(requestTimeout) 251 if err != nil { 252 return errors.New(fmt.Sprintf("%v", err)) 253 } 254 config.Timeout = t 255 } 256 257 server := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_SERVER") 258 if len(server) > 0 { 259 config.ServerName = server 260 } 261 262 token := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_TOKEN") 263 if len(token) > 0 { 264 config.BearerToken = token 265 } 266 267 username := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_USERNAME") 268 if len(username) > 0 { 269 config.Username = username 270 } 271 272 password := os.Getenv("KUBECTL_PLUGINS_GLOBAL_FLAG_PASSWORD") 273 if len(password) > 0 { 274 config.Password = password 275 } 276 277 return nil 278 }