github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facade/registry.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package facade
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  	"runtime"
    10  	"sort"
    11  
    12  	"github.com/juju/errors"
    13  
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  // record represents an entry in a Registry.
    18  type record struct {
    19  	factory    Factory
    20  	facadeType reflect.Type
    21  }
    22  
    23  // versions is our internal structure for tracking specific versions of a
    24  // single facade. We use a map to be able to quickly lookup a version.
    25  type versions map[int]record
    26  
    27  // Registry describes the API facades exposed by some API server.
    28  type Registry struct {
    29  	facades map[string]versions
    30  }
    31  
    32  // RegisterStandard is the more convenient way of registering
    33  // facades. newFunc should have one of the following signatures:
    34  //   func (facade.Context) (*Type, error)
    35  //   func (*state.State, facade.Resources, facade.Authorizer) (*Type, error)
    36  func (f *Registry) RegisterStandard(name string, version int, newFunc interface{}) error {
    37  	wrapped, facadeType, err := wrapNewFacade(newFunc)
    38  	if err != nil {
    39  		return errors.Trace(err)
    40  	}
    41  	err = f.Register(name, version, wrapped, facadeType)
    42  	if err != nil {
    43  		return errors.Trace(err)
    44  	}
    45  	return nil
    46  }
    47  
    48  // Register adds a single named facade at a given version to the registry.
    49  // Factory will be called when someone wants to instantiate an object of
    50  // this facade, and facadeType defines the concrete type that the returned object will be.
    51  // The Type information is used to define what methods will be exported in the
    52  // API, and it must exactly match the actual object returned by the factory.
    53  func (f *Registry) Register(name string, version int, factory Factory, facadeType reflect.Type) error {
    54  	if f.facades == nil {
    55  		f.facades = make(map[string]versions, 1)
    56  	}
    57  	record := record{
    58  		factory:    factory,
    59  		facadeType: facadeType,
    60  	}
    61  	if vers, ok := f.facades[name]; ok {
    62  		if _, ok := vers[version]; ok {
    63  			fullname := fmt.Sprintf("%s(%d)", name, version)
    64  			return fmt.Errorf("object %q already registered", fullname)
    65  		}
    66  		vers[version] = record
    67  	} else {
    68  		f.facades[name] = versions{version: record}
    69  	}
    70  	return nil
    71  }
    72  
    73  // lookup translates a facade name and version into a record.
    74  func (f *Registry) lookup(name string, version int) (record, error) {
    75  	if versions, ok := f.facades[name]; ok {
    76  		if record, ok := versions[version]; ok {
    77  			return record, nil
    78  		}
    79  	}
    80  	return record{}, errors.NotFoundf("%s(%d)", name, version)
    81  }
    82  
    83  // GetFactory returns just the Factory for a given Facade name and version.
    84  // See also GetType for getting the type information instead of the creation factory.
    85  func (f *Registry) GetFactory(name string, version int) (Factory, error) {
    86  	record, err := f.lookup(name, version)
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  	return record.factory, nil
    91  }
    92  
    93  // GetType returns the type information for a given Facade name and version.
    94  // This can be used for introspection purposes (to determine what methods are
    95  // available, etc).
    96  func (f *Registry) GetType(name string, version int) (reflect.Type, error) {
    97  	record, err := f.lookup(name, version)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return record.facadeType, nil
   102  }
   103  
   104  // Description describes the name and what versions of a facade have been
   105  // registered.
   106  type Description struct {
   107  	Name     string
   108  	Versions []int
   109  }
   110  
   111  // descriptionFromVersions aggregates the information in a versions map into a
   112  // more friendly form for List().
   113  func descriptionFromVersions(name string, vers versions) Description {
   114  	intVersions := make([]int, 0, len(vers))
   115  	for version := range vers {
   116  		intVersions = append(intVersions, version)
   117  	}
   118  	sort.Ints(intVersions)
   119  	return Description{
   120  		Name:     name,
   121  		Versions: intVersions,
   122  	}
   123  }
   124  
   125  // List returns a slice describing each of the registered Facades.
   126  func (f *Registry) List() []Description {
   127  	names := make([]string, 0, len(f.facades))
   128  	for name := range f.facades {
   129  		names = append(names, name)
   130  	}
   131  	sort.Strings(names)
   132  	descriptions := make([]Description, 0, len(f.facades))
   133  	for _, name := range names {
   134  		facades := f.facades[name]
   135  		description := descriptionFromVersions(name, facades)
   136  		if len(description.Versions) > 0 {
   137  			descriptions = append(descriptions, description)
   138  		}
   139  	}
   140  	return descriptions
   141  }
   142  
   143  // Details holds information about a facade.
   144  type Details struct {
   145  	// Name is the name of the facade.
   146  	Name string
   147  	// Version holds the version of the facade.
   148  	Version int
   149  	// Factory holds the factory function for making
   150  	// instances of the facade.
   151  	Factory Factory
   152  	// Type holds the type of object that the Factory
   153  	// will return. This can be used to find out
   154  	// details of the facade without actually creating
   155  	// a facade instance (see rpcreflect.ObjTypeOf).
   156  	Type reflect.Type
   157  }
   158  
   159  // ListDetails returns information about all the facades
   160  // registered in f, ordered lexically by name.
   161  func (f *Registry) ListDetails() []Details {
   162  	names := make([]string, 0, len(f.facades))
   163  	for name := range f.facades {
   164  		names = append(names, name)
   165  	}
   166  	sort.Strings(names)
   167  	var details []Details
   168  	for _, name := range names {
   169  		for v, info := range f.facades[name] {
   170  			details = append(details, Details{
   171  				Name:    name,
   172  				Version: v,
   173  				Factory: info.factory,
   174  				Type:    info.facadeType,
   175  			})
   176  		}
   177  	}
   178  	return details
   179  }
   180  
   181  // Discard gets rid of a registration that has already been done. Calling
   182  // discard on an entry that is not present is not considered an error.
   183  func (f *Registry) Discard(name string, version int) {
   184  	if versions, ok := f.facades[name]; ok {
   185  		delete(versions, version)
   186  		if len(versions) == 0 {
   187  			delete(f.facades, name)
   188  		}
   189  	}
   190  }
   191  
   192  // niceFactory defines the preferred facade registration function signature.
   193  type niceFactory func(Context) (interface{}, error)
   194  
   195  // nastyFactory defines the legacy facade registration function signature.
   196  type nastyFactory func(*state.State, Resources, Authorizer) (interface{}, error)
   197  
   198  // validateNewFacade ensures that the facade factory we have has the right
   199  // input and output parameters for being used as a NewFoo function.
   200  func validateNewFacade(funcValue reflect.Value) (bool, error) {
   201  	if !funcValue.IsValid() {
   202  		return false, fmt.Errorf("cannot wrap nil")
   203  	}
   204  	if funcValue.Kind() != reflect.Func {
   205  		return false, fmt.Errorf("wrong type %q is not a function", funcValue.Kind())
   206  	}
   207  	funcType := funcValue.Type()
   208  	funcName := runtime.FuncForPC(funcValue.Pointer()).Name()
   209  
   210  	badSigError := errors.Errorf(""+
   211  		"function %q does not have the signature "+
   212  		"func (facade.Context) (*Type, error), or "+
   213  		"func (*state.State, facade.Resources, facade.Authorizer) (*Type, error)", funcName)
   214  
   215  	if funcType.NumOut() != 2 {
   216  		return false, errors.Trace(badSigError)
   217  	}
   218  	var (
   219  		facadeType reflect.Type
   220  		nice       bool
   221  	)
   222  	inArgCount := funcType.NumIn()
   223  
   224  	switch inArgCount {
   225  	case 1:
   226  		facadeType = reflect.TypeOf((*niceFactory)(nil)).Elem()
   227  		nice = true
   228  	case 3:
   229  		facadeType = reflect.TypeOf((*nastyFactory)(nil)).Elem()
   230  	default:
   231  		return false, errors.Trace(badSigError)
   232  	}
   233  
   234  	isSame := true
   235  	for i := 0; i < inArgCount; i++ {
   236  		if funcType.In(i) != facadeType.In(i) {
   237  			isSame = false
   238  			break
   239  		}
   240  	}
   241  	if funcType.Out(1) != facadeType.Out(1) {
   242  		isSame = false
   243  	}
   244  	if !isSame {
   245  		return false, errors.Trace(badSigError)
   246  	}
   247  	return nice, nil
   248  }
   249  
   250  // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error)
   251  // function and wraps it into a proper facade.Factory function.
   252  func wrapNewFacade(newFunc interface{}) (Factory, reflect.Type, error) {
   253  	funcValue := reflect.ValueOf(newFunc)
   254  	nice, err := validateNewFacade(funcValue)
   255  	if err != nil {
   256  		return nil, reflect.TypeOf(nil), err
   257  	}
   258  	var wrapped Factory
   259  	if nice {
   260  		wrapped = func(context Context) (Facade, error) {
   261  			if context.ID() != "" {
   262  				return nil, errors.New("id not expected")
   263  			}
   264  			in := []reflect.Value{reflect.ValueOf(context)}
   265  			out := funcValue.Call(in)
   266  			if out[1].Interface() != nil {
   267  				err := out[1].Interface().(error)
   268  				return nil, err
   269  			}
   270  			return out[0].Interface(), nil
   271  		}
   272  	} else {
   273  		// So we know newFunc is a func with the right args in and out, so
   274  		// wrap it into a helper function that matches the Factory.
   275  		wrapped = func(context Context) (Facade, error) {
   276  			if context.ID() != "" {
   277  				return nil, errors.New("id not expected")
   278  			}
   279  			st := context.State()
   280  			auth := context.Auth()
   281  			resources := context.Resources()
   282  			// st, resources, or auth is nil, then reflect.Call dies
   283  			// because reflect.ValueOf(anynil) is the Zero Value.
   284  			// So we use &obj.Elem() which gives us a concrete Value object
   285  			// that can refer to nil.
   286  			in := []reflect.Value{
   287  				reflect.ValueOf(&st).Elem(),
   288  				reflect.ValueOf(&resources).Elem(),
   289  				reflect.ValueOf(&auth).Elem(),
   290  			}
   291  			out := funcValue.Call(in)
   292  			if out[1].Interface() != nil {
   293  				err := out[1].Interface().(error)
   294  				return nil, err
   295  			}
   296  			return out[0].Interface(), nil
   297  		}
   298  
   299  	}
   300  	return wrapped, funcValue.Type().Out(0), nil
   301  }