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 }