github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/undertaker/undertaker.go (about) 1 // Copyright 2015-2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package undertaker 5 6 import ( 7 "github.com/juju/errors" 8 9 "github.com/juju/juju/apiserver/params" 10 "github.com/juju/juju/environs" 11 "github.com/juju/juju/status" 12 "github.com/juju/juju/watcher" 13 "github.com/juju/juju/worker/catacomb" 14 ) 15 16 // Facade covers the parts of the api/undertaker.UndertakerClient that we 17 // need for the worker. It's more than a little raw, but we'll survive. 18 type Facade interface { 19 ModelInfo() (params.UndertakerModelInfoResult, error) 20 WatchModelResources() (watcher.NotifyWatcher, error) 21 ProcessDyingModel() error 22 RemoveModel() error 23 SetStatus(status status.Status, message string, data map[string]interface{}) error 24 } 25 26 // Config holds the resources and configuration necessary to run an 27 // undertaker worker. 28 type Config struct { 29 Facade Facade 30 Environ environs.Environ 31 } 32 33 // Validate returns an error if the config cannot be expected to drive 34 // a functional undertaker worker. 35 func (config Config) Validate() error { 36 if config.Facade == nil { 37 return errors.NotValidf("nil Facade") 38 } 39 if config.Environ == nil { 40 return errors.NotValidf("nil Environ") 41 } 42 return nil 43 } 44 45 // NewUndertaker returns a worker which processes a dying model. 46 func NewUndertaker(config Config) (*Undertaker, error) { 47 if err := config.Validate(); err != nil { 48 return nil, errors.Trace(err) 49 } 50 u := &Undertaker{ 51 config: config, 52 } 53 err := catacomb.Invoke(catacomb.Plan{ 54 Site: &u.catacomb, 55 Work: u.run, 56 }) 57 if err != nil { 58 return nil, errors.Trace(err) 59 } 60 return u, nil 61 } 62 63 type Undertaker struct { 64 catacomb catacomb.Catacomb 65 config Config 66 } 67 68 // Kill is part of the worker.Worker interface. 69 func (u *Undertaker) Kill() { 70 u.catacomb.Kill(nil) 71 } 72 73 // Wait is part of the worker.Worker interface. 74 func (u *Undertaker) Wait() error { 75 return u.catacomb.Wait() 76 } 77 78 func (u *Undertaker) run() error { 79 result, err := u.config.Facade.ModelInfo() 80 if err != nil { 81 return errors.Trace(err) 82 } 83 if result.Error != nil { 84 return errors.Trace(result.Error) 85 } 86 modelInfo := result.Result 87 88 if modelInfo.Life == params.Alive { 89 return errors.Errorf("model still alive") 90 } 91 92 if modelInfo.Life == params.Dying { 93 // TODO(axw) 2016-04-14 #1570285 94 // We should update status with information 95 // about the remaining resources here, and 96 // also make the worker responsible for 97 // checking the emptiness criteria before 98 // attempting to remove the model. 99 if err := u.setStatus( 100 status.Destroying, 101 "cleaning up cloud resources", 102 ); err != nil { 103 return errors.Trace(err) 104 } 105 // Process the dying model. This blocks until the model 106 // is dead or the worker is stopped. 107 if err := u.processDyingModel(); err != nil { 108 return errors.Trace(err) 109 } 110 } 111 112 // If we get this far, the model must be dead (or *have been* 113 // dead, but actually removed by something else since the call). 114 if modelInfo.IsSystem { 115 // Nothing to do. We don't destroy environ resources or 116 // delete model docs for a controller model, because we're 117 // running inside that controller and can't safely clean up 118 // our own infrastructure. (That'll be the client's job in 119 // the end, once we've reported that we've tidied up what we 120 // can, by returning nil here, indicating that we've set it 121 // to Dead -- implied by processDyingModel succeeding.) 122 return nil 123 } 124 125 // Now the model is known to be hosted and dead, we can tidy up any 126 // provider resources it might have used. 127 if err := u.setStatus( 128 status.Destroying, "tearing down cloud environment", 129 ); err != nil { 130 return errors.Trace(err) 131 } 132 if err := u.config.Environ.Destroy(); err != nil { 133 return errors.Trace(err) 134 } 135 136 // Finally, remove the model. 137 if err := u.config.Facade.RemoveModel(); err != nil { 138 return errors.Annotate(err, "cannot remove model") 139 } 140 return nil 141 } 142 143 func (u *Undertaker) setStatus(modelStatus status.Status, message string) error { 144 return u.config.Facade.SetStatus(modelStatus, message, nil) 145 } 146 147 func (u *Undertaker) processDyingModel() error { 148 watcher, err := u.config.Facade.WatchModelResources() 149 if err != nil { 150 return errors.Trace(err) 151 } 152 if err := u.catacomb.Add(watcher); err != nil { 153 return errors.Trace(err) 154 } 155 defer watcher.Kill() // The watcher is not needed once this func returns. 156 157 for { 158 select { 159 case <-u.catacomb.Dying(): 160 return u.catacomb.ErrDying() 161 case <-watcher.Changes(): 162 // TODO(fwereade): this is wrong. If there's a time 163 // it's ok to ignore an error, it's when we know 164 // exactly what an error is/means. If there's a 165 // specific code for "not done yet", *that* is what 166 // we should be ignoring. But unknown errors are 167 // *unknown*, and we can't just assume that it's 168 // safe to continue. 169 err := u.config.Facade.ProcessDyingModel() 170 if err == nil { 171 // ProcessDyingModel succeeded. We're free to 172 // destroy any remaining environ resources. 173 return nil 174 } 175 // Yes, we ignore the error. See comment above. 176 } 177 } 178 }