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 }