@@ -21,6 +21,7 @@ import (
21
21
"fmt"
22
22
"math"
23
23
"net"
24
+ "os"
24
25
"strings"
25
26
"time"
26
27
@@ -33,6 +34,8 @@ import (
33
34
34
35
"k8s.io/api/core/v1"
35
36
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
37
+ "k8s.io/apimachinery/pkg/util/sets"
38
+ "k8s.io/apimachinery/pkg/util/validation"
36
39
"k8s.io/apimachinery/pkg/util/wait"
37
40
"k8s.io/client-go/kubernetes"
38
41
"k8s.io/client-go/rest"
@@ -46,8 +49,18 @@ import (
46
49
)
47
50
48
51
const (
49
- secretNameKey = "csiProvisionerSecretName"
50
- secretNamespaceKey = "csiProvisionerSecretNamespace"
52
+ provisionerSecretNameKey = "csiProvisionerSecretName"
53
+ provisionerSecretNamespaceKey = "csiProvisionerSecretNamespace"
54
+
55
+ controllerPublishSecretNameKey = "csiControllerPublishSecretName"
56
+ controllerPublishSecretNamespaceKey = "csiControllerPublishSecretNamespace"
57
+
58
+ nodeStageSecretNameKey = "csiNodeStageSecretName"
59
+ nodeStageSecretNamespaceKey = "csiNodeStageSecretNamespace"
60
+
61
+ nodePublishSecretNameKey = "csiNodePublishSecretName"
62
+ nodePublishSecretNamespaceKey = "csiNodePublishSecretNamespace"
63
+
51
64
// Defines parameters for ExponentialBackoff used for executing
52
65
// CSI CreateVolume API call, it gives approx 4 minutes for the CSI
53
66
// driver to complete a volume creation.
@@ -272,7 +285,7 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
272
285
return nil , err
273
286
}
274
287
275
- share , err := makeVolumeName (p .volumeNamePrefix , fmt .Sprintf ("%s" , options .PVC .ObjectMeta .UID ), p .volumeNameUUIDLength )
288
+ pvName , err := makeVolumeName (p .volumeNamePrefix , fmt .Sprintf ("%s" , options .PVC .ObjectMeta .UID ), p .volumeNameUUIDLength )
276
289
if err != nil {
277
290
return nil , err
278
291
}
@@ -283,7 +296,7 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
283
296
// Create a CSI CreateVolumeRequest and Response
284
297
req := csi.CreateVolumeRequest {
285
298
286
- Name : share ,
299
+ Name : pvName ,
287
300
Parameters : options .Parameters ,
288
301
VolumeCapabilities : []* csi.VolumeCapability {
289
302
{
@@ -297,14 +310,30 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
297
310
}
298
311
rep := & csi.CreateVolumeResponse {}
299
312
300
- secret := v1.SecretReference {}
301
- if options .Parameters != nil {
302
- credentials , err := getCredentialsFromParameters (p .client , options .Parameters )
303
- if err != nil {
304
- return nil , err
305
- }
306
- req .ControllerCreateSecrets = credentials
307
- secret .Name , secret .Namespace , _ = getSecretAndNamespaceFromParameters (options .Parameters )
313
+ // Resolve provision secret credentials.
314
+ // No PVC is provided when resolving provision/delete secret names, since the PVC may or may not exist at delete time.
315
+ provisionerSecretRef , err := getSecretReference (provisionerSecretNameKey , provisionerSecretNamespaceKey , options .Parameters , pvName , nil )
316
+ if err != nil {
317
+ return nil , err
318
+ }
319
+ provisionerCredentials , err := getCredentials (p .client , provisionerSecretRef )
320
+ if err != nil {
321
+ return nil , err
322
+ }
323
+ req .ControllerCreateSecrets = provisionerCredentials
324
+
325
+ // Resolve controller publish, node stage, node publish secret references
326
+ controllerPublishSecretRef , err := getSecretReference (controllerPublishSecretNameKey , controllerPublishSecretNamespaceKey , options .Parameters , pvName , options .PVC )
327
+ if err != nil {
328
+ return nil , err
329
+ }
330
+ nodeStageSecretRef , err := getSecretReference (nodeStageSecretNameKey , nodeStageSecretNamespaceKey , options .Parameters , pvName , options .PVC )
331
+ if err != nil {
332
+ return nil , err
333
+ }
334
+ nodePublishSecretRef , err := getSecretReference (nodePublishSecretNameKey , nodePublishSecretNamespaceKey , options .Parameters , pvName , options .PVC )
335
+ if err != nil {
336
+ return nil , err
308
337
}
309
338
310
339
opts := wait.Backoff {Duration : backoffDuration , Factor : backoffFactor , Steps : backoffSteps }
@@ -345,24 +374,18 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
345
374
delReq := & csi.DeleteVolumeRequest {
346
375
VolumeId : rep .GetVolume ().GetId (),
347
376
}
348
- if options .Parameters != nil {
349
- credentials , err := getCredentialsFromParameters (p .client , options .Parameters )
350
- if err != nil {
351
- return nil , err
352
- }
353
- delReq .ControllerDeleteSecrets = credentials
354
- }
377
+ delReq .ControllerDeleteSecrets = provisionerCredentials
355
378
ctx , cancel := context .WithTimeout (context .Background (), p .timeout )
356
379
defer cancel ()
357
380
_ , err := p .csiClient .DeleteVolume (ctx , delReq )
358
381
if err != nil {
359
- capErr = fmt .Errorf ("%v. Cleanup of volume %s failed, volume is orphaned: %v" , capErr , share , err )
382
+ capErr = fmt .Errorf ("%v. Cleanup of volume %s failed, volume is orphaned: %v" , capErr , pvName , err )
360
383
}
361
384
return nil , capErr
362
385
}
363
386
pv := & v1.PersistentVolume {
364
387
ObjectMeta : metav1.ObjectMeta {
365
- Name : share ,
388
+ Name : pvName ,
366
389
},
367
390
Spec : v1.PersistentVolumeSpec {
368
391
PersistentVolumeReclaimPolicy : options .PersistentVolumeReclaimPolicy ,
@@ -373,16 +396,17 @@ func (p *csiProvisioner) Provision(options controller.VolumeOptions) (*v1.Persis
373
396
// TODO wait for CSI VolumeSource API
374
397
PersistentVolumeSource : v1.PersistentVolumeSource {
375
398
CSI : & v1.CSIPersistentVolumeSource {
376
- Driver : driverName ,
377
- VolumeHandle : p .volumeIdToHandle (rep .Volume .Id ),
378
- VolumeAttributes : volumeAttributes ,
399
+ Driver : driverName ,
400
+ VolumeHandle : p .volumeIdToHandle (rep .Volume .Id ),
401
+ VolumeAttributes : volumeAttributes ,
402
+ ControllerPublishSecretRef : controllerPublishSecretRef ,
403
+ NodeStageSecretRef : nodeStageSecretRef ,
404
+ NodePublishSecretRef : nodePublishSecretRef ,
379
405
},
380
406
},
381
407
},
382
408
}
383
- if len (secret .Name ) != 0 {
384
- pv .Spec .PersistentVolumeSource .CSI .NodePublishSecretRef = & secret
385
- }
409
+
386
410
glog .Infof ("successfully created PV %+v" , pv .Spec .PersistentVolumeSource )
387
411
388
412
return pv , nil
@@ -406,7 +430,13 @@ func (p *csiProvisioner) Delete(volume *v1.PersistentVolume) error {
406
430
storageClassName := volume .Spec .StorageClassName
407
431
if len (storageClassName ) != 0 {
408
432
if storageClass , err := p .client .StorageV1 ().StorageClasses ().Get (storageClassName , metav1.GetOptions {}); err == nil {
409
- credentials , err := getCredentialsFromParameters (p .client , storageClass .Parameters )
433
+ // Resolve provision secret credentials.
434
+ // No PVC is provided when resolving provision/delete secret names, since the PVC may or may not exist at delete time.
435
+ provisionerSecretRef , err := getSecretReference (provisionerSecretNameKey , provisionerSecretNamespaceKey , storageClass .Parameters , volume .Name , nil )
436
+ if err != nil {
437
+ return err
438
+ }
439
+ credentials , err := getCredentials (p .client , provisionerSecretRef )
410
440
if err != nil {
411
441
return err
412
442
}
@@ -422,24 +452,6 @@ func (p *csiProvisioner) Delete(volume *v1.PersistentVolume) error {
422
452
return err
423
453
}
424
454
425
- func getSecretAndNamespaceFromParameters (parameters map [string ]string ) (string , string , bool ) {
426
- if secretName , ok := parameters [secretNameKey ]; ok {
427
- namespace := "default"
428
- if len (parameters [secretNamespaceKey ]) != 0 {
429
- namespace = parameters [secretNamespaceKey ]
430
- }
431
- return secretName , namespace , true
432
- }
433
- return "" , "" , false
434
- }
435
-
436
- func getCredentialsFromParameters (k8s kubernetes.Interface , parameters map [string ]string ) (map [string ]string , error ) {
437
- if secretName , namespace , found := getSecretAndNamespaceFromParameters (parameters ); found {
438
- return getCredentialsFromSecret (k8s , secretName , namespace )
439
- }
440
- return map [string ]string {}, nil
441
- }
442
-
443
455
//TODO use a unique volume handle from and to Id
444
456
func (p * csiProvisioner ) volumeIdToHandle (id string ) string {
445
457
return id
@@ -449,19 +461,116 @@ func (p *csiProvisioner) volumeHandleToId(handle string) string {
449
461
return handle
450
462
}
451
463
452
- func getCredentialsFromSecret (k8s kubernetes.Interface , secretName , nameSpace string ) (map [string ]string , error ) {
453
- credentials := map [string ]string {}
454
- if len (secretName ) == 0 {
455
- return credentials , nil
464
+ // getSecretReference returns a reference to the secret specified in the given nameKey and namespaceKey parameters, or an error if the parameters are not specified correctly.
465
+ // if neither the name or namespace parameter are set, a nil reference and no error is returned.
466
+ // no lookup of the referenced secret is performed, and the secret may or may not exist.
467
+ //
468
+ // supported tokens for name resolution:
469
+ // - ${pv.name}
470
+ // - ${pvc.namespace}
471
+ // - ${pvc.name}
472
+ // - ${pvc.annotations['ANNOTATION_KEY']} (e.g. ${pvc.annotations['example.com/node-publish-secret-name']})
473
+ //
474
+ // supported tokens for namespace resolution:
475
+ // - ${pv.name}
476
+ // - ${pvc.namespace}
477
+ //
478
+ // an error is returned in the following situations:
479
+ // - only one of name or namespace is provided
480
+ // - the name or namespace parameter contains a token that cannot be resolved
481
+ // - the resolved name is not a valid secret name
482
+ // - the resolved namespace is not a valid namespace name
483
+ func getSecretReference (nameKey , namespaceKey string , storageClassParams map [string ]string , pvName string , pvc * v1.PersistentVolumeClaim ) (* v1.SecretReference , error ) {
484
+ nameTemplate , hasName := storageClassParams [nameKey ]
485
+ namespaceTemplate , hasNamespace := storageClassParams [namespaceKey ]
486
+
487
+ if ! hasName && ! hasNamespace {
488
+ return nil , nil
489
+ }
490
+
491
+ if len (nameTemplate ) == 0 || len (namespaceTemplate ) == 0 {
492
+ return nil , fmt .Errorf ("%s and %s parameters must be specified together" , nameKey , namespaceKey )
493
+ }
494
+
495
+ ref := & v1.SecretReference {}
496
+
497
+ {
498
+ // Secret namespace template can make use of the PV name or the PVC namespace.
499
+ // Note that neither of those things are under the control of the PVC user.
500
+ namespaceParams := map [string ]string {"pv.name" : pvName }
501
+ if pvc != nil {
502
+ namespaceParams ["pvc.namespace" ] = pvc .Namespace
503
+ }
504
+
505
+ resolvedNamespace , err := resolveTemplate (namespaceTemplate , namespaceParams )
506
+ if err != nil {
507
+ return nil , fmt .Errorf ("error resolving %s value %q: %v" , namespaceKey , namespaceTemplate , err )
508
+ }
509
+ if len (validation .IsDNS1123Label (resolvedNamespace )) > 0 {
510
+ if namespaceTemplate != resolvedNamespace {
511
+ return nil , fmt .Errorf ("%s parameter %q resolved to %q which is not a valid namespace name" , namespaceKey , namespaceTemplate , resolvedNamespace )
512
+ }
513
+ return nil , fmt .Errorf ("%s parameter %q is not a valid namespace name" , namespaceKey , namespaceTemplate )
514
+ }
515
+ ref .Namespace = resolvedNamespace
516
+ }
517
+
518
+ {
519
+ // Secret name template can make use of the PV name, PVC name or namespace, or a PVC annotation.
520
+ // Note that PVC name and annotations are under the PVC user's control.
521
+ nameParams := map [string ]string {"pv.name" : pvName }
522
+ if pvc != nil {
523
+ nameParams ["pvc.name" ] = pvc .Name
524
+ nameParams ["pvc.namespace" ] = pvc .Namespace
525
+ for k , v := range pvc .Annotations {
526
+ nameParams ["pvc.annotations['" + k + "']" ] = v
527
+ }
528
+ }
529
+ resolvedName , err := resolveTemplate (nameTemplate , nameParams )
530
+ if err != nil {
531
+ return nil , fmt .Errorf ("error resolving %s value %q: %v" , nameKey , nameTemplate , err )
532
+ }
533
+ if len (validation .IsDNS1123Subdomain (resolvedName )) > 0 {
534
+ if nameTemplate != resolvedName {
535
+ return nil , fmt .Errorf ("%s parameter %q resolved to %q which is not a valid secret name" , nameKey , nameTemplate , resolvedName )
536
+ }
537
+ return nil , fmt .Errorf ("%s parameter %q is not a valid secret name" , nameKey , nameTemplate )
538
+ }
539
+ ref .Name = resolvedName
540
+ }
541
+
542
+ return ref , nil
543
+ }
544
+
545
+ func resolveTemplate (template string , params map [string ]string ) (string , error ) {
546
+ missingParams := sets .NewString ()
547
+ resolved := os .Expand (template , func (k string ) string {
548
+ v , ok := params [k ]
549
+ if ! ok {
550
+ missingParams .Insert (k )
551
+ }
552
+ return v
553
+ })
554
+ if missingParams .Len () > 0 {
555
+ return "" , fmt .Errorf ("invalid tokens: %q" , missingParams .List ())
456
556
}
457
- secret , err := k8s .CoreV1 ().Secrets (nameSpace ).Get (secretName , metav1.GetOptions {})
557
+ return resolved , nil
558
+ }
559
+
560
+ func getCredentials (k8s kubernetes.Interface , ref * v1.SecretReference ) (map [string ]string , error ) {
561
+ if ref == nil {
562
+ return nil , nil
563
+ }
564
+
565
+ secret , err := k8s .CoreV1 ().Secrets (ref .Namespace ).Get (ref .Name , metav1.GetOptions {})
458
566
if err != nil {
459
- return credentials , fmt .Errorf ("failed to find the secret %s in the namespace %s with error : %v\n " , secretName , nameSpace , err )
567
+ return nil , fmt .Errorf ("error getting secret %s in namespace %s: %v" , ref . Name , ref . Namespace , err )
460
568
}
569
+
570
+ credentials := map [string ]string {}
461
571
for key , value := range secret .Data {
462
572
credentials [key ] = string (value )
463
573
}
464
-
465
574
return credentials , nil
466
575
}
467
576
0 commit comments