github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/secretspruner/worker.go (about) 1 // Copyright 2023 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package secretspruner 5 6 import ( 7 "strconv" 8 "strings" 9 10 "github.com/juju/collections/set" 11 "github.com/juju/errors" 12 "github.com/juju/worker/v3" 13 "github.com/juju/worker/v3/catacomb" 14 15 coresecrets "github.com/juju/juju/core/secrets" 16 "github.com/juju/juju/core/watcher" 17 ) 18 19 // logger is here to stop the desire of creating a package level logger. 20 // Don't do this, instead use the one passed as manifold config. 21 type logger interface{} 22 23 var _ logger = struct{}{} 24 25 // Logger represents the methods used by the worker to log information. 26 type Logger interface { 27 Debugf(string, ...interface{}) 28 Warningf(string, ...interface{}) 29 Infof(string, ...interface{}) 30 } 31 32 // SecretsFacade instances provide a set of API for the worker to deal with secret prune. 33 type SecretsFacade interface { 34 WatchRevisionsToPrune() (watcher.StringsWatcher, error) 35 DeleteRevisions(uri *coresecrets.URI, revisions ...int) error 36 } 37 38 // Config defines the operation of the Worker. 39 type Config struct { 40 SecretsFacade 41 Logger Logger 42 } 43 44 // Validate returns an error if config cannot drive the Worker. 45 func (config Config) Validate() error { 46 if config.SecretsFacade == nil { 47 return errors.NotValidf("nil SecretsFacade") 48 } 49 if config.Logger == nil { 50 return errors.NotValidf("nil Logger") 51 } 52 return nil 53 } 54 55 // NewWorker returns a secretspruner Worker backed by config, or an error. 56 func NewWorker(config Config) (worker.Worker, error) { 57 if err := config.Validate(); err != nil { 58 return nil, errors.Trace(err) 59 } 60 61 w := &Worker{config: config} 62 err := catacomb.Invoke(catacomb.Plan{ 63 Site: &w.catacomb, 64 Work: w.loop, 65 }) 66 return w, errors.Trace(err) 67 } 68 69 // Worker prunes the user supplied secret revisions. 70 type Worker struct { 71 catacomb catacomb.Catacomb 72 config Config 73 } 74 75 // Kill is defined on worker.Worker. 76 func (w *Worker) Kill() { 77 w.catacomb.Kill(nil) 78 } 79 80 // Wait is part of the worker.Worker interface. 81 func (w *Worker) Wait() error { 82 return w.catacomb.Wait() 83 } 84 85 func (w *Worker) loop() (err error) { 86 watcher, err := w.config.SecretsFacade.WatchRevisionsToPrune() 87 if err != nil { 88 return errors.Trace(err) 89 } 90 if err := w.catacomb.Add(watcher); err != nil { 91 return errors.Trace(err) 92 } 93 94 for { 95 select { 96 case <-w.catacomb.Dying(): 97 return errors.Trace(w.catacomb.ErrDying()) 98 // TODO: watch for secret's auto-prune config changes. 99 // then delete any obsolete revisions. 100 case changes, ok := <-watcher.Changes(): 101 if !ok { 102 return errors.New("secret prune changed watch closed") 103 } 104 w.config.Logger.Debugf("got user supplied secret revisions to prune") 105 106 if len(changes) == 0 { 107 w.config.Logger.Debugf("no secret revisions to prune") 108 continue 109 } 110 revisions, err := w.processChanges(changes...) 111 if err != nil { 112 return errors.Trace(err) 113 } 114 for uriStr, revs := range revisions { 115 w.config.Logger.Debugf("pruning secret revisions %q: %v", uriStr, revs.SortedValues()) 116 uri, err := coresecrets.ParseURI(uriStr) 117 if err != nil { 118 return errors.Trace(err) 119 } 120 if err := w.config.SecretsFacade.DeleteRevisions(uri, revs.SortedValues()...); err != nil { 121 return errors.Trace(err) 122 } 123 } 124 } 125 } 126 } 127 128 func (w *Worker) processChanges(changes ...string) (map[string]set.Ints, error) { 129 out := make(map[string]set.Ints) 130 for _, revInfo := range changes { 131 w.config.Logger.Warningf("revInfo %q, out %#v", revInfo, out) 132 parts := strings.Split(revInfo, "/") 133 uri := parts[0] 134 if len(parts) < 2 { 135 // This should never happen. 136 w.config.Logger.Debugf("secret %q has been removed, no need to prune", revInfo) 137 continue 138 } 139 rev, err := strconv.Atoi(parts[1]) 140 if err != nil { 141 // This should never happen. 142 return nil, errors.NotValidf("secret revision %q for %q", parts[1], uri) 143 } 144 if _, ok := out[uri]; !ok { 145 out[uri] = set.NewInts() 146 } 147 out[uri].Add(rev) 148 } 149 return out, nil 150 }