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  }