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  }