// Handle is part of the watcher.StringsHandler interface. // It should give us any actions currently enqueued for this machine. // We try to execute every action before returning func (h *handler) Handle(_ <-chan struct{}, actionsSlice []string) error { for _, actionId := range actionsSlice { ok := names.IsValidAction(actionId) if !ok { return errors.Errorf("got invalid action id %s", actionId) } actionTag := names.NewActionTag(actionId) action, err := h.config.Facade.Action(actionTag) if err != nil { return errors.Annotatef(err, "could not retrieve action %s", actionId) } err = h.config.Facade.ActionBegin(actionTag) if err != nil { return errors.Annotatef(err, "could not begin action %s", action.Name()) } // We try to handle the action. The result returned from handling the action is // sent through using ActionFinish. We only stop the loop if ActionFinish fails. var finishErr error results, err := h.config.HandleAction(action.Name(), action.Params()) if err != nil { finishErr = h.config.Facade.ActionFinish(actionTag, params.ActionFailed, nil, err.Error()) } else { finishErr = h.config.Facade.ActionFinish(actionTag, params.ActionCompleted, results, "") } if finishErr != nil { return errors.Trace(finishErr) } } return nil }
// NewActionRunner exists to satisfy the Factory interface. func (f *factory) NewActionRunner(actionId string) (Runner, error) { ch, err := getCharm(f.paths.GetCharmDir()) if err != nil { return nil, errors.Trace(err) } ok := names.IsValidAction(actionId) if !ok { return nil, &badActionError{actionId, "not valid actionId"} } tag := names.NewActionTag(actionId) action, err := f.state.Action(tag) if params.IsCodeNotFoundOrCodeUnauthorized(err) { return nil, ErrActionNotAvailable } else if params.IsCodeActionNotAvailable(err) { return nil, ErrActionNotAvailable } else if err != nil { return nil, errors.Trace(err) } name := action.Name() spec, ok := ch.Actions().ActionSpecs[name] if !ok { return nil, &badActionError{name, "not defined"} } params := action.Params() if err := spec.ValidateParams(params); err != nil { return nil, &badActionError{name, err.Error()} } actionData := newActionData(name, &tag, params) ctx, err := f.contextFactory.ActionContext(actionData) runner := NewRunner(ctx, f.paths) return runner, nil }
func (s *actionSuite) TestActionNameFormats(c *gc.C) { marker := names.ActionMarker actionNameTests := []struct { pattern string valid bool }{ {pattern: "", valid: false}, {pattern: "service", valid: false}, {pattern: "service" + marker, valid: false}, {pattern: "service" + marker + "0", valid: true}, {pattern: "service" + marker + "00", valid: false}, {pattern: "service" + marker + "0" + marker + "0", valid: false}, {pattern: "service-name/0" + marker, valid: false}, {pattern: "service-name-0" + marker, valid: false}, {pattern: "service-name/0" + marker + "0", valid: true}, {pattern: "service-name-0" + marker + "0", valid: false}, {pattern: "service-name/0" + marker + "00", valid: false}, {pattern: "service-name-0" + marker + "00", valid: false}, {pattern: "service-name/0" + marker + "01", valid: false}, {pattern: "service-name-0" + marker + "01", valid: false}, {pattern: "service-name/0" + marker + "11", valid: true}, {pattern: "service-name-0" + marker + "11", valid: false}, } assertAction := func(s string, expect bool) { c.Assert(names.IsValidAction(s), gc.Equals, expect) } for i, test := range actionNameTests { c.Logf("test %d: %q", i, test.pattern) assertAction(test.pattern, test.valid) } }
// NewAction is part of the Factory interface. func (f *factory) NewAction(actionId string) (Operation, error) { if !names.IsValidAction(actionId) { return nil, errors.Errorf("invalid action id %q", actionId) } return &runAction{ actionId: actionId, callbacks: f.config.Callbacks, runnerFactory: f.config.RunnerFactory, }, nil }
// FailAction is part of the operation.Callbacks interface. func (opc *operationCallbacks) FailAction(actionId, message string) error { if !names.IsValidAction(actionId) { return errors.Errorf("invalid action id %q", actionId) } tag := names.NewActionTag(actionId) err := opc.u.st.ActionFinish(tag, params.ActionFailed, nil, message) if params.IsCodeNotFoundOrCodeUnauthorized(err) { err = nil } return err }
// Validate returns an error if the info is not valid. func (hi Info) Validate() error { switch hi.Kind { case hooks.RelationJoined, hooks.RelationChanged, hooks.RelationDeparted: if hi.RemoteUnit == "" { return fmt.Errorf("%q hook requires a remote unit", hi.Kind) } fallthrough case hooks.Install, hooks.Start, hooks.ConfigChanged, hooks.UpgradeCharm, hooks.Stop, hooks.RelationBroken: return nil case hooks.ActionRequested: if !names.IsValidAction(hi.ActionId) { return fmt.Errorf("action id %q cannot be parsed as an action tag", hi.ActionId) } return nil } return fmt.Errorf("unknown hook kind %q", hi.Kind) }
func (s *actionSuite) TestAction(c *gc.C) { var actionTests = []struct { description string action params.Action }{{ description: "A simple Action.", action: params.Action{ Name: "fakeaction", Parameters: basicParams, }, }, { description: "An Action with nested parameters.", action: params.Action{ Name: "fakeaction", Parameters: map[string]interface{}{ "outfile": "foo.bz2", "compression": map[string]interface{}{ "kind": "bzip", "quality": float64(5.0), }, }, }, }} for i, actionTest := range actionTests { c.Logf("test %d: %s", i, actionTest.description) a, err := s.uniterSuite.wordpressUnit.AddAction( actionTest.action.Name, actionTest.action.Parameters) c.Assert(err, jc.ErrorIsNil) ok := names.IsValidAction(a.Id()) c.Assert(ok, gc.Equals, true) actionTag := names.NewActionTag(a.Id()) c.Assert(a.Tag(), gc.Equals, actionTag) retrievedAction, err := s.uniter.Action(actionTag) c.Assert(err, jc.ErrorIsNil) c.Assert(retrievedAction.Name(), gc.DeepEquals, actionTest.action.Name) c.Assert(retrievedAction.Params(), gc.DeepEquals, actionTest.action.Parameters) } }
// FindActionTagsByPrefix finds Actions with ids that share the supplied prefix, and // returns a list of corresponding ActionTags. func (st *State) FindActionTagsByPrefix(prefix string) []names.ActionTag { actionLogger.Tracef("FindActionTagsByPrefix() %q", prefix) var results []names.ActionTag var doc struct { Id string `bson:"_id"` } actions, closer := st.getCollection(actionsC) defer closer() iter := actions.Find(bson.D{{"_id", bson.D{{"$regex", "^" + st.docID(prefix)}}}}).Iter() defer iter.Close() for iter.Next(&doc) { actionLogger.Tracef("FindActionTagsByPrefix() iter doc %+v", doc) localID := st.localID(doc.Id) if names.IsValidAction(localID) { results = append(results, names.NewActionTag(localID)) } } actionLogger.Tracef("FindActionTagsByPrefix() %q found %+v", prefix, results) return results }
// ValidateTag should be called before calls to Tag() or ActionTag(). It verifies // that the Action can produce a valid Tag. func (a *Action) ValidateTag() bool { return names.IsValidAction(a.Id()) }