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