github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/caasfirewaller/worker.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasfirewaller 5 6 import ( 7 "github.com/juju/charm/v12" 8 "github.com/juju/errors" 9 "github.com/juju/worker/v3" 10 "github.com/juju/worker/v3/catacomb" 11 12 "github.com/juju/juju/core/life" 13 ) 14 15 // Logger is here to stop the desire of creating a package level Logger. 16 // Don't do this, instead use the one passed as manifold config. 17 type logger interface{} 18 19 var _ logger = struct{}{} 20 21 // Config holds configuration for the CAAS unit firewaller worker. 22 type Config struct { 23 ControllerUUID string 24 ModelUUID string 25 ApplicationGetter ApplicationGetter 26 LifeGetter LifeGetter 27 CharmGetter CharmGetter 28 ServiceExposer ServiceExposer 29 Logger Logger 30 } 31 32 // Validate validates the worker configuration. 33 func (config Config) Validate() error { 34 if config.ControllerUUID == "" { 35 return errors.NotValidf("missing ControllerUUID") 36 } 37 if config.ModelUUID == "" { 38 return errors.NotValidf("missing ModelUUID") 39 } 40 if config.ApplicationGetter == nil { 41 return errors.NotValidf("missing ApplicationGetter") 42 } 43 if config.LifeGetter == nil { 44 return errors.NotValidf("missing LifeGetter") 45 } 46 if config.CharmGetter == nil { 47 return errors.NotValidf("missing CharmGetter") 48 } 49 if config.ServiceExposer == nil { 50 return errors.NotValidf("missing ServiceExposer") 51 } 52 if config.Logger == nil { 53 return errors.NotValidf("missing Logger") 54 } 55 return nil 56 } 57 58 // NewWorker starts and returns a new CAAS unit firewaller worker. 59 func NewWorker(config Config) (worker.Worker, error) { 60 if err := config.Validate(); err != nil { 61 return nil, errors.Trace(err) 62 } 63 p := &firewaller{config: config} 64 err := catacomb.Invoke(catacomb.Plan{ 65 Site: &p.catacomb, 66 Work: p.loop, 67 }) 68 return p, err 69 } 70 71 type firewaller struct { 72 catacomb catacomb.Catacomb 73 config Config 74 } 75 76 // Kill is part of the worker.Worker interface. 77 func (p *firewaller) Kill() { 78 p.catacomb.Kill(nil) 79 } 80 81 // Wait is part of the worker.Worker interface. 82 func (p *firewaller) Wait() error { 83 return p.catacomb.Wait() 84 } 85 86 func (p *firewaller) loop() error { 87 logger := p.config.Logger 88 appWatcher, err := p.config.ApplicationGetter.WatchApplications() 89 if err != nil { 90 return errors.Trace(err) 91 } 92 if err := p.catacomb.Add(appWatcher); err != nil { 93 return errors.Trace(err) 94 } 95 96 appWorkers := make(map[string]worker.Worker) 97 for { 98 select { 99 case <-p.catacomb.Dying(): 100 return p.catacomb.ErrDying() 101 case apps, ok := <-appWatcher.Changes(): 102 if !ok { 103 return errors.New("watcher closed channel") 104 } 105 for _, appName := range apps { 106 // If charm is (now) a v2 charm, skip processing. 107 format, err := p.charmFormat(appName) 108 if errors.IsNotFound(err) { 109 p.config.Logger.Debugf("application %q no longer exists", appName) 110 continue 111 } else if err != nil { 112 return errors.Trace(err) 113 } 114 if format >= charm.FormatV2 { 115 p.config.Logger.Tracef("v1 caasfirewaller got event for v2 app %q, skipping", appName) 116 continue 117 } 118 119 appLife, err := p.config.LifeGetter.Life(appName) 120 if errors.IsNotFound(err) || appLife == life.Dead { 121 if appWorker, ok := appWorkers[appName]; ok { 122 if err := worker.Stop(appWorker); err != nil { 123 logger.Errorf("error stopping caas firewaller: %v", err) 124 } 125 delete(appWorkers, appName) 126 } 127 continue 128 } 129 if err != nil { 130 return errors.Trace(err) 131 } 132 if _, ok := appWorkers[appName]; ok { 133 // Already watching the application. 134 continue 135 } 136 appWorker, err := newApplicationWorker( 137 p.config.ControllerUUID, 138 p.config.ModelUUID, 139 appName, 140 p.config.ApplicationGetter, 141 p.config.ServiceExposer, 142 p.config.LifeGetter, 143 p.config.CharmGetter, 144 logger, 145 ) 146 if err != nil { 147 return errors.Trace(err) 148 } 149 appWorkers[appName] = appWorker 150 _ = p.catacomb.Add(appWorker) 151 } 152 } 153 } 154 } 155 156 func (p *firewaller) charmFormat(appName string) (charm.Format, error) { 157 charmInfo, err := p.config.CharmGetter.ApplicationCharmInfo(appName) 158 if err != nil { 159 return charm.FormatUnknown, errors.Annotatef(err, "failed to get charm info for application %q", appName) 160 } 161 return charm.MetaFormat(charmInfo.Charm()), nil 162 }