github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/uniter/runner/context/contextfactory_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package context_test 5 6 import ( 7 "os" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 "github.com/juju/utils" 14 "github.com/juju/utils/fs" 15 gc "gopkg.in/check.v1" 16 "gopkg.in/juju/charm.v6-unstable/hooks" 17 18 "github.com/juju/juju/apiserver/params" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/storage" 21 "github.com/juju/juju/testcharms" 22 "github.com/juju/juju/worker/leadership" 23 "github.com/juju/juju/worker/uniter/hook" 24 "github.com/juju/juju/worker/uniter/runner/context" 25 runnertesting "github.com/juju/juju/worker/uniter/runner/testing" 26 ) 27 28 type ContextFactorySuite struct { 29 HookContextSuite 30 paths runnertesting.RealPaths 31 factory context.ContextFactory 32 membership map[int][]string 33 } 34 35 var _ = gc.Suite(&ContextFactorySuite{}) 36 37 func (s *ContextFactorySuite) SetUpTest(c *gc.C) { 38 s.HookContextSuite.SetUpTest(c) 39 s.paths = runnertesting.NewRealPaths(c) 40 s.membership = map[int][]string{} 41 42 contextFactory, err := context.NewContextFactory( 43 s.uniter, 44 s.unit.Tag().(names.UnitTag), 45 runnertesting.FakeTracker{}, 46 s.getRelationInfos, 47 s.storage, 48 s.paths, 49 ) 50 c.Assert(err, jc.ErrorIsNil) 51 s.factory = contextFactory 52 } 53 54 func (s *ContextFactorySuite) setUpCacheMethods(c *gc.C) { 55 // The factory's caches are created lazily, so it doesn't have any at all to 56 // begin with. Creating and discarding a context lets us call updateCache 57 // without panicking. (IMO this is less invasive that making updateCache 58 // responsible for creating missing caches etc.) 59 _, err := s.factory.HookContext(hook.Info{Kind: hooks.Install}) 60 c.Assert(err, jc.ErrorIsNil) 61 } 62 63 func (s *ContextFactorySuite) updateCache(relId int, unitName string, settings params.Settings) { 64 context.UpdateCachedSettings(s.factory, relId, unitName, settings) 65 } 66 67 func (s *ContextFactorySuite) getCache(relId int, unitName string) (params.Settings, bool) { 68 return context.CachedSettings(s.factory, relId, unitName) 69 } 70 71 func (s *ContextFactorySuite) SetCharm(c *gc.C, name string) { 72 err := os.RemoveAll(s.paths.GetCharmDir()) 73 c.Assert(err, jc.ErrorIsNil) 74 err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.GetCharmDir()) 75 c.Assert(err, jc.ErrorIsNil) 76 } 77 78 func (s *ContextFactorySuite) getRelationInfos() map[int]*context.RelationInfo { 79 info := map[int]*context.RelationInfo{} 80 for relId, relUnit := range s.apiRelunits { 81 info[relId] = &context.RelationInfo{ 82 RelationUnit: relUnit, 83 MemberNames: s.membership[relId], 84 } 85 } 86 return info 87 } 88 89 func (s *ContextFactorySuite) testLeadershipContextWiring(c *gc.C, createContext func() *context.HookContext) { 90 var stub testing.Stub 91 stub.SetErrors(errors.New("bam")) 92 restore := context.PatchNewLeadershipContext( 93 func(accessor context.LeadershipSettingsAccessor, tracker leadership.Tracker) context.LeadershipContext { 94 stub.AddCall("NewLeadershipContext", accessor, tracker) 95 return &StubLeadershipContext{Stub: &stub} 96 }, 97 ) 98 defer restore() 99 100 ctx := createContext() 101 isLeader, err := ctx.IsLeader() 102 c.Check(err, gc.ErrorMatches, "bam") 103 c.Check(isLeader, jc.IsFalse) 104 105 stub.CheckCalls(c, []testing.StubCall{{ 106 FuncName: "NewLeadershipContext", 107 Args: []interface{}{s.uniter.LeadershipSettings, runnertesting.FakeTracker{}}, 108 }, { 109 FuncName: "IsLeader", 110 }}) 111 112 } 113 114 func (s *ContextFactorySuite) TestNewHookContextLeadershipContext(c *gc.C) { 115 s.testLeadershipContextWiring(c, func() *context.HookContext { 116 ctx, err := s.factory.HookContext(hook.Info{Kind: hooks.ConfigChanged}) 117 c.Assert(err, jc.ErrorIsNil) 118 return ctx 119 }) 120 } 121 122 func (s *ContextFactorySuite) TestNewCommandContextLeadershipContext(c *gc.C) { 123 s.testLeadershipContextWiring(c, func() *context.HookContext { 124 ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: -1}) 125 c.Assert(err, jc.ErrorIsNil) 126 return ctx 127 }) 128 } 129 130 func (s *ContextFactorySuite) TestNewActionContextLeadershipContext(c *gc.C) { 131 s.testLeadershipContextWiring(c, func() *context.HookContext { 132 s.SetCharm(c, "dummy") 133 action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil) 134 c.Assert(err, jc.ErrorIsNil) 135 136 actionData := &context.ActionData{ 137 Name: action.Name(), 138 Tag: names.NewActionTag(action.Id()), 139 Params: action.Parameters(), 140 ResultsMap: map[string]interface{}{}, 141 } 142 143 ctx, err := s.factory.ActionContext(actionData) 144 c.Assert(err, jc.ErrorIsNil) 145 return ctx 146 }) 147 } 148 149 func (s *ContextFactorySuite) TestRelationHookContext(c *gc.C) { 150 hi := hook.Info{ 151 Kind: hooks.RelationBroken, 152 RelationId: 1, 153 } 154 ctx, err := s.factory.HookContext(hi) 155 c.Assert(err, jc.ErrorIsNil) 156 s.AssertCoreContext(c, ctx) 157 s.AssertNotActionContext(c, ctx) 158 s.AssertRelationContext(c, ctx, 1, "") 159 s.AssertNotStorageContext(c, ctx) 160 } 161 162 func (s *ContextFactorySuite) TestNewHookContextWithStorage(c *gc.C) { 163 // We need to set up a unit that has storage metadata defined. 164 ch := s.AddTestingCharm(c, "storage-block") 165 sCons := map[string]state.StorageConstraints{ 166 "data": {Pool: "", Size: 1024, Count: 1}, 167 } 168 service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons) 169 s.machine = nil // allocate a new machine 170 unit := s.AddUnit(c, service) 171 172 storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag()) 173 c.Assert(err, jc.ErrorIsNil) 174 c.Assert(storageAttachments, gc.HasLen, 1) 175 storageTag := storageAttachments[0].StorageInstance() 176 177 volume, err := s.State.StorageInstanceVolume(storageTag) 178 c.Assert(err, jc.ErrorIsNil) 179 volumeTag := volume.VolumeTag() 180 machineTag := s.machine.MachineTag() 181 182 err = s.State.SetVolumeInfo( 183 volumeTag, state.VolumeInfo{ 184 VolumeId: "vol-123", 185 Size: 456, 186 }, 187 ) 188 c.Assert(err, jc.ErrorIsNil) 189 err = s.State.SetVolumeAttachmentInfo( 190 machineTag, volumeTag, state.VolumeAttachmentInfo{ 191 DeviceName: "sdb", 192 }, 193 ) 194 c.Assert(err, jc.ErrorIsNil) 195 196 password, err := utils.RandomPassword() 197 err = unit.SetPassword(password) 198 c.Assert(err, jc.ErrorIsNil) 199 st := s.OpenAPIAs(c, unit.Tag(), password) 200 uniter, err := st.Uniter() 201 c.Assert(err, jc.ErrorIsNil) 202 203 contextFactory, err := context.NewContextFactory( 204 uniter, 205 unit.Tag().(names.UnitTag), 206 runnertesting.FakeTracker{}, 207 s.getRelationInfos, 208 s.storage, 209 s.paths, 210 ) 211 c.Assert(err, jc.ErrorIsNil) 212 ctx, err := contextFactory.HookContext(hook.Info{ 213 Kind: hooks.StorageAttached, 214 StorageId: "data/0", 215 }) 216 c.Assert(err, jc.ErrorIsNil) 217 c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0") 218 s.AssertStorageContext(c, ctx, "data/0", storage.StorageAttachmentInfo{ 219 Kind: storage.StorageKindBlock, 220 Location: "/dev/sdb", 221 }) 222 s.AssertNotActionContext(c, ctx) 223 s.AssertNotRelationContext(c, ctx) 224 } 225 226 func (s *ContextFactorySuite) TestActionContext(c *gc.C) { 227 s.SetCharm(c, "dummy") 228 action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil) 229 c.Assert(err, jc.ErrorIsNil) 230 231 actionData := &context.ActionData{ 232 Name: action.Name(), 233 Tag: names.NewActionTag(action.Id()), 234 Params: action.Parameters(), 235 ResultsMap: map[string]interface{}{}, 236 } 237 238 ctx, err := s.factory.ActionContext(actionData) 239 c.Assert(err, jc.ErrorIsNil) 240 241 s.AssertCoreContext(c, ctx) 242 s.AssertActionContext(c, ctx) 243 s.AssertNotRelationContext(c, ctx) 244 s.AssertNotStorageContext(c, ctx) 245 } 246 247 func (s *ContextFactorySuite) TestCommandContext(c *gc.C) { 248 ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: -1}) 249 c.Assert(err, jc.ErrorIsNil) 250 251 s.AssertCoreContext(c, ctx) 252 s.AssertNotActionContext(c, ctx) 253 s.AssertNotRelationContext(c, ctx) 254 s.AssertNotStorageContext(c, ctx) 255 } 256 257 func (s *ContextFactorySuite) TestCommandContextNoRelation(c *gc.C) { 258 ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: -1}) 259 c.Assert(err, jc.ErrorIsNil) 260 s.AssertCoreContext(c, ctx) 261 s.AssertNotActionContext(c, ctx) 262 s.AssertNotRelationContext(c, ctx) 263 s.AssertNotStorageContext(c, ctx) 264 } 265 266 func (s *ContextFactorySuite) TestNewCommandContextForceNoRemoteUnit(c *gc.C) { 267 ctx, err := s.factory.CommandContext(context.CommandInfo{ 268 RelationId: 0, ForceRemoteUnit: true, 269 }) 270 c.Assert(err, jc.ErrorIsNil) 271 s.AssertCoreContext(c, ctx) 272 s.AssertNotActionContext(c, ctx) 273 s.AssertRelationContext(c, ctx, 0, "") 274 s.AssertNotStorageContext(c, ctx) 275 } 276 277 func (s *ContextFactorySuite) TestNewCommandContextForceRemoteUnitMissing(c *gc.C) { 278 ctx, err := s.factory.CommandContext(context.CommandInfo{ 279 RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true, 280 }) 281 c.Assert(err, gc.IsNil) 282 s.AssertCoreContext(c, ctx) 283 s.AssertNotActionContext(c, ctx) 284 s.AssertRelationContext(c, ctx, 0, "blah/123") 285 s.AssertNotStorageContext(c, ctx) 286 } 287 288 func (s *ContextFactorySuite) TestNewCommandContextInferRemoteUnit(c *gc.C) { 289 s.membership[0] = []string{"foo/2"} 290 ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: 0}) 291 c.Assert(err, jc.ErrorIsNil) 292 s.AssertCoreContext(c, ctx) 293 s.AssertNotActionContext(c, ctx) 294 s.AssertRelationContext(c, ctx, 0, "foo/2") 295 s.AssertNotStorageContext(c, ctx) 296 } 297 298 func (s *ContextFactorySuite) TestNewHookContextPrunesNonMemberCaches(c *gc.C) { 299 300 // Write cached member settings for a member and a non-member. 301 s.setUpCacheMethods(c) 302 s.membership[0] = []string{"rel0/0"} 303 s.updateCache(0, "rel0/0", params.Settings{"keep": "me"}) 304 s.updateCache(0, "rel0/1", params.Settings{"drop": "me"}) 305 306 ctx, err := s.factory.HookContext(hook.Info{Kind: hooks.Install}) 307 c.Assert(err, jc.ErrorIsNil) 308 309 settings0, found := s.getCache(0, "rel0/0") 310 c.Assert(found, jc.IsTrue) 311 c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"}) 312 313 settings1, found := s.getCache(0, "rel0/1") 314 c.Assert(found, jc.IsFalse) 315 c.Assert(settings1, gc.IsNil) 316 317 // Check the caches are being used by the context relations. 318 relCtx, err := ctx.Relation(0) 319 c.Assert(err, jc.ErrorIsNil) 320 321 // Verify that the settings really were cached by trying to look them up. 322 // Nothing's really in scope, so the call would fail if they weren't. 323 settings0, err = relCtx.ReadSettings("rel0/0") 324 c.Assert(err, jc.ErrorIsNil) 325 c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"}) 326 327 // Verify that the non-member settings were purged by looking them up and 328 // checking for the expected error. 329 settings1, err = relCtx.ReadSettings("rel0/1") 330 c.Assert(settings1, gc.IsNil) 331 c.Assert(err, gc.ErrorMatches, "permission denied") 332 } 333 334 func (s *ContextFactorySuite) TestNewHookContextRelationJoinedUpdatesRelationContextAndCaches(c *gc.C) { 335 // Write some cached settings for r/0, so we can verify the cache gets cleared. 336 s.setUpCacheMethods(c) 337 s.membership[1] = []string{"r/0"} 338 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 339 340 ctx, err := s.factory.HookContext(hook.Info{ 341 Kind: hooks.RelationJoined, 342 RelationId: 1, 343 RemoteUnit: "r/0", 344 }) 345 c.Assert(err, jc.ErrorIsNil) 346 s.AssertCoreContext(c, ctx) 347 s.AssertNotActionContext(c, ctx) 348 s.AssertNotStorageContext(c, ctx) 349 rel := s.AssertRelationContext(c, ctx, 1, "r/0") 350 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0"}) 351 cached0, member := s.getCache(1, "r/0") 352 c.Assert(cached0, gc.IsNil) 353 c.Assert(member, jc.IsTrue) 354 } 355 356 func (s *ContextFactorySuite) TestNewHookContextRelationChangedUpdatesRelationContextAndCaches(c *gc.C) { 357 // Update member settings to have actual values, so we can check that 358 // the change for r/4 clears its cache but leaves r/0's alone. 359 s.setUpCacheMethods(c) 360 s.membership[1] = []string{"r/0", "r/4"} 361 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 362 s.updateCache(1, "r/4", params.Settings{"baz": "qux"}) 363 364 ctx, err := s.factory.HookContext(hook.Info{ 365 Kind: hooks.RelationChanged, 366 RelationId: 1, 367 RemoteUnit: "r/4", 368 }) 369 c.Assert(err, jc.ErrorIsNil) 370 s.AssertCoreContext(c, ctx) 371 s.AssertNotActionContext(c, ctx) 372 s.AssertNotStorageContext(c, ctx) 373 rel := s.AssertRelationContext(c, ctx, 1, "r/4") 374 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"}) 375 cached0, member := s.getCache(1, "r/0") 376 c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"}) 377 c.Assert(member, jc.IsTrue) 378 cached4, member := s.getCache(1, "r/4") 379 c.Assert(cached4, gc.IsNil) 380 c.Assert(member, jc.IsTrue) 381 } 382 383 func (s *ContextFactorySuite) TestNewHookContextRelationDepartedUpdatesRelationContextAndCaches(c *gc.C) { 384 // Update member settings to have actual values, so we can check that 385 // the depart for r/0 leaves r/4's cache alone (while discarding r/0's). 386 s.setUpCacheMethods(c) 387 s.membership[1] = []string{"r/0", "r/4"} 388 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 389 s.updateCache(1, "r/4", params.Settings{"baz": "qux"}) 390 391 ctx, err := s.factory.HookContext(hook.Info{ 392 Kind: hooks.RelationDeparted, 393 RelationId: 1, 394 RemoteUnit: "r/0", 395 }) 396 c.Assert(err, jc.ErrorIsNil) 397 s.AssertCoreContext(c, ctx) 398 s.AssertNotActionContext(c, ctx) 399 s.AssertNotStorageContext(c, ctx) 400 rel := s.AssertRelationContext(c, ctx, 1, "r/0") 401 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/4"}) 402 cached0, member := s.getCache(1, "r/0") 403 c.Assert(cached0, gc.IsNil) 404 c.Assert(member, jc.IsFalse) 405 cached4, member := s.getCache(1, "r/4") 406 c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"}) 407 c.Assert(member, jc.IsTrue) 408 } 409 410 func (s *ContextFactorySuite) TestNewHookContextRelationBrokenRetainsCaches(c *gc.C) { 411 // Note that this is bizarre and unrealistic, because we would never usually 412 // run relation-broken on a non-empty relation. But verfying that the settings 413 // stick around allows us to verify that there's no special handling for that 414 // hook -- as there should not be, because the relation caches will be discarded 415 // for the *next* hook, which will be constructed with the current set of known 416 // relations and ignore everything else. 417 s.setUpCacheMethods(c) 418 s.membership[1] = []string{"r/0", "r/4"} 419 s.updateCache(1, "r/0", params.Settings{"foo": "bar"}) 420 s.updateCache(1, "r/4", params.Settings{"baz": "qux"}) 421 422 ctx, err := s.factory.HookContext(hook.Info{ 423 Kind: hooks.RelationBroken, 424 RelationId: 1, 425 }) 426 c.Assert(err, jc.ErrorIsNil) 427 rel := s.AssertRelationContext(c, ctx, 1, "") 428 c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"}) 429 cached0, member := s.getCache(1, "r/0") 430 c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"}) 431 c.Assert(member, jc.IsTrue) 432 cached4, member := s.getCache(1, "r/4") 433 c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"}) 434 c.Assert(member, jc.IsTrue) 435 } 436 437 type StubLeadershipContext struct { 438 context.LeadershipContext 439 *testing.Stub 440 } 441 442 func (stub *StubLeadershipContext) IsLeader() (bool, error) { 443 stub.MethodCall(stub, "IsLeader") 444 return false, stub.NextErr() 445 }