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