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 }