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  }