github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/caasunitprovisioner/worker.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasunitprovisioner 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "gopkg.in/juju/worker.v1" 12 "gopkg.in/juju/worker.v1/catacomb" 13 14 "github.com/juju/juju/core/life" 15 ) 16 17 var logger = loggo.GetLogger("juju.workers.caasunitprovisioner") 18 19 // Config holds configuration for the CAAS unit provisioner worker. 20 type Config struct { 21 ApplicationGetter ApplicationGetter 22 ApplicationUpdater ApplicationUpdater 23 ServiceBroker ServiceBroker 24 25 ContainerBroker ContainerBroker 26 ProvisioningInfoGetter ProvisioningInfoGetter 27 ProvisioningStatusSetter ProvisioningStatusSetter 28 LifeGetter LifeGetter 29 UnitUpdater UnitUpdater 30 } 31 32 // Validate validates the worker configuration. 33 func (config Config) Validate() error { 34 if config.ApplicationGetter == nil { 35 return errors.NotValidf("missing ApplicationGetter") 36 } 37 if config.ApplicationUpdater == nil { 38 return errors.NotValidf("missing ApplicationUpdater") 39 } 40 if config.ServiceBroker == nil { 41 return errors.NotValidf("missing ServiceBroker") 42 } 43 if config.ContainerBroker == nil { 44 return errors.NotValidf("missing ContainerBroker") 45 } 46 if config.ProvisioningInfoGetter == nil { 47 return errors.NotValidf("missing ProvisioningInfoGetter") 48 } 49 if config.LifeGetter == nil { 50 return errors.NotValidf("missing LifeGetter") 51 } 52 if config.UnitUpdater == nil { 53 return errors.NotValidf("missing UnitUpdater") 54 } 55 if config.ProvisioningStatusSetter == nil { 56 return errors.NotValidf("missing ProvisioningStatusSetter") 57 } 58 return nil 59 } 60 61 // NewWorker starts and returns a new CAAS unit provisioner worker. 62 func NewWorker(config Config) (worker.Worker, error) { 63 if err := config.Validate(); err != nil { 64 return nil, errors.Trace(err) 65 } 66 p := &provisioner{config: config} 67 err := catacomb.Invoke(catacomb.Plan{ 68 Site: &p.catacomb, 69 Work: p.loop, 70 }) 71 return p, err 72 } 73 74 type provisioner struct { 75 catacomb catacomb.Catacomb 76 config Config 77 78 // appWorkers holds the worker created to manage each application. 79 // It's defined here so that we can access it in tests. 80 appWorkers map[string]*applicationWorker 81 mu sync.Mutex 82 } 83 84 // Kill is part of the worker.Worker interface. 85 func (p *provisioner) Kill() { 86 p.catacomb.Kill(nil) 87 } 88 89 // Wait is part of the worker.Worker interface. 90 func (p *provisioner) Wait() error { 91 return p.catacomb.Wait() 92 } 93 94 // These helper methods protect the appWorkers map so we can access for testing. 95 96 func (p *provisioner) saveApplicationWorker(appName string, aw *applicationWorker) { 97 p.mu.Lock() 98 defer p.mu.Unlock() 99 100 if p.appWorkers == nil { 101 p.appWorkers = make(map[string]*applicationWorker) 102 } 103 p.appWorkers[appName] = aw 104 } 105 106 func (p *provisioner) deleteApplicationWorker(appName string) { 107 p.mu.Lock() 108 defer p.mu.Unlock() 109 110 delete(p.appWorkers, appName) 111 } 112 113 func (p *provisioner) getApplicationWorker(appName string) (*applicationWorker, bool) { 114 p.mu.Lock() 115 defer p.mu.Unlock() 116 117 if len(p.appWorkers) == 0 { 118 return nil, false 119 } 120 aw, ok := p.appWorkers[appName] 121 return aw, ok 122 } 123 124 func (p *provisioner) loop() error { 125 w, err := p.config.ApplicationGetter.WatchApplications() 126 if err != nil { 127 return errors.Trace(err) 128 } 129 if err := p.catacomb.Add(w); err != nil { 130 return errors.Trace(err) 131 } 132 133 for { 134 select { 135 case <-p.catacomb.Dying(): 136 return p.catacomb.ErrDying() 137 case apps, ok := <-w.Changes(): 138 if !ok { 139 return errors.New("watcher closed channel") 140 } 141 for _, appId := range apps { 142 appLife, err := p.config.LifeGetter.Life(appId) 143 if errors.IsNotFound(err) { 144 // Once an application is deleted, remove the k8s service and ingress resources. 145 if err := p.config.ServiceBroker.UnexposeService(appId); err != nil { 146 return errors.Trace(err) 147 } 148 if err := p.config.ServiceBroker.DeleteService(appId); err != nil { 149 return errors.Trace(err) 150 } 151 w, ok := p.getApplicationWorker(appId) 152 if ok { 153 // Before stopping the application worker, inform it that 154 // the app is gone so it has a chance to clean up. 155 // The worker will act on the removal prior to processing the 156 // Stop() request. 157 if err := worker.Stop(w); err != nil { 158 logger.Errorf("stopping application worker for %v: %v", appId, err) 159 } 160 p.deleteApplicationWorker(appId) 161 } 162 continue 163 } 164 if err != nil { 165 return errors.Trace(err) 166 } 167 if _, ok := p.getApplicationWorker(appId); ok || appLife == life.Dead { 168 // Already watching the application. or we're 169 // not yet watching it and it's dead. 170 continue 171 } 172 w, err := newApplicationWorker( 173 appId, 174 p.config.ServiceBroker, 175 p.config.ContainerBroker, 176 p.config.ProvisioningStatusSetter, 177 p.config.ProvisioningInfoGetter, 178 p.config.ApplicationGetter, 179 p.config.ApplicationUpdater, 180 p.config.UnitUpdater, 181 ) 182 if err != nil { 183 return errors.Trace(err) 184 } 185 p.saveApplicationWorker(appId, w) 186 p.catacomb.Add(w) 187 } 188 } 189 } 190 }