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