@@ -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
209210func (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