github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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/loggo"
     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  var logger = loggo.GetLogger("juju.apiserver.action")
    16  
    17  func init() {
    18  	common.RegisterStandardFacade("Action", 0, NewActionAPI)
    19  }
    20  
    21  // ActionAPI implements the client API for interacting with Actions
    22  type ActionAPI struct {
    23  	state      *state.State
    24  	resources  *common.Resources
    25  	authorizer common.Authorizer
    26  }
    27  
    28  // NewActionAPI returns an initialized ActionAPI
    29  func NewActionAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*ActionAPI, error) {
    30  	if !authorizer.AuthClient() {
    31  		return nil, common.ErrPerm
    32  	}
    33  
    34  	return &ActionAPI{
    35  		state:      st,
    36  		resources:  resources,
    37  		authorizer: authorizer,
    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] = 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  // Enqueue takes a list of Actions and queues them up to be executed by
    88  // the designated ActionReceiver, returning the params.Action for each
    89  // enqueued Action, or an error if there was a problem enqueueing the
    90  // Action.
    91  func (a *ActionAPI) Enqueue(arg params.Actions) (params.ActionResults, error) {
    92  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Actions))}
    93  	for i, action := range arg.Actions {
    94  		currentResult := &response.Results[i]
    95  		receiver, err := tagToActionReceiver(a.state, action.Receiver)
    96  		if err != nil {
    97  			currentResult.Error = common.ServerError(err)
    98  			continue
    99  		}
   100  		enqueued, err := receiver.AddAction(action.Name, action.Parameters)
   101  		if err != nil {
   102  			currentResult.Error = common.ServerError(err)
   103  			continue
   104  		}
   105  
   106  		response.Results[i] = makeActionResult(receiver.Tag(), enqueued)
   107  	}
   108  	return response, nil
   109  }
   110  
   111  // ListAll takes a list of Entities representing ActionReceivers and
   112  // returns all of the Actions that have been enqueued or run by each of
   113  // those Entities.
   114  func (a *ActionAPI) ListAll(arg params.Entities) (params.ActionsByReceivers, error) {
   115  	return a.internalList(arg, combine(pendingActions, runningActions, completedActions))
   116  }
   117  
   118  // ListPending takes a list of Entities representing ActionReceivers
   119  // and returns all of the Actions that are enqueued for each of those
   120  // Entities.
   121  func (a *ActionAPI) ListPending(arg params.Entities) (params.ActionsByReceivers, error) {
   122  	return a.internalList(arg, pendingActions)
   123  }
   124  
   125  // ListRunning takes a list of Entities representing ActionReceivers and
   126  // returns all of the Actions that have are running on each of those
   127  // Entities.
   128  func (a *ActionAPI) ListRunning(arg params.Entities) (params.ActionsByReceivers, error) {
   129  	return a.internalList(arg, runningActions)
   130  }
   131  
   132  // ListCompleted takes a list of Entities representing ActionReceivers
   133  // and returns all of the Actions that have been run on each of those
   134  // Entities.
   135  func (a *ActionAPI) ListCompleted(arg params.Entities) (params.ActionsByReceivers, error) {
   136  	return a.internalList(arg, completedActions)
   137  }
   138  
   139  // Cancel attempts to cancel enqueued Actions from running.
   140  func (a *ActionAPI) Cancel(arg params.Entities) (params.ActionResults, error) {
   141  	response := params.ActionResults{Results: make([]params.ActionResult, len(arg.Entities))}
   142  	for i, entity := range arg.Entities {
   143  		currentResult := &response.Results[i]
   144  		tag, err := names.ParseTag(entity.Tag)
   145  		if err != nil {
   146  			currentResult.Error = common.ServerError(common.ErrBadId)
   147  			continue
   148  		}
   149  		actionTag, ok := tag.(names.ActionTag)
   150  		if !ok {
   151  			currentResult.Error = common.ServerError(common.ErrBadId)
   152  			continue
   153  		}
   154  		action, err := a.state.ActionByTag(actionTag)
   155  		if err != nil {
   156  			currentResult.Error = common.ServerError(err)
   157  			continue
   158  		}
   159  		result, err := action.Finish(state.ActionResults{Status: state.ActionCancelled, Message: "action cancelled via the API"})
   160  		if err != nil {
   161  			currentResult.Error = common.ServerError(err)
   162  			continue
   163  		}
   164  		receiverTag, err := names.ActionReceiverTag(result.Receiver())
   165  		if err != nil {
   166  			currentResult.Error = common.ServerError(err)
   167  			continue
   168  		}
   169  
   170  		response.Results[i] = makeActionResult(receiverTag, result)
   171  	}
   172  	return response, nil
   173  }
   174  
   175  // ServicesCharmActions returns a slice of charm Actions for a slice of
   176  // services.
   177  func (a *ActionAPI) ServicesCharmActions(args params.Entities) (params.ServicesCharmActionsResults, error) {
   178  	result := params.ServicesCharmActionsResults{Results: make([]params.ServiceCharmActionsResult, len(args.Entities))}
   179  	for i, entity := range args.Entities {
   180  		currentResult := &result.Results[i]
   181  		svcTag, err := names.ParseServiceTag(entity.Tag)
   182  		if err != nil {
   183  			currentResult.Error = common.ServerError(common.ErrBadId)
   184  			continue
   185  		}
   186  		currentResult.ServiceTag = svcTag.String()
   187  		svc, err := a.state.Service(svcTag.Id())
   188  		if err != nil {
   189  			currentResult.Error = common.ServerError(err)
   190  			continue
   191  		}
   192  		ch, _, err := svc.Charm()
   193  		if err != nil {
   194  			currentResult.Error = common.ServerError(err)
   195  			continue
   196  		}
   197  		currentResult.Actions = ch.Actions()
   198  	}
   199  	return result, nil
   200  }
   201  
   202  // internalList takes a list of Entities representing ActionReceivers
   203  // and returns all of the Actions the extractorFn can get out of the
   204  // ActionReceiver.
   205  func (a *ActionAPI) internalList(arg params.Entities, fn extractorFn) (params.ActionsByReceivers, error) {
   206  	response := params.ActionsByReceivers{Actions: make([]params.ActionsByReceiver, len(arg.Entities))}
   207  	for i, entity := range arg.Entities {
   208  		currentResult := &response.Actions[i]
   209  		receiver, err := tagToActionReceiver(a.state, entity.Tag)
   210  		if err != nil {
   211  			currentResult.Error = common.ServerError(common.ErrBadId)
   212  			continue
   213  		}
   214  		currentResult.Receiver = receiver.Tag().String()
   215  
   216  		results, err := fn(receiver)
   217  		if err != nil {
   218  			currentResult.Error = common.ServerError(err)
   219  			continue
   220  		}
   221  		currentResult.Actions = results
   222  	}
   223  	return response, nil
   224  }
   225  
   226  // tagToActionReceiver takes a tag string and tries to convert it to an
   227  // ActionReceiver.
   228  func tagToActionReceiver(st *state.State, tag string) (state.ActionReceiver, error) {
   229  	receiverTag, err := names.ParseTag(tag)
   230  	if err != nil {
   231  		return nil, common.ErrBadId
   232  	}
   233  	entity, err := st.FindEntity(receiverTag)
   234  	if err != nil {
   235  		return nil, common.ErrBadId
   236  	}
   237  	receiver, ok := entity.(state.ActionReceiver)
   238  	if !ok {
   239  		return nil, common.ErrBadId
   240  	}
   241  	return receiver, nil
   242  }
   243  
   244  // extractorFn is the generic signature for functions that extract
   245  // state.Actions from an ActionReceiver, and return them as a slice of
   246  // params.ActionResult.
   247  type extractorFn func(state.ActionReceiver) ([]params.ActionResult, error)
   248  
   249  // combine takes multiple extractorFn's and combines them into one
   250  // function.
   251  func combine(funcs ...extractorFn) extractorFn {
   252  	return func(ar state.ActionReceiver) ([]params.ActionResult, error) {
   253  		result := []params.ActionResult{}
   254  		for _, fn := range funcs {
   255  			items, err := fn(ar)
   256  			if err != nil {
   257  				return result, err
   258  			}
   259  			result = append(result, items...)
   260  		}
   261  		return result, nil
   262  	}
   263  }
   264  
   265  // pendingActions iterates through the Actions() enqueued for an
   266  // ActionReceiver, and converts them to a slice of params.ActionResult.
   267  func pendingActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   268  	return convertActions(ar, ar.PendingActions)
   269  }
   270  
   271  // runningActions iterates through the Actions() running on an
   272  // ActionReceiver, and converts them to a slice of params.ActionResult.
   273  func runningActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   274  	return convertActions(ar, ar.RunningActions)
   275  }
   276  
   277  // completedActions iterates through the Actions() that have run to
   278  // completion for an ActionReceiver, and converts them to a slice of
   279  // params.ActionResult.
   280  func completedActions(ar state.ActionReceiver) ([]params.ActionResult, error) {
   281  	return convertActions(ar, ar.CompletedActions)
   282  }
   283  
   284  // getActionsFn declares the function type that returns a slice of
   285  // *state.Action and error, used to curry specific list functions.
   286  type getActionsFn func() ([]*state.Action, error)
   287  
   288  // convertActions takes a generic getActionsFn to obtain a slice
   289  // of *state.Action and then converts them to the API slice of
   290  // params.ActionResult.
   291  func convertActions(ar state.ActionReceiver, fn getActionsFn) ([]params.ActionResult, error) {
   292  	items := []params.ActionResult{}
   293  	actions, err := fn()
   294  	if err != nil {
   295  		return items, err
   296  	}
   297  	for _, action := range actions {
   298  		if action == nil {
   299  			continue
   300  		}
   301  		items = append(items, makeActionResult(ar.Tag(), action))
   302  	}
   303  	return items, nil
   304  }
   305  
   306  // makeActionResult does the actual type conversion from *state.Action
   307  // to params.ActionResult.
   308  func makeActionResult(actionReceiverTag names.Tag, action *state.Action) params.ActionResult {
   309  	output, message := action.Results()
   310  	return params.ActionResult{
   311  		Action: &params.Action{
   312  			Receiver:   actionReceiverTag.String(),
   313  			Tag:        action.ActionTag().String(),
   314  			Name:       action.Name(),
   315  			Parameters: action.Parameters(),
   316  		},
   317  		Status:    string(action.Status()),
   318  		Message:   message,
   319  		Output:    output,
   320  		Enqueued:  action.Enqueued(),
   321  		Started:   action.Started(),
   322  		Completed: action.Completed(),
   323  	}
   324  }