github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "strings" 11 12 "github.com/juju/errors" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/common/apihttp" 16 "github.com/juju/juju/apiserver/facade" 17 "github.com/juju/juju/state" 18 ) 19 20 // Facades is the registry that tracks all of the Facades that will be 21 // exposed in the API. It can be used to List/Get/Register facades. 22 // 23 // Most implementers of a facade will probably want to use 24 // RegisterStandardFacade rather than Facades.Register, as it provides 25 // much cleaner syntax and semantics for calling during init(). 26 // 27 // Developers in a happy future will not want to use Facades at all, 28 // eschewing globals in favour of explicitly building apis and supplying 29 // them directly to an apiserver. 30 var Facades = &facade.Registry{} 31 32 // RegisterFacade updates the global facade registry with a new version of a new type. 33 func RegisterFacade(name string, version int, factory facade.Factory, facadeType reflect.Type) { 34 RegisterFacadeForFeature(name, version, factory, facadeType, "") 35 } 36 37 // RegisterFacadeForFeature updates the global facade registry with a new 38 // version of a new type. If the feature is non-empty, this facade is only 39 // available when the specified feature flag is set. 40 func RegisterFacadeForFeature(name string, version int, factory facade.Factory, facadeType reflect.Type, feature string) { 41 err := Facades.Register(name, version, factory, facadeType, feature) 42 if err != nil { 43 // This is meant to be called during init() so errors should be 44 // considered fatal. 45 panic(err) 46 } 47 logger.Tracef("Registered facade %q v%d", name, version) 48 } 49 50 // validateNewFacade ensures that the facade factory we have has the right 51 // input and output parameters for being used as a NewFoo function. 52 func validateNewFacade(funcValue reflect.Value) error { 53 if !funcValue.IsValid() { 54 return fmt.Errorf("cannot wrap nil") 55 } 56 if funcValue.Kind() != reflect.Func { 57 return fmt.Errorf("wrong type %q is not a function", funcValue.Kind()) 58 } 59 funcType := funcValue.Type() 60 funcName := runtime.FuncForPC(funcValue.Pointer()).Name() 61 if funcType.NumIn() != 3 || funcType.NumOut() != 2 { 62 return fmt.Errorf("function %q does not take 3 parameters and return 2", 63 funcName) 64 } 65 66 type nastyFactory func( 67 st *state.State, 68 resources facade.Resources, 69 authorizer facade.Authorizer, 70 ) ( 71 interface{}, error, 72 ) 73 facadeType := reflect.TypeOf((*nastyFactory)(nil)).Elem() 74 isSame := true 75 for i := 0; i < 3; i++ { 76 if funcType.In(i) != facadeType.In(i) { 77 isSame = false 78 break 79 } 80 } 81 if funcType.Out(1) != facadeType.Out(1) { 82 isSame = false 83 } 84 if !isSame { 85 return fmt.Errorf("function %q does not have the signature func (*state.State, facade.Resources, facade.Authorizer) (*Type, error)", 86 funcName) 87 } 88 return nil 89 } 90 91 // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error) 92 // function and wraps it into a proper facade.Factory function. 93 func wrapNewFacade(newFunc interface{}) (facade.Factory, reflect.Type, error) { 94 funcValue := reflect.ValueOf(newFunc) 95 err := validateNewFacade(funcValue) 96 if err != nil { 97 return nil, reflect.TypeOf(nil), err 98 } 99 // So we know newFunc is a func with the right args in and out, so 100 // wrap it into a helper function that matches the facade.Factory. 101 wrapped := func(context facade.Context) (facade.Facade, error) { 102 if context.ID() != "" { 103 return nil, ErrBadId 104 } 105 st := context.State() 106 auth := context.Auth() 107 resources := context.Resources() 108 // st, resources, or auth is nil, then reflect.Call dies 109 // because reflect.ValueOf(anynil) is the Zero Value. 110 // So we use &obj.Elem() which gives us a concrete Value object 111 // that can refer to nil. 112 in := []reflect.Value{ 113 reflect.ValueOf(&st).Elem(), 114 reflect.ValueOf(&resources).Elem(), 115 reflect.ValueOf(&auth).Elem(), 116 } 117 out := funcValue.Call(in) 118 if out[1].Interface() != nil { 119 err := out[1].Interface().(error) 120 return nil, err 121 } 122 return out[0].Interface(), nil 123 } 124 return wrapped, funcValue.Type().Out(0), nil 125 } 126 127 // NewHookContextFacadeFn specifies the function signature that can be 128 // used to register a hook context facade. 129 type NewHookContextFacadeFn func(*state.State, *state.Unit) (interface{}, error) 130 131 // RegisterHookContextFacade registers facades for use within a hook 132 // context. This function handles the translation from a 133 // hook-context-facade to a standard facade so the caller's factory 134 // method can elide unnecessary arguments. This function also handles 135 // any necessary authorization for the client. 136 // 137 // XXX(fwereade): this is fundamentally broken, because it (1) 138 // arbitrarily creates a new facade for a tiny fragment of a specific 139 // client worker's reponsibilities and (2) actively conceals necessary 140 // auth information from the facade. Don't call it; actively work to 141 // delete code that uses it, and rewrite it properly. 142 func RegisterHookContextFacade(name string, version int, newHookContextFacade NewHookContextFacadeFn, facadeType reflect.Type) { 143 144 newFacade := func(context facade.Context) (facade.Facade, error) { 145 authorizer := context.Auth() 146 st := context.State() 147 148 if !authorizer.AuthUnitAgent() { 149 return nil, ErrPerm 150 } 151 152 // Verify that the unit's ID matches a unit that we know 153 // about. 154 tag := authorizer.GetAuthTag() 155 if _, ok := tag.(names.UnitTag); !ok { 156 return nil, errors.Errorf("expected names.UnitTag, got %T", tag) 157 } 158 159 unit, err := st.Unit(tag.Id()) 160 if err != nil { 161 return nil, errors.Trace(err) 162 } 163 164 return newHookContextFacade(st, unit) 165 } 166 167 RegisterFacade(name, version, newFacade, facadeType) 168 } 169 170 // RegisterStandardFacade registers a factory function for a normal New* style 171 // function. This requires that the function has the form: 172 // NewFoo(*state.State, facade.Resources, facade.Authorizer) (*Type, error) 173 // With that syntax, we will create a helper function that wraps calling NewFoo 174 // with the right parameters, and returns the *Type correctly. 175 func RegisterStandardFacade(name string, version int, newFunc interface{}) { 176 RegisterStandardFacadeForFeature(name, version, newFunc, "") 177 } 178 179 // RegisterStandardFacadeForFeature registers a factory function for a normal 180 // New* style function. This requires that the function has the form: 181 // NewFoo(*state.State, facade.Resources, facade.Authorizer) (*Type, error) 182 // With that syntax, we will create a helper function that wraps calling 183 // NewFoo with the right parameters, and returns the *Type correctly. If the 184 // feature is non-empty, this facade is only available when the specified 185 // feature flag is set. 186 func RegisterStandardFacadeForFeature(name string, version int, newFunc interface{}, feature string) { 187 wrapped, facadeType, err := wrapNewFacade(newFunc) 188 if err != nil { 189 panic(err) 190 } 191 RegisterFacadeForFeature(name, version, wrapped, facadeType, feature) 192 } 193 194 var endpointRegistry = map[string]apihttp.HandlerSpec{} 195 var endpointRegistryOrder []string 196 197 // RegisterAPIModelEndpoint adds the provided endpoint to the registry. 198 // The pattern is prefixed with the model pattern: /model/:modeluuid. 199 func RegisterAPIModelEndpoint(pattern string, spec apihttp.HandlerSpec) error { 200 if !strings.HasPrefix(pattern, "/") { 201 pattern = "/" + pattern 202 } 203 pattern = "/model/:modeluuid" + pattern 204 return registerAPIEndpoint(pattern, spec) 205 } 206 207 func registerAPIEndpoint(pattern string, spec apihttp.HandlerSpec) error { 208 if _, ok := endpointRegistry[pattern]; ok { 209 return errors.NewAlreadyExists(nil, fmt.Sprintf("endpoint %q already registered", pattern)) 210 } 211 endpointRegistry[pattern] = spec 212 endpointRegistryOrder = append(endpointRegistryOrder, pattern) 213 return nil 214 } 215 216 // DefaultHTTPMethods are the HTTP methods supported by default by the API. 217 var DefaultHTTPMethods = []string{"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS"} 218 219 // ResolveAPIEndpoints builds the set of endpoint handlers for all 220 // registered API endpoints. 221 func ResolveAPIEndpoints(newArgs func(apihttp.HandlerConstraints) apihttp.NewHandlerArgs) []apihttp.Endpoint { 222 var endpoints []apihttp.Endpoint 223 for _, pattern := range endpointRegistryOrder { 224 spec := endpointRegistry[pattern] 225 args := newArgs(spec.Constraints) 226 handler := spec.NewHandler(args) 227 for _, method := range DefaultHTTPMethods { 228 endpoints = append(endpoints, apihttp.Endpoint{ 229 Pattern: pattern, 230 Method: method, 231 Handler: handler, 232 }) 233 } 234 } 235 return endpoints 236 }