github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/action/action.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package action
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/facade"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/permission"
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  func init() {
    18  	common.RegisterStandardFacade("Action", 2, NewActionAPI)
    19  }
    20  
    21  // ActionAPI implements the client API for interacting with Actions
    22  type ActionAPI struct {
    23  	state      *state.State
    24  	resources  facade.Resources
    25  	authorizer facade.Authorizer
    26  	check      *common.BlockChecker
    27  }
    28  
    29  // NewActionAPI returns an initialized ActionAPI
    30  func NewActionAPI(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ActionAPI, error) {
    31  	if !authorizer.AuthClient() {
    32  		return nil, common.ErrPerm
    33  	}
    34  
    35  	return &ActionAPI{
    36  		state:      st,
    37  		resources:  resources,
    38  		authorizer: authorizer,
    39  		check:      common.NewBlockChecker(st),
    40  	}, nil
    41  }
    42  
    43  func (a *ActionAPI) checkCanRead() error {
    44  	canRead, err := a.authorizer.HasPermission(permission.ReadAccess, a.state.ModelTag())
    45  	if err != nil {
    46  		return errors.Trace(err)
    47  	}
    48  	if !canRead {
    49  		return common.ErrPerm
    50  	}
    51  	return nil
    52  }
    53  
    54  func (a *ActionAPI) checkCanWrite() error {
    55  	canWrite, err := a.authorizer.HasPermission(permission.WriteAccess, a.state.ModelTag())
    56  	if err != nil {
    57  		return errors.Trace(err)
    58  	}
    59  	if !canWrite {
    60  		return common.ErrPerm
    61  	}
    62  	return nil
    63  }
    64  
    65  func (a *ActionAPI) checkCanAdmin() error {
    66  	canAdmin, err := a.authorizer.HasPermission(permission.AdminAccess, a.state.ModelTag())
    67  	if err != nil {
    68  		return errors.Trace(err)
    69  	}
    70  	if !canAdmin {
    71  		return common.ErrPerm
    72  	}
    73  	return nil
    74  }
    75  
    76  // Actions takes a list of ActionTags, and returns the full Action for
    77  // each ID.
    78  func (a *ActionAPI) Actions(arg params.Entities) (params.ActionResults, error) {
    79  	if err := a.checkCanRead(); err != nil {
    80  		return params.ActionResults{}, errors.Trace(err)
    81  	}
    82  
    83  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))}
    84  	for i, entity := range arg.Entities {
    85  		currentResult := &response.Results[i]
    86  		tag, err := names.ParseTag(entity.Tag)
    87  		if err != nil {
    88  			currentResult.Error = common.ServerError(common.ErrBadId)
    89  			continue
    90  		}
    91  		actionTag, ok := tag.(names.ActionTag)
    92  		if !ok {
    93  			currentResult.Error = common.ServerError(common.ErrBadId)
    94  			continue
    95  		}
    96  		action, err := a.state.ActionByTag(actionTag)
    97  		if err != nil {
    98  			currentResult.Error = common.ServerError(common.ErrBadId)
    99  			continue
   100  		}
   101  		receiverTag, err := names.ActionReceiverTag(action.Receiver())
   102  		if err != nil {
   103  			currentResult.Error = common.ServerError(err)
   104  			continue
   105  		}
   106  		response.Results[i] = common.MakeActionResult(receiverTag, action)
   107  	}
   108  	return response, nil
   109  }
   110  
   111  // FindActionTagsByPrefix takes a list of string prefixes and finds
   112  // corresponding ActionTags that match that prefix.
   113  func (a *ActionAPI) FindActionTagsByPrefix(arg params.FindTags) (params.FindTagsResults, error) {
   114  	if err := a.checkCanRead(); err != nil {
   115  		return params.FindTagsResults{}, errors.Trace(err)
   116  	}
   117  
   118  	response := params.FindTagsResults{Matches: make(map[string][]params.Entity)}
   119  	for _, prefix := range arg.Prefixes {
   120  		found := a.state.FindActionTagsByPrefix(prefix)
   121  		matches := make([]params.Entity, len(found))
   122  		for i, tag := range found {
   123  			matches[i] = params.Entity{Tag: tag.String()}
   124  		}
   125  		response.Matches[prefix] = matches
   126  	}
   127  	return response, nil
   128  }
   129  
   130  func (a *ActionAPI) FindActionsByNames(arg params.FindActionsByNames) (params.ActionsByNames, error) {
   131  	if err := a.checkCanWrite(); err != nil {
   132  		return params.ActionsByNames{}, errors.Trace(err)
   133  	}
   134  
   135  	response := params.ActionsByNames{Actions: make([]params.ActionsByName, len(arg.ActionNames))}
   136  	for i, name := range arg.ActionNames {
   137  		currentResult := &response.Actions[i]
   138  		currentResult.Name = name
   139  
   140  		actions, err := a.state.FindActionsByName(name)
   141  		if err != nil {
   142  			currentResult.Error = common.ServerError(err)
   143  			continue
   144  		}
   145  		for _, action := range actions {
   146  			recvTag, err := names.ActionReceiverTag(action.Receiver())
   147  			if err != nil {
   148  				currentResult.Actions = append(currentResult.Actions, params.ActionResult{Error: common.ServerError(err)})
   149  				continue
   150  			}
   151  			currentAction := common.MakeActionResult(recvTag, action)
   152  			currentResult.Actions = append(currentResult.Actions, currentAction)
   153  		}
   154  	}
   155  	return response, nil
   156  }
   157  
   158  // Enqueue takes a list of Actions and queues them up to be executed by
   159  // the designated ActionReceiver, returning the params.Action for each
   160  // enqueued Action, or an error if there was a problem enqueueing the
   161  // Action.
   162  func (a *ActionAPI) Enqueue(arg params.Actions) (params.ActionResults, error) {
   163  	if err := a.checkCanWrite(); err != nil {
   164  		return params.ActionResults{}, errors.Trace(err)
   165  	}
   166  
   167  	if err := a.check.ChangeAllowed(); err != nil {
   168  		return params.ActionResults{}, errors.Trace(err)
   169  	}
   170  
   171  	tagToActionReceiver := common.TagToActionReceiverFn(a.state.FindEntity)
   172  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Actions))}
   173  	for i, action := range arg.Actions {
   174  		currentResult := &response.Results[i]
   175  		receiver, err := tagToActionReceiver(action.Receiver)
   176  		if err != nil {
   177  			currentResult.Error = common.ServerError(err)
   178  			continue
   179  		}
   180  		enqueued, err := receiver.AddAction(action.Name, action.Parameters)
   181  		if err != nil {
   182  			currentResult.Error = common.ServerError(err)
   183  			continue
   184  		}
   185  
   186  		response.Results[i] = common.MakeActionResult(receiver.Tag(), enqueued)
   187  	}
   188  	return response, nil
   189  }
   190  
   191  // ListAll takes a list of Entities representing ActionReceivers and
   192  // returns all of the Actions that have been enqueued or run by each of
   193  // those Entities.
   194  func (a *ActionAPI) ListAll(arg params.Entities) (params.ActionsByReceivers, error) {
   195  	if err := a.checkCanRead(); err != nil {
   196  		return params.ActionsByReceivers{}, errors.Trace(err)
   197  	}
   198  
   199  	return a.internalList(arg, combine(pendingActions, runningActions, completedActions))
   200  }
   201  
   202  // ListPending takes a list of Entities representing ActionReceivers
   203  // and returns all of the Actions that are enqueued for each of those
   204  // Entities.
   205  func (a *ActionAPI) ListPending(arg params.Entities) (params.ActionsByReceivers, error) {
   206  	if err := a.checkCanRead(); err != nil {
   207  		return params.ActionsByReceivers{}, errors.Trace(err)
   208  	}
   209  
   210  	return a.internalList(arg, pendingActions)
   211  }
   212  
   213  // ListRunning takes a list of Entities representing ActionReceivers and
   214  // returns all of the Actions that have are running on each of those
   215  // Entities.
   216  func (a *ActionAPI) ListRunning(arg params.Entities) (params.ActionsByReceivers, error) {
   217  	if err := a.checkCanRead(); err != nil {
   218  		return params.ActionsByReceivers{}, errors.Trace(err)
   219  	}
   220  
   221  	return a.internalList(arg, runningActions)
   222  }
   223  
   224  // ListCompleted takes a list of Entities representing ActionReceivers
   225  // and returns all of the Actions that have been run on each of those
   226  // Entities.
   227  func (a *ActionAPI) ListCompleted(arg params.Entities) (params.ActionsByReceivers, error) {
   228  	if err := a.checkCanRead(); err != nil {
   229  		return params.ActionsByReceivers{}, errors.Trace(err)
   230  	}
   231  
   232  	return a.internalList(arg, completedActions)
   233  }
   234  
   235  // Cancel attempts to cancel enqueued Actions from running.
   236  func (a *ActionAPI) Cancel(arg params.Entities) (params.ActionResults, error) {
   237  	if err := a.checkCanWrite(); err != nil {
   238  		return params.ActionResults{}, errors.Trace(err)
   239  	}
   240  
   241  	if err := a.check.ChangeAllowed(); err != nil {
   242  		return params.ActionResults{}, errors.Trace(err)
   243  	}
   244  
   245  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))}
   246  	for i, entity := range arg.Entities {
   247  		currentResult := &response.Results[i]
   248  		tag, err := names.ParseTag(entity.Tag)
   249  		if err != nil {
   250  			currentResult.Error = common.ServerError(common.ErrBadId)
   251  			continue
   252  		}
   253  		actionTag, ok := tag.(names.ActionTag)
   254  		if !ok {
   255  			currentResult.Error = common.ServerError(common.ErrBadId)
   256  			continue
   257  		}
   258  		action, err := a.state.ActionByTag(actionTag)
   259  		if err != nil {
   260  			currentResult.Error = common.ServerError(err)
   261  			continue
   262  		}
   263  		result, err := action.Finish(state.ActionResults{Status: state.ActionCancelled, Message: "action cancelled via the API"})
   264  		if err != nil {
   265  			currentResult.Error = common.ServerError(err)
   266  			continue
   267  		}
   268  		receiverTag, err := names.ActionReceiverTag(result.Receiver())
   269  		if err != nil {
   270  			currentResult.Error = common.ServerError(err)
   271  			continue
   272  		}
   273  
   274  		response.Results[i] = common.MakeActionResult(receiverTag, result)
   275  	}
   276  	return response, nil
   277  }
   278  
   279  // ApplicationsCharmsActions returns a slice of charm Actions for a slice of
   280  // services.
   281  func (a *ActionAPI) ApplicationsCharmsActions(args params.Entities) (params.ApplicationsCharmActionsResults, error) {
   282  	result := params.ApplicationsCharmActionsResults{Results: make([]params.ApplicationCharmActionsResult, len(args.Entities))}
   283  	if err := a.checkCanWrite(); err != nil {
   284  		return result, errors.Trace(err)
   285  	}
   286  
   287  	for i, entity := range args.Entities {
   288  		currentResult := &result.Results[i]
   289  		svcTag, err := names.ParseApplicationTag(entity.Tag)
   290  		if err != nil {
   291  			currentResult.Error = common.ServerError(common.ErrBadId)
   292  			continue
   293  		}
   294  		currentResult.ApplicationTag = svcTag.String()
   295  		svc, err := a.state.Application(svcTag.Id())
   296  		if err != nil {
   297  			currentResult.Error = common.ServerError(err)
   298  			continue
   299  		}
   300  		ch, _, err := svc.Charm()
   301  		if err != nil {
   302  			currentResult.Error = common.ServerError(err)
   303  			continue
   304  		}
   305  		if actions := ch.Actions(); actions != nil {
   306  			charmActions := make(map[string]params.ActionSpec)
   307  			for key, value := range actions.ActionSpecs {
   308  				charmActions[key] = params.ActionSpec{
   309  					Description: value.Description,
   310  					Params:      value.Params,
   311  				}
   312  			}
   313  			currentResult.Actions = charmActions
   314  		}
   315  	}
   316  	return result, nil
   317  }
   318  
   319  // internalList takes a list of Entities representing ActionReceivers
   320  // and returns all of the Actions the extractorFn can get out of the
   321  // ActionReceiver.
   322  func (a *ActionAPI) internalList(arg params.Entities, fn extractorFn) (params.ActionsByReceivers, error) {
   323  	tagToActionReceiver := common.TagToActionReceiverFn(a.state.FindEntity)
   324  	response := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(arg.Entities))}
   325  	for i, entity := range arg.Entities {
   326  		currentResult := &response.Actions[i]
   327  		receiver, err := tagToActionReceiver(entity.Tag)
   328  		if err != nil {
   329  			currentResult.Error = common.ServerError(common.ErrBadId)
   330  			continue
   331  		}
   332  		currentResult.Receiver = receiver.Tag().String()
   333  
   334  		results, err := fn(receiver)
   335  		if err != nil {
   336  			currentResult.Error = common.ServerError(err)
   337  			continue
   338  		}
   339  		currentResult.Actions = results
   340  	}
   341  	return response, nil
   342  }
   343  
   344  // extractorFn is the generic signature for functions that extract
   345  // state.Actions from an ActionReceiver, and return them as a slice of
   346  // params.ActionResult.
   347  type extractorFn func(state.ActionReceiver) ([]params.ActionResult, error)
   348  
   349  // combine takes multiple extractorFn's and combines them into one
   350  // function.
   351  func combine(funcs ...extractorFn) extractorFn {
   352  	return func(ar state.ActionReceiver) ([]params.ActionResult, error) {
   353  		result := []params.ActionResult{}
   354  		for _, fn := range funcs {
   355  			items, err := fn(ar)
   356  			if err != nil {
   357  				return result, errors.Trace(err)
   358  			}
   359  			result = append(result, items...)
   360  		}
   361  		return result, nil
   362  	}
   363  }
   364  
   365  // pendingActions iterates through the Actions() enqueued for an
   366  // ActionReceiver, and converts them to a slice of params.ActionResult.
   367  func pendingActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   368  	return common.ConvertActions(ar, ar.PendingActions)
   369  }
   370  
   371  // runningActions iterates through the Actions() running on an
   372  // ActionReceiver, and converts them to a slice of params.ActionResult.
   373  func runningActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   374  	return common.ConvertActions(ar, ar.RunningActions)
   375  }
   376  
   377  // completedActions iterates through the Actions() that have run to
   378  // completion for an ActionReceiver, and converts them to a slice of
   379  // params.ActionResult.
   380  func completedActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   381  	return common.ConvertActions(ar, ar.CompletedActions)
   382  }