github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/uniter/operation/runaction_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package operation_test 5 6 import ( 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.v5/hooks" 12 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 ) 17 18 type RunActionSuite struct { 19 testing.IsolationSuite 20 } 21 22 var _ = gc.Suite(&RunActionSuite{}) 23 24 func (s *RunActionSuite) TestPrepareErrorBadActionAndFailSucceeds(c *gc.C) { 25 errBadAction := runner.NewBadActionError("some-action-id", "splat") 26 runnerFactory := &MockRunnerFactory{ 27 MockNewActionRunner: &MockNewActionRunner{err: errBadAction}, 28 } 29 callbacks := &RunActionCallbacks{ 30 MockFailAction: &MockFailAction{err: errors.New("squelch")}, 31 } 32 factory := operation.NewFactory(operation.FactoryParams{ 33 RunnerFactory: runnerFactory, 34 Callbacks: callbacks, 35 }) 36 op, err := factory.NewAction(someActionId) 37 c.Assert(err, jc.ErrorIsNil) 38 39 newState, err := op.Prepare(operation.State{}) 40 c.Assert(newState, gc.IsNil) 41 c.Assert(err, gc.ErrorMatches, "squelch") 42 c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId) 43 c.Assert(*callbacks.MockFailAction.gotActionId, gc.Equals, someActionId) 44 c.Assert(*callbacks.MockFailAction.gotMessage, gc.Equals, errBadAction.Error()) 45 } 46 47 func (s *RunActionSuite) TestPrepareErrorBadActionAndFailErrors(c *gc.C) { 48 errBadAction := runner.NewBadActionError("some-action-id", "foof") 49 runnerFactory := &MockRunnerFactory{ 50 MockNewActionRunner: &MockNewActionRunner{err: errBadAction}, 51 } 52 callbacks := &RunActionCallbacks{ 53 MockFailAction: &MockFailAction{}, 54 } 55 factory := operation.NewFactory(operation.FactoryParams{ 56 RunnerFactory: runnerFactory, 57 Callbacks: callbacks, 58 }) 59 op, err := factory.NewAction(someActionId) 60 c.Assert(err, jc.ErrorIsNil) 61 62 newState, err := op.Prepare(operation.State{}) 63 c.Assert(newState, gc.IsNil) 64 c.Assert(err, gc.Equals, operation.ErrSkipExecute) 65 c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId) 66 c.Assert(*callbacks.MockFailAction.gotActionId, gc.Equals, someActionId) 67 c.Assert(*callbacks.MockFailAction.gotMessage, gc.Equals, errBadAction.Error()) 68 } 69 70 func (s *RunActionSuite) TestPrepareErrorActionNotAvailable(c *gc.C) { 71 runnerFactory := &MockRunnerFactory{ 72 MockNewActionRunner: &MockNewActionRunner{err: runner.ErrActionNotAvailable}, 73 } 74 factory := operation.NewFactory(operation.FactoryParams{ 75 RunnerFactory: runnerFactory, 76 }) 77 op, err := factory.NewAction(someActionId) 78 c.Assert(err, jc.ErrorIsNil) 79 80 newState, err := op.Prepare(operation.State{}) 81 c.Assert(newState, gc.IsNil) 82 c.Assert(err, gc.Equals, operation.ErrSkipExecute) 83 c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId) 84 } 85 86 func (s *RunActionSuite) TestPrepareErrorOther(c *gc.C) { 87 runnerFactory := &MockRunnerFactory{ 88 MockNewActionRunner: &MockNewActionRunner{err: errors.New("foop")}, 89 } 90 factory := operation.NewFactory(operation.FactoryParams{ 91 RunnerFactory: runnerFactory, 92 }) 93 op, err := factory.NewAction(someActionId) 94 c.Assert(err, jc.ErrorIsNil) 95 96 newState, err := op.Prepare(operation.State{}) 97 c.Assert(newState, gc.IsNil) 98 c.Assert(err, gc.ErrorMatches, `cannot create runner for action ".*": foop`) 99 c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId) 100 } 101 102 func (s *RunActionSuite) TestPrepareCtxCalled(c *gc.C) { 103 ctx := &MockContext{actionData: &runner.ActionData{Name: "some-action-name"}} 104 runnerFactory := &MockRunnerFactory{ 105 MockNewActionRunner: &MockNewActionRunner{ 106 runner: &MockRunner{ 107 context: ctx, 108 }, 109 }, 110 } 111 factory := operation.NewFactory(operation.FactoryParams{ 112 RunnerFactory: runnerFactory, 113 }) 114 op, err := factory.NewAction(someActionId) 115 c.Assert(err, jc.ErrorIsNil) 116 117 newState, err := op.Prepare(operation.State{}) 118 c.Assert(err, jc.ErrorIsNil) 119 c.Assert(newState, gc.NotNil) 120 ctx.CheckCall(c, 0, "Prepare") 121 } 122 123 func (s *RunActionSuite) TestPrepareCtxError(c *gc.C) { 124 ctx := &MockContext{actionData: &runner.ActionData{Name: "some-action-name"}} 125 ctx.SetErrors(errors.New("ctx prepare error")) 126 runnerFactory := &MockRunnerFactory{ 127 MockNewActionRunner: &MockNewActionRunner{ 128 runner: &MockRunner{ 129 context: ctx, 130 }, 131 }, 132 } 133 factory := operation.NewFactory(operation.FactoryParams{ 134 RunnerFactory: runnerFactory, 135 }) 136 op, err := factory.NewAction(someActionId) 137 c.Assert(err, jc.ErrorIsNil) 138 139 newState, err := op.Prepare(operation.State{}) 140 c.Assert(err, gc.ErrorMatches, `ctx prepare error`) 141 c.Assert(newState, gc.IsNil) 142 ctx.CheckCall(c, 0, "Prepare") 143 } 144 145 func (s *RunActionSuite) TestPrepareSuccessCleanState(c *gc.C) { 146 runnerFactory := NewRunActionRunnerFactory(errors.New("should not call")) 147 factory := operation.NewFactory(operation.FactoryParams{ 148 RunnerFactory: runnerFactory, 149 }) 150 op, err := factory.NewAction(someActionId) 151 c.Assert(err, jc.ErrorIsNil) 152 153 newState, err := op.Prepare(operation.State{}) 154 c.Assert(err, jc.ErrorIsNil) 155 c.Assert(newState, jc.DeepEquals, &operation.State{ 156 Kind: operation.RunAction, 157 Step: operation.Pending, 158 ActionId: &someActionId, 159 }) 160 c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId) 161 } 162 163 func (s *RunActionSuite) TestPrepareSuccessDirtyState(c *gc.C) { 164 runnerFactory := NewRunActionRunnerFactory(errors.New("should not call")) 165 factory := operation.NewFactory(operation.FactoryParams{ 166 RunnerFactory: runnerFactory, 167 }) 168 op, err := factory.NewAction(someActionId) 169 c.Assert(err, jc.ErrorIsNil) 170 171 newState, err := op.Prepare(overwriteState) 172 c.Assert(err, jc.ErrorIsNil) 173 c.Assert(newState, jc.DeepEquals, &operation.State{ 174 Kind: operation.RunAction, 175 Step: operation.Pending, 176 ActionId: &someActionId, 177 Started: true, 178 CollectMetricsTime: 1234567, 179 UpdateStatusTime: 1234567, 180 Hook: &hook.Info{Kind: hooks.Install}, 181 }) 182 c.Assert(*runnerFactory.MockNewActionRunner.gotActionId, gc.Equals, someActionId) 183 } 184 185 func (s *RunActionSuite) TestExecuteSuccess(c *gc.C) { 186 var stateChangeTests = []struct { 187 description string 188 before operation.State 189 after operation.State 190 }{{ 191 description: "empty state", 192 after: operation.State{ 193 Kind: operation.RunAction, 194 Step: operation.Done, 195 ActionId: &someActionId, 196 }, 197 }, { 198 description: "preserves appropriate fields", 199 before: overwriteState, 200 after: operation.State{ 201 Kind: operation.RunAction, 202 Step: operation.Done, 203 ActionId: &someActionId, 204 Hook: &hook.Info{Kind: hooks.Install}, 205 Started: true, 206 CollectMetricsTime: 1234567, 207 UpdateStatusTime: 1234567, 208 }, 209 }} 210 211 for i, test := range stateChangeTests { 212 c.Logf("test %d: %s", i, test.description) 213 runnerFactory := NewRunActionRunnerFactory(nil) 214 callbacks := &RunActionCallbacks{} 215 factory := operation.NewFactory(operation.FactoryParams{ 216 RunnerFactory: runnerFactory, 217 Callbacks: callbacks, 218 }) 219 op, err := factory.NewAction(someActionId) 220 c.Assert(err, jc.ErrorIsNil) 221 midState, err := op.Prepare(test.before) 222 c.Assert(midState, gc.NotNil) 223 c.Assert(err, jc.ErrorIsNil) 224 225 newState, err := op.Execute(*midState) 226 c.Assert(err, jc.ErrorIsNil) 227 c.Assert(newState, jc.DeepEquals, &test.after) 228 c.Assert(callbacks.executingMessage, gc.Equals, "running action some-action-name") 229 c.Assert(*runnerFactory.MockNewActionRunner.runner.MockRunAction.gotName, gc.Equals, "some-action-name") 230 } 231 } 232 233 func (s *RunActionSuite) TestCommit(c *gc.C) { 234 var stateChangeTests = []struct { 235 description string 236 before operation.State 237 after operation.State 238 }{{ 239 description: "empty state", 240 after: operation.State{ 241 Kind: operation.Continue, 242 Step: operation.Pending, 243 }, 244 }, { 245 description: "preserves only appropriate fields, no hook", 246 before: operation.State{ 247 Kind: operation.Continue, 248 Step: operation.Pending, 249 Started: true, 250 CollectMetricsTime: 1234567, 251 UpdateStatusTime: 1234567, 252 CharmURL: curl("cs:quantal/wordpress-2"), 253 ActionId: &randomActionId, 254 }, 255 after: operation.State{ 256 Kind: operation.Continue, 257 Step: operation.Pending, 258 Started: true, 259 CollectMetricsTime: 1234567, 260 UpdateStatusTime: 1234567, 261 }, 262 }, { 263 description: "preserves only appropriate fields, with hook", 264 before: operation.State{ 265 Kind: operation.Continue, 266 Step: operation.Pending, 267 Started: true, 268 CollectMetricsTime: 1234567, 269 UpdateStatusTime: 1234567, 270 CharmURL: curl("cs:quantal/wordpress-2"), 271 ActionId: &randomActionId, 272 Hook: &hook.Info{Kind: hooks.Install}, 273 }, 274 after: operation.State{ 275 Kind: operation.RunHook, 276 Step: operation.Pending, 277 Hook: &hook.Info{Kind: hooks.Install}, 278 Started: true, 279 CollectMetricsTime: 1234567, 280 UpdateStatusTime: 1234567, 281 }, 282 }} 283 284 for i, test := range stateChangeTests { 285 c.Logf("test %d: %s", i, test.description) 286 factory := operation.NewFactory(operation.FactoryParams{}) 287 op, err := factory.NewAction(someActionId) 288 c.Assert(err, jc.ErrorIsNil) 289 290 newState, err := op.Commit(test.before) 291 c.Assert(newState, jc.DeepEquals, &test.after) 292 } 293 } 294 295 func (s *RunActionSuite) TestNeedsGlobalMachineLock(c *gc.C) { 296 factory := operation.NewFactory(operation.FactoryParams{}) 297 op, err := factory.NewAction(someActionId) 298 c.Assert(err, jc.ErrorIsNil) 299 c.Assert(op.NeedsGlobalMachineLock(), jc.IsTrue) 300 }