github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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  	"github.com/juju/names"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/state"
    13  )
    14  
    15  func init() {
    16  	common.RegisterStandardFacade("Action", 1, NewActionAPI)
    17  }
    18  
    19  // ActionAPI implements the client API for interacting with Actions
    20  type ActionAPI struct {
    21  	state      *state.State
    22  	resources  *common.Resources
    23  	authorizer common.Authorizer
    24  	check      *common.BlockChecker
    25  }
    26  
    27  // NewActionAPI returns an initialized ActionAPI
    28  func NewActionAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ActionAPI, error) {
    29  	if !authorizer.AuthClient() {
    30  		return nil, common.ErrPerm
    31  	}
    32  
    33  	return &ActionAPI{
    34  		state:      st,
    35  		resources:  resources,
    36  		authorizer: authorizer,
    37  		check:      common.NewBlockChecker(st),
    38  	}, nil
    39  }
    40  
    41  // Actions takes a list of ActionTags, and returns the full Action for
    42  // each ID.
    43  func (a *ActionAPI) Actions(arg params.Entities) (params.ActionResults, error) {
    44  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))}
    45  	for i, entity := range arg.Entities {
    46  		currentResult := &response.Results[i]
    47  		tag, err := names.ParseTag(entity.Tag)
    48  		if err != nil {
    49  			currentResult.Error = common.ServerError(common.ErrBadId)
    50  			continue
    51  		}
    52  		actionTag, ok := tag.(names.ActionTag)
    53  		if !ok {
    54  			currentResult.Error = common.ServerError(common.ErrBadId)
    55  			continue
    56  		}
    57  		action, err := a.state.ActionByTag(actionTag)
    58  		if err != nil {
    59  			currentResult.Error = common.ServerError(common.ErrBadId)
    60  			continue
    61  		}
    62  		receiverTag, err := names.ActionReceiverTag(action.Receiver())
    63  		if err != nil {
    64  			currentResult.Error = common.ServerError(err)
    65  			continue
    66  		}
    67  		response.Results[i] = common.MakeActionResult(receiverTag, action)
    68  	}
    69  	return response, nil
    70  }
    71  
    72  // FindActionTagsByPrefix takes a list of string prefixes and finds
    73  // corresponding ActionTags that match that prefix.
    74  func (a *ActionAPI) FindActionTagsByPrefix(arg params.FindTags) (params.FindTagsResults, error) {
    75  	response := params.FindTagsResults{Matches: make(map[string][]params.Entity)}
    76  	for _, prefix := range arg.Prefixes {
    77  		found := a.state.FindActionTagsByPrefix(prefix)
    78  		matches := make([]params.Entity, len(found))
    79  		for i, tag := range found {
    80  			matches[i] = params.Entity{Tag: tag.String()}
    81  		}
    82  		response.Matches[prefix] = matches
    83  	}
    84  	return response, nil
    85  }
    86  
    87  func (a *ActionAPI) FindActionsByNames(arg params.FindActionsByNames) (params.ActionsByNames, error) {
    88  	response := params.ActionsByNames{Actions: make([]params.ActionsByName, len(arg.ActionNames))}
    89  	for i, name := range arg.ActionNames {
    90  		currentResult := &response.Actions[i]
    91  		currentResult.Name = name
    92  
    93  		actions, err := a.state.FindActionsByName(name)
    94  		if err != nil {
    95  			currentResult.Error = common.ServerError(err)
    96  			continue
    97  		}
    98  		for _, action := range actions {
    99  			recvTag, err := names.ActionReceiverTag(action.Receiver())
   100  			if err != nil {
   101  				currentResult.Actions = append(currentResult.Actions, params.ActionResult{Error: common.ServerError(err)})
   102  				continue
   103  			}
   104  			currentAction := common.MakeActionResult(recvTag, action)
   105  			currentResult.Actions = append(currentResult.Actions, currentAction)
   106  		}
   107  	}
   108  	return response, nil
   109  }
   110  
   111  // Enqueue takes a list of Actions and queues them up to be executed by
   112  // the designated ActionReceiver, returning the params.Action for each
   113  // enqueued Action, or an error if there was a problem enqueueing the
   114  // Action.
   115  func (a *ActionAPI) Enqueue(arg params.Actions) (params.ActionResults, error) {
   116  	if err := a.check.ChangeAllowed(); err != nil {
   117  		return params.ActionResults{}, errors.Trace(err)
   118  	}
   119  
   120  	tagToActionReceiver := common.TagToActionReceiverFn(a.state.FindEntity)
   121  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Actions))}
   122  	for i, action := range arg.Actions {
   123  		currentResult := &response.Results[i]
   124  		receiver, err := tagToActionReceiver(action.Receiver)
   125  		if err != nil {
   126  			currentResult.Error = common.ServerError(err)
   127  			continue
   128  		}
   129  		enqueued, err := receiver.AddAction(action.Name, action.Parameters)
   130  		if err != nil {
   131  			currentResult.Error = common.ServerError(err)
   132  			continue
   133  		}
   134  
   135  		response.Results[i] = common.MakeActionResult(receiver.Tag(), enqueued)
   136  	}
   137  	return response, nil
   138  }
   139  
   140  // ListAll takes a list of Entities representing ActionReceivers and
   141  // returns all of the Actions that have been enqueued or run by each of
   142  // those Entities.
   143  func (a *ActionAPI) ListAll(arg params.Entities) (params.ActionsByReceivers, error) {
   144  	return a.internalList(arg, combine(pendingActions, runningActions, completedActions))
   145  }
   146  
   147  // ListPending takes a list of Entities representing ActionReceivers
   148  // and returns all of the Actions that are enqueued for each of those
   149  // Entities.
   150  func (a *ActionAPI) ListPending(arg params.Entities) (params.ActionsByReceivers, error) {
   151  	return a.internalList(arg, pendingActions)
   152  }
   153  
   154  // ListRunning takes a list of Entities representing ActionReceivers and
   155  // returns all of the Actions that have are running on each of those
   156  // Entities.
   157  func (a *ActionAPI) ListRunning(arg params.Entities) (params.ActionsByReceivers, error) {
   158  	return a.internalList(arg, runningActions)
   159  }
   160  
   161  // ListCompleted takes a list of Entities representing ActionReceivers
   162  // and returns all of the Actions that have been run on each of those
   163  // Entities.
   164  func (a *ActionAPI) ListCompleted(arg params.Entities) (params.ActionsByReceivers, error) {
   165  	return a.internalList(arg, completedActions)
   166  }
   167  
   168  // Cancel attempts to cancel enqueued Actions from running.
   169  func (a *ActionAPI) Cancel(arg params.Entities) (params.ActionResults, error) {
   170  	if err := a.check.ChangeAllowed(); err != nil {
   171  		return params.ActionResults{}, errors.Trace(err)
   172  	}
   173  
   174  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))}
   175  	for i, entity := range arg.Entities {
   176  		currentResult := &response.Results[i]
   177  		tag, err := names.ParseTag(entity.Tag)
   178  		if err != nil {
   179  			currentResult.Error = common.ServerError(common.ErrBadId)
   180  			continue
   181  		}
   182  		actionTag, ok := tag.(names.ActionTag)
   183  		if !ok {
   184  			currentResult.Error = common.ServerError(common.ErrBadId)
   185  			continue
   186  		}
   187  		action, err := a.state.ActionByTag(actionTag)
   188  		if err != nil {
   189  			currentResult.Error = common.ServerError(err)
   190  			continue
   191  		}
   192  		result, err := action.Finish(state.ActionResults{Status: state.ActionCancelled, Message: "action cancelled via the API"})
   193  		if err != nil {
   194  			currentResult.Error = common.ServerError(err)
   195  			continue
   196  		}
   197  		receiverTag, err := names.ActionReceiverTag(result.Receiver())
   198  		if err != nil {
   199  			currentResult.Error = common.ServerError(err)
   200  			continue
   201  		}
   202  
   203  		response.Results[i] = common.MakeActionResult(receiverTag, result)
   204  	}
   205  	return response, nil
   206  }
   207  
   208  // ServicesCharmActions returns a slice of charm Actions for a slice of
   209  // services.
   210  func (a *ActionAPI) ServicesCharmActions(args params.Entities) (params.ServicesCharmActionsResults, error) {
   211  	result := params.ServicesCharmActionsResults{Results: make([]params.ServiceCharmActionsResult, len(args.Entities))}
   212  	for i, entity := range args.Entities {
   213  		currentResult := &result.Results[i]
   214  		svcTag, err := names.ParseServiceTag(entity.Tag)
   215  		if err != nil {
   216  			currentResult.Error = common.ServerError(common.ErrBadId)
   217  			continue
   218  		}
   219  		currentResult.ServiceTag = svcTag.String()
   220  		svc, err := a.state.Service(svcTag.Id())
   221  		if err != nil {
   222  			currentResult.Error = common.ServerError(err)
   223  			continue
   224  		}
   225  		ch, _, err := svc.Charm()
   226  		if err != nil {
   227  			currentResult.Error = common.ServerError(err)
   228  			continue
   229  		}
   230  		currentResult.Actions = ch.Actions()
   231  	}
   232  	return result, nil
   233  }
   234  
   235  // internalList takes a list of Entities representing ActionReceivers
   236  // and returns all of the Actions the extractorFn can get out of the
   237  // ActionReceiver.
   238  func (a *ActionAPI) internalList(arg params.Entities, fn extractorFn) (params.ActionsByReceivers, error) {
   239  	tagToActionReceiver := common.TagToActionReceiverFn(a.state.FindEntity)
   240  	response := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(arg.Entities))}
   241  	for i, entity := range arg.Entities {
   242  		currentResult := &response.Actions[i]
   243  		receiver, err := tagToActionReceiver(entity.Tag)
   244  		if err != nil {
   245  			currentResult.Error = common.ServerError(common.ErrBadId)
   246  			continue
   247  		}
   248  		currentResult.Receiver = receiver.Tag().String()
   249  
   250  		results, err := fn(receiver)
   251  		if err != nil {
   252  			currentResult.Error = common.ServerError(err)
   253  			continue
   254  		}
   255  		currentResult.Actions = results
   256  	}
   257  	return response, nil
   258  }
   259  
   260  // extractorFn is the generic signature for functions that extract
   261  // state.Actions from an ActionReceiver, and return them as a slice of
   262  // params.ActionResult.
   263  type extractorFn func(state.ActionReceiver) ([]params.ActionResult, error)
   264  
   265  // combine takes multiple extractorFn's and combines them into one
   266  // function.
   267  func combine(funcs ...extractorFn) extractorFn {
   268  	return func(ar state.ActionReceiver) ([]params.ActionResult, error) {
   269  		result := []params.ActionResult{}
   270  		for _, fn := range funcs {
   271  			items, err := fn(ar)
   272  			if err != nil {
   273  				return result, err
   274  			}
   275  			result = append(result, items...)
   276  		}
   277  		return result, nil
   278  	}
   279  }
   280  
   281  // pendingActions iterates through the Actions() enqueued for an
   282  // ActionReceiver, and converts them to a slice of params.ActionResult.
   283  func pendingActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   284  	return common.ConvertActions(ar, ar.PendingActions)
   285  }
   286  
   287  // runningActions iterates through the Actions() running on an
   288  // ActionReceiver, and converts them to a slice of params.ActionResult.
   289  func runningActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   290  	return common.ConvertActions(ar, ar.RunningActions)
   291  }
   292  
   293  // completedActions iterates through the Actions() that have run to
   294  // completion for an ActionReceiver, and converts them to a slice of
   295  // params.ActionResult.
   296  func completedActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   297  	return common.ConvertActions(ar, ar.CompletedActions)
   298  }