@@ -454,22 +454,67 @@ func (m *Manager) submitRequest(ctx context.Context, meta metadata.Metadata, csr
454
454
return req , nil
455
455
}
456
456
457
- // ManageVolume will initiate management of data for the given volumeID.
458
- func (m * Manager ) ManageVolume (volumeID string ) error {
457
+ // ManageVolumeImmediate will register a volume for management and immediately attempt a single issuance.
458
+ // This
459
+ func (m * Manager ) ManageVolumeImmediate (ctx context.Context , volumeID string ) (managed bool , err error ) {
460
+ if ! m .manageVolumeIfNotManaged (volumeID ) {
461
+ return false , nil
462
+ }
463
+
464
+ meta , err := m .metadataReader .ReadMetadata (volumeID )
465
+ if err != nil {
466
+ return true , fmt .Errorf ("reading metadata: %w" , err )
467
+ }
468
+
469
+ // Only attempt issuance immediately if there isn't already an issued certificate
470
+ if meta .NextIssuanceTime == nil {
471
+ // If issuance fails, immediately return without retrying so the caller can decide
472
+ // how to proceed depending on the context this method was called within.
473
+ if err := m .issue (ctx , volumeID ); err != nil {
474
+ return true , err
475
+ }
476
+ }
477
+
478
+ if ! m .startRenewalRoutine (volumeID ) {
479
+ return true , fmt .Errorf ("unexpected state: renewal routine not started, please open an issue at https://github.com/cert-manager/csi-lib" )
480
+ }
481
+
482
+ return true , nil
483
+ }
484
+
485
+ // manageVolumeIfNotManaged will ensure the named volume has been registered for management.
486
+ // It returns 'true' if the volume was not previously managed, and false if the volume was already managed.
487
+ func (m * Manager ) manageVolumeIfNotManaged (volumeID string ) (managed bool ) {
459
488
m .lock .Lock ()
460
489
defer m .lock .Unlock ()
461
490
log := m .log .WithValues ("volume_id" , volumeID )
462
491
463
492
// if the volume is already managed, return early
464
- if _ , ok := m .managedVolumes [volumeID ]; ok {
493
+ if _ , managed := m .managedVolumes [volumeID ]; managed {
465
494
log .V (2 ).Info ("Volume already registered for management" )
466
- return nil
495
+ return false
467
496
}
468
497
469
498
// construct a new channel used to stop management of the volume
470
499
stopCh := make (chan struct {})
471
500
m .managedVolumes [volumeID ] = stopCh
472
501
502
+ return true
503
+ }
504
+
505
+ // startRenewalRoutine will begin the background issuance goroutine for the given volumeID.
506
+ // It is the caller's responsibility to ensure this is only called once per volume.
507
+ func (m * Manager ) startRenewalRoutine (volumeID string ) (started bool ) {
508
+ m .lock .Lock ()
509
+ defer m .lock .Unlock ()
510
+ log := m .log .WithValues ("volume_id" , volumeID )
511
+
512
+ stopCh , ok := m .managedVolumes [volumeID ]
513
+ if ! ok {
514
+ log .Info ("Volume not registered for management, cannot start renewal routine..." )
515
+ return false
516
+ }
517
+
473
518
// Create a context that will be cancelled when the stopCh is closed
474
519
ctx , cancel := context .WithCancel (context .Background ())
475
520
go func () {
@@ -481,7 +526,6 @@ func (m *Manager) ManageVolume(volumeID string) error {
481
526
482
527
go func () {
483
528
// check every volume once per second
484
- // TODO: optimise this to not check so often
485
529
ticker := time .NewTicker (time .Second )
486
530
for {
487
531
select {
@@ -496,9 +540,14 @@ func (m *Manager) ManageVolume(volumeID string) error {
496
540
}
497
541
498
542
if meta .NextIssuanceTime == nil || m .clock .Now ().After (* meta .NextIssuanceTime ) {
499
- wait .ExponentialBackoffWithContext (ctx , wait.Backoff {
500
- // 2s is the 'base' amount of time for the backoff
501
- Duration : time .Second * 2 ,
543
+ // If issuing a certificate fails, we don't go around the outer for loop again (as we'd then be creating
544
+ // a new CertificateRequest every second).
545
+ // Instead, retry within the same iteration of the for loop and apply an exponential backoff.
546
+ // Because we pass ctx through to the 'wait' package, if the stopCh is closed/context is cancelled,
547
+ // we'll immediately stop waiting and 'continue' which will then hit the `case <-stopCh` case in the `select`.
548
+ if err := wait .ExponentialBackoffWithContext (ctx , wait.Backoff {
549
+ // 8s is the 'base' amount of time for the backoff
550
+ Duration : time .Second * 8 ,
502
551
// We multiple the 'duration' by 2.0 if the attempt fails/errors
503
552
Factor : 2.0 ,
504
553
// Add a jitter of +/- 1s (0.5 of the 'duration')
@@ -507,22 +556,39 @@ func (m *Manager) ManageVolume(volumeID string) error {
507
556
// reset back to the 'base duration'. Set this to the MaxInt32, as we never want to
508
557
// reset this unless we get a successful attempt.
509
558
Steps : math .MaxInt32 ,
510
- // The maximum time between calls will be 1 minute
511
- Cap : time .Minute ,
559
+ // The maximum time between calls will be 5 minutes
560
+ Cap : time .Minute * 5 ,
512
561
}, func () (bool , error ) {
513
562
log .Info ("Triggering new issuance" )
514
563
if err := m .issue (ctx , volumeID ); err != nil {
515
564
log .Error (err , "Failed to issue certificate, retrying after applying exponential backoff" )
516
565
return false , nil
517
566
}
518
567
return true , nil
519
- })
568
+ }); err != nil {
569
+ if errors .Is (err , wait .ErrWaitTimeout ) || errors .Is (err , context .DeadlineExceeded ) {
570
+ continue
571
+ }
572
+ // this should never happen as the function above never actually returns errors
573
+ log .Error (err , "unexpected error" )
574
+ }
520
575
}
521
576
}
522
577
}
523
578
}()
579
+ return true
580
+ }
524
581
525
- return nil
582
+ // ManageVolume will initiate management of data for the given volumeID.
583
+ func (m * Manager ) ManageVolume (volumeID string ) (managed bool ) {
584
+ log := m .log .WithValues ("volume_id" , volumeID )
585
+ if managed := m .manageVolumeIfNotManaged (volumeID ); ! managed {
586
+ return false
587
+ }
588
+ if started := m .startRenewalRoutine (volumeID ); ! started {
589
+ log .Info ("unexpected state: renewal routine not started, please open an issue at https://github.com/cert-manager/csi-lib" )
590
+ }
591
+ return true
526
592
}
527
593
528
594
func (m * Manager ) UnmanageVolume (volumeID string ) {
@@ -546,6 +612,13 @@ func (m *Manager) IsVolumeReadyToRequest(volumeID string) (bool, string) {
546
612
}
547
613
548
614
func (m * Manager ) IsVolumeReady (volumeID string ) bool {
615
+ m .lock .Lock ()
616
+ defer m .lock .Unlock ()
617
+ // a volume is not classed as Ready if it is not managed
618
+ if _ , managed := m .managedVolumes [volumeID ]; ! managed {
619
+ return false
620
+ }
621
+
549
622
meta , err := m .metadataReader .ReadMetadata (volumeID )
550
623
if err != nil {
551
624
m .log .Error (err , "failed to read metadata" , "volume_id" , volumeID )
0 commit comments