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 }