github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/httpserverargs/manifold.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package httpserverargs 5 6 import ( 7 "reflect" 8 9 "github.com/juju/clock" 10 "github.com/juju/errors" 11 "gopkg.in/juju/worker.v1" 12 "gopkg.in/juju/worker.v1/dependency" 13 "gopkg.in/tomb.v2" 14 15 "github.com/juju/juju/apiserver/apiserverhttp" 16 "github.com/juju/juju/apiserver/httpcontext" 17 workerstate "github.com/juju/juju/worker/state" 18 ) 19 20 // ManifoldConfig holds the resources needed to run an httpserverargs 21 // worker. 22 type ManifoldConfig struct { 23 ClockName string 24 ControllerPortName string 25 StateName string 26 27 NewStateAuthenticator NewStateAuthenticatorFunc 28 } 29 30 // Validate checks that we have all of the things we need. 31 func (config ManifoldConfig) Validate() error { 32 if config.ClockName == "" { 33 return errors.NotValidf("empty ClockName") 34 } 35 if config.ControllerPortName == "" { 36 return errors.NotValidf("empty ControllerPortName") 37 } 38 if config.StateName == "" { 39 return errors.NotValidf("empty StateName") 40 } 41 if config.NewStateAuthenticator == nil { 42 return errors.NotValidf("nil NewStateAuthenticator") 43 } 44 return nil 45 } 46 47 func (config ManifoldConfig) start(context dependency.Context) (worker.Worker, error) { 48 if err := config.Validate(); err != nil { 49 return nil, errors.Trace(err) 50 } 51 52 var clock clock.Clock 53 if err := context.Get(config.ClockName, &clock); err != nil { 54 return nil, errors.Trace(err) 55 } 56 57 // Ensure that the controller-port worker is running. 58 if err := context.Get(config.ControllerPortName, nil); err != nil { 59 return nil, errors.Trace(err) 60 } 61 62 var stTracker workerstate.StateTracker 63 if err := context.Get(config.StateName, &stTracker); err != nil { 64 return nil, errors.Trace(err) 65 } 66 statePool, err := stTracker.Use() 67 if err != nil { 68 return nil, errors.Trace(err) 69 } 70 71 mux := apiserverhttp.NewMux() 72 abort := make(chan struct{}) 73 authenticator, err := config.NewStateAuthenticator(statePool, mux, clock, abort) 74 if err != nil { 75 stTracker.Done() 76 return nil, errors.Trace(err) 77 } 78 w := newWorker(mux, authenticator, func() { 79 close(abort) 80 stTracker.Done() 81 }) 82 return w, nil 83 } 84 85 // Manifold returns a dependency.Manifold to run a worker to hold the 86 // http server mux and authenticator. This means that we can ensure 87 // that all workers that need to register with them can be finished 88 // starting up before the httpserver responds to connections. 89 func Manifold(config ManifoldConfig) dependency.Manifold { 90 return dependency.Manifold{ 91 Inputs: []string{ 92 config.ClockName, 93 config.ControllerPortName, 94 config.StateName, 95 }, 96 Start: config.start, 97 Output: manifoldOutput, 98 } 99 } 100 101 var ( 102 muxType = reflect.TypeOf(&apiserverhttp.Mux{}) 103 authenticatorType = reflect.TypeOf((*httpcontext.LocalMacaroonAuthenticator)(nil)).Elem() 104 ) 105 106 func manifoldOutput(in worker.Worker, out interface{}) error { 107 w, ok := in.(*argsWorker) 108 if !ok { 109 return errors.Errorf("expected worker of type *argsWorker, got %T", in) 110 } 111 rv := reflect.ValueOf(out) 112 if rt := rv.Type(); rt.Kind() == reflect.Ptr { 113 elemType := rt.Elem() 114 switch { 115 case muxType.AssignableTo(elemType): 116 rv.Elem().Set(reflect.ValueOf(w.mux)) 117 return nil 118 case authenticatorType.AssignableTo(elemType): 119 rv.Elem().Set(reflect.ValueOf(w.authenticator)) 120 return nil 121 } 122 } 123 return errors.Errorf("unexpected output type %T", out) 124 } 125 126 func newWorker( 127 mux *apiserverhttp.Mux, 128 authenticator httpcontext.LocalMacaroonAuthenticator, 129 cleanup func(), 130 ) worker.Worker { 131 w := argsWorker{ 132 mux: mux, 133 authenticator: authenticator, 134 } 135 w.tomb.Go(func() error { 136 <-w.tomb.Dying() 137 cleanup() 138 return nil 139 }) 140 return &w 141 } 142 143 type argsWorker struct { 144 mux *apiserverhttp.Mux 145 authenticator httpcontext.LocalMacaroonAuthenticator 146 tomb tomb.Tomb 147 } 148 149 func (w *argsWorker) Kill() { 150 w.tomb.Kill(nil) 151 } 152 153 func (w *argsWorker) Wait() error { 154 return w.tomb.Wait() 155 }