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