github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/action.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"fmt"
     8  	"regexp"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	"labix.org/v2/mgo/txn"
    13  )
    14  
    15  type actionDoc struct {
    16  	// Id is the key for this document.  Action.Id() has a specfic form
    17  	// to facilitate filtering the actions collection for a given unit,
    18  	// or in the future a given service.
    19  	// The format of the Action.Id() will be:
    20  	//   <unit globalKey> + actionMarker + <generated state sequence>
    21  	Id string `bson:"_id"`
    22  
    23  	// Name identifies the action; it should match an action defined by
    24  	// the unit's charm.
    25  	Name string
    26  
    27  	// Payload holds the action's parameters, if any; it should validate
    28  	// against the schema defined by the named action in the unit's charm
    29  	Payload map[string]interface{}
    30  }
    31  
    32  // Action represents an instruction to do some "action" and is expected
    33  // to match an action definition in a charm.
    34  type Action struct {
    35  	st  *State
    36  	doc actionDoc
    37  }
    38  
    39  // newAction builds an Action from the supplied state and actionDoc
    40  func newAction(st *State, adoc actionDoc) *Action {
    41  	return &Action{
    42  		st:  st,
    43  		doc: adoc,
    44  	}
    45  }
    46  
    47  // actionMarker is the token used to delimit the prefix from
    48  // the unique suffix of an Action Id.  Useful for filtering
    49  // on a given prefix.
    50  const actionMarker string = "#a#"
    51  
    52  // actionPrefix returns a suitable prefix for an action given the
    53  // globalKey of a containing item
    54  func actionPrefix(globalKey string) string {
    55  	return globalKey + actionMarker
    56  }
    57  
    58  // newActionId generates a new unique key from another globalKey as
    59  // a prefix, and a generated unique number
    60  func newActionId(st *State, globalKey string) (string, error) {
    61  	prefix := actionPrefix(globalKey)
    62  	suffix, err := st.sequence(prefix)
    63  	if err != nil {
    64  		return "", errors.Errorf("cannot assign new sequence for prefix '%s': %v", prefix, err)
    65  	}
    66  	return fmt.Sprintf("%s%d", prefix, suffix), nil
    67  }
    68  
    69  // getActionIdPrefix returns the prefix for the given action id.
    70  // Useful when finding a prefix to filter on.
    71  func getActionIdPrefix(actionId string) string {
    72  	return strings.Split(actionId, actionMarker)[0]
    73  }
    74  
    75  // Id returns the id of the Action
    76  func (a *Action) Id() string {
    77  	return a.doc.Id
    78  }
    79  
    80  // Name returns the name of the Action
    81  func (a *Action) Name() string {
    82  	return a.doc.Name
    83  }
    84  
    85  // Payload will contain a structure representing arguments or parameters to
    86  // an action, and is expected to be validated by the Unit using the Charm
    87  // definition of the Action
    88  func (a *Action) Payload() map[string]interface{} {
    89  	return a.doc.Payload
    90  }
    91  
    92  // Complete removes action from the pending queue and creates an ActionResult
    93  // to capture the output and end state of the action.
    94  func (a *Action) Complete(output string) error {
    95  	return a.removeAndLog(ActionCompleted, output)
    96  }
    97  
    98  // Fail removes an Action from the queue, and creates an ActionResult that
    99  // will capture the reason for the failure.
   100  func (a *Action) Fail(reason string) error {
   101  	return a.removeAndLog(ActionFailed, reason)
   102  }
   103  
   104  // removeAndLog takes the action off of the pending queue, and creates an
   105  // actionresult to capture the outcome of the action.
   106  func (a *Action) removeAndLog(finalStatus ActionStatus, output string) error {
   107  	result, err := newActionResultDoc(a, finalStatus, output)
   108  	if err != nil {
   109  		return err
   110  	}
   111  	return a.st.runTransaction([]txn.Op{
   112  		addActionResultOp(a.st, result),
   113  		{
   114  			C:      a.st.actions.Name,
   115  			Id:     a.doc.Id,
   116  			Remove: true,
   117  		},
   118  	})
   119  }
   120  
   121  var validAction = regexp.MustCompile("^.+" + regexp.QuoteMeta(actionMarker) + "\\d+$")
   122  
   123  // IsAction returns whether actionId is a valid action Id.
   124  func IsAction(actionId string) bool {
   125  	return validAction.MatchString(actionId)
   126  }