func (s *actionsSuite) TestAuthAndActionFromTagFn(c *gc.C) { notFoundActionTag := names.NewActionTag(utils.MustNewUUID().String()) authorizedActionTag := names.NewActionTag(utils.MustNewUUID().String()) authorizedMachineTag := names.NewMachineTag("1") authorizedAction := fakeAction{name: "action1", receiver: authorizedMachineTag.Id()} unauthorizedActionTag := names.NewActionTag(utils.MustNewUUID().String()) unauthorizedMachineTag := names.NewMachineTag("10") unauthorizedAction := fakeAction{name: "action2", receiver: unauthorizedMachineTag.Id()} invalidReceiverActionTag := names.NewActionTag(utils.MustNewUUID().String()) invalidReceiverAction := fakeAction{name: "action2", receiver: "masterexploder"} canAccess := makeCanAccess(map[names.Tag]bool{ authorizedMachineTag: true, }) getActionByTag := makeGetActionByTag(map[names.ActionTag]state.Action{ authorizedActionTag: authorizedAction, unauthorizedActionTag: unauthorizedAction, invalidReceiverActionTag: invalidReceiverAction, }) tagFn := common.AuthAndActionFromTagFn(canAccess, getActionByTag) for i, test := range []struct { tag string errString string err error expectedAction state.Action }{{ tag: "invalid-action-tag", errString: `"invalid-action-tag" is not a valid tag`, }, { tag: notFoundActionTag.String(), errString: "action not found", }, { tag: invalidReceiverActionTag.String(), errString: `invalid actionreceiver name "masterexploder"`, }, { tag: unauthorizedActionTag.String(), err: common.ErrPerm, }, { tag: authorizedActionTag.String(), expectedAction: authorizedAction, }} { c.Logf("test %d", i) action, err := tagFn(test.tag) if test.errString != "" { c.Check(err, gc.ErrorMatches, test.errString) c.Check(action, gc.IsNil) } else if test.err != nil { c.Check(err, gc.Equals, test.err) c.Check(action, gc.IsNil) } else { c.Check(err, jc.ErrorIsNil) c.Check(action, gc.Equals, action) } } }
func (s *actionSuite) TestParseActionTag(c *gc.C) { parseActionTagTests := []struct { tag string expected names.Tag err error }{ { tag: "", expected: nil, err: names.InvalidTagError("", ""), }, { tag: "action-good" + names.ActionMarker + "123", expected: names.NewActionTag("good" + names.ActionMarker + "123"), err: nil, }, { tag: "action-good/0" + names.ActionMarker + "123", expected: names.NewActionTag("good/0" + names.ActionMarker + "123"), err: nil, }, { tag: "action-bad/00" + names.ActionMarker + "123", expected: nil, err: names.InvalidTagError("action-bad/00"+names.ActionMarker+"123", names.ActionTagKind), }, { tag: "dave", expected: nil, err: names.InvalidTagError("dave", ""), }, { tag: "action-dave/0", expected: nil, err: names.InvalidTagError("action-dave/0", names.ActionTagKind), }, { tag: "action", expected: nil, err: names.InvalidTagError("action", ""), }, { tag: "user-dave", expected: nil, err: names.InvalidTagError("user-dave", names.ActionTagKind), }} for i, t := range parseActionTagTests { c.Logf("test %d: %s", i, t.tag) got, err := names.ParseActionTag(t.tag) if err != nil || t.err != nil { c.Check(err, gc.DeepEquals, t.err) continue } c.Check(got, gc.FitsTypeOf, t.expected) c.Check(got, gc.Equals, t.expected) } }
// 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 }
func (s *ClientSuite) TestActionFinishSuccess(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) status := "stubstatus" actionResults := map[string]interface{}{"stub": "stub"} message := "stubmsg" expectedCalls := []jujutesting.StubCall{{ "MachineActions.FinishActions", []interface{}{"", params.ActionExecutionResults{ Results: []params.ActionExecutionResult{{ ActionTag: tag.String(), Status: status, Results: actionResults, Message: message, }}, }}, }} var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) *(result.(*params.ErrorResults)) = params.ErrorResults{ Results: []params.ErrorResult{{}}, } return nil }) client := machineactions.NewClient(apiCaller) err := client.ActionFinish(tag, status, actionResults, message) c.Assert(err, jc.ErrorIsNil) stub.CheckCalls(c, expectedCalls) }
func (s *ClientSuite) TestActionFinishTooManyResults(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) expectedCalls := []jujutesting.StubCall{{ "MachineActions.FinishActions", []interface{}{"", params.ActionExecutionResults{ Results: []params.ActionExecutionResult{{ ActionTag: tag.String(), Status: "", Results: nil, Message: "", }}, }}, }} var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) res := result.(*params.ErrorResults) res.Results = make([]params.ErrorResult, 2) return nil }) client := machineactions.NewClient(apiCaller) err := client.ActionFinish(tag, "", nil, "") c.Assert(err, gc.ErrorMatches, "expected 1 result, got 2") stub.CheckCalls(c, expectedCalls) }
func (s *ClientSuite) TestGetActionSuccess(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) expectedCalls := []jujutesting.StubCall{{ "MachineActions.Actions", []interface{}{"", params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, }}, }} expectedName := "ack" expectedParams := map[string]interface{}{"floob": "zgloob"} var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ActionResults{}) *(result.(*params.ActionResults)) = params.ActionResults{ Results: []params.ActionResult{{ Action: ¶ms.Action{ Name: expectedName, Parameters: expectedParams, }, }}, } return nil }) client := machineactions.NewClient(apiCaller) action, err := client.Action(tag) c.Assert(err, jc.ErrorIsNil) c.Assert(action.Name(), gc.Equals, expectedName) c.Assert(action.Params(), gc.DeepEquals, expectedParams) stub.CheckCalls(c, expectedCalls) }
func (s *ClientSuite) TestGetActionResultError(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) expectedCalls := []jujutesting.StubCall{{ "MachineActions.Actions", []interface{}{"", params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, }}, }} expectedErr := ¶ms.Error{ Message: "rigged", Code: params.CodeNotAssigned, } var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ActionResults{}) *(result.(*params.ActionResults)) = params.ActionResults{ Results: []params.ActionResult{{ Error: expectedErr, }}, } return nil }) client := machineactions.NewClient(apiCaller) action, err := client.Action(tag) c.Assert(errors.Cause(err), gc.Equals, expectedErr) c.Assert(action, gc.IsNil) stub.CheckCalls(c, expectedCalls) }
func (s *actionSuite) TestPrefixSuffix(c *gc.C) { var tests = []struct { prefix string suffix int }{ {prefix: "asdf", suffix: 0}, {prefix: "qwer/0", suffix: 10}, {prefix: "zxcv/3", suffix: 11}, } for _, test := range tests { suf := fmt.Sprintf("%d", test.suffix) action := names.NewActionTag(test.prefix + names.ActionMarker + suf) c.Assert(action.Prefix(), gc.Equals, test.prefix) c.Assert(action.Sequence(), gc.Equals, test.suffix) result := names.NewActionResultTag(test.prefix + names.ActionResultMarker + suf) c.Assert(result.Prefix(), gc.Equals, test.prefix) c.Assert(result.Sequence(), gc.Equals, test.suffix) c.Assert(action.PrefixTag(), gc.Not(gc.IsNil)) c.Assert(action.PrefixTag(), gc.DeepEquals, result.PrefixTag()) } }
// 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 *ClientSuite) TestActionBeginSuccess(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) expectedCalls := []jujutesting.StubCall{{ "MachineActions.BeginActions", []interface{}{"", params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, }}, }} var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) *(result.(*params.ErrorResults)) = params.ErrorResults{ Results: []params.ErrorResult{{}}, } return nil }) client := machineactions.NewClient(apiCaller) err := client.ActionBegin(tag) c.Assert(err, jc.ErrorIsNil) stub.CheckCalls(c, expectedCalls) }
func (s *ClientSuite) TestActionFinishError(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) expectedCalls := []jujutesting.StubCall{{ "MachineActions.FinishActions", []interface{}{"", params.ActionExecutionResults{ Results: []params.ActionExecutionResult{{ ActionTag: tag.String(), Status: "", Results: nil, Message: "", }}, }}, }} expectedErr := errors.Errorf("blam") var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ErrorResults{}) return expectedErr }) client := machineactions.NewClient(apiCaller) err := client.ActionFinish(tag, "", nil, "") c.Assert(errors.Cause(err), gc.Equals, expectedErr) stub.CheckCalls(c, expectedCalls) }
func makeActionQuery(actionID string, receiverType string, receiverTag names.Tag) actionQuery { return actionQuery{ actionTag: names.NewActionTag(actionID), receiver: actionReceiver{ receiverType: receiverType, tag: receiverTag, }, } }
func (s *commonSuite) TestAuthFuncForTagKind(c *gc.C) { // TODO(dimitern): This list of all supported tags and kinds needs // to live in juju/names. uuid, err := utils.NewUUID() c.Assert(err, jc.ErrorIsNil) allTags := []names.Tag{ nil, // invalid tag names.NewActionTag(uuid.String()), names.NewCharmTag("cs:precise/missing"), names.NewModelTag(uuid.String()), names.NewFilesystemTag("20/20"), names.NewLocalUserTag("user"), names.NewMachineTag("42"), names.NewNetworkTag("public"), names.NewRelationTag("wordpress:mysql mysql:db"), names.NewServiceTag("wordpress"), names.NewSpaceTag("apps"), names.NewStorageTag("foo/42"), names.NewUnitTag("wordpress/5"), names.NewUserTag("joe"), names.NewVolumeTag("80/20"), } for i, allowedTag := range allTags { c.Logf("test #%d: allowedTag: %v", i, allowedTag) var allowedKind string if allowedTag != nil { allowedKind = allowedTag.Kind() } getAuthFunc := common.AuthFuncForTagKind(allowedKind) authFunc, err := getAuthFunc() if allowedKind == "" { c.Check(err, gc.ErrorMatches, "tag kind cannot be empty") c.Check(authFunc, gc.IsNil) continue } else if !c.Check(err, jc.ErrorIsNil) { continue } for j, givenTag := range allTags { c.Logf("test #%d.%d: givenTag: %v", i, j, givenTag) var givenKind string if givenTag != nil { givenKind = givenTag.Kind() } if allowedKind == givenKind { c.Check(authFunc(givenTag), jc.IsTrue) } else { c.Check(authFunc(givenTag), jc.IsFalse) } } } }
func (m *mockRunAPI) setResponse(id string, mock mockResponse) { if m.runResponses == nil { m.runResponses = make(map[string]params.ActionResult) } if m.receiverIdMap == nil { m.receiverIdMap = make(map[string]string) } actionTag := names.NewActionTag(utils.MustNewUUID().String()) m.receiverIdMap[id] = actionTag.Id() m.runResponses[id] = makeActionResult(mock, actionTag.String()) }
// 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 }
func (s *actionSuite) TestInvalidActionNamesPanic(c *gc.C) { invalidActionNameTests := []string{ "", // blank is not a valid action id "admin", // probably a user name, which isn't a valid action id } for _, name := range invalidActionNameTests { expect := fmt.Sprintf("%q is not a valid action id", name) testFunc := func() { names.NewActionTag(name) } c.Assert(testFunc, gc.PanicMatches, expect) } }
func (s *ContextFactorySuite) TestNewActionRunnerLeadershipContext(c *gc.C) { s.testLeadershipContextWiring(c, func() runner.Context { s.SetCharm(c, "dummy") action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil) c.Assert(err, jc.ErrorIsNil) actionData := &runner.ActionData{ Name: action.Name(), Tag: names.NewActionTag(action.Id()), Params: action.Parameters(), ResultsMap: map[string]interface{}{}, } ctx, err := s.factory.ActionContext(actionData) c.Assert(err, jc.ErrorIsNil) return ctx }) }
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) } }
func (s *ContextFactorySuite) TestActionContext(c *gc.C) { s.SetCharm(c, "dummy") action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil) c.Assert(err, jc.ErrorIsNil) actionData := &runner.ActionData{ Name: action.Name(), Tag: names.NewActionTag(action.Id()), Params: action.Parameters(), ResultsMap: map[string]interface{}{}, } ctx, err := s.factory.ActionContext(actionData) c.Assert(err, jc.ErrorIsNil) s.AssertCoreContext(c, ctx) s.AssertActionContext(c, ctx) s.AssertNotRelationContext(c, ctx) s.AssertNotStorageContext(c, ctx) }
// 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 }
func makeActionResult(mock mockResponse, actionTag string) params.ActionResult { var receiverTag string if mock.unitTag != "" { receiverTag = mock.unitTag } else { receiverTag = mock.machineTag } if actionTag == "" { actionTag = names.NewActionTag(utils.MustNewUUID().String()).String() } return params.ActionResult{ Action: ¶ms.Action{ Tag: actionTag, Receiver: receiverTag, }, Message: mock.message, Error: mock.error, Output: map[string]interface{}{ "Stdout": mock.stdout, "Stderr": mock.stderr, "Code": mock.code, }, } }
func (s *ClientSuite) TestGetActionTooManyResults(c *gc.C) { tag := names.NewActionTag(utils.MustNewUUID().String()) expectedCalls := []jujutesting.StubCall{{ "MachineActions.Actions", []interface{}{"", params.Entities{ Entities: []params.Entity{{Tag: tag.String()}}, }}, }} var stub jujutesting.Stub apiCaller := apitesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error { stub.AddCall(objType+"."+request, id, arg) c.Check(result, gc.FitsTypeOf, ¶ms.ActionResults{}) res := result.(*params.ActionResults) res.Results = make([]params.ActionResult, 2) return nil }) client := machineactions.NewClient(apiCaller) action, err := client.Action(tag) c.Assert(err, gc.ErrorMatches, "expected only 1 action query result, got 2") c.Assert(action, gc.IsNil) stub.CheckCalls(c, expectedCalls) }
// ActionTag returns an ActionTag constructed from this action's // Prefix and Sequence. func (a *action) ActionTag() names.ActionTag { return names.NewActionTag(a.Id()) }
c.Logf("test %d: %q", i, test.pattern) assertAction(test.pattern, test.valid) } } var parseActionTagTests = []struct { tag string expected names.Tag err error }{{ tag: "", expected: nil, err: names.InvalidTagError("", ""), }, { tag: "action-dave" + names.ActionMarker + "123", expected: names.NewActionTag("dave" + names.ActionMarker + "123"), err: nil, }, { tag: "dave", expected: nil, err: names.InvalidTagError("dave", ""), }, { tag: "action-dave/0", expected: nil, err: names.InvalidTagError("action-dave/0", names.ActionTagKind), }, { tag: "action", expected: nil, err: names.InvalidTagError("action", ""), }, { tag: "user-dave",
gc "gopkg.in/check.v1" "github.com/juju/names" ) type actionSuite struct{} var _ = gc.Suite(&actionSuite{}) var parseActionTagTests = []struct { tag string expected names.Tag err error }{ {tag: "", err: names.InvalidTagError("", "")}, {tag: "action-f47ac10b-58cc-4372-a567-0e02b2c3d479", expected: names.NewActionTag("f47ac10b-58cc-4372-a567-0e02b2c3d479")}, {tag: "action-012345678", err: names.InvalidTagError("action-012345678", "action")}, {tag: "action-1234567", err: names.InvalidTagError("action-1234567", "action")}, {tag: "bob", err: names.InvalidTagError("bob", "")}, {tag: "service-ned", err: names.InvalidTagError("service-ned", names.ActionTagKind)}} func (s *actionSuite) TestParseActionTag(c *gc.C) { for i, t := range parseActionTagTests { c.Logf("test %d: %s", i, t.tag) got, err := names.ParseActionTag(t.tag) if err != nil || t.err != nil { c.Check(err, gc.DeepEquals, t.err) continue } c.Check(got, gc.FitsTypeOf, t.expected) c.Check(got, gc.Equals, t.expected)
// runHook executes the supplied hook.Info in an appropriate hook context. If // the hook itself fails to execute, it returns errHookFailed. func (u *Uniter) runHook(hi hook.Info) (err error) { // Prepare context. if err = hi.Validate(); err != nil { return err } hookName := string(hi.Kind) actionParams := map[string]interface{}(nil) // This value is needed to pass results of Action param validation // in case of error or invalidation. This is probably bad form; it // will be corrected in PR refactoring HookContext. // TODO(binary132): handle errors before grabbing hook context. var actionParamsErr error = nil relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } else if hi.Kind == hooks.ActionRequested { action, err := u.st.Action(names.NewActionTag(hi.ActionId)) if err != nil { return err } actionParams = action.Params() hookName = action.Name() _, actionParamsErr = u.validateAction(hookName, actionParams) } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.acquireHookLock(lockMessage); err != nil { return err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit, actionParams) if err != nil { return err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return err } defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } logger.Infof("running %q hook", hookName) ranHook := true // The reason for the conditional at this point is that once inside // RunHook, we don't know whether we're running an Action or a regular // Hook. RunAction simply calls the exact same method as RunHook, but // with the location as "actions" instead of "hooks". if hi.Kind == hooks.ActionRequested { if actionParamsErr != nil { logger.Errorf("action %q param validation failed: %s", hookName, actionParamsErr.Error()) u.notifyHookFailed(hookName, hctx) return u.commitHook(hi) } err = hctx.RunAction(hookName, u.charmPath, u.toolsDir, socketPath) } else { err = hctx.RunHook(hookName, u.charmPath, u.toolsDir, socketPath) } // Since the Action validation error was separated, regular error pathways // will still occur correctly. if IsMissingHookError(err) { ranHook = false } else if err != nil { logger.Errorf("hook failed: %s", err) u.notifyHookFailed(hookName, hctx) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } if ranHook { logger.Infof("ran %q hook", hookName) u.notifyHookCompleted(hookName, hctx) } else { logger.Infof("skipped %q hook (missing)", hookName) } return u.commitHook(hi) }
func (s *actionSuite) TestActionNotFound(c *gc.C) { _, err := s.uniter.Action(names.NewActionTag("feedface-0123-4567-8901-2345deadbeef")) c.Assert(err, gc.NotNil) c.Assert(err, gc.ErrorMatches, `action "feedface-0123-4567-8901-2345deadbeef" not found`) }
expectType: names.ActionTag{}, resultId: "wordpress/0" + names.ActionMarker + "333", }, { tag: "foo", resultErr: `"foo" is not a valid tag`, }} var makeTag = map[string]func(string) names.Tag{ names.MachineTagKind: func(tag string) names.Tag { return names.NewMachineTag(tag) }, names.UnitTagKind: func(tag string) names.Tag { return names.NewUnitTag(tag) }, names.ServiceTagKind: func(tag string) names.Tag { return names.NewServiceTag(tag) }, names.RelationTagKind: func(tag string) names.Tag { return names.NewRelationTag(tag) }, names.EnvironTagKind: func(tag string) names.Tag { return names.NewEnvironTag(tag) }, names.UserTagKind: func(tag string) names.Tag { return names.NewUserTag(tag) }, names.NetworkTagKind: func(tag string) names.Tag { return names.NewNetworkTag(tag) }, names.ActionTagKind: func(tag string) names.Tag { return names.NewActionTag(tag) }, } func (*tagSuite) TestParseTag(c *gc.C) { for i, test := range parseTagTests { c.Logf("test %d: %q expectKind %q", i, test.tag, test.expectKind) tag, err := names.ParseTag(test.tag) if test.resultErr != "" { c.Assert(err, gc.ErrorMatches, test.resultErr) c.Assert(tag, gc.IsNil) // If the tag has a valid kind which matches the // expected kind, test that using an empty // expectKind does not change the error message. if tagKind, err := names.TagKind(test.tag); err == nil && tagKind == test.expectKind { tag, err := names.ParseTag(test.tag)
// runHook executes the supplied hook.Info in an appropriate hook context. If // the hook itself fails to execute, it returns errHookFailed. func (u *Uniter) runHook(hi hook.Info) (err error) { // Prepare context. if err = hi.Validate(); err != nil { return err } hookName := string(hi.Kind) actionParams := map[string]interface{}(nil) relationId := -1 if hi.Kind.IsRelation() { relationId = hi.RelationId if hookName, err = u.relationers[relationId].PrepareHook(hi); err != nil { return err } } else if hi.Kind == hooks.ActionRequested { action, err := u.st.Action(names.NewActionTag(hi.ActionId)) if err != nil { return err } actionParams = action.Params() hookName = action.Name() } hctxId := fmt.Sprintf("%s:%s:%d", u.unit.Name(), hookName, u.rand.Int63()) lockMessage := fmt.Sprintf("%s: running hook %q", u.unit.Name(), hookName) if err = u.acquireHookLock(lockMessage); err != nil { return err } defer u.hookLock.Unlock() hctx, err := u.getHookContext(hctxId, relationId, hi.RemoteUnit, actionParams) if err != nil { return err } srv, socketPath, err := u.startJujucServer(hctx) if err != nil { return err } defer srv.Close() // Run the hook. if err := u.writeState(RunHook, Pending, &hi, nil); err != nil { return err } logger.Infof("running %q hook", hookName) ranHook := true // The reason for the switch at this point is that once inside RunHook, // we don't know whether we're running an Action or a regular Hook. // RunAction simply calls the exact same method as RunHook, but with // the location as "actions" instead of "hooks". if hi.Kind == hooks.ActionRequested { err = hctx.RunAction(hookName, u.charmPath, u.toolsDir, socketPath) } else { err = hctx.RunHook(hookName, u.charmPath, u.toolsDir, socketPath) } if IsMissingHookError(err) { ranHook = false } else if err != nil { logger.Errorf("hook failed: %s", err) u.notifyHookFailed(hookName, hctx) return errHookFailed } if err := u.writeState(RunHook, Done, &hi, nil); err != nil { return err } if ranHook { logger.Infof("ran %q hook", hookName) u.notifyHookCompleted(hookName, hctx) } else { logger.Infof("skipped %q hook (missing)", hookName) } return u.commitHook(hi) }
func (s *actionSuite) TestActionNotFound(c *gc.C) { actionTag := names.NewActionTag(names.NewUnitTag("wordpress/0"), 0) _, err := s.uniter.Action(actionTag) c.Assert(err, gc.NotNil) c.Assert(err, gc.ErrorMatches, "action .*wordpress/0[^0-9]+0[^0-9]+ not found") }