github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/storageprovisioner/caasworker.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 "gopkg.in/juju/names.v2" 11 "gopkg.in/juju/worker.v1" 12 "gopkg.in/juju/worker.v1/catacomb" 13 14 "github.com/juju/juju/apiserver/params" 15 "github.com/juju/juju/core/watcher" 16 ) 17 18 // ApplicationWatcher provides an interface for 19 // watching for the lifecycle state changes 20 // (including addition) of applications. 21 type ApplicationWatcher interface { 22 WatchApplications() (watcher.StringsWatcher, error) 23 } 24 25 // NewCaasWorker starts and returns a new CAAS storage provisioner worker. 26 // The worker provisions model scoped storage and also watches and starts 27 // provisioner workers to handle storage for application units. 28 func NewCaasWorker(config Config) (worker.Worker, error) { 29 if err := config.Validate(); err != nil { 30 return nil, errors.Trace(err) 31 } 32 p := &provisioner{config: config} 33 err := catacomb.Invoke(catacomb.Plan{ 34 Site: &p.catacomb, 35 Work: p.loop, 36 }) 37 return p, err 38 } 39 40 type provisioner struct { 41 catacomb catacomb.Catacomb 42 config Config 43 44 // provisioners holds the worker created to manage each application. 45 // It's defined here so that we can access it in tests. 46 provisioners map[string]worker.Worker 47 mu sync.Mutex 48 } 49 50 // Kill is part of the worker.Worker interface. 51 func (p *provisioner) Kill() { 52 p.catacomb.Kill(nil) 53 } 54 55 // Wait is part of the worker.Worker interface. 56 func (p *provisioner) Wait() error { 57 return p.catacomb.Wait() 58 } 59 60 // These helper methods protect the provisioners map so we can access for testing. 61 62 func (p *provisioner) saveApplicationWorker(appName string, aw worker.Worker) { 63 p.mu.Lock() 64 defer p.mu.Unlock() 65 66 if p.provisioners == nil { 67 p.provisioners = make(map[string]worker.Worker) 68 } 69 p.provisioners[appName] = aw 70 } 71 72 func (p *provisioner) deleteApplicationWorker(appName string) { 73 p.mu.Lock() 74 defer p.mu.Unlock() 75 76 delete(p.provisioners, appName) 77 } 78 79 func (p *provisioner) getApplicationWorker(appName string) (worker.Worker, bool) { 80 p.mu.Lock() 81 defer p.mu.Unlock() 82 83 if len(p.provisioners) == 0 { 84 return nil, false 85 } 86 aw, ok := p.provisioners[appName] 87 return aw, ok 88 } 89 90 func (p *provisioner) loop() error { 91 w, err := p.config.Applications.WatchApplications() 92 if err != nil { 93 return errors.Trace(err) 94 } 95 if err := p.catacomb.Add(w); err != nil { 96 return errors.Trace(err) 97 } 98 99 modelProvisioner, err := NewStorageProvisioner(p.config) 100 if err != nil { 101 return errors.Trace(err) 102 } 103 if err := p.catacomb.Add(modelProvisioner); err != nil { 104 return errors.Trace(err) 105 } 106 107 for { 108 select { 109 case <-p.catacomb.Dying(): 110 return p.catacomb.ErrDying() 111 case apps, ok := <-w.Changes(): 112 if !ok { 113 return errors.New("watcher closed channel") 114 } 115 appTags := make([]names.Tag, len(apps)) 116 for i, appId := range apps { 117 appTags[i] = names.NewApplicationTag(appId) 118 } 119 appsLife, err := p.config.Life.Life(appTags) 120 if err != nil { 121 return errors.Annotate(err, "getting application life") 122 } 123 for i, appId := range apps { 124 appLifeResult := appsLife[i] 125 if appLifeResult.Error != nil && params.IsCodeNotFound(appLifeResult.Error) { 126 logger.Debugf("app %v not found", appId) 127 _, ok := p.getApplicationWorker(appId) 128 if ok { 129 if err := worker.Stop(w); err != nil { 130 logger.Errorf("stopping application storage worker for %v: %v", appId, err) 131 } 132 p.deleteApplicationWorker(appId) 133 } 134 continue 135 } 136 if _, ok := p.getApplicationWorker(appId); ok || appLifeResult.Life == params.Dead { 137 // Already watching the application. or we're 138 // not yet watching it and it's dead. 139 continue 140 } 141 cfg := p.config 142 cfg.Scope = appTags[i] 143 w, err := NewStorageProvisioner(cfg) 144 if err != nil { 145 return errors.Trace(err) 146 } 147 p.saveApplicationWorker(appId, w) 148 p.catacomb.Add(w) 149 } 150 } 151 } 152 }