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  }