github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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  
    12  	"github.com/juju/errors"
    13  	"github.com/juju/utils/featureflag"
    14  
    15  	"github.com/juju/juju/state"
    16  )
    17  
    18  // FacadeFactory represent a way of creating a Facade from the current
    19  // connection to the State.
    20  type FacadeFactory func(
    21  	st *state.State, resources *Resources, authorizer Authorizer, id string,
    22  ) (
    23  	interface{}, error,
    24  )
    25  
    26  type facadeRecord struct {
    27  	factory    FacadeFactory
    28  	facadeType reflect.Type
    29  	// If the feature is not the empty string, then this facade
    30  	// is only returned when that feature flag is set.
    31  	feature string
    32  }
    33  
    34  // RegisterFacade updates the global facade registry with a new version of a new type.
    35  func RegisterFacade(name string, version int, factory FacadeFactory, facadeType reflect.Type) {
    36  	RegisterFacadeForFeature(name, version, factory, facadeType, "")
    37  }
    38  
    39  // RegisterFacadeForFeature updates the global facade registry with a new
    40  // version of a new type. If the feature is non-empty, this facade is only
    41  // available when the specified feature flag is set.
    42  func RegisterFacadeForFeature(name string, version int, factory FacadeFactory, facadeType reflect.Type, feature string) {
    43  	err := Facades.Register(name, version, factory, facadeType, feature)
    44  	if err != nil {
    45  		// This is meant to be called during init() so errors should be
    46  		// considered fatal.
    47  		panic(err)
    48  	}
    49  }
    50  
    51  // validateNewFacade ensures that the facade factory we have has the right
    52  // input and output parameters for being used as a NewFoo function.
    53  func validateNewFacade(funcValue reflect.Value) error {
    54  	if !funcValue.IsValid() {
    55  		return fmt.Errorf("cannot wrap nil")
    56  	}
    57  	if funcValue.Kind() != reflect.Func {
    58  		return fmt.Errorf("wrong type %q is not a function", funcValue.Kind())
    59  	}
    60  	funcType := funcValue.Type()
    61  	funcName := runtime.FuncForPC(funcValue.Pointer()).Name()
    62  	if funcType.NumIn() != 3 || funcType.NumOut() != 2 {
    63  		return fmt.Errorf("function %q does not take 3 parameters and return 2",
    64  			funcName)
    65  	}
    66  	facadeType := reflect.TypeOf((*FacadeFactory)(nil)).Elem()
    67  	isSame := true
    68  	for i := 0; i < 3; i++ {
    69  		if funcType.In(i) != facadeType.In(i) {
    70  			isSame = false
    71  			break
    72  		}
    73  	}
    74  	if funcType.Out(1) != facadeType.Out(1) {
    75  		isSame = false
    76  	}
    77  	if !isSame {
    78  		return fmt.Errorf("function %q does not have the signature func (*state.State, *common.Resources, common.Authorizer) (*Type, error)",
    79  			funcName)
    80  	}
    81  	return nil
    82  }
    83  
    84  // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error)
    85  // function and wraps it into a proper FacadeFactory function.
    86  func wrapNewFacade(newFunc interface{}) (FacadeFactory, reflect.Type, error) {
    87  	funcValue := reflect.ValueOf(newFunc)
    88  	err := validateNewFacade(funcValue)
    89  	if err != nil {
    90  		return nil, reflect.TypeOf(nil), err
    91  	}
    92  	// So we know newFunc is a func with the right args in and out, so
    93  	// wrap it into a helper function that matches the FacadeFactory.
    94  	wrapped := func(
    95  		st *state.State, resources *Resources, auth Authorizer, id string,
    96  	) (
    97  		interface{}, error,
    98  	) {
    99  		if id != "" {
   100  			return nil, ErrBadId
   101  		}
   102  		// st, resources, or auth is nil, then reflect.Call dies
   103  		// because reflect.ValueOf(anynil) is the Zero Value.
   104  		// So we use &obj.Elem() which gives us a concrete Value object
   105  		// that can refer to nil.
   106  		in := []reflect.Value{
   107  			reflect.ValueOf(&st).Elem(),
   108  			reflect.ValueOf(&resources).Elem(),
   109  			reflect.ValueOf(&auth).Elem(),
   110  		}
   111  		out := funcValue.Call(in)
   112  		if out[1].Interface() != nil {
   113  			err := out[1].Interface().(error)
   114  			return nil, err
   115  		}
   116  		return out[0].Interface(), nil
   117  	}
   118  	return wrapped, funcValue.Type().Out(0), nil
   119  }
   120  
   121  // RegisterStandardFacade registers a factory function for a normal New* style
   122  // function. This requires that the function has the form:
   123  // NewFoo(*state.State, *common.Resources, common.Authorizer) (*Type, error)
   124  // With that syntax, we will create a helper function that wraps calling NewFoo
   125  // with the right parameters, and returns the *Type correctly.
   126  func RegisterStandardFacade(name string, version int, newFunc interface{}) {
   127  	RegisterStandardFacadeForFeature(name, version, newFunc, "")
   128  }
   129  
   130  // RegisterStandardFacadeForFeature registers a factory function for a normal
   131  // New* style function. This requires that the function has the form:
   132  // NewFoo(*state.State, *common.Resources, common.Authorizer) (*Type, error)
   133  // With that syntax, we will create a helper function that wraps calling
   134  // NewFoo with the right parameters, and returns the *Type correctly. If the
   135  // feature is non-empty, this facade is only available when the specified
   136  // feature flag is set.
   137  func RegisterStandardFacadeForFeature(name string, version int, newFunc interface{}, feature string) {
   138  	wrapped, facadeType, err := wrapNewFacade(newFunc)
   139  	if err != nil {
   140  		panic(err)
   141  	}
   142  	RegisterFacadeForFeature(name, version, wrapped, facadeType, feature)
   143  }
   144  
   145  // Facades is the registry that tracks all of the Facades that will be exposed in the API.
   146  // It can be used to List/Get/Register facades.
   147  // Most implementers of a facade will probably want to use
   148  // RegisterStandardFacade rather than Facades.Register, as it provides much
   149  // cleaner syntax and semantics for calling during init().
   150  var Facades = &FacadeRegistry{}
   151  
   152  // versions is our internal structure for tracking specific versions of a
   153  // single facade. We use a map to be able to quickly lookup a version.
   154  type versions map[int]facadeRecord
   155  
   156  // FacadeRegistry is responsible for tracking what Facades are going to be exported in the API.
   157  // See the variable "Facades" for the singleton that tracks them.
   158  // It would be possible to have multiple registries if we decide to change how
   159  // the API exposes methods based on Login information.
   160  type FacadeRegistry struct {
   161  	facades map[string]versions
   162  }
   163  
   164  // Register adds a single named facade at a given version to the registry.
   165  // FacadeFactory will be called when someone wants to instantiate an object of
   166  // this facade, and facadeType defines the concrete type that the returned object will be.
   167  // The Type information is used to define what methods will be exported in the
   168  // API, and it must exactly match the actual object returned by the factory.
   169  func (f *FacadeRegistry) Register(name string, version int, factory FacadeFactory, facadeType reflect.Type, feature string) error {
   170  	if f.facades == nil {
   171  		f.facades = make(map[string]versions, 1)
   172  	}
   173  	record := facadeRecord{
   174  		factory:    factory,
   175  		facadeType: facadeType,
   176  		feature:    feature,
   177  	}
   178  	if vers, ok := f.facades[name]; ok {
   179  		if _, ok := vers[version]; ok {
   180  			fullname := fmt.Sprintf("%s(%d)", name, version)
   181  			return fmt.Errorf("object %q already registered", fullname)
   182  		}
   183  		vers[version] = record
   184  	} else {
   185  		f.facades[name] = versions{version: record}
   186  	}
   187  	return nil
   188  }
   189  
   190  // lookup translates a facade name and version into a facadeRecord.
   191  func (f *FacadeRegistry) lookup(name string, version int) (facadeRecord, error) {
   192  	if versions, ok := f.facades[name]; ok {
   193  		if record, ok := versions[version]; ok {
   194  			if featureflag.Enabled(record.feature) {
   195  				return record, nil
   196  			}
   197  		}
   198  	}
   199  	return facadeRecord{}, errors.NotFoundf("%s(%d)", name, version)
   200  }
   201  
   202  // GetFactory returns just the FacadeFactory for a given Facade name and version.
   203  // See also GetType for getting the type information instead of the creation factory.
   204  func (f *FacadeRegistry) GetFactory(name string, version int) (FacadeFactory, error) {
   205  	record, err := f.lookup(name, version)
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	return record.factory, nil
   210  }
   211  
   212  // GetType returns the type information for a given Facade name and version.
   213  // This can be used for introspection purposes (to determine what methods are
   214  // available, etc).
   215  func (f *FacadeRegistry) GetType(name string, version int) (reflect.Type, error) {
   216  	record, err := f.lookup(name, version)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  	return record.facadeType, nil
   221  }
   222  
   223  // FacadeDescription describes the name and what versions of a facade have been
   224  // registered.
   225  type FacadeDescription struct {
   226  	Name     string
   227  	Versions []int
   228  }
   229  
   230  // descriptionFromVersions aggregates the information in a versions map into a
   231  // more friendly form for List().
   232  func descriptionFromVersions(name string, vers versions) FacadeDescription {
   233  	intVersions := make([]int, 0, len(vers))
   234  	for version, record := range vers {
   235  		if featureflag.Enabled(record.feature) {
   236  			intVersions = append(intVersions, version)
   237  		}
   238  	}
   239  	sort.Ints(intVersions)
   240  	return FacadeDescription{
   241  		Name:     name,
   242  		Versions: intVersions,
   243  	}
   244  }
   245  
   246  // List returns a slice describing each of the registered Facades.
   247  func (f *FacadeRegistry) List() []FacadeDescription {
   248  	names := make([]string, 0, len(f.facades))
   249  	for name := range f.facades {
   250  		names = append(names, name)
   251  	}
   252  	sort.Strings(names)
   253  	descriptions := make([]FacadeDescription, 0, len(f.facades))
   254  	for _, name := range names {
   255  		facades := f.facades[name]
   256  		description := descriptionFromVersions(name, facades)
   257  		if len(description.Versions) > 0 {
   258  			descriptions = append(descriptions, description)
   259  		}
   260  	}
   261  	return descriptions
   262  }
   263  
   264  // Discard gets rid of a registration that has already been done. Calling
   265  // discard on an entry that is not present is not considered an error.
   266  func (f *FacadeRegistry) Discard(name string, version int) {
   267  	if versions, ok := f.facades[name]; ok {
   268  		delete(versions, version)
   269  		if len(versions) == 0 {
   270  			delete(f.facades, name)
   271  		}
   272  	}
   273  }