7
7
"errors"
8
8
"fmt"
9
9
"github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
10
+ "github.com/andres-erbsen/clock"
10
11
"github.com/gofrs/uuid/v5"
11
12
"github.com/hashicorp/go-hclog"
12
13
"github.com/hashicorp/hcl"
@@ -18,6 +19,7 @@ import (
18
19
"google.golang.org/grpc/status"
19
20
"os"
20
21
"sync"
22
+ "time"
21
23
)
22
24
23
25
const (
@@ -41,8 +43,10 @@ type keyEntry struct {
41
43
}
42
44
43
45
type pluginHooks struct {
46
+ clk clock.Clock
44
47
// Used for testing only.
45
- lookupEnv func (string ) (string , bool )
48
+ lookupEnv func (string ) (string , bool )
49
+ scheduleDeleteSignal chan error
46
50
}
47
51
48
52
// Config provides configuration context for the plugin.
@@ -134,6 +138,9 @@ type Plugin struct {
134
138
cc * ClientConfig
135
139
vc * Client
136
140
141
+ scheduleDelete chan string
142
+ cancelTasks context.CancelFunc
143
+
137
144
hooks pluginHooks
138
145
}
139
146
@@ -148,7 +155,9 @@ func newPlugin() *Plugin {
148
155
entries : make (map [string ]keyEntry ),
149
156
hooks : pluginHooks {
150
157
lookupEnv : os .LookupEnv ,
158
+ clk : clock .New (),
151
159
},
160
+ scheduleDelete : make (chan string , 120 ),
152
161
}
153
162
}
154
163
@@ -214,6 +223,15 @@ func (p *Plugin) Configure(ctx context.Context, req *configv1.ConfigureRequest)
214
223
215
224
p .setCache (keyEntries )
216
225
226
+ // Cancel previous tasks in case of re-configure.
227
+ if p .cancelTasks != nil {
228
+ p .cancelTasks ()
229
+ }
230
+
231
+ // start tasks
232
+ ctx , p .cancelTasks = context .WithCancel (context .Background ())
233
+ go p .scheduleDeleteTask (ctx )
234
+
217
235
return & configv1.ConfigureResponse {}, nil
218
236
}
219
237
@@ -301,6 +319,61 @@ func (p *Plugin) getEnvOrDefault(envKey, fallback string) string {
301
319
return fallback
302
320
}
303
321
322
+ // scheduleDeleteTask is a long-running task that deletes keys that are stale
323
+ func (p * Plugin ) scheduleDeleteTask (ctx context.Context ) {
324
+ backoffMin := 1 * time .Second
325
+ backoffMax := 60 * time .Second
326
+ backoff := backoffMin
327
+
328
+ for {
329
+ select {
330
+ case <- ctx .Done ():
331
+ return
332
+ case keyID := <- p .scheduleDelete :
333
+ log := p .logger .With ("key_id" , keyID )
334
+
335
+ if p .vc == nil {
336
+ err := p .genVaultClient ()
337
+ if err != nil {
338
+ log .Error ("Failed to generate vault client" , "reason" , err )
339
+ p .notifyDelete (err )
340
+ // TODO: Should we re-enqueue here?
341
+ }
342
+ }
343
+
344
+ err := p .vc .DeleteKey (ctx , keyID )
345
+
346
+ if err == nil {
347
+ log .Debug ("Key deleted" )
348
+ backoff = backoffMin
349
+ p .notifyDelete (nil )
350
+ continue
351
+ }
352
+
353
+ // For any other error, log it and re-enqueue the key for deletion as it might be a recoverable error
354
+ log .Error ("It was not possible to schedule key for deletion. Trying to re-enqueue it for deletion" , "reason" , err )
355
+
356
+ select {
357
+ case p .scheduleDelete <- keyID :
358
+ log .Debug ("Key re-enqueued for deletion" )
359
+ default :
360
+ log .Error ("Failed to re-enqueue key for deletion" )
361
+ }
362
+
363
+ p .notifyDelete (nil )
364
+ backoff = min (backoff * 2 , backoffMax )
365
+ p .hooks .clk .Sleep (backoff )
366
+ }
367
+ }
368
+ }
369
+
370
+ // Used for testing only
371
+ func (p * Plugin ) notifyDelete (err error ) {
372
+ if p .hooks .scheduleDeleteSignal != nil {
373
+ p .hooks .scheduleDeleteSignal <- err
374
+ }
375
+ }
376
+
304
377
func (p * Plugin ) GenerateKey (ctx context.Context , req * keymanagerv1.GenerateKeyRequest ) (* keymanagerv1.GenerateKeyResponse , error ) {
305
378
if req .KeyId == "" {
306
379
return nil , status .Error (codes .InvalidArgument , "key id is required" )
@@ -318,6 +391,15 @@ func (p *Plugin) GenerateKey(ctx context.Context, req *keymanagerv1.GenerateKeyR
318
391
return nil , err
319
392
}
320
393
394
+ if keyEntry , ok := p .getKeyEntry (spireKeyID ); ok {
395
+ select {
396
+ case p .scheduleDelete <- keyEntry .KeyName :
397
+ p .logger .Debug ("Key enqueued for deletion" , "key_name" , keyEntry .KeyName )
398
+ default :
399
+ p .logger .Error ("Failed to enqueue key for deletion" , "key_name" , keyEntry .KeyName )
400
+ }
401
+ }
402
+
321
403
p .setKeyEntry (spireKeyID , * newKeyEntry )
322
404
323
405
return & keymanagerv1.GenerateKeyResponse {
0 commit comments