Skip to content

Commit a763455

Browse files
jrhoustondak1n1
andauthored
Credentials changes for v2.0.0 (#1052)
* Remove load_config_file and support for KUBECONFIG environment variable * Change config_path -> config_paths * Support config_path and config_paths * Fix crash in kubernetes_job waiter Co-authored-by: Stef Forrester <[email protected]>
1 parent 695f78a commit a763455

File tree

5 files changed

+241
-155
lines changed

5 files changed

+241
-155
lines changed

kubernetes/provider.go

Lines changed: 123 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7-
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
87
"log"
98
"net/http"
9+
"os"
10+
"path/filepath"
11+
12+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
1013

1114
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
1215
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -67,16 +70,18 @@ func Provider() *schema.Provider {
6770
DefaultFunc: schema.EnvDefaultFunc("KUBE_CLUSTER_CA_CERT_DATA", ""),
6871
Description: "PEM-encoded root certificates bundle for TLS authentication.",
6972
},
73+
"config_paths": {
74+
Type: schema.TypeList,
75+
Elem: &schema.Schema{Type: schema.TypeString},
76+
Optional: true,
77+
Description: "A list of paths to kube config files. Can be set with KUBE_CONFIG_PATHS environment variable.",
78+
},
7079
"config_path": {
71-
Type: schema.TypeString,
72-
Optional: true,
73-
DefaultFunc: schema.MultiEnvDefaultFunc(
74-
[]string{
75-
"KUBE_CONFIG",
76-
"KUBECONFIG",
77-
},
78-
"~/.kube/config"),
79-
Description: "Path to the kube config file, defaults to ~/.kube/config",
80+
Type: schema.TypeString,
81+
Optional: true,
82+
DefaultFunc: schema.EnvDefaultFunc("KUBE_CONFIG_PATH", nil),
83+
Description: "Path to the kube config file. Can be set with KUBE_CONFIG_PATH.",
84+
ConflictsWith: []string{"config_paths"},
8085
},
8186
"config_context": {
8287
Type: schema.TypeString,
@@ -101,12 +106,6 @@ func Provider() *schema.Provider {
101106
DefaultFunc: schema.EnvDefaultFunc("KUBE_TOKEN", ""),
102107
Description: "Token to authenticate an service account",
103108
},
104-
"load_config_file": {
105-
Type: schema.TypeBool,
106-
Optional: true,
107-
DefaultFunc: schema.EnvDefaultFunc("KUBE_LOAD_CONFIG_FILE", true),
108-
Description: "Load local kubeconfig.",
109-
},
110109
"exec": {
111110
Type: schema.TypeList,
112111
Optional: true,
@@ -204,12 +203,19 @@ type kubeClientsets struct {
204203
config *restclient.Config
205204
mainClientset *kubernetes.Clientset
206205
aggregatorClientset *aggregator.Clientset
206+
207+
configData *schema.ResourceData
207208
}
208209

209210
func (k kubeClientsets) MainClientset() (*kubernetes.Clientset, error) {
210211
if k.mainClientset != nil {
211212
return k.mainClientset, nil
212213
}
214+
215+
if err := checkConfigurationValid(k.configData); err != nil {
216+
return nil, err
217+
}
218+
213219
if k.config != nil {
214220
kc, err := kubernetes.NewForConfig(k.config)
215221
if err != nil {
@@ -234,8 +240,53 @@ func (k kubeClientsets) AggregatorClientset() (*aggregator.Clientset, error) {
234240
return k.aggregatorClientset, nil
235241
}
236242

237-
func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVersion string) (interface{}, diag.Diagnostics) {
243+
var apiTokenMountPath = "/var/run/secrets/kubernetes.io/serviceaccount"
244+
245+
func inCluster() bool {
246+
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
247+
if host == "" || port == "" {
248+
return false
249+
}
250+
251+
if _, err := os.Stat(apiTokenMountPath); err != nil {
252+
return false
253+
}
254+
return true
255+
}
256+
257+
var authDocumentationURL = "https://registry.terraform.io/providers/hashicorp/kubernetes/latest/docs#authentication"
238258

259+
func checkConfigurationValid(d *schema.ResourceData) error {
260+
if inCluster() {
261+
log.Printf("[DEBUG] Terraform appears to be running inside the Kubernetes cluster")
262+
return nil
263+
}
264+
265+
if os.Getenv("KUBE_CONFIG_PATHS") != "" {
266+
return nil
267+
}
268+
269+
atLeastOneOf := []string{
270+
"host",
271+
"config_path",
272+
"config_paths",
273+
"client_certificate",
274+
"token",
275+
"exec",
276+
}
277+
for _, a := range atLeastOneOf {
278+
if _, ok := d.GetOk(a); ok {
279+
return nil
280+
}
281+
}
282+
283+
return fmt.Errorf(`provider not configured: you must configure a path to your kubeconfig
284+
or explicitly supply credentials via the provider block or environment variables.
285+
286+
See our documentation at: %s`, authDocumentationURL)
287+
}
288+
289+
func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVersion string) (interface{}, diag.Diagnostics) {
239290
// Config initialization
240291
cfg, err := initializeConfiguration(d)
241292
if err != nil {
@@ -262,6 +313,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVer
262313
config: cfg,
263314
mainClientset: nil,
264315
aggregatorClientset: nil,
316+
configData: d,
265317
}
266318
return m, diag.Diagnostics{}
267319
}
@@ -270,40 +322,64 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error)
270322
overrides := &clientcmd.ConfigOverrides{}
271323
loader := &clientcmd.ClientConfigLoadingRules{}
272324

273-
if d.Get("load_config_file").(bool) {
274-
log.Printf("[DEBUG] Trying to load configuration from file")
275-
if configPath, ok := d.GetOk("config_path"); ok && configPath.(string) != "" {
276-
path, err := homedir.Expand(configPath.(string))
325+
configPaths := []string{}
326+
327+
if v, ok := d.Get("config_path").(string); ok && v != "" {
328+
configPaths = []string{v}
329+
} else if v, ok := d.Get("config_paths").([]interface{}); ok && len(v) > 0 {
330+
for _, p := range v {
331+
configPaths = append(configPaths, p.(string))
332+
}
333+
} else if v := os.Getenv("KUBE_CONFIG_PATHS"); v != "" {
334+
// NOTE we have to do this here because the schema
335+
// does not yet allow you to set a default for a TypeList
336+
configPaths = filepath.SplitList(v)
337+
}
338+
339+
if len(configPaths) > 0 {
340+
expandedPaths := []string{}
341+
for _, p := range configPaths {
342+
path, err := homedir.Expand(p)
277343
if err != nil {
278344
return nil, err
279345
}
280-
log.Printf("[DEBUG] Configuration file is: %s", path)
281-
loader.ExplicitPath = path
282-
283-
ctxSuffix := "; default context"
284-
285-
kubectx, ctxOk := d.GetOk("config_context")
286-
authInfo, authInfoOk := d.GetOk("config_context_auth_info")
287-
cluster, clusterOk := d.GetOk("config_context_cluster")
288-
if ctxOk || authInfoOk || clusterOk {
289-
ctxSuffix = "; overriden context"
290-
if ctxOk {
291-
overrides.CurrentContext = kubectx.(string)
292-
ctxSuffix += fmt.Sprintf("; config ctx: %s", overrides.CurrentContext)
293-
log.Printf("[DEBUG] Using custom current context: %q", overrides.CurrentContext)
294-
}
295-
296-
overrides.Context = clientcmdapi.Context{}
297-
if authInfoOk {
298-
overrides.Context.AuthInfo = authInfo.(string)
299-
ctxSuffix += fmt.Sprintf("; auth_info: %s", overrides.Context.AuthInfo)
300-
}
301-
if clusterOk {
302-
overrides.Context.Cluster = cluster.(string)
303-
ctxSuffix += fmt.Sprintf("; cluster: %s", overrides.Context.Cluster)
304-
}
305-
log.Printf("[DEBUG] Using overidden context: %#v", overrides.Context)
346+
if _, err := os.Stat(path); err != nil {
347+
return nil, fmt.Errorf("could not open kubeconfig %q: %v", p, err)
348+
}
349+
350+
log.Printf("[DEBUG] Using kubeconfig: %s", path)
351+
expandedPaths = append(expandedPaths, path)
352+
}
353+
354+
if len(expandedPaths) == 1 {
355+
loader.ExplicitPath = expandedPaths[0]
356+
} else {
357+
loader.Precedence = expandedPaths
358+
}
359+
360+
ctxSuffix := "; default context"
361+
362+
kubectx, ctxOk := d.GetOk("config_context")
363+
authInfo, authInfoOk := d.GetOk("config_context_auth_info")
364+
cluster, clusterOk := d.GetOk("config_context_cluster")
365+
if ctxOk || authInfoOk || clusterOk {
366+
ctxSuffix = "; overriden context"
367+
if ctxOk {
368+
overrides.CurrentContext = kubectx.(string)
369+
ctxSuffix += fmt.Sprintf("; config ctx: %s", overrides.CurrentContext)
370+
log.Printf("[DEBUG] Using custom current context: %q", overrides.CurrentContext)
371+
}
372+
373+
overrides.Context = clientcmdapi.Context{}
374+
if authInfoOk {
375+
overrides.Context.AuthInfo = authInfo.(string)
376+
ctxSuffix += fmt.Sprintf("; auth_info: %s", overrides.Context.AuthInfo)
377+
}
378+
if clusterOk {
379+
overrides.Context.Cluster = cluster.(string)
380+
ctxSuffix += fmt.Sprintf("; cluster: %s", overrides.Context.Cluster)
306381
}
382+
log.Printf("[DEBUG] Using overidden context: %#v", overrides.Context)
307383
}
308384
}
309385

@@ -367,7 +443,6 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error)
367443
return nil, nil
368444
}
369445

370-
log.Printf("[INFO] Successfully initialized config")
371446
return cfg, nil
372447
}
373448

0 commit comments

Comments
 (0)