1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"github.com/juju/testing"
9
jc "github.com/juju/testing/checkers"
10
gc "gopkg.in/check.v1"
11
"gopkg.in/juju/charm.v6-unstable/hooks"
13
"github.com/juju/juju/worker/uniter/hook"
14
"github.com/juju/juju/worker/uniter/operation"
15
"github.com/juju/juju/worker/uniter/runner"
16
"github.com/juju/juju/worker/uniter/runner/context"
19
type RunActionSuite struct {
20
testing.IsolationSuite
23
var _ = gc.Suite(&RunActionSuite{})
25
func (s *RunActionSuite) TestPrepareErrorBadActionAndFailSucceeds(c *gc.C) {
26
errBadAction := runner.NewBadActionError("some-action-id", "splat")
27
runnerFactory := &MockRunnerFactory{
28
MockNewActionRunner: &MockNewActionRunner{err: errBadAction},
30
callbacks := &RunActionCallbacks{
31
MockFailAction: &MockFailAction{err: errors.New("squelch")},
33
factory := operation.NewFactory(operation.FactoryParams{
34
RunnerFactory: runnerFactory,
37
op, err := factory.NewAction(someActionId)
38
c.Assert(err, jc.ErrorIsNil)
40
newState, err := op.Prepare(operation.State{})
41
c.Assert(newState, gc.IsNil)
42
c.Assert(err, gc.ErrorMatches, "squelch")
43
c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId)
44
c.Assert(*callbacks.MockFailAction.gotActionId, gc.Equals, someActionId)
45
c.Assert(*callbacks.MockFailAction.gotMessage, gc.Equals, errBadAction.Error())
48
func (s *RunActionSuite) TestPrepareErrorBadActionAndFailErrors(c *gc.C) {
49
errBadAction := runner.NewBadActionError("some-action-id", "foof")
50
runnerFactory := &MockRunnerFactory{
51
MockNewActionRunner: &MockNewActionRunner{err: errBadAction},
53
callbacks := &RunActionCallbacks{
54
MockFailAction: &MockFailAction{},
56
factory := operation.NewFactory(operation.FactoryParams{
57
RunnerFactory: runnerFactory,
60
op, err := factory.NewAction(someActionId)
61
c.Assert(err, jc.ErrorIsNil)
63
newState, err := op.Prepare(operation.State{})
64
c.Assert(newState, gc.IsNil)
65
c.Assert(err, gc.Equals, operation.ErrSkipExecute)
66
c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId)
67
c.Assert(*callbacks.MockFailAction.gotActionId, gc.Equals, someActionId)
68
c.Assert(*callbacks.MockFailAction.gotMessage, gc.Equals, errBadAction.Error())
71
func (s *RunActionSuite) TestPrepareErrorActionNotAvailable(c *gc.C) {
72
runnerFactory := &MockRunnerFactory{
73
MockNewActionRunner: &MockNewActionRunner{err: runner.ErrActionNotAvailable},
75
factory := operation.NewFactory(operation.FactoryParams{
76
RunnerFactory: runnerFactory,
78
op, err := factory.NewAction(someActionId)
79
c.Assert(err, jc.ErrorIsNil)
81
newState, err := op.Prepare(operation.State{})
82
c.Assert(newState, gc.IsNil)
83
c.Assert(err, gc.Equals, operation.ErrSkipExecute)
84
c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId)
87
func (s *RunActionSuite) TestPrepareErrorOther(c *gc.C) {
88
runnerFactory := &MockRunnerFactory{
89
MockNewActionRunner: &MockNewActionRunner{err: errors.New("foop")},
91
factory := operation.NewFactory(operation.FactoryParams{
92
RunnerFactory: runnerFactory,
94
op, err := factory.NewAction(someActionId)
95
c.Assert(err, jc.ErrorIsNil)
97
newState, err := op.Prepare(operation.State{})
98
c.Assert(newState, gc.IsNil)
99
c.Assert(err, gc.ErrorMatches, `cannot create runner for action ".*": foop`)
100
c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId)
103
func (s *RunActionSuite) TestPrepareCtxCalled(c *gc.C) {
104
ctx := &MockContext{actionData: &context.ActionData{Name: "some-action-name"}}
105
runnerFactory := &MockRunnerFactory{
106
MockNewActionRunner: &MockNewActionRunner{
112
factory := operation.NewFactory(operation.FactoryParams{
113
RunnerFactory: runnerFactory,
115
op, err := factory.NewAction(someActionId)
116
c.Assert(err, jc.ErrorIsNil)
118
newState, err := op.Prepare(operation.State{})
119
c.Assert(err, jc.ErrorIsNil)
120
c.Assert(newState, gc.NotNil)
121
ctx.CheckCall(c, 0, "Prepare")
124
func (s *RunActionSuite) TestPrepareCtxError(c *gc.C) {
125
ctx := &MockContext{actionData: &context.ActionData{Name: "some-action-name"}}
126
ctx.SetErrors(errors.New("ctx prepare error"))
127
runnerFactory := &MockRunnerFactory{
128
MockNewActionRunner: &MockNewActionRunner{
134
factory := operation.NewFactory(operation.FactoryParams{
135
RunnerFactory: runnerFactory,
137
op, err := factory.NewAction(someActionId)
138
c.Assert(err, jc.ErrorIsNil)
140
newState, err := op.Prepare(operation.State{})
141
c.Assert(err, gc.ErrorMatches, `ctx prepare error`)
142
c.Assert(newState, gc.IsNil)
143
ctx.CheckCall(c, 0, "Prepare")
146
func (s *RunActionSuite) TestPrepareSuccessCleanState(c *gc.C) {
147
runnerFactory := NewRunActionRunnerFactory(errors.New("should not call"))
148
factory := operation.NewFactory(operation.FactoryParams{
149
RunnerFactory: runnerFactory,
151
op, err := factory.NewAction(someActionId)
152
c.Assert(err, jc.ErrorIsNil)
154
newState, err := op.Prepare(operation.State{})
155
c.Assert(err, jc.ErrorIsNil)
156
c.Assert(newState, jc.DeepEquals, &operation.State{
157
Kind: operation.RunAction,
158
Step: operation.Pending,
159
ActionId: &someActionId,
161
c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId)
164
func (s *RunActionSuite) TestPrepareSuccessDirtyState(c *gc.C) {
165
runnerFactory := NewRunActionRunnerFactory(errors.New("should not call"))
166
factory := operation.NewFactory(operation.FactoryParams{
167
RunnerFactory: runnerFactory,
169
op, err := factory.NewAction(someActionId)
170
c.Assert(err, jc.ErrorIsNil)
172
newState, err := op.Prepare(overwriteState)
173
c.Assert(err, jc.ErrorIsNil)
174
c.Assert(newState, jc.DeepEquals, &operation.State{
175
Kind: operation.RunAction,
176
Step: operation.Pending,
177
ActionId: &someActionId,
179
Hook: &hook.Info{Kind: hooks.Install},
181
c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId)
184
func (s *RunActionSuite) TestExecuteSuccess(c *gc.C) {
185
var stateChangeTests = []struct {
187
before operation.State
188
after operation.State
190
description: "empty state",
191
after: operation.State{
192
Kind: operation.RunAction,
193
Step: operation.Done,
194
ActionId: &someActionId,
197
description: "preserves appropriate fields",
198
before: overwriteState,
199
after: operation.State{
200
Kind: operation.RunAction,
201
Step: operation.Done,
202
ActionId: &someActionId,
203
Hook: &hook.Info{Kind: hooks.Install},
208
for i, test := range stateChangeTests {
209
c.Logf("test %d: %s", i, test.description)
210
runnerFactory := NewRunActionRunnerFactory(nil)
211
callbacks := &RunActionCallbacks{}
212
factory := operation.NewFactory(operation.FactoryParams{
213
RunnerFactory: runnerFactory,
214
Callbacks: callbacks,
216
op, err := factory.NewAction(someActionId)
217
c.Assert(err, jc.ErrorIsNil)
218
midState, err := op.Prepare(test.before)
219
c.Assert(midState, gc.NotNil)
220
c.Assert(err, jc.ErrorIsNil)
222
newState, err := op.Execute(*midState)
223
c.Assert(err, jc.ErrorIsNil)
224
c.Assert(newState, jc.DeepEquals, &test.after)
225
c.Assert(callbacks.executingMessage, gc.Equals, "running action some-action-name")
226
c.Assert(*runnerFactory.MockNewActionRunner.runner.MockRunAction.gotName, gc.Equals, "some-action-name")
230
func (s *RunActionSuite) TestCommit(c *gc.C) {
231
var stateChangeTests = []struct {
233
before operation.State
234
after operation.State
236
description: "empty state",
237
after: operation.State{
238
Kind: operation.Continue,
239
Step: operation.Pending,
242
description: "preserves only appropriate fields, no hook",
243
before: operation.State{
244
Kind: operation.Continue,
245
Step: operation.Pending,
247
CharmURL: curl("cs:quantal/wordpress-2"),
248
ActionId: &randomActionId,
250
after: operation.State{
251
Kind: operation.Continue,
252
Step: operation.Pending,
256
description: "preserves only appropriate fields, with hook",
257
before: operation.State{
258
Kind: operation.Continue,
259
Step: operation.Pending,
261
CharmURL: curl("cs:quantal/wordpress-2"),
262
ActionId: &randomActionId,
263
Hook: &hook.Info{Kind: hooks.Install},
265
after: operation.State{
266
Kind: operation.RunHook,
267
Step: operation.Pending,
268
Hook: &hook.Info{Kind: hooks.Install},
273
for i, test := range stateChangeTests {
274
c.Logf("test %d: %s", i, test.description)
275
factory := operation.NewFactory(operation.FactoryParams{})
276
op, err := factory.NewAction(someActionId)
277
c.Assert(err, jc.ErrorIsNil)
279
newState, err := op.Commit(test.before)
280
c.Assert(newState, jc.DeepEquals, &test.after)
284
func (s *RunActionSuite) TestNeedsGlobalMachineLock(c *gc.C) {
285
factory := operation.NewFactory(operation.FactoryParams{})
286
op, err := factory.NewAction(someActionId)
287
c.Assert(err, jc.ErrorIsNil)
288
c.Assert(op.NeedsGlobalMachineLock(), jc.IsTrue)