github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/root.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver 5 6 import ( 7 "reflect" 8 "sync" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 14 "github.com/juju/juju/apiserver/common" 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/rpc" 17 "github.com/juju/juju/rpc/rpcreflect" 18 "github.com/juju/juju/state" 19 ) 20 21 var ( 22 // maxClientPingInterval defines the timeframe until the ping timeout 23 // closes the monitored connection. TODO(mue): Idea by Roger: 24 // Move to API (e.g. params) so that the pinging there may 25 // depend on the interval. 26 maxClientPingInterval = 3 * time.Minute 27 28 // mongoPingInterval defines the interval at which an API server 29 // will ping the mongo session to make sure that it's still 30 // alive. When the ping returns an error, the server will be 31 // terminated. 32 mongoPingInterval = 10 * time.Second 33 ) 34 35 type objectKey struct { 36 name string 37 version int 38 objId string 39 } 40 41 // apiHandler represents a single client's connection to the state 42 // after it has logged in. It contains an rpc.MethodFinder which it 43 // uses to dispatch Api calls appropriately. 44 type apiHandler struct { 45 state *state.State 46 rpcConn *rpc.Conn 47 resources *common.Resources 48 entity state.Entity 49 // An empty modelUUID means that the user has logged in through the 50 // root of the API server rather than the /model/:model-uuid/api 51 // path, logins processed with v2 or later will only offer the 52 // user manager and model manager api endpoints from here. 53 modelUUID string 54 } 55 56 var _ = (*apiHandler)(nil) 57 58 // newApiHandler returns a new apiHandler. 59 func newApiHandler(srv *Server, st *state.State, rpcConn *rpc.Conn, reqNotifier *requestNotifier, modelUUID string) (*apiHandler, error) { 60 r := &apiHandler{ 61 state: st, 62 resources: common.NewResources(), 63 rpcConn: rpcConn, 64 modelUUID: modelUUID, 65 } 66 if err := r.resources.RegisterNamed("machineID", common.StringResource(srv.tag.Id())); err != nil { 67 return nil, errors.Trace(err) 68 } 69 if err := r.resources.RegisterNamed("dataDir", common.StringResource(srv.dataDir)); err != nil { 70 return nil, errors.Trace(err) 71 } 72 if err := r.resources.RegisterNamed("logDir", common.StringResource(srv.logDir)); err != nil { 73 return nil, errors.Trace(err) 74 } 75 if err := r.resources.RegisterNamed("createLocalLoginMacaroon", common.ValueResource{ 76 srv.authCtxt.userAuth.CreateLocalLoginMacaroon, 77 }); err != nil { 78 return nil, errors.Trace(err) 79 } 80 return r, nil 81 } 82 83 func (r *apiHandler) getResources() *common.Resources { 84 return r.resources 85 } 86 87 func (r *apiHandler) getRpcConn() *rpc.Conn { 88 return r.rpcConn 89 } 90 91 // Kill implements rpc.Killer, cleaning up any resources that need 92 // cleaning up to ensure that all outstanding requests return. 93 func (r *apiHandler) Kill() { 94 r.resources.StopAll() 95 } 96 97 // srvCaller is our implementation of the rpcreflect.MethodCaller interface. 98 // It lives just long enough to encapsulate the methods that should be 99 // available for an RPC call and allow the RPC code to instantiate an object 100 // and place a call on its method. 101 type srvCaller struct { 102 objMethod rpcreflect.ObjMethod 103 goType reflect.Type 104 creator func(id string) (reflect.Value, error) 105 } 106 107 // ParamsType defines the parameters that should be supplied to this function. 108 // See rpcreflect.MethodCaller for more detail. 109 func (s *srvCaller) ParamsType() reflect.Type { 110 return s.objMethod.Params 111 } 112 113 // ReturnType defines the object that is returned from the function.` 114 // See rpcreflect.MethodCaller for more detail. 115 func (s *srvCaller) ResultType() reflect.Type { 116 return s.objMethod.Result 117 } 118 119 // Call takes the object Id and an instance of ParamsType to create an object and place 120 // a call on its method. It then returns an instance of ResultType. 121 func (s *srvCaller) Call(objId string, arg reflect.Value) (reflect.Value, error) { 122 objVal, err := s.creator(objId) 123 if err != nil { 124 return reflect.Value{}, err 125 } 126 return s.objMethod.Call(objVal, arg) 127 } 128 129 // apiRoot implements basic method dispatching to the facade registry. 130 type apiRoot struct { 131 state *state.State 132 resources *common.Resources 133 authorizer common.Authorizer 134 objectMutex sync.RWMutex 135 objectCache map[objectKey]reflect.Value 136 } 137 138 // newApiRoot returns a new apiRoot. 139 func newApiRoot(st *state.State, resources *common.Resources, authorizer common.Authorizer) *apiRoot { 140 r := &apiRoot{ 141 state: st, 142 resources: resources, 143 authorizer: authorizer, 144 objectCache: make(map[objectKey]reflect.Value), 145 } 146 return r 147 } 148 149 // Kill implements rpc.Killer, stopping the root's resources. 150 func (r *apiRoot) Kill() { 151 r.resources.StopAll() 152 } 153 154 // FindMethod looks up the given rootName and version in our facade registry 155 // and returns a MethodCaller that will be used by the RPC code to place calls on 156 // that facade. 157 // FindMethod uses the global registry apiserver/common.Facades. 158 // For more information about how FindMethod should work, see rpc/server.go and 159 // rpc/rpcreflect/value.go 160 func (r *apiRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { 161 goType, objMethod, err := lookupMethod(rootName, version, methodName) 162 if err != nil { 163 return nil, err 164 } 165 166 creator := func(id string) (reflect.Value, error) { 167 objKey := objectKey{name: rootName, version: version, objId: id} 168 r.objectMutex.RLock() 169 objValue, ok := r.objectCache[objKey] 170 r.objectMutex.RUnlock() 171 if ok { 172 return objValue, nil 173 } 174 r.objectMutex.Lock() 175 defer r.objectMutex.Unlock() 176 if objValue, ok := r.objectCache[objKey]; ok { 177 return objValue, nil 178 } 179 // Now that we have the write lock, check one more time in case 180 // someone got the write lock before us. 181 factory, err := common.Facades.GetFactory(rootName, version) 182 if err != nil { 183 // We don't check for IsNotFound here, because it 184 // should have already been handled in the GetType 185 // check. 186 return reflect.Value{}, err 187 } 188 obj, err := factory(r.state, r.resources, r.authorizer, id) 189 if err != nil { 190 return reflect.Value{}, err 191 } 192 objValue = reflect.ValueOf(obj) 193 if !objValue.Type().AssignableTo(goType) { 194 return reflect.Value{}, errors.Errorf( 195 "internal error, %s(%d) claimed to return %s but returned %T", 196 rootName, version, goType, obj) 197 } 198 if goType.Kind() == reflect.Interface { 199 // If the original function wanted to return an 200 // interface type, the indirection in the factory via 201 // an interface{} strips the original interface 202 // information off. So here we have to create the 203 // interface again, and assign it. 204 asInterface := reflect.New(goType).Elem() 205 asInterface.Set(objValue) 206 objValue = asInterface 207 } 208 r.objectCache[objKey] = objValue 209 return objValue, nil 210 } 211 return &srvCaller{ 212 creator: creator, 213 objMethod: objMethod, 214 }, nil 215 } 216 217 func lookupMethod(rootName string, version int, methodName string) (reflect.Type, rpcreflect.ObjMethod, error) { 218 noMethod := rpcreflect.ObjMethod{} 219 goType, err := common.Facades.GetType(rootName, version) 220 if err != nil { 221 if errors.IsNotFound(err) { 222 return nil, noMethod, &rpcreflect.CallNotImplementedError{ 223 RootMethod: rootName, 224 Version: version, 225 } 226 } 227 return nil, noMethod, err 228 } 229 rpcType := rpcreflect.ObjTypeOf(goType) 230 objMethod, err := rpcType.Method(methodName) 231 if err != nil { 232 if err == rpcreflect.ErrMethodNotFound { 233 return nil, noMethod, &rpcreflect.CallNotImplementedError{ 234 RootMethod: rootName, 235 Version: version, 236 Method: methodName, 237 } 238 } 239 return nil, noMethod, err 240 } 241 return goType, objMethod, nil 242 } 243 244 // AnonRoot dispatches API calls to those available to an anonymous connection 245 // which has not logged in. 246 type anonRoot struct { 247 *apiHandler 248 adminApis map[int]interface{} 249 } 250 251 // NewAnonRoot creates a new AnonRoot which dispatches to the given Admin API implementation. 252 func newAnonRoot(h *apiHandler, adminApis map[int]interface{}) *anonRoot { 253 r := &anonRoot{ 254 apiHandler: h, 255 adminApis: adminApis, 256 } 257 return r 258 } 259 260 func (r *anonRoot) FindMethod(rootName string, version int, methodName string) (rpcreflect.MethodCaller, error) { 261 if rootName != "Admin" { 262 return nil, &rpcreflect.CallNotImplementedError{ 263 RootMethod: rootName, 264 Version: version, 265 } 266 } 267 if api, ok := r.adminApis[version]; ok { 268 return rpcreflect.ValueOf(reflect.ValueOf(api)).FindMethod(rootName, 0, methodName) 269 } 270 return nil, &rpc.RequestError{ 271 Code: params.CodeNotSupported, 272 Message: "this version of Juju does not support login from old clients", 273 } 274 } 275 276 // AuthMachineAgent returns whether the current client is a machine agent. 277 func (r *apiHandler) AuthMachineAgent() bool { 278 _, isMachine := r.GetAuthTag().(names.MachineTag) 279 return isMachine 280 } 281 282 // AuthUnitAgent returns whether the current client is a unit agent. 283 func (r *apiHandler) AuthUnitAgent() bool { 284 _, isUnit := r.GetAuthTag().(names.UnitTag) 285 return isUnit 286 } 287 288 // AuthOwner returns whether the authenticated user's tag matches the 289 // given entity tag. 290 func (r *apiHandler) AuthOwner(tag names.Tag) bool { 291 return r.entity.Tag() == tag 292 } 293 294 // AuthModelManager returns whether the authenticated user is a 295 // machine with running the ManageEnviron job. 296 func (r *apiHandler) AuthModelManager() bool { 297 return isMachineWithJob(r.entity, state.JobManageModel) 298 } 299 300 // AuthClient returns whether the authenticated entity is a client 301 // user. 302 func (r *apiHandler) AuthClient() bool { 303 _, isUser := r.GetAuthTag().(names.UserTag) 304 return isUser 305 } 306 307 // GetAuthTag returns the tag of the authenticated entity. 308 func (r *apiHandler) GetAuthTag() names.Tag { 309 return r.entity.Tag() 310 } 311 312 // GetAuthEntity returns the authenticated entity. 313 func (r *apiHandler) GetAuthEntity() state.Entity { 314 return r.entity 315 } 316 317 // DescribeFacades returns the list of available Facades and their Versions 318 func DescribeFacades() []params.FacadeVersions { 319 facades := common.Facades.List() 320 result := make([]params.FacadeVersions, len(facades)) 321 for i, facade := range facades { 322 result[i].Name = facade.Name 323 result[i].Versions = facade.Versions 324 } 325 return result 326 }