github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/operation/executor_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 "path/filepath" 8 9 "github.com/juju/errors" 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 ft "github.com/juju/testing/filetesting" 13 gc "gopkg.in/check.v1" 14 corecharm "gopkg.in/juju/charm.v4" 15 "gopkg.in/juju/charm.v4/hooks" 16 17 "github.com/juju/juju/worker/uniter/hook" 18 "github.com/juju/juju/worker/uniter/operation" 19 ) 20 21 type NewExecutorSuite struct { 22 testing.IsolationSuite 23 basePath string 24 } 25 26 var _ = gc.Suite(&NewExecutorSuite{}) 27 28 func failGetInstallCharm() (*corecharm.URL, error) { 29 return nil, errors.New("lol!") 30 } 31 32 func (s *NewExecutorSuite) SetUpTest(c *gc.C) { 33 s.IsolationSuite.SetUpTest(c) 34 s.basePath = c.MkDir() 35 } 36 37 func (s *NewExecutorSuite) path(path string) string { 38 return filepath.Join(s.basePath, path) 39 } 40 41 func (s *NewExecutorSuite) TestNewExecutorNoFileNoCharm(c *gc.C) { 42 executor, err := operation.NewExecutor(s.path("missing"), failGetInstallCharm) 43 c.Assert(executor, gc.IsNil) 44 c.Assert(err, gc.ErrorMatches, "lol!") 45 } 46 47 func (s *NewExecutorSuite) TestNewExecutorInvalidFile(c *gc.C) { 48 ft.File{"existing", "", 0666}.Create(c, s.basePath) 49 executor, err := operation.NewExecutor(s.path("existing"), failGetInstallCharm) 50 c.Assert(executor, gc.IsNil) 51 c.Assert(err, gc.ErrorMatches, `cannot read ".*": invalid operation state: .*`) 52 } 53 54 func (s *NewExecutorSuite) TestNewExecutorNoFile(c *gc.C) { 55 charmURL := corecharm.MustParseURL("cs:quantal/nyancat-323") 56 getInstallCharm := func() (*corecharm.URL, error) { 57 return charmURL, nil 58 } 59 executor, err := operation.NewExecutor(s.path("missing"), getInstallCharm) 60 c.Assert(err, jc.ErrorIsNil) 61 c.Assert(executor.State(), gc.DeepEquals, operation.State{ 62 Kind: operation.Install, 63 Step: operation.Queued, 64 CharmURL: charmURL, 65 }) 66 ft.Removed{"missing"}.Check(c, s.basePath) 67 } 68 69 func (s *NewExecutorSuite) TestNewExecutorValidFile(c *gc.C) { 70 // note: this content matches valid persistent state as of 1.21; we expect 71 // that "hook" will have to become "last-hook" to enable action execution 72 // during hook error states. If you do this, please leave at least one test 73 // with this form of the yaml in place. 74 ft.File{"existing", ` 75 started: true 76 op: continue 77 opstep: pending 78 hook: {kind: config-changed} 79 `[1:], 0666}.Create(c, s.basePath) 80 executor, err := operation.NewExecutor(s.path("existing"), failGetInstallCharm) 81 c.Assert(err, jc.ErrorIsNil) 82 c.Assert(executor.State(), gc.DeepEquals, operation.State{ 83 Kind: operation.Continue, 84 Step: operation.Pending, 85 Started: true, 86 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 87 }) 88 } 89 90 type ExecutorSuite struct { 91 testing.IsolationSuite 92 } 93 94 var _ = gc.Suite(&ExecutorSuite{}) 95 96 func assertWroteState(c *gc.C, path string, expect operation.State) { 97 actual, err := operation.NewStateFile(path).Read() 98 c.Assert(err, jc.ErrorIsNil) 99 c.Assert(*actual, gc.DeepEquals, expect) 100 } 101 102 func newExecutor(c *gc.C, st *operation.State) (operation.Executor, string) { 103 path := filepath.Join(c.MkDir(), "state") 104 err := operation.NewStateFile(path).Write(st) 105 c.Assert(err, jc.ErrorIsNil) 106 executor, err := operation.NewExecutor(path, failGetInstallCharm) 107 c.Assert(err, jc.ErrorIsNil) 108 return executor, path 109 } 110 111 func justInstalledState() operation.State { 112 return operation.State{ 113 Kind: operation.Continue, 114 Step: operation.Pending, 115 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 116 } 117 } 118 119 func (s *ExecutorSuite) TestSucceedNoStateChanges(c *gc.C) { 120 initialState := justInstalledState() 121 executor, statePath := newExecutor(c, &initialState) 122 op := &mockOperation{ 123 prepare: newStep(nil, nil), 124 execute: newStep(nil, nil), 125 commit: newStep(nil, nil), 126 } 127 128 err := executor.Run(op) 129 c.Assert(err, jc.ErrorIsNil) 130 131 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 132 c.Assert(op.execute.gotState, gc.DeepEquals, initialState) 133 c.Assert(op.commit.gotState, gc.DeepEquals, initialState) 134 assertWroteState(c, statePath, initialState) 135 c.Assert(executor.State(), gc.DeepEquals, initialState) 136 } 137 138 func (s *ExecutorSuite) TestSucceedWithStateChanges(c *gc.C) { 139 initialState := justInstalledState() 140 executor, statePath := newExecutor(c, &initialState) 141 op := &mockOperation{ 142 prepare: newStep(&operation.State{ 143 Kind: operation.RunHook, 144 Step: operation.Pending, 145 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 146 }, nil), 147 execute: newStep(&operation.State{ 148 Kind: operation.RunHook, 149 Step: operation.Done, 150 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 151 }, nil), 152 commit: newStep(&operation.State{ 153 Kind: operation.RunHook, 154 Step: operation.Queued, 155 Hook: &hook.Info{Kind: hooks.Start}, 156 }, nil), 157 } 158 159 err := executor.Run(op) 160 c.Assert(err, jc.ErrorIsNil) 161 162 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 163 c.Assert(op.execute.gotState, gc.DeepEquals, *op.prepare.newState) 164 c.Assert(op.commit.gotState, gc.DeepEquals, *op.execute.newState) 165 assertWroteState(c, statePath, *op.commit.newState) 166 c.Assert(executor.State(), gc.DeepEquals, *op.commit.newState) 167 } 168 169 func (s *ExecutorSuite) TestErrSkipExecute(c *gc.C) { 170 initialState := justInstalledState() 171 executor, statePath := newExecutor(c, &initialState) 172 op := &mockOperation{ 173 prepare: newStep(&operation.State{ 174 Kind: operation.RunHook, 175 Step: operation.Pending, 176 Hook: &hook.Info{Kind: hooks.ConfigChanged}, 177 }, operation.ErrSkipExecute), 178 commit: newStep(&operation.State{ 179 Kind: operation.RunHook, 180 Step: operation.Queued, 181 Hook: &hook.Info{Kind: hooks.Start}, 182 }, nil), 183 } 184 185 err := executor.Run(op) 186 c.Assert(err, jc.ErrorIsNil) 187 188 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 189 c.Assert(op.commit.gotState, gc.DeepEquals, *op.prepare.newState) 190 assertWroteState(c, statePath, *op.commit.newState) 191 c.Assert(executor.State(), gc.DeepEquals, *op.commit.newState) 192 } 193 194 func (s *ExecutorSuite) TestValidateStateChange(c *gc.C) { 195 initialState := justInstalledState() 196 executor, statePath := newExecutor(c, &initialState) 197 op := &mockOperation{ 198 prepare: newStep(&operation.State{ 199 Kind: operation.RunHook, 200 Step: operation.Pending, 201 }, nil), 202 } 203 204 err := executor.Run(op) 205 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": invalid operation state: missing hook info`) 206 c.Assert(errors.Cause(err), gc.ErrorMatches, "missing hook info") 207 208 assertWroteState(c, statePath, initialState) 209 c.Assert(executor.State(), gc.DeepEquals, initialState) 210 } 211 212 func (s *ExecutorSuite) TestFailPrepareNoStateChange(c *gc.C) { 213 initialState := justInstalledState() 214 executor, statePath := newExecutor(c, &initialState) 215 op := &mockOperation{ 216 prepare: newStep(nil, errors.New("pow")), 217 } 218 219 err := executor.Run(op) 220 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": pow`) 221 c.Assert(errors.Cause(err), gc.ErrorMatches, "pow") 222 223 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 224 assertWroteState(c, statePath, initialState) 225 c.Assert(executor.State(), gc.DeepEquals, initialState) 226 } 227 228 func (s *ExecutorSuite) TestFailPrepareWithStateChange(c *gc.C) { 229 initialState := justInstalledState() 230 executor, statePath := newExecutor(c, &initialState) 231 op := &mockOperation{ 232 prepare: newStep(&operation.State{ 233 Kind: operation.RunHook, 234 Step: operation.Pending, 235 Hook: &hook.Info{Kind: hooks.Start}, 236 }, errors.New("blam")), 237 } 238 239 err := executor.Run(op) 240 c.Assert(err, gc.ErrorMatches, `preparing operation "mock operation": blam`) 241 c.Assert(errors.Cause(err), gc.ErrorMatches, "blam") 242 243 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 244 assertWroteState(c, statePath, *op.prepare.newState) 245 c.Assert(executor.State(), gc.DeepEquals, *op.prepare.newState) 246 } 247 248 func (s *ExecutorSuite) TestFailExecuteNoStateChange(c *gc.C) { 249 initialState := justInstalledState() 250 executor, statePath := newExecutor(c, &initialState) 251 op := &mockOperation{ 252 prepare: newStep(nil, nil), 253 execute: newStep(nil, errors.New("splat")), 254 } 255 256 err := executor.Run(op) 257 c.Assert(err, gc.ErrorMatches, `executing operation "mock operation": splat`) 258 c.Assert(errors.Cause(err), gc.ErrorMatches, "splat") 259 260 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 261 assertWroteState(c, statePath, initialState) 262 c.Assert(executor.State(), gc.DeepEquals, initialState) 263 } 264 265 func (s *ExecutorSuite) TestFailExecuteWithStateChange(c *gc.C) { 266 initialState := justInstalledState() 267 executor, statePath := newExecutor(c, &initialState) 268 op := &mockOperation{ 269 prepare: newStep(nil, nil), 270 execute: newStep(&operation.State{ 271 Kind: operation.RunHook, 272 Step: operation.Pending, 273 Hook: &hook.Info{Kind: hooks.Start}, 274 }, errors.New("kerblooie")), 275 } 276 277 err := executor.Run(op) 278 c.Assert(err, gc.ErrorMatches, `executing operation "mock operation": kerblooie`) 279 c.Assert(errors.Cause(err), gc.ErrorMatches, "kerblooie") 280 281 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 282 assertWroteState(c, statePath, *op.execute.newState) 283 c.Assert(executor.State(), gc.DeepEquals, *op.execute.newState) 284 } 285 286 func (s *ExecutorSuite) TestFailCommitNoStateChange(c *gc.C) { 287 initialState := justInstalledState() 288 executor, statePath := newExecutor(c, &initialState) 289 op := &mockOperation{ 290 prepare: newStep(nil, nil), 291 execute: newStep(nil, nil), 292 commit: newStep(nil, errors.New("whack")), 293 } 294 295 err := executor.Run(op) 296 c.Assert(err, gc.ErrorMatches, `committing operation "mock operation": whack`) 297 c.Assert(errors.Cause(err), gc.ErrorMatches, "whack") 298 299 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 300 assertWroteState(c, statePath, initialState) 301 c.Assert(executor.State(), gc.DeepEquals, initialState) 302 } 303 304 func (s *ExecutorSuite) TestFailCommitWithStateChange(c *gc.C) { 305 initialState := justInstalledState() 306 executor, statePath := newExecutor(c, &initialState) 307 op := &mockOperation{ 308 prepare: newStep(nil, nil), 309 execute: newStep(nil, nil), 310 commit: newStep(&operation.State{ 311 Kind: operation.RunHook, 312 Step: operation.Pending, 313 Hook: &hook.Info{Kind: hooks.Start}, 314 }, errors.New("take that you bandit")), 315 } 316 317 err := executor.Run(op) 318 c.Assert(err, gc.ErrorMatches, `committing operation "mock operation": take that you bandit`) 319 c.Assert(errors.Cause(err), gc.ErrorMatches, "take that you bandit") 320 321 c.Assert(op.prepare.gotState, gc.DeepEquals, initialState) 322 assertWroteState(c, statePath, *op.commit.newState) 323 c.Assert(executor.State(), gc.DeepEquals, *op.commit.newState) 324 } 325 326 type mockStep struct { 327 gotState operation.State 328 newState *operation.State 329 err error 330 } 331 332 func newStep(newState *operation.State, err error) *mockStep { 333 return &mockStep{newState: newState, err: err} 334 } 335 336 func (step *mockStep) run(state operation.State) (*operation.State, error) { 337 step.gotState = state 338 return step.newState, step.err 339 } 340 341 type mockOperation struct { 342 prepare *mockStep 343 execute *mockStep 344 commit *mockStep 345 } 346 347 func (op *mockOperation) String() string { 348 return "mock operation" 349 } 350 351 func (op *mockOperation) Prepare(state operation.State) (*operation.State, error) { 352 return op.prepare.run(state) 353 } 354 355 func (op *mockOperation) Execute(state operation.State) (*operation.State, error) { 356 return op.execute.run(state) 357 } 358 359 func (op *mockOperation) Commit(state operation.State) (*operation.State, error) { 360 return op.commit.run(state) 361 }