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