github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	"sort"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/utils/featureflag"
    13  )
    14  
    15  // record represents an entry in a Registry.
    16  type record struct {
    17  	factory    Factory
    18  	facadeType reflect.Type
    19  	// If the feature is not the empty string, then this facade
    20  	// is only returned when that feature flag is set.
    21  	//
    22  	// It is not a good thing that we depend on yet another flavour
    23  	// of global in the implementation of the Registry that itself
    24  	// only meaningfully exists as a global.
    25  	feature string
    26  }
    27  
    28  // versions is our internal structure for tracking specific versions of a
    29  // single facade. We use a map to be able to quickly lookup a version.
    30  type versions map[int]record
    31  
    32  // Registry describes Facades the facades exposed by some API server.
    33  //
    34  // It's only actually used as a global -- `apiserver/common.Facades` --
    35  // but if we were smarter we could just create a Registry directly and
    36  // pass it into the apiserver.
    37  type Registry struct {
    38  	facades map[string]versions
    39  }
    40  
    41  // Register adds a single named facade at a given version to the registry.
    42  // Factory will be called when someone wants to instantiate an object of
    43  // this facade, and facadeType defines the concrete type that the returned object will be.
    44  // The Type information is used to define what methods will be exported in the
    45  // API, and it must exactly match the actual object returned by the factory.
    46  func (f *Registry) Register(name string, version int, factory Factory, facadeType reflect.Type, feature string) error {
    47  	if f.facades == nil {
    48  		f.facades = make(map[string]versions, 1)
    49  	}
    50  	record := record{
    51  		factory:    factory,
    52  		facadeType: facadeType,
    53  		feature:    feature,
    54  	}
    55  	if vers, ok := f.facades[name]; ok {
    56  		if _, ok := vers[version]; ok {
    57  			fullname := fmt.Sprintf("%s(%d)", name, version)
    58  			return fmt.Errorf("object %q already registered", fullname)
    59  		}
    60  		vers[version] = record
    61  	} else {
    62  		f.facades[name] = versions{version: record}
    63  	}
    64  	return nil
    65  }
    66  
    67  // lookup translates a facade name and version into a record.
    68  func (f *Registry) lookup(name string, version int) (record, error) {
    69  	if versions, ok := f.facades[name]; ok {
    70  		if record, ok := versions[version]; ok {
    71  			if featureflag.Enabled(record.feature) {
    72  				return record, nil
    73  			}
    74  		}
    75  	}
    76  	return record{}, errors.NotFoundf("%s(%d)", name, version)
    77  }
    78  
    79  // GetFactory returns just the Factory for a given Facade name and version.
    80  // See also GetType for getting the type information instead of the creation factory.
    81  func (f *Registry) GetFactory(name string, version int) (Factory, error) {
    82  	record, err := f.lookup(name, version)
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	return record.factory, nil
    87  }
    88  
    89  // GetType returns the type information for a given Facade name and version.
    90  // This can be used for introspection purposes (to determine what methods are
    91  // available, etc).
    92  func (f *Registry) GetType(name string, version int) (reflect.Type, error) {
    93  	record, err := f.lookup(name, version)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return record.facadeType, nil
    98  }
    99  
   100  // Description describes the name and what versions of a facade have been
   101  // registered.
   102  type Description struct {
   103  	Name     string
   104  	Versions []int
   105  }
   106  
   107  // descriptionFromVersions aggregates the information in a versions map into a
   108  // more friendly form for List().
   109  func descriptionFromVersions(name string, vers versions) Description {
   110  	intVersions := make([]int, 0, len(vers))
   111  	for version, record := range vers {
   112  		if featureflag.Enabled(record.feature) {
   113  			intVersions = append(intVersions, version)
   114  		}
   115  	}
   116  	sort.Ints(intVersions)
   117  	return Description{
   118  		Name:     name,
   119  		Versions: intVersions,
   120  	}
   121  }
   122  
   123  // List returns a slice describing each of the registered Facades.
   124  func (f *Registry) List() []Description {
   125  	names := make([]string, 0, len(f.facades))
   126  	for name := range f.facades {
   127  		names = append(names, name)
   128  	}
   129  	sort.Strings(names)
   130  	descriptions := make([]Description, 0, len(f.facades))
   131  	for _, name := range names {
   132  		facades := f.facades[name]
   133  		description := descriptionFromVersions(name, facades)
   134  		if len(description.Versions) > 0 {
   135  			descriptions = append(descriptions, description)
   136  		}
   137  	}
   138  	return descriptions
   139  }
   140  
   141  // Discard gets rid of a registration that has already been done. Calling
   142  // discard on an entry that is not present is not considered an error.
   143  func (f *Registry) Discard(name string, version int) {
   144  	if versions, ok := f.facades[name]; ok {
   145  		delete(versions, version)
   146  		if len(versions) == 0 {
   147  			delete(f.facades, name)
   148  		}
   149  	}
   150  }