github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/registry.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package common
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"runtime"
    10  	"strings"
    11  
    12  	"github.com/juju/errors"
    13  	"gopkg.in/juju/names.v2"
    14  
    15  	"github.com/juju/juju/apiserver/common/apihttp"
    16  	"github.com/juju/juju/apiserver/facade"
    17  	"github.com/juju/juju/state"
    18  )
    19  
    20  // Facades is the registry that tracks all of the Facades that will be
    21  // exposed in the API. It can be used to List/Get/Register facades.
    22  //
    23  // Most implementers of a facade will probably want to use
    24  // RegisterStandardFacade rather than Facades.Register, as it provides
    25  // much cleaner syntax and semantics for calling during init().
    26  //
    27  // Developers in a happy future will not want to use Facades at all,
    28  // eschewing globals in favour of explicitly building apis and supplying
    29  // them directly to an apiserver.
    30  var Facades = &facade.Registry{}
    31  
    32  // RegisterFacade updates the global facade registry with a new version of a new type.
    33  func RegisterFacade(name string, version int, factory facade.Factory, facadeType reflect.Type) {
    34  	RegisterFacadeForFeature(name, version, factory, facadeType, "")
    35  }
    36  
    37  // RegisterFacadeForFeature updates the global facade registry with a new
    38  // version of a new type. If the feature is non-empty, this facade is only
    39  // available when the specified feature flag is set.
    40  func RegisterFacadeForFeature(name string, version int, factory facade.Factory, facadeType reflect.Type, feature string) {
    41  	err := Facades.Register(name, version, factory, facadeType, feature)
    42  	if err != nil {
    43  		// This is meant to be called during init() so errors should be
    44  		// considered fatal.
    45  		panic(err)
    46  	}
    47  	logger.Tracef("Registered facade %q v%d", name, version)
    48  }
    49  
    50  // validateNewFacade ensures that the facade factory we have has the right
    51  // input and output parameters for being used as a NewFoo function.
    52  func validateNewFacade(funcValue reflect.Value) error {
    53  	if !funcValue.IsValid() {
    54  		return fmt.Errorf("cannot wrap nil")
    55  	}
    56  	if funcValue.Kind() != reflect.Func {
    57  		return fmt.Errorf("wrong type %q is not a function", funcValue.Kind())
    58  	}
    59  	funcType := funcValue.Type()
    60  	funcName := runtime.FuncForPC(funcValue.Pointer()).Name()
    61  	if funcType.NumIn() != 3 || funcType.NumOut() != 2 {
    62  		return fmt.Errorf("function %q does not take 3 parameters and return 2",
    63  			funcName)
    64  	}
    65  
    66  	type nastyFactory func(
    67  		st *state.State,
    68  		resources facade.Resources,
    69  		authorizer facade.Authorizer,
    70  	) (
    71  		interface{}, error,
    72  	)
    73  	facadeType := reflect.TypeOf((*nastyFactory)(nil)).Elem()
    74  	isSame := true
    75  	for i := 0; i < 3; i++ {
    76  		if funcType.In(i) != facadeType.In(i) {
    77  			isSame = false
    78  			break
    79  		}
    80  	}
    81  	if funcType.Out(1) != facadeType.Out(1) {
    82  		isSame = false
    83  	}
    84  	if !isSame {
    85  		return fmt.Errorf("function %q does not have the signature func (*state.State, facade.Resources, facade.Authorizer) (*Type, error)",
    86  			funcName)
    87  	}
    88  	return nil
    89  }
    90  
    91  // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error)
    92  // function and wraps it into a proper facade.Factory function.
    93  func wrapNewFacade(newFunc interface{}) (facade.Factory, reflect.Type, error) {
    94  	funcValue := reflect.ValueOf(newFunc)
    95  	err := validateNewFacade(funcValue)
    96  	if err != nil {
    97  		return nil, reflect.TypeOf(nil), err
    98  	}
    99  	// So we know newFunc is a func with the right args in and out, so
   100  	// wrap it into a helper function that matches the facade.Factory.
   101  	wrapped := func(context facade.Context) (facade.Facade, error) {
   102  		if context.ID() != "" {
   103  			return nil, ErrBadId
   104  		}
   105  		st := context.State()
   106  		auth := context.Auth()
   107  		resources := context.Resources()
   108  		// st, resources, or auth is nil, then reflect.Call dies
   109  		// because reflect.ValueOf(anynil) is the Zero Value.
   110  		// So we use &obj.Elem() which gives us a concrete Value object
   111  		// that can refer to nil.
   112  		in := []reflect.Value{
   113  			reflect.ValueOf(&st).Elem(),
   114  			reflect.ValueOf(&resources).Elem(),
   115  			reflect.ValueOf(&auth).Elem(),
   116  		}
   117  		out := funcValue.Call(in)
   118  		if out[1].Interface() != nil {
   119  			err := out[1].Interface().(error)
   120  			return nil, err
   121  		}
   122  		return out[0].Interface(), nil
   123  	}
   124  	return wrapped, funcValue.Type().Out(0), nil
   125  }
   126  
   127  // NewHookContextFacadeFn specifies the function signature that can be
   128  // used to register a hook context facade.
   129  type NewHookContextFacadeFn func(*state.State, *state.Unit) (interface{}, error)
   130  
   131  // RegisterHookContextFacade registers facades for use within a hook
   132  // context. This function handles the translation from a
   133  // hook-context-facade to a standard facade so the caller's factory
   134  // method can elide unnecessary arguments. This function also handles
   135  // any necessary authorization for the client.
   136  //
   137  // XXX(fwereade): this is fundamentally broken, because it (1)
   138  // arbitrarily creates a new facade for a tiny fragment of a specific
   139  // client worker's reponsibilities and (2) actively conceals necessary
   140  // auth information from the facade. Don't call it; actively work to
   141  // delete code that uses it, and rewrite it properly.
   142  func RegisterHookContextFacade(name string, version int, newHookContextFacade NewHookContextFacadeFn, facadeType reflect.Type) {
   143  
   144  	newFacade := func(context facade.Context) (facade.Facade, error) {
   145  		authorizer := context.Auth()
   146  		st := context.State()
   147  
   148  		if !authorizer.AuthUnitAgent() {
   149  			return nil, ErrPerm
   150  		}
   151  
   152  		// Verify that the unit's ID matches a unit that we know
   153  		// about.
   154  		tag := authorizer.GetAuthTag()
   155  		if _, ok := tag.(names.UnitTag); !ok {
   156  			return nil, errors.Errorf("expected names.UnitTag, got %T", tag)
   157  		}
   158  
   159  		unit, err := st.Unit(tag.Id())
   160  		if err != nil {
   161  			return nil, errors.Trace(err)
   162  		}
   163  
   164  		return newHookContextFacade(st, unit)
   165  	}
   166  
   167  	RegisterFacade(name, version, newFacade, facadeType)
   168  }
   169  
   170  // RegisterStandardFacade registers a factory function for a normal New* style
   171  // function. This requires that the function has the form:
   172  // NewFoo(*state.State, facade.Resources, facade.Authorizer) (*Type, error)
   173  // With that syntax, we will create a helper function that wraps calling NewFoo
   174  // with the right parameters, and returns the *Type correctly.
   175  func RegisterStandardFacade(name string, version int, newFunc interface{}) {
   176  	RegisterStandardFacadeForFeature(name, version, newFunc, "")
   177  }
   178  
   179  // RegisterStandardFacadeForFeature registers a factory function for a normal
   180  // New* style function. This requires that the function has the form:
   181  // NewFoo(*state.State, facade.Resources, facade.Authorizer) (*Type, error)
   182  // With that syntax, we will create a helper function that wraps calling
   183  // NewFoo with the right parameters, and returns the *Type correctly. If the
   184  // feature is non-empty, this facade is only available when the specified
   185  // feature flag is set.
   186  func RegisterStandardFacadeForFeature(name string, version int, newFunc interface{}, feature string) {
   187  	wrapped, facadeType, err := wrapNewFacade(newFunc)
   188  	if err != nil {
   189  		panic(err)
   190  	}
   191  	RegisterFacadeForFeature(name, version, wrapped, facadeType, feature)
   192  }
   193  
   194  var endpointRegistry = map[string]apihttp.HandlerSpec{}
   195  var endpointRegistryOrder []string
   196  
   197  // RegisterAPIModelEndpoint adds the provided endpoint to the registry.
   198  // The pattern is prefixed with the model pattern: /model/:modeluuid.
   199  func RegisterAPIModelEndpoint(pattern string, spec apihttp.HandlerSpec) error {
   200  	if !strings.HasPrefix(pattern, "/") {
   201  		pattern = "/" + pattern
   202  	}
   203  	pattern = "/model/:modeluuid" + pattern
   204  	return registerAPIEndpoint(pattern, spec)
   205  }
   206  
   207  func registerAPIEndpoint(pattern string, spec apihttp.HandlerSpec) error {
   208  	if _, ok := endpointRegistry[pattern]; ok {
   209  		return errors.NewAlreadyExists(nil, fmt.Sprintf("endpoint %q already registered", pattern))
   210  	}
   211  	endpointRegistry[pattern] = spec
   212  	endpointRegistryOrder = append(endpointRegistryOrder, pattern)
   213  	return nil
   214  }
   215  
   216  // DefaultHTTPMethods are the HTTP methods supported by default by the API.
   217  var DefaultHTTPMethods = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS"}
   218  
   219  // ResolveAPIEndpoints builds the set of endpoint handlers for all
   220  // registered API endpoints.
   221  func ResolveAPIEndpoints(newArgs func(apihttp.HandlerConstraints) apihttp.NewHandlerArgs) []apihttp.Endpoint {
   222  	var endpoints []apihttp.Endpoint
   223  	for _, pattern := range endpointRegistryOrder {
   224  		spec := endpointRegistry[pattern]
   225  		args := newArgs(spec.Constraints)
   226  		handler := spec.NewHandler(args)
   227  		for _, method := range DefaultHTTPMethods {
   228  			endpoints = append(endpoints, apihttp.Endpoint{
   229  				Pattern: pattern,
   230  				Method:  method,
   231  				Handler: handler,
   232  			})
   233  		}
   234  	}
   235  	return endpoints
   236  }