github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/uniter/runner/factory_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runner_test 5 6 import ( 7 "os" 8 "strings" 9 "time" 10 11 "github.com/juju/errors" 12 "github.com/juju/names" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils" 15 "github.com/juju/utils/fs" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/charm.v5/hooks" 18 19 "github.com/juju/juju/apiserver/params" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/storage" 22 "github.com/juju/juju/testcharms" 23 "github.com/juju/juju/worker/uniter/hook" 24 "github.com/juju/juju/worker/uniter/runner" 25 ) 26 27 type FactorySuite struct { 28 HookContextSuite 29 paths RealPaths 30 factory runner.Factory 31 membership map[int][]string 32 } 33 34 var _ = gc.Suite(&FactorySuite{}) 35 36 func (s *FactorySuite) SetUpTest(c *gc.C) { 37 s.HookContextSuite.SetUpTest(c) 38 s.paths = NewRealPaths(c) 39 s.membership = map[int][]string{} 40 41 contextFactory, err := runner.NewContextFactory( 42 s.uniter, 43 s.unit.Tag().(names.UnitTag), 44 fakeTracker{}, 45 s.getRelationInfos, 46 s.storage, 47 s.paths, 48 ) 49 c.Assert(err, jc.ErrorIsNil) 50 51 factory, err := runner.NewFactory( 52 s.uniter, 53 s.paths, 54 contextFactory, 55 ) 56 c.Assert(err, jc.ErrorIsNil) 57 s.factory = factory 58 } 59 60 func (s *FactorySuite) SetCharm(c *gc.C, name string) { 61 err := os.RemoveAll(s.paths.charm) 62 c.Assert(err, jc.ErrorIsNil) 63 err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.charm) 64 c.Assert(err, jc.ErrorIsNil) 65 } 66 67 func (s *FactorySuite) getRelationInfos() map[int]*runner.RelationInfo { 68 info := map[int]*runner.RelationInfo{} 69 for relId, relUnit := range s.apiRelunits { 70 info[relId] = &runner.RelationInfo{ 71 RelationUnit: relUnit, 72 MemberNames: s.membership[relId], 73 } 74 } 75 return info 76 } 77 78 func (s *FactorySuite) setUpCacheMethods(c *gc.C) { 79 // The factory's caches are created lazily, so it doesn't have any at all to 80 // begin with. Creating and discarding a context lets us call updateCache 81 // without panicking. (IMO this is less invasive that making updateCache 82 // responsible for creating missing caches etc.) 83 _, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install}) 84 c.Assert(err, jc.ErrorIsNil) 85 } 86 87 func (s *FactorySuite) updateCache(relId int, unitName string, settings params.Settings) { 88 runner.UpdateCachedSettings(s.factory, relId, unitName, settings) 89 } 90 91 func (s *FactorySuite) getCache(relId int, unitName string) (params.Settings, bool) { 92 return runner.CachedSettings(s.factory, relId, unitName) 93 } 94 95 func (s *FactorySuite) AssertPaths(c *gc.C, rnr runner.Runner) { 96 c.Assert(runner.RunnerPaths(rnr), gc.DeepEquals, s.paths) 97 } 98 99 func (s *FactorySuite) TestNewCommandRunnerNoRelation(c *gc.C) { 100 rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: -1}) 101 c.Assert(err, jc.ErrorIsNil) 102 s.AssertPaths(c, rnr) 103 ctx := rnr.Context() 104 s.AssertCoreContext(c, ctx) 105 s.AssertNotActionContext(c, ctx) 106 s.AssertNotRelationContext(c, ctx) 107 s.AssertNotStorageContext(c, ctx) 108 } 109 110 func (s *FactorySuite) TestNewCommandRunnerRelationIdDoesNotExist(c *gc.C) { 111 for _, value := range []bool{true, false} { 112 _, err := s.factory.NewCommandRunner(runner.CommandInfo{ 113 RelationId: 12, ForceRemoteUnit: value, 114 }) 115 c.Check(err, gc.ErrorMatches, `unknown relation id: 12`) 116 } 117 } 118 119 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInvalid(c *gc.C) { 120 for _, value := range []bool{true, false} { 121 _, err := s.factory.NewCommandRunner(runner.CommandInfo{ 122 RelationId: 0, RemoteUnitName: "blah", ForceRemoteUnit: value, 123 }) 124 c.Check(err, gc.ErrorMatches, `invalid remote unit: blah`) 125 } 126 } 127 128 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInappropriate(c *gc.C) { 129 for _, value := range []bool{true, false} { 130 _, err := s.factory.NewCommandRunner(runner.CommandInfo{ 131 RelationId: -1, RemoteUnitName: "blah/123", ForceRemoteUnit: value, 132 }) 133 c.Check(err, gc.ErrorMatches, `remote unit provided without a relation: blah/123`) 134 } 135 } 136 137 func (s *FactorySuite) TestNewCommandRunnerEmptyRelation(c *gc.C) { 138 _, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 1}) 139 c.Check(err, gc.ErrorMatches, `cannot infer remote unit in empty relation 1`) 140 } 141 142 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitAmbiguous(c *gc.C) { 143 s.membership[1] = []string{"foo/0", "foo/1"} 144 _, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 1}) 145 c.Check(err, gc.ErrorMatches, `ambiguous remote unit; possibilities are \[foo/0 foo/1\]`) 146 } 147 148 func (s *FactorySuite) TestNewCommandRunnerRemoteUnitMissing(c *gc.C) { 149 s.membership[0] = []string{"foo/0", "foo/1"} 150 _, err := s.factory.NewCommandRunner(runner.CommandInfo{ 151 RelationId: 0, RemoteUnitName: "blah/123", 152 }) 153 c.Check(err, gc.ErrorMatches, `unknown remote unit blah/123; possibilities are \[foo/0 foo/1\]`) 154 } 155 156 func (s *FactorySuite) TestNewCommandRunnerForceNoRemoteUnit(c *gc.C) { 157 rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{ 158 RelationId: 0, ForceRemoteUnit: true, 159 }) 160 c.Assert(err, jc.ErrorIsNil) 161 s.AssertPaths(c, rnr) 162 ctx := rnr.Context() 163 s.AssertCoreContext(c, ctx) 164 s.AssertNotActionContext(c, ctx) 165 s.AssertRelationContext(c, ctx, 0, "") 166 s.AssertNotStorageContext(c, ctx) 167 } 168 169 func (s *FactorySuite) TestNewCommandRunnerForceRemoteUnitMissing(c *gc.C) { 170 rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{ 171 RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true, 172 }) 173 c.Assert(err, gc.IsNil) 174 ctx := rnr.Context() 175 s.AssertCoreContext(c, ctx) 176 s.AssertNotActionContext(c, ctx) 177 s.AssertRelationContext(c, ctx, 0, "blah/123") 178 s.AssertNotStorageContext(c, ctx) 179 } 180 181 func (s *FactorySuite) TestNewCommandRunnerInferRemoteUnit(c *gc.C) { 182 s.membership[0] = []string{"foo/2"} 183 rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 0}) 184 c.Assert(err, jc.ErrorIsNil) 185 s.AssertPaths(c, rnr) 186 ctx := rnr.Context() 187 s.AssertCoreContext(c, ctx) 188 s.AssertNotActionContext(c, ctx) 189 s.AssertRelationContext(c, ctx, 0, "foo/2") 190 s.AssertNotStorageContext(c, ctx) 191 } 192 193 func (s *FactorySuite) TestNewHookRunner(c *gc.C) { 194 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.ConfigChanged}) 195 c.Assert(err, jc.ErrorIsNil) 196 s.AssertPaths(c, rnr) 197 ctx := rnr.Context() 198 s.AssertCoreContext(c, ctx) 199 s.AssertNotActionContext(c, ctx) 200 s.AssertNotRelationContext(c, ctx) 201 s.AssertNotStorageContext(c, ctx) 202 } 203 204 func (s *FactorySuite) TestNewHookRunnerWithBadHook(c *gc.C) { 205 rnr, err := s.factory.NewHookRunner(hook.Info{}) 206 c.Assert(rnr, gc.IsNil) 207 c.Assert(err, gc.ErrorMatches, `unknown hook kind ""`) 208 } 209 210 func (s *FactorySuite) TestNewHookRunnerWithStorage(c *gc.C) { 211 // We need to set up a unit that has storage metadata defined. 212 ch := s.AddTestingCharm(c, "storage-block") 213 sCons := map[string]state.StorageConstraints{ 214 "data": {Pool: "", Size: 1024, Count: 1}, 215 } 216 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons) 217 s.machine = nil // allocate a new machine 218 unit := s.AddUnit(c, service) 219 220 storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag()) 221 c.Assert(err, jc.ErrorIsNil) 222 c.Assert(storageAttachments, gc.HasLen, 1) 223 storageTag := storageAttachments[0].StorageInstance() 224 225 volume, err := s.State.StorageInstanceVolume(storageTag) 226 c.Assert(err, jc.ErrorIsNil) 227 volumeTag := volume.VolumeTag() 228 machineTag := s.machine.MachineTag() 229 230 err = s.State.SetVolumeInfo( 231 volumeTag, state.VolumeInfo{ 232 VolumeId: "vol-123", 233 Size: 456, 234 }, 235 ) 236 c.Assert(err, jc.ErrorIsNil) 237 err = s.State.SetVolumeAttachmentInfo( 238 machineTag, volumeTag, state.VolumeAttachmentInfo{ 239 DeviceName: "sdb", 240 }, 241 ) 242 c.Assert(err, jc.ErrorIsNil) 243 244 password, err := utils.RandomPassword() 245 err = unit.SetPassword(password) 246 c.Assert(err, jc.ErrorIsNil) 247 st := s.OpenAPIAs(c, unit.Tag(), password) 248 uniter, err := st.Uniter() 249 c.Assert(err, jc.ErrorIsNil) 250 251 contextFactory, err := runner.NewContextFactory( 252 uniter, 253 unit.Tag().(names.UnitTag), 254 fakeTracker{}, 255 s.getRelationInfos, 256 s.storage, 257 s.paths, 258 ) 259 c.Assert(err, jc.ErrorIsNil) 260 factory, err := runner.NewFactory( 261 uniter, 262 s.paths, 263 contextFactory, 264 ) 265 c.Assert(err, jc.ErrorIsNil) 266 267 rnr, err := factory.NewHookRunner(hook.Info{ 268 Kind: hooks.StorageAttached, 269 StorageId: "data/0", 270 }) 271 c.Assert(err, jc.ErrorIsNil) 272 s.AssertPaths(c, rnr) 273 ctx := rnr.Context() 274 c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0") 275 s.AssertStorageContext(c, ctx, "data/0", storage.StorageAttachmentInfo{ 276 Kind: storage.StorageKindBlock, 277 Location: "/dev/sdb", 278 }) 279 s.AssertNotActionContext(c, ctx) 280 s.AssertNotRelationContext(c, ctx) 281 } 282 283 func (s *FactorySuite) TestNewHookRunnerWithRelation(c *gc.C) { 284 rnr, err := s.factory.NewHookRunner(hook.Info{ 285 Kind: hooks.RelationBroken, 286 RelationId: 1, 287 }) 288 c.Assert(err, jc.ErrorIsNil) 289 s.AssertPaths(c, rnr) 290 ctx := rnr.Context() 291 s.AssertCoreContext(c, ctx) 292 s.AssertNotActionContext(c, ctx) 293 s.AssertRelationContext(c, ctx, 1, "") 294 s.AssertNotStorageContext(c, ctx) 295 } 296 297 func (s *FactorySuite) TestNewHookRunnerPrunesNonMemberCaches(c *gc.C) { 298 299 // Write cached member settings for a member and a non-member. 300 s.setUpCacheMethods(c) 301 s.membership[0] = []string{"rel0/0"} 302 s.updateCache(0, "rel0/0", params.Settings{"keep": "me"}) 303 s.updateCache(0, "rel0/1", params.Settings{"drop": "me"}) 304 305 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install}) 306 c.Assert(err, jc.ErrorIsNil) 307 s.AssertPaths(c, rnr) 308 ctx := rnr.Context() 309 310 settings0, found := s.getCache(0, "rel0/0") 311 c.Assert(found, jc.IsTrue) 312 c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"}) 313 314 settings1, found := s.getCache(0, "rel0/1") 315 c.Assert(found, jc.IsFalse) 316 c.Assert(settings1, gc.IsNil) 317 318 // Check the caches are being used by the context relations. 319 relCtx, found := ctx.Relation(0) 320 c.Assert(found, jc.IsTrue) 321 322 // Verify that the settings really were cached by trying to look them up. 323 // Nothing's really in scope, so the call would fail if they weren't. 324 settings0, err = relCtx.ReadSettings("rel0/0") 325 c.Assert(err, jc.ErrorIsNil) 326 c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"}) 327 328 // Verify that the non-member settings were purged by looking them up and 329 // checking for the expected error. 330 settings1, err = relCtx.ReadSettings("rel0/1") 331 c.Assert(settings1, gc.IsNil) 332 c.Assert(err, gc.ErrorMatches, "permission denied") 333 } 334 335 func (s *FactorySuite) TestNewHookRunnerRelationJoinedUpdatesRelationContextAndCaches(c *gc.C) { 336 // Write some cached settings for r/0, so we can verify the cache gets cleared. 337 s.setUpCacheMethods(c) 338 s.membership[1] = []string{"r/0"} 339 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 340 341 rnr, err := s.factory.NewHookRunner(hook.Info{ 342 Kind: hooks.RelationJoined, 343 RelationId: 1, 344 RemoteUnit: "r/0", 345 }) 346 c.Assert(err, jc.ErrorIsNil) 347 s.AssertPaths(c, rnr) 348 ctx := rnr.Context() 349 s.AssertCoreContext(c, ctx) 350 s.AssertNotActionContext(c, ctx) 351 s.AssertNotStorageContext(c, ctx) 352 rel := s.AssertRelationContext(c, ctx, 1, "r/0") 353 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0"}) 354 cached0, member := s.getCache(1, "r/0") 355 c.Assert(cached0, gc.IsNil) 356 c.Assert(member, jc.IsTrue) 357 } 358 359 func (s *FactorySuite) TestNewHookRunnerRelationChangedUpdatesRelationContextAndCaches(c *gc.C) { 360 // Update member settings to have actual values, so we can check that 361 // the change for r/4 clears its cache but leaves r/0's alone. 362 s.setUpCacheMethods(c) 363 s.membership[1] = []string{"r/0", "r/4"} 364 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 365 s.updateCache(1, "r/4", params.Settings{"baz": "qux"}) 366 367 rnr, err := s.factory.NewHookRunner(hook.Info{ 368 Kind: hooks.RelationChanged, 369 RelationId: 1, 370 RemoteUnit: "r/4", 371 }) 372 c.Assert(err, jc.ErrorIsNil) 373 s.AssertPaths(c, rnr) 374 ctx := rnr.Context() 375 s.AssertCoreContext(c, ctx) 376 s.AssertNotActionContext(c, ctx) 377 s.AssertNotStorageContext(c, ctx) 378 rel := s.AssertRelationContext(c, ctx, 1, "r/4") 379 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"}) 380 cached0, member := s.getCache(1, "r/0") 381 c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"}) 382 c.Assert(member, jc.IsTrue) 383 cached4, member := s.getCache(1, "r/4") 384 c.Assert(cached4, gc.IsNil) 385 c.Assert(member, jc.IsTrue) 386 } 387 388 func (s *FactorySuite) TestNewHookRunnerRelationDepartedUpdatesRelationContextAndCaches(c *gc.C) { 389 // Update member settings to have actual values, so we can check that 390 // the depart for r/0 leaves r/4's cache alone (while discarding r/0's). 391 s.setUpCacheMethods(c) 392 s.membership[1] = []string{"r/0", "r/4"} 393 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 394 s.updateCache(1, "r/4", params.Settings{"baz": "qux"}) 395 396 rnr, err := s.factory.NewHookRunner(hook.Info{ 397 Kind: hooks.RelationDeparted, 398 RelationId: 1, 399 RemoteUnit: "r/0", 400 }) 401 c.Assert(err, jc.ErrorIsNil) 402 s.AssertPaths(c, rnr) 403 ctx := rnr.Context() 404 s.AssertCoreContext(c, ctx) 405 s.AssertNotActionContext(c, ctx) 406 s.AssertNotStorageContext(c, ctx) 407 rel := s.AssertRelationContext(c, ctx, 1, "r/0") 408 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/4"}) 409 cached0, member := s.getCache(1, "r/0") 410 c.Assert(cached0, gc.IsNil) 411 c.Assert(member, jc.IsFalse) 412 cached4, member := s.getCache(1, "r/4") 413 c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"}) 414 c.Assert(member, jc.IsTrue) 415 } 416 417 func (s *FactorySuite) TestNewHookRunnerRelationBrokenRetainsCaches(c *gc.C) { 418 // Note that this is bizarre and unrealistic, because we would never usually 419 // run relation-broken on a non-empty relation. But verfying that the settings 420 // stick around allows us to verify that there's no special handling for that 421 // hook -- as there should not be, because the relation caches will be discarded 422 // for the *next* hook, which will be constructed with the current set of known 423 // relations and ignore everything else. 424 s.setUpCacheMethods(c) 425 s.membership[1] = []string{"r/0", "r/4"} 426 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 427 s.updateCache(1, "r/4", params.Settings{"baz": "qux"}) 428 429 rnr, err := s.factory.NewHookRunner(hook.Info{ 430 Kind: hooks.RelationBroken, 431 RelationId: 1, 432 }) 433 c.Assert(err, jc.ErrorIsNil) 434 s.AssertPaths(c, rnr) 435 ctx := rnr.Context() 436 rel := s.AssertRelationContext(c, ctx, 1, "") 437 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"}) 438 cached0, member := s.getCache(1, "r/0") 439 c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"}) 440 c.Assert(member, jc.IsTrue) 441 cached4, member := s.getCache(1, "r/4") 442 c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"}) 443 c.Assert(member, jc.IsTrue) 444 } 445 446 func (s *FactorySuite) TestNewHookRunnerWithBadRelation(c *gc.C) { 447 rnr, err := s.factory.NewHookRunner(hook.Info{ 448 Kind: hooks.RelationBroken, 449 RelationId: 12345, 450 }) 451 c.Assert(rnr, gc.IsNil) 452 c.Assert(err, gc.ErrorMatches, `unknown relation id: 12345`) 453 } 454 455 func (s *FactorySuite) TestNewHookRunnerMetricsDisabledHook(c *gc.C) { 456 s.SetCharm(c, "metered") 457 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install}) 458 c.Assert(err, jc.ErrorIsNil) 459 s.AssertPaths(c, rnr) 460 ctx := rnr.Context() 461 err = ctx.AddMetric("key", "value", time.Now()) 462 c.Assert(err, gc.ErrorMatches, "metrics disabled") 463 } 464 465 func (s *FactorySuite) TestNewHookRunnerMetricsDisabledUndeclared(c *gc.C) { 466 s.SetCharm(c, "mysql") 467 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics}) 468 c.Assert(err, jc.ErrorIsNil) 469 s.AssertPaths(c, rnr) 470 ctx := rnr.Context() 471 err = ctx.AddMetric("key", "value", time.Now()) 472 c.Assert(err, gc.ErrorMatches, "metrics disabled") 473 } 474 475 func (s *FactorySuite) TestNewHookRunnerMetricsDeclarationError(c *gc.C) { 476 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics}) 477 c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) 478 c.Assert(rnr, gc.IsNil) 479 } 480 481 func (s *FactorySuite) TestNewHookRunnerMetricsEnabled(c *gc.C) { 482 s.SetCharm(c, "metered") 483 484 rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics}) 485 c.Assert(err, jc.ErrorIsNil) 486 s.AssertPaths(c, rnr) 487 ctx := rnr.Context() 488 err = ctx.AddMetric("pings", "0.5", time.Now()) 489 c.Assert(err, jc.ErrorIsNil) 490 } 491 492 func (s *FactorySuite) TestNewActionRunnerGood(c *gc.C) { 493 s.SetCharm(c, "dummy") 494 action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{ 495 "outfile": "/some/file.bz2", 496 }) 497 c.Assert(err, jc.ErrorIsNil) 498 rnr, err := s.factory.NewActionRunner(action.Id()) 499 c.Assert(err, jc.ErrorIsNil) 500 s.AssertPaths(c, rnr) 501 ctx := rnr.Context() 502 data, err := ctx.ActionData() 503 c.Assert(err, jc.ErrorIsNil) 504 c.Assert(data, jc.DeepEquals, &runner.ActionData{ 505 Name: "snapshot", 506 Tag: action.ActionTag(), 507 Params: map[string]interface{}{ 508 "outfile": "/some/file.bz2", 509 }, 510 ResultsMap: map[string]interface{}{}, 511 }) 512 vars := ctx.HookVars(s.paths) 513 c.Assert(len(vars) > 0, jc.IsTrue, gc.Commentf("expected HookVars but found none")) 514 combined := strings.Join(vars, "|") 515 c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_NAME=snapshot(\|.*|$)`) 516 c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_UUID=`+action.Id()+`(\|.*|$)`) 517 c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_TAG=`+action.Tag().String()+`(\|.*|$)`) 518 } 519 520 func (s *FactorySuite) TestNewActionRunnerBadCharm(c *gc.C) { 521 rnr, err := s.factory.NewActionRunner("irrelevant") 522 c.Assert(rnr, gc.IsNil) 523 c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist) 524 c.Assert(err, gc.Not(jc.Satisfies), runner.IsBadActionError) 525 } 526 527 func (s *FactorySuite) TestNewActionRunnerBadName(c *gc.C) { 528 s.SetCharm(c, "dummy") 529 action, err := s.State.EnqueueAction(s.unit.Tag(), "no-such-action", nil) 530 c.Assert(err, jc.ErrorIsNil) // this will fail when using AddAction on unit 531 rnr, err := s.factory.NewActionRunner(action.Id()) 532 c.Check(rnr, gc.IsNil) 533 c.Check(err, gc.ErrorMatches, "cannot run \"no-such-action\" action: not defined") 534 c.Check(err, jc.Satisfies, runner.IsBadActionError) 535 } 536 537 func (s *FactorySuite) TestNewActionRunnerBadParams(c *gc.C) { 538 s.SetCharm(c, "dummy") 539 action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{ 540 "outfile": 123, 541 }) 542 c.Assert(err, jc.ErrorIsNil) // this will fail when state is done right 543 rnr, err := s.factory.NewActionRunner(action.Id()) 544 c.Check(rnr, gc.IsNil) 545 c.Check(err, gc.ErrorMatches, "cannot run \"snapshot\" action: .*") 546 c.Check(err, jc.Satisfies, runner.IsBadActionError) 547 } 548 549 func (s *FactorySuite) TestNewActionRunnerMissingAction(c *gc.C) { 550 s.SetCharm(c, "dummy") 551 action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil) 552 c.Assert(err, jc.ErrorIsNil) 553 _, err = s.unit.CancelAction(action) 554 c.Assert(err, jc.ErrorIsNil) 555 rnr, err := s.factory.NewActionRunner(action.Id()) 556 c.Check(rnr, gc.IsNil) 557 c.Check(err, gc.ErrorMatches, "action no longer available") 558 c.Check(err, gc.Equals, runner.ErrActionNotAvailable) 559 } 560 561 func (s *FactorySuite) TestNewActionRunnerUnauthAction(c *gc.C) { 562 s.SetCharm(c, "dummy") 563 otherUnit, err := s.service.AddUnit() 564 c.Assert(err, jc.ErrorIsNil) 565 action, err := s.State.EnqueueAction(otherUnit.Tag(), "snapshot", nil) 566 c.Assert(err, jc.ErrorIsNil) 567 rnr, err := s.factory.NewActionRunner(action.Id()) 568 c.Check(rnr, gc.IsNil) 569 c.Check(err, gc.ErrorMatches, "action no longer available") 570 c.Check(err, gc.Equals, runner.ErrActionNotAvailable) 571 }