github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"sort"
    11  	"strings"
    12  
    13  	"github.com/juju/errors"
    14  	"github.com/juju/names"
    15  	"github.com/juju/utils/featureflag"
    16  
    17  	"github.com/juju/juju/apiserver/common/apihttp"
    18  	"github.com/juju/juju/state"
    19  )
    20  
    21  // FacadeFactory represent a way of creating a Facade from the current
    22  // connection to the State.
    23  type FacadeFactory func(
    24  	st *state.State, resources *Resources, authorizer Authorizer, id string,
    25  ) (
    26  	interface{}, error,
    27  )
    28  
    29  type facadeRecord struct {
    30  	factory    FacadeFactory
    31  	facadeType reflect.Type
    32  	// If the feature is not the empty string, then this facade
    33  	// is only returned when that feature flag is set.
    34  	feature string
    35  }
    36  
    37  // RegisterFacade updates the global facade registry with a new version of a new type.
    38  func RegisterFacade(name string, version int, factory FacadeFactory, facadeType reflect.Type) {
    39  	RegisterFacadeForFeature(name, version, factory, facadeType, "")
    40  }
    41  
    42  // RegisterFacadeForFeature updates the global facade registry with a new
    43  // version of a new type. If the feature is non-empty, this facade is only
    44  // available when the specified feature flag is set.
    45  func RegisterFacadeForFeature(name string, version int, factory FacadeFactory, facadeType reflect.Type, feature string) {
    46  	err := Facades.Register(name, version, factory, facadeType, feature)
    47  	if err != nil {
    48  		// This is meant to be called during init() so errors should be
    49  		// considered fatal.
    50  		panic(err)
    51  	}
    52  }
    53  
    54  // validateNewFacade ensures that the facade factory we have has the right
    55  // input and output parameters for being used as a NewFoo function.
    56  func validateNewFacade(funcValue reflect.Value) error {
    57  	if !funcValue.IsValid() {
    58  		return fmt.Errorf("cannot wrap nil")
    59  	}
    60  	if funcValue.Kind() != reflect.Func {
    61  		return fmt.Errorf("wrong type %q is not a function", funcValue.Kind())
    62  	}
    63  	funcType := funcValue.Type()
    64  	funcName := runtime.FuncForPC(funcValue.Pointer()).Name()
    65  	if funcType.NumIn() != 3 || funcType.NumOut() != 2 {
    66  		return fmt.Errorf("function %q does not take 3 parameters and return 2",
    67  			funcName)
    68  	}
    69  	facadeType := reflect.TypeOf((*FacadeFactory)(nil)).Elem()
    70  	isSame := true
    71  	for i := 0; i < 3; i++ {
    72  		if funcType.In(i) != facadeType.In(i) {
    73  			isSame = false
    74  			break
    75  		}
    76  	}
    77  	if funcType.Out(1) != facadeType.Out(1) {
    78  		isSame = false
    79  	}
    80  	if !isSame {
    81  		return fmt.Errorf("function %q does not have the signature func (*state.State, *common.Resources, common.Authorizer) (*Type, error)",
    82  			funcName)
    83  	}
    84  	return nil
    85  }
    86  
    87  // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error)
    88  // function and wraps it into a proper FacadeFactory function.
    89  func wrapNewFacade(newFunc interface{}) (FacadeFactory, reflect.Type, error) {
    90  	funcValue := reflect.ValueOf(newFunc)
    91  	err := validateNewFacade(funcValue)
    92  	if err != nil {
    93  		return nil, reflect.TypeOf(nil), err
    94  	}
    95  	// So we know newFunc is a func with the right args in and out, so
    96  	// wrap it into a helper function that matches the FacadeFactory.
    97  	wrapped := func(
    98  		st *state.State, resources *Resources, auth Authorizer, id string,
    99  	) (
   100  		interface{}, error,
   101  	) {
   102  		if id != "" {
   103  			return nil, ErrBadId
   104  		}
   105  		// st, resources, or auth is nil, then reflect.Call dies
   106  		// because reflect.ValueOf(anynil) is the Zero Value.
   107  		// So we use &obj.Elem() which gives us a concrete Value object
   108  		// that can refer to nil.
   109  		in := []reflect.Value{
   110  			reflect.ValueOf(&st).Elem(),
   111  			reflect.ValueOf(&resources).Elem(),
   112  			reflect.ValueOf(&auth).Elem(),
   113  		}
   114  		out := funcValue.Call(in)
   115  		if out[1].Interface() != nil {
   116  			err := out[1].Interface().(error)
   117  			return nil, err
   118  		}
   119  		return out[0].Interface(), nil
   120  	}
   121  	return wrapped, funcValue.Type().Out(0), nil
   122  }
   123  
   124  // NewHookContextFacadeFn specifies the function signature that can be
   125  // used to register a hook context facade.
   126  type NewHookContextFacadeFn func(*state.State, *state.Unit) (interface{}, error)
   127  
   128  // RegisterHookContextFacade registers facades for use within a hook
   129  // context. This function handles the translation from a
   130  // hook-context-facade to a standard facade so the caller's factory
   131  // method can elide unnecessary arguments. This function also handles
   132  // any necessary authorization for the client.
   133  func RegisterHookContextFacade(name string, version int, newHookContextFacade NewHookContextFacadeFn, facadeType reflect.Type) {
   134  
   135  	newFacade := func(st *state.State, _ *Resources, authorizer Authorizer, _ string) (interface{}, error) {
   136  
   137  		if !authorizer.AuthUnitAgent() {
   138  			return nil, ErrPerm
   139  		}
   140  
   141  		// Verify that the unit's ID matches a unit that we know
   142  		// about.
   143  		tag := authorizer.GetAuthTag()
   144  		if _, ok := tag.(names.UnitTag); !ok {
   145  			return nil, errors.Errorf("expected names.UnitTag, got %T", tag)
   146  		}
   147  
   148  		unit, err := st.Unit(tag.Id())
   149  		if err != nil {
   150  			return nil, errors.Trace(err)
   151  		}
   152  
   153  		return newHookContextFacade(st, unit)
   154  	}
   155  
   156  	RegisterFacade(name, version, newFacade, facadeType)
   157  }
   158  
   159  // RegisterStandardFacade registers a factory function for a normal New* style
   160  // function. This requires that the function has the form:
   161  // NewFoo(*state.State, *common.Resources, common.Authorizer) (*Type, error)
   162  // With that syntax, we will create a helper function that wraps calling NewFoo
   163  // with the right parameters, and returns the *Type correctly.
   164  func RegisterStandardFacade(name string, version int, newFunc interface{}) {
   165  	RegisterStandardFacadeForFeature(name, version, newFunc, "")
   166  }
   167  
   168  // RegisterStandardFacadeForFeature registers a factory function for a normal
   169  // New* style function. This requires that the function has the form:
   170  // NewFoo(*state.State, *common.Resources, common.Authorizer) (*Type, error)
   171  // With that syntax, we will create a helper function that wraps calling
   172  // NewFoo with the right parameters, and returns the *Type correctly. If the
   173  // feature is non-empty, this facade is only available when the specified
   174  // feature flag is set.
   175  func RegisterStandardFacadeForFeature(name string, version int, newFunc interface{}, feature string) {
   176  	wrapped, facadeType, err := wrapNewFacade(newFunc)
   177  	if err != nil {
   178  		panic(err)
   179  	}
   180  	RegisterFacadeForFeature(name, version, wrapped, facadeType, feature)
   181  }
   182  
   183  // Facades is the registry that tracks all of the Facades that will be exposed in the API.
   184  // It can be used to List/Get/Register facades.
   185  // Most implementers of a facade will probably want to use
   186  // RegisterStandardFacade rather than Facades.Register, as it provides much
   187  // cleaner syntax and semantics for calling during init().
   188  var Facades = &FacadeRegistry{}
   189  
   190  // versions is our internal structure for tracking specific versions of a
   191  // single facade. We use a map to be able to quickly lookup a version.
   192  type versions map[int]facadeRecord
   193  
   194  // FacadeRegistry is responsible for tracking what Facades are going to be exported in the API.
   195  // See the variable "Facades" for the singleton that tracks them.
   196  // It would be possible to have multiple registries if we decide to change how
   197  // the API exposes methods based on Login information.
   198  type FacadeRegistry struct {
   199  	facades map[string]versions
   200  }
   201  
   202  // Register adds a single named facade at a given version to the registry.
   203  // FacadeFactory will be called when someone wants to instantiate an object of
   204  // this facade, and facadeType defines the concrete type that the returned object will be.
   205  // The Type information is used to define what methods will be exported in the
   206  // API, and it must exactly match the actual object returned by the factory.
   207  func (f *FacadeRegistry) Register(name string, version int, factory FacadeFactory, facadeType reflect.Type, feature string) error {
   208  	if f.facades == nil {
   209  		f.facades = make(map[string]versions, 1)
   210  	}
   211  	record := facadeRecord{
   212  		factory:    factory,
   213  		facadeType: facadeType,
   214  		feature:    feature,
   215  	}
   216  	if vers, ok := f.facades[name]; ok {
   217  		if _, ok := vers[version]; ok {
   218  			fullname := fmt.Sprintf("%s(%d)", name, version)
   219  			return fmt.Errorf("object %q already registered", fullname)
   220  		}
   221  		vers[version] = record
   222  	} else {
   223  		f.facades[name] = versions{version: record}
   224  	}
   225  	logger.Tracef("Registered facade %q v%d", name, version)
   226  	return nil
   227  }
   228  
   229  // lookup translates a facade name and version into a facadeRecord.
   230  func (f *FacadeRegistry) lookup(name string, version int) (facadeRecord, error) {
   231  	if versions, ok := f.facades[name]; ok {
   232  		if record, ok := versions[version]; ok {
   233  			if featureflag.Enabled(record.feature) {
   234  				return record, nil
   235  			}
   236  		}
   237  	}
   238  	return facadeRecord{}, errors.NotFoundf("%s(%d)", name, version)
   239  }
   240  
   241  // GetFactory returns just the FacadeFactory for a given Facade name and version.
   242  // See also GetType for getting the type information instead of the creation factory.
   243  func (f *FacadeRegistry) GetFactory(name string, version int) (FacadeFactory, error) {
   244  	record, err := f.lookup(name, version)
   245  	if err != nil {
   246  		return nil, err
   247  	}
   248  	return record.factory, nil
   249  }
   250  
   251  // GetType returns the type information for a given Facade name and version.
   252  // This can be used for introspection purposes (to determine what methods are
   253  // available, etc).
   254  func (f *FacadeRegistry) GetType(name string, version int) (reflect.Type, error) {
   255  	record, err := f.lookup(name, version)
   256  	if err != nil {
   257  		return nil, err
   258  	}
   259  	return record.facadeType, nil
   260  }
   261  
   262  // FacadeDescription describes the name and what versions of a facade have been
   263  // registered.
   264  type FacadeDescription struct {
   265  	Name     string
   266  	Versions []int
   267  }
   268  
   269  // descriptionFromVersions aggregates the information in a versions map into a
   270  // more friendly form for List().
   271  func descriptionFromVersions(name string, vers versions) FacadeDescription {
   272  	intVersions := make([]int, 0, len(vers))
   273  	for version, record := range vers {
   274  		if featureflag.Enabled(record.feature) {
   275  			intVersions = append(intVersions, version)
   276  		}
   277  	}
   278  	sort.Ints(intVersions)
   279  	return FacadeDescription{
   280  		Name:     name,
   281  		Versions: intVersions,
   282  	}
   283  }
   284  
   285  // List returns a slice describing each of the registered Facades.
   286  func (f *FacadeRegistry) List() []FacadeDescription {
   287  	names := make([]string, 0, len(f.facades))
   288  	for name := range f.facades {
   289  		names = append(names, name)
   290  	}
   291  	sort.Strings(names)
   292  	descriptions := make([]FacadeDescription, 0, len(f.facades))
   293  	for _, name := range names {
   294  		facades := f.facades[name]
   295  		description := descriptionFromVersions(name, facades)
   296  		if len(description.Versions) > 0 {
   297  			descriptions = append(descriptions, description)
   298  		}
   299  	}
   300  	return descriptions
   301  }
   302  
   303  // Discard gets rid of a registration that has already been done. Calling
   304  // discard on an entry that is not present is not considered an error.
   305  func (f *FacadeRegistry) Discard(name string, version int) {
   306  	if versions, ok := f.facades[name]; ok {
   307  		delete(versions, version)
   308  		if len(versions) == 0 {
   309  			delete(f.facades, name)
   310  		}
   311  	}
   312  }
   313  
   314  var endpointRegistry = map[string]apihttp.HandlerSpec{}
   315  var endpointRegistryOrder []string
   316  
   317  // RegisterAPIModelEndpoint adds the provided endpoint to the registry.
   318  // The pattern is prefixed with the model pattern: /model/:modeluuid.
   319  func RegisterAPIModelEndpoint(pattern string, spec apihttp.HandlerSpec) error {
   320  	if !strings.HasPrefix(pattern, "/") {
   321  		pattern = "/" + pattern
   322  	}
   323  	pattern = "/model/:modeluuid" + pattern
   324  	return registerAPIEndpoint(pattern, spec)
   325  }
   326  
   327  func registerAPIEndpoint(pattern string, spec apihttp.HandlerSpec) error {
   328  	if _, ok := endpointRegistry[pattern]; ok {
   329  		return errors.NewAlreadyExists(nil, fmt.Sprintf("endpoint %q already registered", pattern))
   330  	}
   331  	endpointRegistry[pattern] = spec
   332  	endpointRegistryOrder = append(endpointRegistryOrder, pattern)
   333  	return nil
   334  }
   335  
   336  // DefaultHTTPMethods are the HTTP methods supported by default by the API.
   337  var DefaultHTTPMethods = []string{"GET", "POST", "HEAD", "PUT", "DEL", "OPTIONS"}
   338  
   339  // ResolveAPIEndpoints builds the set of endpoint handlers for all
   340  // registered API endpoints.
   341  func ResolveAPIEndpoints(newArgs func(apihttp.HandlerConstraints) apihttp.NewHandlerArgs) []apihttp.Endpoint {
   342  	var endpoints []apihttp.Endpoint
   343  	for _, pattern := range endpointRegistryOrder {
   344  		spec := endpointRegistry[pattern]
   345  		args := newArgs(spec.Constraints)
   346  		handler := spec.NewHandler(args)
   347  		for _, method := range DefaultHTTPMethods {
   348  			endpoints = append(endpoints, apihttp.Endpoint{
   349  				Pattern: pattern,
   350  				Method:  method,
   351  				Handler: handler,
   352  			})
   353  		}
   354  	}
   355  	return endpoints
   356  }