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