github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/uniter/runner/context/context_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package context_test 5 6 import ( 7 "errors" 8 "time" 9 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 "gopkg.in/juju/charm.v6-unstable" 14 15 "github.com/juju/juju/apiserver/params" 16 "github.com/juju/juju/network" 17 "github.com/juju/juju/status" 18 "github.com/juju/juju/worker/uniter/runner" 19 "github.com/juju/juju/worker/uniter/runner/context" 20 "github.com/juju/juju/worker/uniter/runner/jujuc" 21 ) 22 23 type InterfaceSuite struct { 24 HookContextSuite 25 stub testing.Stub 26 } 27 28 var _ = gc.Suite(&InterfaceSuite{}) 29 30 func (s *InterfaceSuite) TestUnitName(c *gc.C) { 31 ctx := s.GetContext(c, -1, "") 32 c.Assert(ctx.UnitName(), gc.Equals, "u/0") 33 } 34 35 func (s *InterfaceSuite) TestHookRelation(c *gc.C) { 36 ctx := s.GetContext(c, -1, "") 37 r, err := ctx.HookRelation() 38 c.Assert(err, gc.ErrorMatches, ".*") 39 c.Assert(r, gc.IsNil) 40 } 41 42 func (s *InterfaceSuite) TestRemoteUnitName(c *gc.C) { 43 ctx := s.GetContext(c, -1, "") 44 name, err := ctx.RemoteUnitName() 45 c.Assert(err, gc.ErrorMatches, ".*") 46 c.Assert(name, gc.Equals, "") 47 } 48 49 func (s *InterfaceSuite) TestRelationIds(c *gc.C) { 50 ctx := s.GetContext(c, -1, "") 51 relIds, err := ctx.RelationIds() 52 c.Assert(err, jc.ErrorIsNil) 53 c.Assert(relIds, gc.HasLen, 2) 54 r, err := ctx.Relation(0) 55 c.Assert(err, jc.ErrorIsNil) 56 c.Assert(r.Name(), gc.Equals, "db") 57 c.Assert(r.FakeId(), gc.Equals, "db:0") 58 r, err = ctx.Relation(123) 59 c.Assert(err, gc.ErrorMatches, ".*") 60 c.Assert(r, gc.IsNil) 61 } 62 63 func (s *InterfaceSuite) TestRelationContext(c *gc.C) { 64 ctx := s.GetContext(c, 1, "") 65 r, err := ctx.HookRelation() 66 c.Assert(err, jc.ErrorIsNil) 67 c.Assert(r.Name(), gc.Equals, "db") 68 c.Assert(r.FakeId(), gc.Equals, "db:1") 69 } 70 71 func (s *InterfaceSuite) TestRelationContextWithRemoteUnitName(c *gc.C) { 72 ctx := s.GetContext(c, 1, "u/123") 73 name, err := ctx.RemoteUnitName() 74 c.Assert(err, jc.ErrorIsNil) 75 c.Assert(name, gc.Equals, "u/123") 76 } 77 78 func (s *InterfaceSuite) TestAddingMetricsInWrongContext(c *gc.C) { 79 ctx := s.GetContext(c, 1, "u/123") 80 err := ctx.AddMetric("key", "123", time.Now()) 81 c.Assert(err, gc.ErrorMatches, "metrics not allowed in this context") 82 } 83 84 func (s *InterfaceSuite) TestAvailabilityZone(c *gc.C) { 85 ctx := s.GetContext(c, -1, "") 86 zone, err := ctx.AvailabilityZone() 87 c.Check(err, jc.ErrorIsNil) 88 c.Check(zone, gc.Equals, "a-zone") 89 } 90 91 func (s *InterfaceSuite) TestUnitNetworkConfig(c *gc.C) { 92 // Only the error case is tested to ensure end-to-end integration, the rest 93 // of the cases are tested separately for network-get, api/uniter, and 94 // apiserver/uniter, respectively. 95 ctx := s.GetContext(c, -1, "") 96 netConfig, err := ctx.NetworkConfig("unknown") 97 c.Check(err, gc.ErrorMatches, `binding name "unknown" not defined by the unit's charm`) 98 c.Check(netConfig, gc.IsNil) 99 } 100 101 func (s *InterfaceSuite) TestUnitStatus(c *gc.C) { 102 ctx := s.GetContext(c, -1, "") 103 defer context.PatchCachedStatus(ctx.(runner.Context), "maintenance", "working", map[string]interface{}{"hello": "world"})() 104 status, err := ctx.UnitStatus() 105 c.Check(err, jc.ErrorIsNil) 106 c.Check(status.Status, gc.Equals, "maintenance") 107 c.Check(status.Info, gc.Equals, "working") 108 c.Check(status.Data, gc.DeepEquals, map[string]interface{}{"hello": "world"}) 109 } 110 111 func (s *InterfaceSuite) TestSetUnitStatus(c *gc.C) { 112 ctx := s.GetContext(c, -1, "") 113 status := jujuc.StatusInfo{ 114 Status: "maintenance", 115 Info: "doing work", 116 } 117 err := ctx.SetUnitStatus(status) 118 c.Check(err, jc.ErrorIsNil) 119 unitStatus, err := ctx.UnitStatus() 120 c.Check(err, jc.ErrorIsNil) 121 c.Check(unitStatus.Status, gc.Equals, "maintenance") 122 c.Check(unitStatus.Info, gc.Equals, "doing work") 123 c.Check(unitStatus.Data, gc.DeepEquals, map[string]interface{}{}) 124 } 125 126 func (s *InterfaceSuite) TestSetUnitStatusUpdatesFlag(c *gc.C) { 127 ctx := s.GetContext(c, -1, "") 128 c.Assert(ctx.(runner.Context).HasExecutionSetUnitStatus(), jc.IsFalse) 129 status := jujuc.StatusInfo{ 130 Status: "maintenance", 131 Info: "doing work", 132 } 133 err := ctx.SetUnitStatus(status) 134 c.Check(err, jc.ErrorIsNil) 135 c.Assert(ctx.(runner.Context).HasExecutionSetUnitStatus(), jc.IsTrue) 136 } 137 138 func (s *InterfaceSuite) TestUnitStatusCaching(c *gc.C) { 139 ctx := s.GetContext(c, -1, "") 140 unitStatus, err := ctx.UnitStatus() 141 c.Check(err, jc.ErrorIsNil) 142 c.Check(unitStatus.Status, gc.Equals, "unknown") 143 c.Check(unitStatus.Data, gc.DeepEquals, map[string]interface{}{}) 144 145 // Change remote state. 146 err = s.unit.SetStatus(status.StatusActive, "it works", nil) 147 c.Assert(err, jc.ErrorIsNil) 148 149 // Local view is unchanged. 150 unitStatus, err = ctx.UnitStatus() 151 c.Check(err, jc.ErrorIsNil) 152 c.Check(unitStatus.Status, gc.Equals, "unknown") 153 c.Check(unitStatus.Data, gc.DeepEquals, map[string]interface{}{}) 154 } 155 156 func (s *InterfaceSuite) TestUnitCaching(c *gc.C) { 157 ctx := s.GetContext(c, -1, "") 158 pr, err := ctx.PrivateAddress() 159 c.Assert(err, jc.ErrorIsNil) 160 c.Assert(pr, gc.Equals, "u-0.testing.invalid") 161 pa, err := ctx.PublicAddress() 162 c.Assert(err, jc.ErrorIsNil) 163 // Initially the public address is the same as the private address since 164 // the "most public" address is chosen. 165 c.Assert(pr, gc.Equals, pa) 166 167 // Change remote state. 168 err = s.machine.SetProviderAddresses( 169 network.NewScopedAddress("blah.testing.invalid", network.ScopePublic), 170 ) 171 c.Assert(err, jc.ErrorIsNil) 172 173 // Local view is unchanged. 174 pr, err = ctx.PrivateAddress() 175 c.Assert(err, jc.ErrorIsNil) 176 c.Assert(pr, gc.Equals, "u-0.testing.invalid") 177 pa, err = ctx.PublicAddress() 178 c.Assert(err, jc.ErrorIsNil) 179 c.Assert(pr, gc.Equals, pa) 180 } 181 182 func (s *InterfaceSuite) TestConfigCaching(c *gc.C) { 183 ctx := s.GetContext(c, -1, "") 184 settings, err := ctx.ConfigSettings() 185 c.Assert(err, jc.ErrorIsNil) 186 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 187 188 // Change remote config. 189 err = s.service.UpdateConfigSettings(charm.Settings{ 190 "blog-title": "Something Else", 191 }) 192 c.Assert(err, jc.ErrorIsNil) 193 194 // Local view is not changed. 195 settings, err = ctx.ConfigSettings() 196 c.Assert(err, jc.ErrorIsNil) 197 c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"}) 198 } 199 200 // TestNonActionCallsToActionMethodsFail does exactly what its name says: 201 // it simply makes sure that Action-related calls to HookContexts with a nil 202 // actionData member error out correctly. 203 func (s *InterfaceSuite) TestNonActionCallsToActionMethodsFail(c *gc.C) { 204 ctx := context.HookContext{} 205 _, err := ctx.ActionParams() 206 c.Check(err, gc.ErrorMatches, "not running an action") 207 err = ctx.SetActionFailed() 208 c.Check(err, gc.ErrorMatches, "not running an action") 209 err = ctx.SetActionMessage("foo") 210 c.Check(err, gc.ErrorMatches, "not running an action") 211 err = ctx.UpdateActionResults([]string{"1", "2", "3"}, "value") 212 c.Check(err, gc.ErrorMatches, "not running an action") 213 } 214 215 // TestUpdateActionResults demonstrates that UpdateActionResults functions 216 // as expected. 217 func (s *InterfaceSuite) TestUpdateActionResults(c *gc.C) { 218 tests := []struct { 219 initial map[string]interface{} 220 keys []string 221 value string 222 expected map[string]interface{} 223 }{{ 224 initial: map[string]interface{}{}, 225 keys: []string{"foo"}, 226 value: "bar", 227 expected: map[string]interface{}{ 228 "foo": "bar", 229 }, 230 }, { 231 initial: map[string]interface{}{ 232 "foo": "bar", 233 }, 234 keys: []string{"foo", "bar"}, 235 value: "baz", 236 expected: map[string]interface{}{ 237 "foo": map[string]interface{}{ 238 "bar": "baz", 239 }, 240 }, 241 }, { 242 initial: map[string]interface{}{ 243 "foo": map[string]interface{}{ 244 "bar": "baz", 245 }, 246 }, 247 keys: []string{"foo"}, 248 value: "bar", 249 expected: map[string]interface{}{ 250 "foo": "bar", 251 }, 252 }} 253 254 for i, t := range tests { 255 c.Logf("UpdateActionResults test %d: %#v: %#v", i, t.keys, t.value) 256 hctx := context.GetStubActionContext(t.initial) 257 err := hctx.UpdateActionResults(t.keys, t.value) 258 c.Assert(err, jc.ErrorIsNil) 259 actionData, err := hctx.ActionData() 260 c.Assert(err, jc.ErrorIsNil) 261 c.Assert(actionData.ResultsMap, jc.DeepEquals, t.expected) 262 } 263 } 264 265 // TestSetActionFailed ensures SetActionFailed works properly. 266 func (s *InterfaceSuite) TestSetActionFailed(c *gc.C) { 267 hctx := context.GetStubActionContext(nil) 268 err := hctx.SetActionFailed() 269 c.Assert(err, jc.ErrorIsNil) 270 actionData, err := hctx.ActionData() 271 c.Assert(err, jc.ErrorIsNil) 272 c.Check(actionData.Failed, jc.IsTrue) 273 } 274 275 // TestSetActionMessage ensures SetActionMessage works properly. 276 func (s *InterfaceSuite) TestSetActionMessage(c *gc.C) { 277 hctx := context.GetStubActionContext(nil) 278 err := hctx.SetActionMessage("because reasons") 279 c.Assert(err, jc.ErrorIsNil) 280 actionData, err := hctx.ActionData() 281 c.Check(err, jc.ErrorIsNil) 282 c.Check(actionData.ResultsMessage, gc.Equals, "because reasons") 283 } 284 285 func (s *InterfaceSuite) TestRequestRebootAfterHook(c *gc.C) { 286 var killed bool 287 p := &mockProcess{func() error { 288 killed = true 289 return nil 290 }} 291 ctx := s.GetContext(c, -1, "").(*context.HookContext) 292 ctx.SetProcess(p) 293 err := ctx.RequestReboot(jujuc.RebootAfterHook) 294 c.Assert(err, jc.ErrorIsNil) 295 c.Assert(killed, jc.IsFalse) 296 priority := ctx.GetRebootPriority() 297 c.Assert(priority, gc.Equals, jujuc.RebootAfterHook) 298 } 299 300 func (s *InterfaceSuite) TestRequestRebootNow(c *gc.C) { 301 ctx := s.GetContext(c, -1, "").(*context.HookContext) 302 303 var stub testing.Stub 304 var p *mockProcess 305 p = &mockProcess{func() error { 306 // Reboot priority should be set before the process 307 // is killed, or else the client waiting for the 308 // process to exit will race with the setting of 309 // the priority. 310 priority := ctx.GetRebootPriority() 311 c.Assert(priority, gc.Equals, jujuc.RebootNow) 312 return stub.NextErr() 313 }} 314 stub.SetErrors(errors.New("process is already dead")) 315 ctx.SetProcess(p) 316 317 err := ctx.RequestReboot(jujuc.RebootNow) 318 c.Assert(err, jc.ErrorIsNil) 319 320 // Everything went well, so priority should still be RebootNow. 321 priority := ctx.GetRebootPriority() 322 c.Assert(priority, gc.Equals, jujuc.RebootNow) 323 } 324 325 func (s *InterfaceSuite) TestRequestRebootNowTimeout(c *gc.C) { 326 ctx := s.GetContext(c, -1, "").(*context.HookContext) 327 328 var advanced bool 329 var p *mockProcess 330 p = &mockProcess{func() error { 331 // Reboot priority should be set before the process 332 // is killed, or else the client waiting for the 333 // process to exit will race with the setting of 334 // the priority. 335 priority := ctx.GetRebootPriority() 336 c.Assert(priority, gc.Equals, jujuc.RebootNow) 337 if !advanced { 338 advanced = true 339 s.clock.Advance(time.Hour) // force timeout 340 } 341 return nil 342 }} 343 ctx.SetProcess(p) 344 345 err := ctx.RequestReboot(jujuc.RebootNow) 346 c.Assert(err, gc.ErrorMatches, "failed to kill context process 123") 347 348 // RequestReboot failed, so priority should revert to RebootSkip. 349 priority := ctx.GetRebootPriority() 350 c.Assert(priority, gc.Equals, jujuc.RebootSkip) 351 } 352 353 func (s *InterfaceSuite) TestRequestRebootNowNoProcess(c *gc.C) { 354 // A normal hook run or a juju-run command will record the *os.Process 355 // object of the running command, in HookContext. When requesting a 356 // reboot with the --now flag, the process is killed and only 357 // then will we set the reboot priority. This test basically simulates 358 // the case when the process calling juju-reboot is not recorded. 359 ctx := context.HookContext{} 360 err := ctx.RequestReboot(jujuc.RebootNow) 361 c.Assert(err, gc.ErrorMatches, "no process to kill") 362 priority := ctx.GetRebootPriority() 363 c.Assert(priority, gc.Equals, jujuc.RebootNow) 364 } 365 366 func (s *InterfaceSuite) TestStorageAddConstraints(c *gc.C) { 367 expected := map[string][]params.StorageConstraints{ 368 "data": []params.StorageConstraints{ 369 params.StorageConstraints{}, 370 }, 371 } 372 373 ctx := context.HookContext{} 374 addStorageToContext(&ctx, "data", params.StorageConstraints{}) 375 assertStorageAddInContext(c, ctx, expected) 376 } 377 378 var two = uint64(2) 379 380 func (s *InterfaceSuite) TestStorageAddConstraintsSameStorage(c *gc.C) { 381 expected := map[string][]params.StorageConstraints{ 382 "data": []params.StorageConstraints{ 383 params.StorageConstraints{}, 384 params.StorageConstraints{Count: &two}, 385 }, 386 } 387 388 ctx := context.HookContext{} 389 addStorageToContext(&ctx, "data", params.StorageConstraints{}) 390 addStorageToContext(&ctx, "data", params.StorageConstraints{Count: &two}) 391 assertStorageAddInContext(c, ctx, expected) 392 } 393 394 func (s *InterfaceSuite) TestStorageAddConstraintsDifferentStorage(c *gc.C) { 395 expected := map[string][]params.StorageConstraints{ 396 "data": []params.StorageConstraints{params.StorageConstraints{}}, 397 "diff": []params.StorageConstraints{ 398 params.StorageConstraints{Count: &two}}, 399 } 400 401 ctx := context.HookContext{} 402 addStorageToContext(&ctx, "data", params.StorageConstraints{}) 403 addStorageToContext(&ctx, "diff", params.StorageConstraints{Count: &two}) 404 assertStorageAddInContext(c, ctx, expected) 405 } 406 407 func addStorageToContext(ctx *context.HookContext, 408 name string, 409 cons params.StorageConstraints, 410 ) { 411 addOne := map[string]params.StorageConstraints{name: cons} 412 ctx.AddUnitStorage(addOne) 413 } 414 415 func assertStorageAddInContext(c *gc.C, 416 ctx context.HookContext, expected map[string][]params.StorageConstraints, 417 ) { 418 obtained := context.StorageAddConstraints(&ctx) 419 c.Assert(len(obtained), gc.Equals, len(expected)) 420 for k, v := range obtained { 421 c.Assert(v, jc.SameContents, expected[k]) 422 } 423 } 424 425 type mockProcess struct { 426 kill func() error 427 } 428 429 func (p *mockProcess) Kill() error { 430 return p.kill() 431 } 432 433 func (p *mockProcess) Pid() int { 434 return 123 435 }