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