func (r *rootSuite) TestFindMethodCachesFacadesWithId(c *gc.C) { srvRoot := apiserver.TestingAPIRoot(nil) defer common.Facades.Discard("my-counting-facade", 0) var count int64 // like newCounter, but also tracks the "id" that was requested for // this counter newIdCounter := func(context facade.Context) (facade.Facade, error) { count += 1 return &countingType{count: count, id: context.ID()}, nil } reflectType := reflect.TypeOf((*countingType)(nil)) common.RegisterFacade("my-counting-facade", 0, newIdCounter, reflectType) // The first time we call FindMethod, it should lookup a facade, and // request a new object. caller, err := srvRoot.FindMethod("my-counting-facade", 0, "Count") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "orig-id", "orig-id1") // However, if we place another call for a different Id, it should grab // a new object assertCallResult(c, caller, "alt-id", "alt-id2") // Asking for the original object gives us the cached value assertCallResult(c, caller, "orig-id", "orig-id1") // Asking for the original object gives us the cached value assertCallResult(c, caller, "alt-id", "alt-id2") // We get the same results asking for the other method caller, err = srvRoot.FindMethod("my-counting-facade", 0, "AltCount") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "orig-id", "ALT-orig-id1") assertCallResult(c, caller, "alt-id", "ALT-alt-id2") assertCallResult(c, caller, "third-id", "ALT-third-id3") }
func (r *rootSuite) TestFindMethodCachesFacades(c *gc.C) { srvRoot := apiserver.TestingAPIRoot(nil) defer common.Facades.Discard("my-counting-facade", 0) defer common.Facades.Discard("my-counting-facade", 1) var count int64 newCounter := func( *state.State, facade.Resources, facade.Authorizer, ) ( *countingType, error, ) { count += 1 return &countingType{count: count, id: ""}, nil } common.RegisterStandardFacade("my-counting-facade", 0, newCounter) common.RegisterStandardFacade("my-counting-facade", 1, newCounter) // The first time we call FindMethod, it should lookup a facade, and // request a new object. caller, err := srvRoot.FindMethod("my-counting-facade", 0, "Count") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "", "1") // The second time we ask for a method on the same facade, it should // reuse that object, rather than creating another instance caller, err = srvRoot.FindMethod("my-counting-facade", 0, "AltCount") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "", "ALT-1") // But when we ask for a different version, we should get a new // instance caller, err = srvRoot.FindMethod("my-counting-facade", 1, "Count") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "", "2") // But it, too, should be cached caller, err = srvRoot.FindMethod("my-counting-facade", 1, "AltCount") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "", "ALT-2") }
func (r *rootSuite) TestFindMethodUnknownFacade(c *gc.C) { root := apiserver.TestingAPIRoot(nil) caller, err := root.FindMethod("unknown-testing-facade", 0, "Method") c.Check(caller, gc.IsNil) c.Check(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Check(err, gc.ErrorMatches, `unknown object type "unknown-testing-facade"`) }
func (r *rootSuite) TestFindMethodUnknownVersion(c *gc.C) { srvRoot := apiserver.TestingAPIRoot(nil) defer common.Facades.Discard("my-testing-facade", 0) myGoodFacade := func( *state.State, facade.Resources, facade.Authorizer, ) ( *testingType, error, ) { return &testingType{}, nil } common.RegisterStandardFacade("my-testing-facade", 0, myGoodFacade) caller, err := srvRoot.FindMethod("my-testing-facade", 1, "Exposed") c.Check(caller, gc.IsNil) c.Check(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Check(err, gc.ErrorMatches, `unknown version \(1\) of interface "my-testing-facade"`) }
func (r *rootSuite) TestFindMethodEnsuresTypeMatch(c *gc.C) { srvRoot := apiserver.TestingAPIRoot(nil) defer common.Facades.Discard("my-testing-facade", 0) defer common.Facades.Discard("my-testing-facade", 1) defer common.Facades.Discard("my-testing-facade", 2) myBadFacade := func(facade.Context) (facade.Facade, error) { return &badType{}, nil } myGoodFacade := func(facade.Context) (facade.Facade, error) { return &testingType{}, nil } myErrFacade := func(context facade.Context) (facade.Facade, error) { return nil, fmt.Errorf("you shall not pass") } expectedType := reflect.TypeOf((*testingType)(nil)) common.RegisterFacade("my-testing-facade", 0, myBadFacade, expectedType) common.RegisterFacade("my-testing-facade", 1, myGoodFacade, expectedType) common.RegisterFacade("my-testing-facade", 2, myErrFacade, expectedType) // Now, myGoodFacade returns the right type, so calling it should work // fine caller, err := srvRoot.FindMethod("my-testing-facade", 1, "Exposed") c.Assert(err, jc.ErrorIsNil) _, err = caller.Call("", reflect.Value{}) c.Check(err, gc.ErrorMatches, "Exposed was bogus") // However, myBadFacade returns the wrong type, so trying to access it // should create an error caller, err = srvRoot.FindMethod("my-testing-facade", 0, "Exposed") c.Assert(err, jc.ErrorIsNil) _, err = caller.Call("", reflect.Value{}) c.Check(err, gc.ErrorMatches, `internal error, my-testing-facade\(0\) claimed to return \*apiserver_test.testingType but returned \*apiserver_test.badType`) // myErrFacade had the permissions change, so calling it returns an // error, but that shouldn't trigger the type checking code. caller, err = srvRoot.FindMethod("my-testing-facade", 2, "Exposed") c.Assert(err, jc.ErrorIsNil) res, err := caller.Call("", reflect.Value{}) c.Check(err, gc.ErrorMatches, `you shall not pass`) c.Check(res.IsValid(), jc.IsFalse) }
func (r *rootSuite) TestFindMethodHandlesInterfaceTypes(c *gc.C) { srvRoot := apiserver.TestingAPIRoot(nil) defer common.Facades.Discard("my-interface-facade", 0) defer common.Facades.Discard("my-interface-facade", 1) common.RegisterStandardFacade("my-interface-facade", 0, func( *state.State, facade.Resources, facade.Authorizer, ) ( smallInterface, error, ) { return &firstImpl{}, nil }) common.RegisterStandardFacade("my-interface-facade", 1, func( *state.State, facade.Resources, facade.Authorizer, ) ( smallInterface, error, ) { return &secondImpl{}, nil }) caller, err := srvRoot.FindMethod("my-interface-facade", 0, "OneMethod") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller, "", "first") caller2, err := srvRoot.FindMethod("my-interface-facade", 1, "OneMethod") c.Assert(err, jc.ErrorIsNil) assertCallResult(c, caller2, "", "second") // We should *not* be able to see AMethod or ZMethod caller, err = srvRoot.FindMethod("my-interface-facade", 1, "AMethod") c.Check(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Check(err, gc.ErrorMatches, `no such request - method my-interface-facade\(1\)\.AMethod is not implemented`) c.Check(caller, gc.IsNil) caller, err = srvRoot.FindMethod("my-interface-facade", 1, "ZMethod") c.Check(err, gc.FitsTypeOf, (*rpcreflect.CallNotImplementedError)(nil)) c.Check(err, gc.ErrorMatches, `no such request - method my-interface-facade\(1\)\.ZMethod is not implemented`) c.Check(caller, gc.IsNil) }
func (r *rootSuite) TestFindMethodCacheRaceSafe(c *gc.C) { srvRoot := apiserver.TestingAPIRoot(nil) defer common.Facades.Discard("my-counting-facade", 0) var count int64 newIdCounter := func(context facade.Context) (facade.Facade, error) { count += 1 return &countingType{count: count, id: context.ID()}, nil } reflectType := reflect.TypeOf((*countingType)(nil)) common.RegisterFacade("my-counting-facade", 0, newIdCounter, reflectType) caller, err := srvRoot.FindMethod("my-counting-facade", 0, "Count") c.Assert(err, jc.ErrorIsNil) // This is designed to trigger the race detector var wg sync.WaitGroup wg.Add(4) go func() { caller.Call("first", reflect.Value{}); wg.Done() }() go func() { caller.Call("second", reflect.Value{}); wg.Done() }() go func() { caller.Call("first", reflect.Value{}); wg.Done() }() go func() { caller.Call("second", reflect.Value{}); wg.Done() }() wg.Wait() // Once we're done, we should have only instantiated 2 different // objects. If we pass a different Id, we should be at 3 total count. assertCallResult(c, caller, "third", "third3") }