github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 "strings" 12 13 "github.com/juju/errors" 14 "github.com/juju/names" 15 "github.com/juju/utils/featureflag" 16 17 "github.com/juju/juju/apiserver/common/apihttp" 18 "github.com/juju/juju/state" 19 ) 20 21 // FacadeFactory represent a way of creating a Facade from the current 22 // connection to the State. 23 type FacadeFactory func( 24 st *state.State, resources *Resources, authorizer Authorizer, id string, 25 ) ( 26 interface{}, error, 27 ) 28 29 type facadeRecord struct { 30 factory FacadeFactory 31 facadeType reflect.Type 32 // If the feature is not the empty string, then this facade 33 // is only returned when that feature flag is set. 34 feature string 35 } 36 37 // RegisterFacade updates the global facade registry with a new version of a new type. 38 func RegisterFacade(name string, version int, factory FacadeFactory, facadeType reflect.Type) { 39 RegisterFacadeForFeature(name, version, factory, facadeType, "") 40 } 41 42 // RegisterFacadeForFeature updates the global facade registry with a new 43 // version of a new type. If the feature is non-empty, this facade is only 44 // available when the specified feature flag is set. 45 func RegisterFacadeForFeature(name string, version int, factory FacadeFactory, facadeType reflect.Type, feature string) { 46 err := Facades.Register(name, version, factory, facadeType, feature) 47 if err != nil { 48 // This is meant to be called during init() so errors should be 49 // considered fatal. 50 panic(err) 51 } 52 } 53 54 // validateNewFacade ensures that the facade factory we have has the right 55 // input and output parameters for being used as a NewFoo function. 56 func validateNewFacade(funcValue reflect.Value) error { 57 if !funcValue.IsValid() { 58 return fmt.Errorf("cannot wrap nil") 59 } 60 if funcValue.Kind() != reflect.Func { 61 return fmt.Errorf("wrong type %q is not a function", funcValue.Kind()) 62 } 63 funcType := funcValue.Type() 64 funcName := runtime.FuncForPC(funcValue.Pointer()).Name() 65 if funcType.NumIn() != 3 || funcType.NumOut() != 2 { 66 return fmt.Errorf("function %q does not take 3 parameters and return 2", 67 funcName) 68 } 69 facadeType := reflect.TypeOf((*FacadeFactory)(nil)).Elem() 70 isSame := true 71 for i := 0; i < 3; i++ { 72 if funcType.In(i) != facadeType.In(i) { 73 isSame = false 74 break 75 } 76 } 77 if funcType.Out(1) != facadeType.Out(1) { 78 isSame = false 79 } 80 if !isSame { 81 return fmt.Errorf("function %q does not have the signature func (*state.State, *common.Resources, common.Authorizer) (*Type, error)", 82 funcName) 83 } 84 return nil 85 } 86 87 // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error) 88 // function and wraps it into a proper FacadeFactory function. 89 func wrapNewFacade(newFunc interface{}) (FacadeFactory, reflect.Type, error) { 90 funcValue := reflect.ValueOf(newFunc) 91 err := validateNewFacade(funcValue) 92 if err != nil { 93 return nil, reflect.TypeOf(nil), err 94 } 95 // So we know newFunc is a func with the right args in and out, so 96 // wrap it into a helper function that matches the FacadeFactory. 97 wrapped := func( 98 st *state.State, resources *Resources, auth Authorizer, id string, 99 ) ( 100 interface{}, error, 101 ) { 102 if id != "" { 103 return nil, ErrBadId 104 } 105 // st, resources, or auth is nil, then reflect.Call dies 106 // because reflect.ValueOf(anynil) is the Zero Value. 107 // So we use &obj.Elem() which gives us a concrete Value object 108 // that can refer to nil. 109 in := []reflect.Value{ 110 reflect.ValueOf(&st).Elem(), 111 reflect.ValueOf(&resources).Elem(), 112 reflect.ValueOf(&auth).Elem(), 113 } 114 out := funcValue.Call(in) 115 if out[1].Interface() != nil { 116 err := out[1].Interface().(error) 117 return nil, err 118 } 119 return out[0].Interface(), nil 120 } 121 return wrapped, funcValue.Type().Out(0), nil 122 } 123 124 // NewHookContextFacadeFn specifies the function signature that can be 125 // used to register a hook context facade. 126 type NewHookContextFacadeFn func(*state.State, *state.Unit) (interface{}, error) 127 128 // RegisterHookContextFacade registers facades for use within a hook 129 // context. This function handles the translation from a 130 // hook-context-facade to a standard facade so the caller's factory 131 // method can elide unnecessary arguments. This function also handles 132 // any necessary authorization for the client. 133 func RegisterHookContextFacade(name string, version int, newHookContextFacade NewHookContextFacadeFn, facadeType reflect.Type) { 134 135 newFacade := func(st *state.State, _ *Resources, authorizer Authorizer, _ string) (interface{}, error) { 136 137 if !authorizer.AuthUnitAgent() { 138 return nil, ErrPerm 139 } 140 141 // Verify that the unit's ID matches a unit that we know 142 // about. 143 tag := authorizer.GetAuthTag() 144 if _, ok := tag.(names.UnitTag); !ok { 145 return nil, errors.Errorf("expected names.UnitTag, got %T", tag) 146 } 147 148 unit, err := st.Unit(tag.Id()) 149 if err != nil { 150 return nil, errors.Trace(err) 151 } 152 153 return newHookContextFacade(st, unit) 154 } 155 156 RegisterFacade(name, version, newFacade, facadeType) 157 } 158 159 // RegisterStandardFacade registers a factory function for a normal New* style 160 // function. This requires that the function has the form: 161 // NewFoo(*state.State, *common.Resources, common.Authorizer) (*Type, error) 162 // With that syntax, we will create a helper function that wraps calling NewFoo 163 // with the right parameters, and returns the *Type correctly. 164 func RegisterStandardFacade(name string, version int, newFunc interface{}) { 165 RegisterStandardFacadeForFeature(name, version, newFunc, "") 166 } 167 168 // RegisterStandardFacadeForFeature registers a factory function for a normal 169 // New* style function. This requires that the function has the form: 170 // NewFoo(*state.State, *common.Resources, common.Authorizer) (*Type, error) 171 // With that syntax, we will create a helper function that wraps calling 172 // NewFoo with the right parameters, and returns the *Type correctly. If the 173 // feature is non-empty, this facade is only available when the specified 174 // feature flag is set. 175 func RegisterStandardFacadeForFeature(name string, version int, newFunc interface{}, feature string) { 176 wrapped, facadeType, err := wrapNewFacade(newFunc) 177 if err != nil { 178 panic(err) 179 } 180 RegisterFacadeForFeature(name, version, wrapped, facadeType, feature) 181 } 182 183 // Facades is the registry that tracks all of the Facades that will be exposed in the API. 184 // It can be used to List/Get/Register facades. 185 // Most implementers of a facade will probably want to use 186 // RegisterStandardFacade rather than Facades.Register, as it provides much 187 // cleaner syntax and semantics for calling during init(). 188 var Facades = &FacadeRegistry{} 189 190 // versions is our internal structure for tracking specific versions of a 191 // single facade. We use a map to be able to quickly lookup a version. 192 type versions map[int]facadeRecord 193 194 // FacadeRegistry is responsible for tracking what Facades are going to be exported in the API. 195 // See the variable "Facades" for the singleton that tracks them. 196 // It would be possible to have multiple registries if we decide to change how 197 // the API exposes methods based on Login information. 198 type FacadeRegistry struct { 199 facades map[string]versions 200 } 201 202 // Register adds a single named facade at a given version to the registry. 203 // FacadeFactory will be called when someone wants to instantiate an object of 204 // this facade, and facadeType defines the concrete type that the returned object will be. 205 // The Type information is used to define what methods will be exported in the 206 // API, and it must exactly match the actual object returned by the factory. 207 func (f *FacadeRegistry) Register(name string, version int, factory FacadeFactory, facadeType reflect.Type, feature string) error { 208 if f.facades == nil { 209 f.facades = make(map[string]versions, 1) 210 } 211 record := facadeRecord{ 212 factory: factory, 213 facadeType: facadeType, 214 feature: feature, 215 } 216 if vers, ok := f.facades[name]; ok { 217 if _, ok := vers[version]; ok { 218 fullname := fmt.Sprintf("%s(%d)", name, version) 219 return fmt.Errorf("object %q already registered", fullname) 220 } 221 vers[version] = record 222 } else { 223 f.facades[name] = versions{version: record} 224 } 225 logger.Tracef("Registered facade %q v%d", name, version) 226 return nil 227 } 228 229 // lookup translates a facade name and version into a facadeRecord. 230 func (f *FacadeRegistry) lookup(name string, version int) (facadeRecord, error) { 231 if versions, ok := f.facades[name]; ok { 232 if record, ok := versions[version]; ok { 233 if featureflag.Enabled(record.feature) { 234 return record, nil 235 } 236 } 237 } 238 return facadeRecord{}, errors.NotFoundf("%s(%d)", name, version) 239 } 240 241 // GetFactory returns just the FacadeFactory for a given Facade name and version. 242 // See also GetType for getting the type information instead of the creation factory. 243 func (f *FacadeRegistry) GetFactory(name string, version int) (FacadeFactory, error) { 244 record, err := f.lookup(name, version) 245 if err != nil { 246 return nil, err 247 } 248 return record.factory, nil 249 } 250 251 // GetType returns the type information for a given Facade name and version. 252 // This can be used for introspection purposes (to determine what methods are 253 // available, etc). 254 func (f *FacadeRegistry) GetType(name string, version int) (reflect.Type, error) { 255 record, err := f.lookup(name, version) 256 if err != nil { 257 return nil, err 258 } 259 return record.facadeType, nil 260 } 261 262 // FacadeDescription describes the name and what versions of a facade have been 263 // registered. 264 type FacadeDescription struct { 265 Name string 266 Versions []int 267 } 268 269 // descriptionFromVersions aggregates the information in a versions map into a 270 // more friendly form for List(). 271 func descriptionFromVersions(name string, vers versions) FacadeDescription { 272 intVersions := make([]int, 0, len(vers)) 273 for version, record := range vers { 274 if featureflag.Enabled(record.feature) { 275 intVersions = append(intVersions, version) 276 } 277 } 278 sort.Ints(intVersions) 279 return FacadeDescription{ 280 Name: name, 281 Versions: intVersions, 282 } 283 } 284 285 // List returns a slice describing each of the registered Facades. 286 func (f *FacadeRegistry) List() []FacadeDescription { 287 names := make([]string, 0, len(f.facades)) 288 for name := range f.facades { 289 names = append(names, name) 290 } 291 sort.Strings(names) 292 descriptions := make([]FacadeDescription, 0, len(f.facades)) 293 for _, name := range names { 294 facades := f.facades[name] 295 description := descriptionFromVersions(name, facades) 296 if len(description.Versions) > 0 { 297 descriptions = append(descriptions, description) 298 } 299 } 300 return descriptions 301 } 302 303 // Discard gets rid of a registration that has already been done. Calling 304 // discard on an entry that is not present is not considered an error. 305 func (f *FacadeRegistry) Discard(name string, version int) { 306 if versions, ok := f.facades[name]; ok { 307 delete(versions, version) 308 if len(versions) == 0 { 309 delete(f.facades, name) 310 } 311 } 312 } 313 314 var endpointRegistry = map[string]apihttp.HandlerSpec{} 315 var endpointRegistryOrder []string 316 317 // RegisterAPIModelEndpoint adds the provided endpoint to the registry. 318 // The pattern is prefixed with the model pattern: /model/:modeluuid. 319 func RegisterAPIModelEndpoint(pattern string, spec apihttp.HandlerSpec) error { 320 if !strings.HasPrefix(pattern, "/") { 321 pattern = "/" + pattern 322 } 323 pattern = "/model/:modeluuid" + pattern 324 return registerAPIEndpoint(pattern, spec) 325 } 326 327 func registerAPIEndpoint(pattern string, spec apihttp.HandlerSpec) error { 328 if _, ok := endpointRegistry[pattern]; ok { 329 return errors.NewAlreadyExists(nil, fmt.Sprintf("endpoint %q already registered", pattern)) 330 } 331 endpointRegistry[pattern] = spec 332 endpointRegistryOrder = append(endpointRegistryOrder, pattern) 333 return nil 334 } 335 336 // DefaultHTTPMethods are the HTTP methods supported by default by the API. 337 var DefaultHTTPMethods = []string{"GET", "POST", "HEAD", "PUT", "DEL", "OPTIONS"} 338 339 // ResolveAPIEndpoints builds the set of endpoint handlers for all 340 // registered API endpoints. 341 func ResolveAPIEndpoints(newArgs func(apihttp.HandlerConstraints) apihttp.NewHandlerArgs) []apihttp.Endpoint { 342 var endpoints []apihttp.Endpoint 343 for _, pattern := range endpointRegistryOrder { 344 spec := endpointRegistry[pattern] 345 args := newArgs(spec.Constraints) 346 handler := spec.NewHandler(args) 347 for _, method := range DefaultHTTPMethods { 348 endpoints = append(endpoints, apihttp.Endpoint{ 349 Pattern: pattern, 350 Method: method, 351 Handler: handler, 352 }) 353 } 354 } 355 return endpoints 356 }