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  }