@@ -4,9 +4,12 @@ import (
4
4
"bytes"
5
5
"context"
6
6
"fmt"
7
- "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8
7
"log"
9
8
"net/http"
9
+ "os"
10
+ "path/filepath"
11
+
12
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
10
13
11
14
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/logging"
12
15
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
@@ -67,16 +70,18 @@ func Provider() *schema.Provider {
67
70
DefaultFunc : schema .EnvDefaultFunc ("KUBE_CLUSTER_CA_CERT_DATA" , "" ),
68
71
Description : "PEM-encoded root certificates bundle for TLS authentication." ,
69
72
},
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
+ },
70
79
"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" },
80
85
},
81
86
"config_context" : {
82
87
Type : schema .TypeString ,
@@ -101,12 +106,6 @@ func Provider() *schema.Provider {
101
106
DefaultFunc : schema .EnvDefaultFunc ("KUBE_TOKEN" , "" ),
102
107
Description : "Token to authenticate an service account" ,
103
108
},
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
- },
110
109
"exec" : {
111
110
Type : schema .TypeList ,
112
111
Optional : true ,
@@ -204,12 +203,19 @@ type kubeClientsets struct {
204
203
config * restclient.Config
205
204
mainClientset * kubernetes.Clientset
206
205
aggregatorClientset * aggregator.Clientset
206
+
207
+ configData * schema.ResourceData
207
208
}
208
209
209
210
func (k kubeClientsets ) MainClientset () (* kubernetes.Clientset , error ) {
210
211
if k .mainClientset != nil {
211
212
return k .mainClientset , nil
212
213
}
214
+
215
+ if err := checkConfigurationValid (k .configData ); err != nil {
216
+ return nil , err
217
+ }
218
+
213
219
if k .config != nil {
214
220
kc , err := kubernetes .NewForConfig (k .config )
215
221
if err != nil {
@@ -234,8 +240,53 @@ func (k kubeClientsets) AggregatorClientset() (*aggregator.Clientset, error) {
234
240
return k .aggregatorClientset , nil
235
241
}
236
242
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"
238
258
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 ) {
239
290
// Config initialization
240
291
cfg , err := initializeConfiguration (d )
241
292
if err != nil {
@@ -262,6 +313,7 @@ func providerConfigure(ctx context.Context, d *schema.ResourceData, terraformVer
262
313
config : cfg ,
263
314
mainClientset : nil ,
264
315
aggregatorClientset : nil ,
316
+ configData : d ,
265
317
}
266
318
return m , diag.Diagnostics {}
267
319
}
@@ -270,40 +322,64 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error)
270
322
overrides := & clientcmd.ConfigOverrides {}
271
323
loader := & clientcmd.ClientConfigLoadingRules {}
272
324
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 )
277
343
if err != nil {
278
344
return nil , err
279
345
}
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 )
306
381
}
382
+ log .Printf ("[DEBUG] Using overidden context: %#v" , overrides .Context )
307
383
}
308
384
}
309
385
@@ -367,7 +443,6 @@ func initializeConfiguration(d *schema.ResourceData) (*restclient.Config, error)
367
443
return nil , nil
368
444
}
369
445
370
- log .Printf ("[INFO] Successfully initialized config" )
371
446
return cfg , nil
372
447
}
373
448
0 commit comments