github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/migrationminion/worker.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package migrationminion 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 10 "github.com/juju/juju/agent" 11 "github.com/juju/juju/core/migration" 12 "github.com/juju/juju/network" 13 "github.com/juju/juju/watcher" 14 "github.com/juju/juju/worker" 15 "github.com/juju/juju/worker/catacomb" 16 "github.com/juju/juju/worker/fortress" 17 ) 18 19 var logger = loggo.GetLogger("juju.worker.migrationminion") 20 21 // Facade exposes controller functionality to a Worker. 22 type Facade interface { 23 24 // Watch returns a watcher which reports when the status changes 25 // for the migration for the model associated with the API 26 // connection. 27 Watch() (watcher.MigrationStatusWatcher, error) 28 } 29 30 // Config defines the operation of a Worker. 31 type Config struct { 32 Agent agent.Agent 33 Facade Facade 34 Guard fortress.Guard 35 } 36 37 // Validate returns an error if config cannot drive a Worker. 38 func (config Config) Validate() error { 39 if config.Agent == nil { 40 return errors.NotValidf("nil Agent") 41 } 42 if config.Facade == nil { 43 return errors.NotValidf("nil Facade") 44 } 45 if config.Guard == nil { 46 return errors.NotValidf("nil Guard") 47 } 48 return nil 49 } 50 51 // New returns a Worker backed by config, or an error. 52 func New(config Config) (worker.Worker, error) { 53 if err := config.Validate(); err != nil { 54 return nil, errors.Trace(err) 55 } 56 w := &Worker{config: config} 57 err := catacomb.Invoke(catacomb.Plan{ 58 Site: &w.catacomb, 59 Work: w.loop, 60 }) 61 if err != nil { 62 return nil, errors.Trace(err) 63 } 64 return w, nil 65 } 66 67 // Worker waits for a model migration to be active, then locks down the 68 // configured fortress and implements the migration. 69 type Worker struct { 70 catacomb catacomb.Catacomb 71 config Config 72 } 73 74 // Kill implements worker.Worker. 75 func (w *Worker) Kill() { 76 w.catacomb.Kill(nil) 77 } 78 79 // Wait implements worker.Worker. 80 func (w *Worker) Wait() error { 81 return w.catacomb.Wait() 82 } 83 84 func (w *Worker) loop() error { 85 watcher, err := w.config.Facade.Watch() 86 if err != nil { 87 return errors.Annotate(err, "setting up watcher") 88 } 89 if err := w.catacomb.Add(watcher); err != nil { 90 return errors.Trace(err) 91 } 92 93 for { 94 select { 95 case <-w.catacomb.Dying(): 96 return w.catacomb.ErrDying() 97 case status, ok := <-watcher.Changes(): 98 if !ok { 99 return errors.New("watcher channel closed") 100 } 101 if err := w.handle(status); err != nil { 102 return errors.Trace(err) 103 } 104 } 105 } 106 } 107 108 func (w *Worker) handle(status watcher.MigrationStatus) error { 109 logger.Infof("migration phase is now: %s", status.Phase) 110 111 if status.Phase == migration.NONE { 112 return w.config.Guard.Unlock() 113 } 114 115 err := w.config.Guard.Lockdown(w.catacomb.Dying()) 116 if errors.Cause(err) == fortress.ErrAborted { 117 return w.catacomb.ErrDying() 118 } else if err != nil { 119 return errors.Trace(err) 120 } 121 122 switch status.Phase { 123 case migration.QUIESCE: 124 // TODO(mjs) - once Will's stable mode work comes 125 // together this worker will only start up when a 126 // migration is active. Here the minion should report 127 // to the controller that it is running so that the 128 // migration can progress to READONLY. 129 case migration.VALIDATION: 130 // TODO(mjs) - check connection to the target 131 // controller here and report success/failure. 132 case migration.SUCCESS: 133 err := w.doSUCCESS(status.TargetAPIAddrs, status.TargetCACert) 134 if err != nil { 135 return errors.Trace(err) 136 } 137 case migration.ABORT: 138 // TODO(mjs) - exit here once Will's stable mode work 139 // comes together. The minion is done if these phases 140 // are reached. 141 default: 142 // The minion doesn't need to do anything for other 143 // migration phases. 144 } 145 return nil 146 } 147 148 func (w *Worker) doSUCCESS(targetAddrs []string, caCert string) error { 149 hps, err := apiAddrsToHostPorts(targetAddrs) 150 if err != nil { 151 return errors.Annotate(err, "converting API addresses") 152 } 153 err = w.config.Agent.ChangeConfig(func(conf agent.ConfigSetter) error { 154 conf.SetAPIHostPorts(hps) 155 conf.SetCACert(caCert) 156 return nil 157 }) 158 return errors.Annotate(err, "setting agent config") 159 } 160 161 func apiAddrsToHostPorts(addrs []string) ([][]network.HostPort, error) { 162 hps, err := network.ParseHostPorts(addrs...) 163 if err != nil { 164 return nil, errors.Trace(err) 165 } 166 return [][]network.HostPort{hps}, nil 167 }