launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/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 "launchpad.net/errgo/errors" 8 "time" 9 10 "launchpad.net/tomb" 11 12 "launchpad.net/juju-core/rpc" 13 "launchpad.net/juju-core/state" 14 "launchpad.net/juju-core/state/apiserver/agent" 15 "launchpad.net/juju-core/state/apiserver/charmrevisionupdater" 16 "launchpad.net/juju-core/state/apiserver/client" 17 "launchpad.net/juju-core/state/apiserver/common" 18 "launchpad.net/juju-core/state/apiserver/deployer" 19 "launchpad.net/juju-core/state/apiserver/environment" 20 "launchpad.net/juju-core/state/apiserver/firewaller" 21 "launchpad.net/juju-core/state/apiserver/keymanager" 22 "launchpad.net/juju-core/state/apiserver/keyupdater" 23 loggerapi "launchpad.net/juju-core/state/apiserver/logger" 24 "launchpad.net/juju-core/state/apiserver/machine" 25 "launchpad.net/juju-core/state/apiserver/provisioner" 26 "launchpad.net/juju-core/state/apiserver/uniter" 27 "launchpad.net/juju-core/state/apiserver/upgrader" 28 "launchpad.net/juju-core/state/multiwatcher" 29 ) 30 31 type clientAPI struct{ *client.API } 32 33 type taggedAuthenticator interface { 34 state.Entity 35 state.Authenticator 36 } 37 38 // maxPingInterval defines the timeframe until the ping 39 // timeout closes the monitored connection. 40 // TODO(mue): Idea by Roger: Move to API (e.g. params) so 41 // that the pinging there may depend on the interval. 42 var maxPingInterval = 3 * time.Minute 43 44 // srvRoot represents a single client's connection to the state 45 // after it has logged in. 46 type srvRoot struct { 47 clientAPI 48 srv *Server 49 rpcConn *rpc.Conn 50 resources *common.Resources 51 pingTimeout *pingTimeout 52 53 entity taggedAuthenticator 54 } 55 56 // newSrvRoot creates the client's connection representation 57 // and starts a ping timeout for the monitoring of this 58 // connection. 59 func newSrvRoot(root *initialRoot, entity taggedAuthenticator) *srvRoot { 60 r := &srvRoot{ 61 srv: root.srv, 62 rpcConn: root.rpcConn, 63 resources: common.NewResources(), 64 entity: entity, 65 } 66 r.clientAPI.API = client.NewAPI(r.srv.state, r.resources, r, r.srv.dataDir) 67 return r 68 } 69 70 // Kill implements rpc.Killer. It cleans up any resources that need 71 // cleaning up to ensure that all outstanding requests return. 72 func (r *srvRoot) Kill() { 73 r.resources.StopAll() 74 if r.pingTimeout != nil { 75 r.pingTimeout.stop() 76 } 77 } 78 79 // requireAgent checks whether the current client is an agent and hence 80 // may access the agent APIs. We filter out non-agents when calling one 81 // of the accessor functions (Machine, Unit, etc) which avoids us making 82 // the check in every single request method. 83 func (r *srvRoot) requireAgent() error { 84 if !isAgent(r.entity) { 85 return common.ErrPerm 86 } 87 return nil 88 } 89 90 // requireClient returns an error unless the current 91 // client is a juju client user. 92 func (r *srvRoot) requireClient() error { 93 if isAgent(r.entity) { 94 return common.ErrPerm 95 } 96 return nil 97 } 98 99 // KeyManager returns an object that provides access to the KeyManager API 100 // facade. The id argument is reserved for future use and currently 101 // needs to be empty. 102 func (r *srvRoot) KeyManager(id string) (*keymanager.KeyManagerAPI, error) { 103 if id != "" { 104 return nil, common.ErrBadId 105 } 106 return keymanager.NewKeyManagerAPI(r.srv.state, r.resources, r) 107 } 108 109 // Machiner returns an object that provides access to the Machiner API 110 // facade. The id argument is reserved for future use and currently 111 // needs to be empty. 112 func (r *srvRoot) Machiner(id string) (*machine.MachinerAPI, error) { 113 if id != "" { 114 // Safeguard id for possible future use. 115 return nil, common.ErrBadId 116 } 117 return machine.NewMachinerAPI(r.srv.state, r.resources, r) 118 } 119 120 // Provisioner returns an object that provides access to the 121 // Provisioner API facade. The id argument is reserved for future use 122 // and currently needs to be empty. 123 func (r *srvRoot) Provisioner(id string) (*provisioner.ProvisionerAPI, error) { 124 if id != "" { 125 // Safeguard id for possible future use. 126 return nil, common.ErrBadId 127 } 128 return provisioner.NewProvisionerAPI(r.srv.state, r.resources, r) 129 } 130 131 // Uniter returns an object that provides access to the Uniter API 132 // facade. The id argument is reserved for future use and currently 133 // needs to be empty. 134 func (r *srvRoot) Uniter(id string) (*uniter.UniterAPI, error) { 135 if id != "" { 136 // Safeguard id for possible future use. 137 return nil, common.ErrBadId 138 } 139 return uniter.NewUniterAPI(r.srv.state, r.resources, r) 140 } 141 142 // Firewaller returns an object that provides access to the Firewaller 143 // API facade. The id argument is reserved for future use and 144 // currently needs to be empty. 145 func (r *srvRoot) Firewaller(id string) (*firewaller.FirewallerAPI, error) { 146 if id != "" { 147 // Safeguard id for possible future use. 148 return nil, common.ErrBadId 149 } 150 return firewaller.NewFirewallerAPI(r.srv.state, r.resources, r) 151 } 152 153 // Agent returns an object that provides access to the 154 // agent API. The id argument is reserved for future use and must currently 155 // be empty. 156 func (r *srvRoot) Agent(id string) (*agent.API, error) { 157 if id != "" { 158 return nil, common.ErrBadId 159 } 160 return agent.NewAPI(r.srv.state, r) 161 } 162 163 // Deployer returns an object that provides access to the Deployer API facade. 164 // The id argument is reserved for future use and must be empty. 165 func (r *srvRoot) Deployer(id string) (*deployer.DeployerAPI, error) { 166 if id != "" { 167 // TODO(dimitern): There is no direct test for this 168 return nil, common.ErrBadId 169 } 170 return deployer.NewDeployerAPI(r.srv.state, r.resources, r) 171 } 172 173 // Environment returns an object that provides access to the Environment API 174 // facade. The id argument is reserved for future use and currently needs to 175 // be empty. 176 func (r *srvRoot) Environment(id string) (*environment.EnvironmentAPI, error) { 177 if id != "" { 178 // Safeguard id for possible future use. 179 return nil, common.ErrBadId 180 } 181 return environment.NewEnvironmentAPI(r.srv.state, r.resources, r) 182 } 183 184 // Logger returns an object that provides access to the Logger API facade. 185 // The id argument is reserved for future use and must be empty. 186 func (r *srvRoot) Logger(id string) (*loggerapi.LoggerAPI, error) { 187 if id != "" { 188 // TODO: There is no direct test for this 189 return nil, common.ErrBadId 190 } 191 return loggerapi.NewLoggerAPI(r.srv.state, r.resources, r) 192 } 193 194 // Upgrader returns an object that provides access to the Upgrader API facade. 195 // The id argument is reserved for future use and must be empty. 196 func (r *srvRoot) Upgrader(id string) (*upgrader.UpgraderAPI, error) { 197 if id != "" { 198 // TODO: There is no direct test for this 199 return nil, common.ErrBadId 200 } 201 return upgrader.NewUpgraderAPI(r.srv.state, r.resources, r) 202 } 203 204 // KeyUpdater returns an object that provides access to the KeyUpdater API facade. 205 // The id argument is reserved for future use and must be empty. 206 func (r *srvRoot) KeyUpdater(id string) (*keyupdater.KeyUpdaterAPI, error) { 207 if id != "" { 208 // TODO: There is no direct test for this 209 return nil, common.ErrBadId 210 } 211 return keyupdater.NewKeyUpdaterAPI(r.srv.state, r.resources, r) 212 } 213 214 // CharmRevisionUpdater returns an object that provides access to the CharmRevisionUpdater API facade. 215 // The id argument is reserved for future use and must be empty. 216 func (r *srvRoot) CharmRevisionUpdater(id string) (*charmrevisionupdater.CharmRevisionUpdaterAPI, error) { 217 if id != "" { 218 // TODO: There is no direct test for this 219 return nil, common.ErrBadId 220 } 221 return charmrevisionupdater.NewCharmRevisionUpdaterAPI(r.srv.state, r.resources, r) 222 } 223 224 // NotifyWatcher returns an object that provides 225 // API access to methods on a state.NotifyWatcher. 226 // Each client has its own current set of watchers, stored 227 // in r.resources. 228 func (r *srvRoot) NotifyWatcher(id string) (*srvNotifyWatcher, error) { 229 if err := r.requireAgent(); err != nil { 230 return nil, mask(err) 231 } 232 watcher, ok := r.resources.Get(id).(state.NotifyWatcher) 233 if !ok { 234 return nil, common.ErrUnknownWatcher 235 } 236 return &srvNotifyWatcher{ 237 watcher: watcher, 238 id: id, 239 resources: r.resources, 240 }, nil 241 } 242 243 // StringsWatcher returns an object that provides API access to 244 // methods on a state.StringsWatcher. Each client has its own 245 // current set of watchers, stored in r.resources. 246 func (r *srvRoot) StringsWatcher(id string) (*srvStringsWatcher, error) { 247 if err := r.requireAgent(); err != nil { 248 return nil, mask(err) 249 } 250 watcher, ok := r.resources.Get(id).(state.StringsWatcher) 251 if !ok { 252 return nil, common.ErrUnknownWatcher 253 } 254 return &srvStringsWatcher{ 255 watcher: watcher, 256 id: id, 257 resources: r.resources, 258 }, nil 259 } 260 261 // RelationUnitsWatcher returns an object that provides API access to 262 // methods on a state.RelationUnitsWatcher. Each client has its own 263 // current set of watchers, stored in r.resources. 264 func (r *srvRoot) RelationUnitsWatcher(id string) (*srvRelationUnitsWatcher, error) { 265 if err := r.requireAgent(); err != nil { 266 return nil, mask(err) 267 } 268 watcher, ok := r.resources.Get(id).(state.RelationUnitsWatcher) 269 if !ok { 270 return nil, common.ErrUnknownWatcher 271 } 272 return &srvRelationUnitsWatcher{ 273 watcher: watcher, 274 id: id, 275 resources: r.resources, 276 }, nil 277 } 278 279 // AllWatcher returns an object that provides API access to methods on 280 // a state/multiwatcher.Watcher, which watches any changes to the 281 // state. Each client has its own current set of watchers, stored in 282 // r.resources. 283 func (r *srvRoot) AllWatcher(id string) (*srvClientAllWatcher, error) { 284 if err := r.requireClient(); err != nil { 285 return nil, mask(err) 286 } 287 watcher, ok := r.resources.Get(id).(*multiwatcher.Watcher) 288 if !ok { 289 return nil, common.ErrUnknownWatcher 290 } 291 return &srvClientAllWatcher{ 292 watcher: watcher, 293 id: id, 294 resources: r.resources, 295 }, nil 296 } 297 298 // Pinger returns an object that can be pinged 299 // by calling its Ping method. If this method 300 // is not called frequently enough, the connection 301 // will be dropped. 302 func (r *srvRoot) Pinger(id string) (pinger, error) { 303 if r.pingTimeout == nil { 304 return nullPinger{}, nil 305 } 306 return r.pingTimeout, nil 307 } 308 309 type nullPinger struct{} 310 311 func (nullPinger) Ping() {} 312 313 // AuthMachineAgent returns whether the current client is a machine agent. 314 func (r *srvRoot) AuthMachineAgent() bool { 315 _, ok := r.entity.(*state.Machine) 316 return ok 317 } 318 319 // AuthUnitAgent returns whether the current client is a unit agent. 320 func (r *srvRoot) AuthUnitAgent() bool { 321 _, ok := r.entity.(*state.Unit) 322 return ok 323 } 324 325 // AuthOwner returns whether the authenticated user's tag matches the 326 // given entity tag. 327 func (r *srvRoot) AuthOwner(tag string) bool { 328 return r.entity.Tag() == tag 329 } 330 331 // AuthEnvironManager returns whether the authenticated user is a 332 // machine with running the ManageEnviron job. 333 func (r *srvRoot) AuthEnvironManager() bool { 334 return isMachineWithJob(r.entity, state.JobManageEnviron) 335 } 336 337 // AuthClient returns whether the authenticated entity is a client 338 // user. 339 func (r *srvRoot) AuthClient() bool { 340 return !isAgent(r.entity) 341 } 342 343 // GetAuthTag returns the tag of the authenticated entity. 344 func (r *srvRoot) GetAuthTag() string { 345 return r.entity.Tag() 346 } 347 348 // GetAuthEntity returns the authenticated entity. 349 func (r *srvRoot) GetAuthEntity() state.Entity { 350 return r.entity 351 } 352 353 // pinger describes a type that can be pinged. 354 type pinger interface { 355 Ping() 356 } 357 358 // pingTimeout listens for pings and will call the 359 // passed action in case of a timeout. This way broken 360 // or inactive connections can be closed. 361 type pingTimeout struct { 362 tomb tomb.Tomb 363 action func() 364 timeout time.Duration 365 reset chan struct{} 366 } 367 368 // newPingTimeout returns a new pingTimeout instance 369 // that invokes the given action asynchronously if there 370 // is more than the given timeout interval between calls 371 // to its Ping method. 372 func newPingTimeout(action func(), timeout time.Duration) *pingTimeout { 373 pt := &pingTimeout{ 374 action: action, 375 timeout: timeout, 376 reset: make(chan struct{}), 377 } 378 go func() { 379 defer pt.tomb.Done() 380 pt.tomb.Kill(pt.loop()) 381 }() 382 return pt 383 } 384 385 // Ping is used by the client heartbeat monitor and resets 386 // the killer. 387 func (pt *pingTimeout) Ping() { 388 select { 389 case <-pt.tomb.Dying(): 390 case pt.reset <- struct{}{}: 391 } 392 } 393 394 // stop terminates the ping timeout. 395 func (pt *pingTimeout) stop() error { 396 pt.tomb.Kill(nil) 397 return pt.tomb.Wait() 398 } 399 400 // loop waits for a reset signal, otherwise it performs 401 // the initially passed action. 402 func (pt *pingTimeout) loop() error { 403 timer := time.NewTimer(pt.timeout) 404 defer timer.Stop() 405 for { 406 select { 407 case <-pt.tomb.Dying(): 408 return nil 409 case <-timer.C: 410 go pt.action() 411 return errors.New("ping timeout") 412 case <-pt.reset: 413 timer.Reset(pt.timeout) 414 } 415 } 416 }