github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/agent/agent.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // Package agent implements the API interfaces
     5  // used by the machine agent.
     6  
     7  package agent
     8  
     9  import (
    10  	"github.com/juju/errors"
    11  	"gopkg.in/juju/names.v2"
    12  
    13  	"github.com/juju/juju/apiserver/common"
    14  	"github.com/juju/juju/apiserver/common/cloudspec"
    15  	"github.com/juju/juju/apiserver/facade"
    16  	"github.com/juju/juju/apiserver/params"
    17  	"github.com/juju/juju/mongo"
    18  	"github.com/juju/juju/state"
    19  	"github.com/juju/juju/state/multiwatcher"
    20  	"github.com/juju/juju/state/stateenvirons"
    21  	"github.com/juju/juju/state/watcher"
    22  )
    23  
    24  func init() {
    25  	common.RegisterStandardFacade("Agent", 2, NewAgentAPIV2)
    26  }
    27  
    28  // AgentAPIV2 implements the version 2 of the API provided to an agent.
    29  type AgentAPIV2 struct {
    30  	*common.PasswordChanger
    31  	*common.RebootFlagClearer
    32  	*common.ModelWatcher
    33  	*common.ControllerConfigAPI
    34  	cloudspec.CloudSpecAPI
    35  
    36  	st        *state.State
    37  	auth      facade.Authorizer
    38  	resources facade.Resources
    39  }
    40  
    41  // NewAgentAPIV2 returns an object implementing version 2 of the Agent API
    42  // with the given authorizer representing the currently logged in client.
    43  func NewAgentAPIV2(st *state.State, resources facade.Resources, auth facade.Authorizer) (*AgentAPIV2, error) {
    44  	// Agents are defined to be any user that's not a client user.
    45  	if !auth.AuthMachineAgent() && !auth.AuthUnitAgent() {
    46  		return nil, common.ErrPerm
    47  	}
    48  	getCanChange := func() (common.AuthFunc, error) {
    49  		return auth.AuthOwner, nil
    50  	}
    51  	environConfigGetter := stateenvirons.EnvironConfigGetter{st}
    52  	return &AgentAPIV2{
    53  		PasswordChanger:     common.NewPasswordChanger(st, getCanChange),
    54  		RebootFlagClearer:   common.NewRebootFlagClearer(st, getCanChange),
    55  		ModelWatcher:        common.NewModelWatcher(st, resources, auth),
    56  		ControllerConfigAPI: common.NewControllerConfig(st),
    57  		CloudSpecAPI:        cloudspec.NewCloudSpec(environConfigGetter.CloudSpec, common.AuthFuncForTag(st.ModelTag())),
    58  		st:                  st,
    59  		auth:                auth,
    60  		resources:           resources,
    61  	}, nil
    62  }
    63  
    64  func (api *AgentAPIV2) GetEntities(args params.Entities) params.AgentGetEntitiesResults {
    65  	results := params.AgentGetEntitiesResults{
    66  		Entities: make([]params.AgentGetEntitiesResult, len(args.Entities)),
    67  	}
    68  	for i, entity := range args.Entities {
    69  		tag, err := names.ParseTag(entity.Tag)
    70  		if err != nil {
    71  			results.Entities[i].Error = common.ServerError(err)
    72  			continue
    73  		}
    74  		result, err := api.getEntity(tag)
    75  		result.Error = common.ServerError(err)
    76  		results.Entities[i] = result
    77  	}
    78  	return results
    79  }
    80  
    81  func (api *AgentAPIV2) getEntity(tag names.Tag) (result params.AgentGetEntitiesResult, err error) {
    82  	// Allow only for the owner agent.
    83  	// Note: having a bulk API call for this is utter madness, given that
    84  	// this check means we can only ever return a single object.
    85  	if !api.auth.AuthOwner(tag) {
    86  		err = common.ErrPerm
    87  		return
    88  	}
    89  	entity0, err := api.st.FindEntity(tag)
    90  	if err != nil {
    91  		return
    92  	}
    93  	entity, ok := entity0.(state.Lifer)
    94  	if !ok {
    95  		err = common.NotSupportedError(tag, "life cycles")
    96  		return
    97  	}
    98  	result.Life = params.Life(entity.Life().String())
    99  	if machine, ok := entity.(*state.Machine); ok {
   100  		result.Jobs = stateJobsToAPIParamsJobs(machine.Jobs())
   101  		result.ContainerType = machine.ContainerType()
   102  	}
   103  	return
   104  }
   105  
   106  func (api *AgentAPIV2) StateServingInfo() (result params.StateServingInfo, err error) {
   107  	if !api.auth.AuthModelManager() {
   108  		err = common.ErrPerm
   109  		return
   110  	}
   111  	info, err := api.st.StateServingInfo()
   112  	if err != nil {
   113  		return params.StateServingInfo{}, errors.Trace(err)
   114  	}
   115  	result = params.StateServingInfo{
   116  		APIPort:        info.APIPort,
   117  		StatePort:      info.StatePort,
   118  		Cert:           info.Cert,
   119  		PrivateKey:     info.PrivateKey,
   120  		CAPrivateKey:   info.CAPrivateKey,
   121  		SharedSecret:   info.SharedSecret,
   122  		SystemIdentity: info.SystemIdentity,
   123  	}
   124  
   125  	return result, nil
   126  }
   127  
   128  // MongoIsMaster is called by the IsMaster API call
   129  // instead of mongo.IsMaster. It exists so it can
   130  // be overridden by tests.
   131  var MongoIsMaster = mongo.IsMaster
   132  
   133  func (api *AgentAPIV2) IsMaster() (params.IsMasterResult, error) {
   134  	if !api.auth.AuthModelManager() {
   135  		return params.IsMasterResult{}, common.ErrPerm
   136  	}
   137  
   138  	switch tag := api.auth.GetAuthTag().(type) {
   139  	case names.MachineTag:
   140  		machine, err := api.st.Machine(tag.Id())
   141  		if err != nil {
   142  			return params.IsMasterResult{}, common.ErrPerm
   143  		}
   144  
   145  		session := api.st.MongoSession()
   146  		isMaster, err := MongoIsMaster(session, machine)
   147  		return params.IsMasterResult{Master: isMaster}, err
   148  	default:
   149  		return params.IsMasterResult{}, errors.Errorf("authenticated entity is not a Machine")
   150  	}
   151  }
   152  
   153  func stateJobsToAPIParamsJobs(jobs []state.MachineJob) []multiwatcher.MachineJob {
   154  	pjobs := make([]multiwatcher.MachineJob, len(jobs))
   155  	for i, job := range jobs {
   156  		pjobs[i] = multiwatcher.MachineJob(job.String())
   157  	}
   158  	return pjobs
   159  }
   160  
   161  // WatchCredentials watches for changes to the specified credentials.
   162  func (api *AgentAPIV2) WatchCredentials(args params.Entities) (params.NotifyWatchResults, error) {
   163  	if !api.auth.AuthModelManager() {
   164  		return params.NotifyWatchResults{}, common.ErrPerm
   165  	}
   166  
   167  	results := params.NotifyWatchResults{
   168  		Results: make([]params.NotifyWatchResult, len(args.Entities)),
   169  	}
   170  	for i, entity := range args.Entities {
   171  		credentialTag, err := names.ParseCloudCredentialTag(entity.Tag)
   172  		if err != nil {
   173  			results.Results[i].Error = common.ServerError(err)
   174  			continue
   175  		}
   176  		watch := api.st.WatchCredential(credentialTag)
   177  		// Consume the initial event. Technically, API calls to Watch
   178  		// 'transmit' the initial event in the Watch response. But
   179  		// NotifyWatchers have no state to transmit.
   180  		if _, ok := <-watch.Changes(); ok {
   181  			results.Results[i].NotifyWatcherId = api.resources.Register(watch)
   182  		} else {
   183  			err = watcher.EnsureErr(watch)
   184  			results.Results[i].Error = common.ServerError(err)
   185  		}
   186  	}
   187  	return results, nil
   188  }