github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/restorewatcher/manifold.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package restorewatcher 5 6 import ( 7 "sync" 8 9 "github.com/juju/errors" 10 "gopkg.in/juju/worker.v1" 11 "gopkg.in/juju/worker.v1/dependency" 12 13 "github.com/juju/juju/state" 14 workerstate "github.com/juju/juju/worker/state" 15 ) 16 17 // ManifoldConfig holds the information necessary to run a restorewatcher 18 // in a dependency.Engine. 19 type ManifoldConfig struct { 20 StateName string 21 NewWorker func(Config) (RestoreStatusWorker, error) 22 } 23 24 // Validate validates the manifold configuration. 25 func (config ManifoldConfig) Validate() error { 26 if config.StateName == "" { 27 return errors.NotValidf("empty StateName") 28 } 29 if config.NewWorker == nil { 30 return errors.NotValidf("nil NewWorker") 31 } 32 return nil 33 } 34 35 // RestoreStatusWorker is a worker that provides a means of observing 36 // the restore status. 37 type RestoreStatusWorker interface { 38 worker.Worker 39 40 // RestoreStatus returns the most recently observed restore status. 41 RestoreStatus() state.RestoreStatus 42 } 43 44 // Manifold returns a dependency.Manifold that will run a restorewatcher. 45 func Manifold(config ManifoldConfig) dependency.Manifold { 46 return dependency.Manifold{ 47 Inputs: []string{config.StateName}, 48 Start: config.start, 49 Output: manifoldOutput, 50 } 51 } 52 53 // start is a method on ManifoldConfig because it's more readable than a closure. 54 func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) { 55 if err := config.Validate(); err != nil { 56 return nil, errors.Trace(err) 57 } 58 59 var stTracker workerstate.StateTracker 60 if err := context.Get(config.StateName, &stTracker); err != nil { 61 return nil, errors.Trace(err) 62 } 63 statePool, err := stTracker.Use() 64 if err != nil { 65 return nil, errors.Trace(err) 66 } 67 68 st := statePool.SystemState() 69 w, err := config.NewWorker(Config{ 70 RestoreInfoWatcher: RestoreInfoWatcherShim{st}, 71 }) 72 if err != nil { 73 stTracker.Done() 74 return nil, errors.Trace(err) 75 } 76 return &cleanupWorker{ 77 RestoreStatusWorker: w, 78 cleanup: func() { stTracker.Done() }, 79 }, nil 80 } 81 82 func manifoldOutput(in worker.Worker, out interface{}) error { 83 inWorker, _ := in.(*cleanupWorker) 84 if inWorker == nil { 85 return errors.Errorf("in should be a %T; got %T", inWorker, in) 86 } 87 outf, ok := out.(*func() state.RestoreStatus) 88 if !ok { 89 return errors.Errorf("out should have type %T; got %T", outf, out) 90 } 91 *outf = inWorker.RestoreStatus 92 return nil 93 } 94 95 type cleanupWorker struct { 96 RestoreStatusWorker 97 cleanupOnce sync.Once 98 cleanup func() 99 } 100 101 func (w *cleanupWorker) Wait() error { 102 err := w.RestoreStatusWorker.Wait() 103 w.cleanupOnce.Do(w.cleanup) 104 return err 105 }