Beispiel #1
0
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)
		}
	}
}
Beispiel #2
0
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)
	}
}
Beispiel #3
0
// 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, &params.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, &params.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, &params.ActionResults{})
		*(result.(*params.ActionResults)) = params.ActionResults{
			Results: []params.ActionResult{{
				Action: &params.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 := &params.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, &params.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)
}
Beispiel #8
0
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())
	}
}
Beispiel #9
0
// 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, &params.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, &params.ErrorResults{})
		return expectedErr
	})

	client := machineactions.NewClient(apiCaller)
	err := client.ActionFinish(tag, "", nil, "")
	c.Assert(errors.Cause(err), gc.Equals, expectedErr)
	stub.CheckCalls(c, expectedCalls)
}
Beispiel #12
0
func makeActionQuery(actionID string, receiverType string, receiverTag names.Tag) actionQuery {
	return actionQuery{
		actionTag: names.NewActionTag(actionID),
		receiver: actionReceiver{
			receiverType: receiverType,
			tag:          receiverTag,
		},
	}
}
Beispiel #13
0
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)
			}
		}
	}
}
Beispiel #14
0
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())
}
Beispiel #15
0
// 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
}
Beispiel #16
0
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)
	}
}
Beispiel #17
0
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
	})
}
Beispiel #18
0
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)
	}
}
Beispiel #19
0
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)
}
Beispiel #20
0
// 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
}
Beispiel #21
0
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: &params.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, &params.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)
}
Beispiel #23
0
// ActionTag returns an ActionTag constructed from this action's
// Prefix and Sequence.
func (a *action) ActionTag() names.ActionTag {
	return names.NewActionTag(a.Id())
}
Beispiel #24
0
		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",
Beispiel #25
0
	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)
Beispiel #26
0
// 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)
}
Beispiel #27
0
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`)
}
Beispiel #28
0
	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)
Beispiel #29
0
// 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)
}
Beispiel #30
0
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")
}