Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| // Copyright 2014 Canonical Ltd. | |
| // Licensed under the AGPLv3, see LICENCE file for details. | |
| package common | |
| import ( | |
| "fmt" | |
| "reflect" | |
| "runtime" | |
| "github.com/juju/errors" | |
| "gopkg.in/juju/names.v2" | |
| "github.com/juju/juju/apiserver/facade" | |
| "github.com/juju/juju/state" | |
| ) | |
| // Facades is the registry that tracks all of the Facades that will be | |
| // exposed in the API. It can be used to List/Get/Register facades. | |
| // | |
| // Most implementers of a facade will probably want to use | |
| // RegisterStandardFacade rather than Facades.Register, as it provides | |
| // much cleaner syntax and semantics for calling during init(). | |
| // | |
| // Developers in a happy future will not want to use Facades at all, | |
| // eschewing globals in favour of explicitly building apis and supplying | |
| // them directly to an apiserver. | |
| var Facades = &facade.Registry{} | |
| // RegisterFacade updates the global facade registry with a new version of a new type. | |
| func RegisterFacade(name string, version int, factory facade.Factory, facadeType reflect.Type) { | |
| RegisterFacadeForFeature(name, version, factory, facadeType, "") | |
| } | |
| // RegisterFacadeForFeature updates the global facade registry with a new | |
| // version of a new type. If the feature is non-empty, this facade is only | |
| // available when the specified feature flag is set. | |
| func RegisterFacadeForFeature(name string, version int, factory facade.Factory, facadeType reflect.Type, feature string) { | |
| err := Facades.Register(name, version, factory, facadeType, feature) | |
| if err != nil { | |
| // This is meant to be called during init() so errors should be | |
| // considered fatal. | |
| panic(err) | |
| } | |
| logger.Tracef("Registered facade %q v%d", name, version) | |
| } | |
| type niceFactory func(facade.Context) (interface{}, error) | |
| type nastyFactory func( | |
| st *state.State, | |
| resources facade.Resources, | |
| authorizer facade.Authorizer, | |
| ) ( | |
| interface{}, error, | |
| ) | |
| // validateNewFacade ensures that the facade factory we have has the right | |
| // input and output parameters for being used as a NewFoo function. | |
| func validateNewFacade(funcValue reflect.Value) (bool, error) { | |
| if !funcValue.IsValid() { | |
| return false, fmt.Errorf("cannot wrap nil") | |
| } | |
| if funcValue.Kind() != reflect.Func { | |
| return false, fmt.Errorf("wrong type %q is not a function", funcValue.Kind()) | |
| } | |
| funcType := funcValue.Type() | |
| funcName := runtime.FuncForPC(funcValue.Pointer()).Name() | |
| badSigError := errors.Errorf(""+ | |
| "function %q does not have the signature "+ | |
| "func (facade.Context) (*Type, error), or "+ | |
| "func (*state.State, facade.Resources, facade.Authorizer) (*Type, error)", funcName) | |
| if funcType.NumOut() != 2 { | |
| return false, errors.Trace(badSigError) | |
| } | |
| var ( | |
| facadeType reflect.Type | |
| nice bool | |
| ) | |
| inArgCount := funcType.NumIn() | |
| switch inArgCount { | |
| case 1: | |
| facadeType = reflect.TypeOf((*niceFactory)(nil)).Elem() | |
| nice = true | |
| case 3: | |
| facadeType = reflect.TypeOf((*nastyFactory)(nil)).Elem() | |
| default: | |
| return false, errors.Trace(badSigError) | |
| } | |
| isSame := true | |
| for i := 0; i < inArgCount; i++ { | |
| if funcType.In(i) != facadeType.In(i) { | |
| isSame = false | |
| break | |
| } | |
| } | |
| if funcType.Out(1) != facadeType.Out(1) { | |
| isSame = false | |
| } | |
| if !isSame { | |
| return false, errors.Trace(badSigError) | |
| } | |
| return nice, nil | |
| } | |
| // wrapNewFacade turns a given NewFoo(st, resources, authorizer) (*Instance, error) | |
| // function and wraps it into a proper facade.Factory function. | |
| func wrapNewFacade(newFunc interface{}) (facade.Factory, reflect.Type, error) { | |
| funcValue := reflect.ValueOf(newFunc) | |
| nice, err := validateNewFacade(funcValue) | |
| if err != nil { | |
| return nil, reflect.TypeOf(nil), err | |
| } | |
| var wrapped facade.Factory | |
| if nice { | |
| wrapped = func(context facade.Context) (facade.Facade, error) { | |
| if context.ID() != "" { | |
| return nil, ErrBadId | |
| } | |
| in := []reflect.Value{reflect.ValueOf(context)} | |
| out := funcValue.Call(in) | |
| if out[1].Interface() != nil { | |
| err := out[1].Interface().(error) | |
| return nil, err | |
| } | |
| return out[0].Interface(), nil | |
| } | |
| } else { | |
| // So we know newFunc is a func with the right args in and out, so | |
| // wrap it into a helper function that matches the facade.Factory. | |
| wrapped = func(context facade.Context) (facade.Facade, error) { | |
| if context.ID() != "" { | |
| return nil, ErrBadId | |
| } | |
| st := context.State() | |
| auth := context.Auth() | |
| resources := context.Resources() | |
| // st, resources, or auth is nil, then reflect.Call dies | |
| // because reflect.ValueOf(anynil) is the Zero Value. | |
| // So we use &obj.Elem() which gives us a concrete Value object | |
| // that can refer to nil. | |
| in := []reflect.Value{ | |
| reflect.ValueOf(&st).Elem(), | |
| reflect.ValueOf(&resources).Elem(), | |
| reflect.ValueOf(&auth).Elem(), | |
| } | |
| out := funcValue.Call(in) | |
| if out[1].Interface() != nil { | |
| err := out[1].Interface().(error) | |
| return nil, err | |
| } | |
| return out[0].Interface(), nil | |
| } | |
| } | |
| return wrapped, funcValue.Type().Out(0), nil | |
| } | |
| // NewHookContextFacadeFn specifies the function signature that can be | |
| // used to register a hook context facade. | |
| type NewHookContextFacadeFn func(*state.State, *state.Unit) (interface{}, error) | |
| // RegisterHookContextFacade registers facades for use within a hook | |
| // context. This function handles the translation from a | |
| // hook-context-facade to a standard facade so the caller's factory | |
| // method can elide unnecessary arguments. This function also handles | |
| // any necessary authorization for the client. | |
| // | |
| // XXX(fwereade): this is fundamentally broken, because it (1) | |
| // arbitrarily creates a new facade for a tiny fragment of a specific | |
| // client worker's reponsibilities and (2) actively conceals necessary | |
| // auth information from the facade. Don't call it; actively work to | |
| // delete code that uses it, and rewrite it properly. | |
| func RegisterHookContextFacade(name string, version int, newHookContextFacade NewHookContextFacadeFn, facadeType reflect.Type) { | |
| newFacade := func(context facade.Context) (facade.Facade, error) { | |
| authorizer := context.Auth() | |
| st := context.State() | |
| if !authorizer.AuthUnitAgent() { | |
| return nil, ErrPerm | |
| } | |
| // Verify that the unit's ID matches a unit that we know | |
| // about. | |
| tag := authorizer.GetAuthTag() | |
| if _, ok := tag.(names.UnitTag); !ok { | |
| return nil, errors.Errorf("expected names.UnitTag, got %T", tag) | |
| } | |
| unit, err := st.Unit(tag.Id()) | |
| if err != nil { | |
| return nil, errors.Trace(err) | |
| } | |
| return newHookContextFacade(st, unit) | |
| } | |
| RegisterFacade(name, version, newFacade, facadeType) | |
| } | |
| // RegisterStandardFacade registers a factory function for a normal New* style | |
| // function. This requires that the function has the form: | |
| // NewFoo(*state.State, facade.Resources, facade.Authorizer) (*Type, error) | |
| // With that syntax, we will create a helper function that wraps calling NewFoo | |
| // with the right parameters, and returns the *Type correctly. | |
| func RegisterStandardFacade(name string, version int, newFunc interface{}) { | |
| RegisterStandardFacadeForFeature(name, version, newFunc, "") | |
| } | |
| // RegisterStandardFacadeForFeature registers a factory function for a normal | |
| // New* style function. This requires that the function has the form: | |
| // NewFoo(*state.State, facade.Resources, facade.Authorizer) (*Type, error) | |
| // With that syntax, we will create a helper function that wraps calling | |
| // NewFoo with the right parameters, and returns the *Type correctly. If the | |
| // feature is non-empty, this facade is only available when the specified | |
| // feature flag is set. | |
| func RegisterStandardFacadeForFeature(name string, version int, newFunc interface{}, feature string) { | |
| wrapped, facadeType, err := wrapNewFacade(newFunc) | |
| if err != nil { | |
| panic(err) | |
| } | |
| RegisterFacadeForFeature(name, version, wrapped, facadeType, feature) | |
| } |