github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/runner/factory_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package runner_test
     5  
     6  import (
     7  	"os"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/charm.v6-unstable/hooks"
    17  	"gopkg.in/juju/names.v2"
    18  
    19  	"github.com/juju/juju/state"
    20  	"github.com/juju/juju/worker/uniter/hook"
    21  	"github.com/juju/juju/worker/uniter/runner"
    22  	"github.com/juju/juju/worker/uniter/runner/context"
    23  	runnertesting "github.com/juju/juju/worker/uniter/runner/testing"
    24  )
    25  
    26  type FactorySuite struct {
    27  	ContextSuite
    28  }
    29  
    30  var _ = gc.Suite(&FactorySuite{})
    31  
    32  func (s *FactorySuite) AssertPaths(c *gc.C, rnr runner.Runner) {
    33  	c.Assert(runner.RunnerPaths(rnr), gc.DeepEquals, s.paths)
    34  }
    35  
    36  func (s *FactorySuite) TestNewCommandRunnerNoRelation(c *gc.C) {
    37  	rnr, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: -1})
    38  	c.Assert(err, jc.ErrorIsNil)
    39  	s.AssertPaths(c, rnr)
    40  }
    41  
    42  func (s *FactorySuite) TestNewCommandRunnerRelationIdDoesNotExist(c *gc.C) {
    43  	for _, value := range []bool{true, false} {
    44  		_, err := s.factory.NewCommandRunner(context.CommandInfo{
    45  			RelationId: 12, ForceRemoteUnit: value,
    46  		})
    47  		c.Check(err, gc.ErrorMatches, `unknown relation id: 12`)
    48  	}
    49  }
    50  
    51  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInvalid(c *gc.C) {
    52  	for _, value := range []bool{true, false} {
    53  		_, err := s.factory.NewCommandRunner(context.CommandInfo{
    54  			RelationId: 0, RemoteUnitName: "blah", ForceRemoteUnit: value,
    55  		})
    56  		c.Check(err, gc.ErrorMatches, `invalid remote unit: blah`)
    57  	}
    58  }
    59  
    60  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInappropriate(c *gc.C) {
    61  	for _, value := range []bool{true, false} {
    62  		_, err := s.factory.NewCommandRunner(context.CommandInfo{
    63  			RelationId: -1, RemoteUnitName: "blah/123", ForceRemoteUnit: value,
    64  		})
    65  		c.Check(err, gc.ErrorMatches, `remote unit provided without a relation: blah/123`)
    66  	}
    67  }
    68  
    69  func (s *FactorySuite) TestNewCommandRunnerEmptyRelation(c *gc.C) {
    70  	_, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: 1})
    71  	c.Check(err, gc.ErrorMatches, `cannot infer remote unit in empty relation 1`)
    72  }
    73  
    74  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitAmbiguous(c *gc.C) {
    75  	s.membership[1] = []string{"foo/0", "foo/1"}
    76  	_, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: 1})
    77  	c.Check(err, gc.ErrorMatches, `ambiguous remote unit; possibilities are \[foo/0 foo/1\]`)
    78  }
    79  
    80  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitMissing(c *gc.C) {
    81  	s.membership[0] = []string{"foo/0", "foo/1"}
    82  	_, err := s.factory.NewCommandRunner(context.CommandInfo{
    83  		RelationId: 0, RemoteUnitName: "blah/123",
    84  	})
    85  	c.Check(err, gc.ErrorMatches, `unknown remote unit blah/123; possibilities are \[foo/0 foo/1\]`)
    86  }
    87  
    88  func (s *FactorySuite) TestNewCommandRunnerForceNoRemoteUnit(c *gc.C) {
    89  	rnr, err := s.factory.NewCommandRunner(context.CommandInfo{
    90  		RelationId: 0, ForceRemoteUnit: true,
    91  	})
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	s.AssertPaths(c, rnr)
    94  }
    95  
    96  func (s *FactorySuite) TestNewCommandRunnerForceRemoteUnitMissing(c *gc.C) {
    97  	_, err := s.factory.NewCommandRunner(context.CommandInfo{
    98  		RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true,
    99  	})
   100  	c.Assert(err, gc.IsNil)
   101  }
   102  
   103  func (s *FactorySuite) TestNewCommandRunnerInferRemoteUnit(c *gc.C) {
   104  	s.membership[0] = []string{"foo/2"}
   105  	rnr, err := s.factory.NewCommandRunner(context.CommandInfo{RelationId: 0})
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	s.AssertPaths(c, rnr)
   108  }
   109  
   110  func (s *FactorySuite) TestNewHookRunner(c *gc.C) {
   111  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.ConfigChanged})
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	s.AssertPaths(c, rnr)
   114  }
   115  
   116  func (s *FactorySuite) TestNewHookRunnerWithBadHook(c *gc.C) {
   117  	rnr, err := s.factory.NewHookRunner(hook.Info{})
   118  	c.Assert(rnr, gc.IsNil)
   119  	c.Assert(err, gc.ErrorMatches, `unknown hook kind ""`)
   120  }
   121  
   122  func (s *FactorySuite) TestNewHookRunnerWithStorage(c *gc.C) {
   123  	// We need to set up a unit that has storage metadata defined.
   124  	ch := s.AddTestingCharm(c, "storage-block")
   125  	sCons := map[string]state.StorageConstraints{
   126  		"data": {Pool: "", Size: 1024, Count: 1},
   127  	}
   128  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons)
   129  	s.machine = nil // allocate a new machine
   130  	unit := s.AddUnit(c, service)
   131  
   132  	storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	c.Assert(storageAttachments, gc.HasLen, 1)
   135  	storageTag := storageAttachments[0].StorageInstance()
   136  
   137  	volume, err := s.State.StorageInstanceVolume(storageTag)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	volumeTag := volume.VolumeTag()
   140  	machineTag := s.machine.MachineTag()
   141  
   142  	err = s.State.SetVolumeInfo(
   143  		volumeTag, state.VolumeInfo{
   144  			VolumeId: "vol-123",
   145  			Size:     456,
   146  		},
   147  	)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	err = s.State.SetVolumeAttachmentInfo(
   150  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   151  			DeviceName: "sdb",
   152  		},
   153  	)
   154  	c.Assert(err, jc.ErrorIsNil)
   155  
   156  	password, err := utils.RandomPassword()
   157  	err = unit.SetPassword(password)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	st := s.OpenAPIAs(c, unit.Tag(), password)
   160  	uniter, err := st.Uniter()
   161  	c.Assert(err, jc.ErrorIsNil)
   162  
   163  	contextFactory, err := context.NewContextFactory(
   164  		uniter,
   165  		unit.Tag().(names.UnitTag),
   166  		runnertesting.FakeTracker{},
   167  		s.getRelationInfos,
   168  		s.storage,
   169  		s.paths,
   170  		testing.NewClock(time.Time{}),
   171  	)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	factory, err := runner.NewFactory(
   174  		uniter,
   175  		s.paths,
   176  		contextFactory,
   177  	)
   178  	c.Assert(err, jc.ErrorIsNil)
   179  
   180  	rnr, err := factory.NewHookRunner(hook.Info{
   181  		Kind:      hooks.StorageAttached,
   182  		StorageId: "data/0",
   183  	})
   184  	c.Assert(err, jc.ErrorIsNil)
   185  	s.AssertPaths(c, rnr)
   186  	ctx := rnr.Context()
   187  	c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0")
   188  }
   189  
   190  func (s *FactorySuite) TestNewHookRunnerWithRelation(c *gc.C) {
   191  	rnr, err := s.factory.NewHookRunner(hook.Info{
   192  		Kind:       hooks.RelationBroken,
   193  		RelationId: 1,
   194  	})
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	s.AssertPaths(c, rnr)
   197  }
   198  
   199  func (s *FactorySuite) TestNewHookRunnerWithBadRelation(c *gc.C) {
   200  	rnr, err := s.factory.NewHookRunner(hook.Info{
   201  		Kind:       hooks.RelationBroken,
   202  		RelationId: 12345,
   203  	})
   204  	c.Assert(rnr, gc.IsNil)
   205  	c.Assert(err, gc.ErrorMatches, `unknown relation id: 12345`)
   206  }
   207  
   208  func (s *FactorySuite) TestNewActionRunnerGood(c *gc.C) {
   209  	s.SetCharm(c, "dummy")
   210  	for i, test := range []struct {
   211  		actionName string
   212  		payload    map[string]interface{}
   213  	}{
   214  		{
   215  			actionName: "snapshot",
   216  			payload: map[string]interface{}{
   217  				"outfile": "/some/file.bz2",
   218  			},
   219  		},
   220  		{
   221  			// juju-run should work as a predefined action even if
   222  			// it's not part of the charm
   223  			actionName: "juju-run",
   224  			payload: map[string]interface{}{
   225  				"command": "foo",
   226  				"timeout": 0.0,
   227  			},
   228  		},
   229  	} {
   230  		c.Logf("test %d", i)
   231  		action, err := s.State.EnqueueAction(s.unit.Tag(), test.actionName, test.payload)
   232  		c.Assert(err, jc.ErrorIsNil)
   233  		rnr, err := s.factory.NewActionRunner(action.Id())
   234  		c.Assert(err, jc.ErrorIsNil)
   235  		s.AssertPaths(c, rnr)
   236  		ctx := rnr.Context()
   237  		data, err := ctx.ActionData()
   238  		c.Assert(err, jc.ErrorIsNil)
   239  		c.Assert(data, jc.DeepEquals, &context.ActionData{
   240  			Name:       test.actionName,
   241  			Tag:        action.ActionTag(),
   242  			Params:     test.payload,
   243  			ResultsMap: map[string]interface{}{},
   244  		})
   245  		vars, err := ctx.HookVars(s.paths)
   246  		c.Assert(err, jc.ErrorIsNil)
   247  		c.Assert(len(vars) > 0, jc.IsTrue, gc.Commentf("expected HookVars but found none"))
   248  		combined := strings.Join(vars, "|")
   249  		c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_NAME=`+test.actionName+`(\|.*|$)`)
   250  		c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_UUID=`+action.Id()+`(\|.*|$)`)
   251  		c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_TAG=`+action.Tag().String()+`(\|.*|$)`)
   252  	}
   253  }
   254  
   255  func (s *FactorySuite) TestNewActionRunnerBadCharm(c *gc.C) {
   256  	rnr, err := s.factory.NewActionRunner("irrelevant")
   257  	c.Assert(rnr, gc.IsNil)
   258  	c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
   259  	c.Assert(err, gc.Not(jc.Satisfies), runner.IsBadActionError)
   260  }
   261  
   262  func (s *FactorySuite) TestNewActionRunnerBadName(c *gc.C) {
   263  	s.SetCharm(c, "dummy")
   264  	action, err := s.State.EnqueueAction(s.unit.Tag(), "no-such-action", nil)
   265  	c.Assert(err, jc.ErrorIsNil) // this will fail when using AddAction on unit
   266  	rnr, err := s.factory.NewActionRunner(action.Id())
   267  	c.Check(rnr, gc.IsNil)
   268  	c.Check(err, gc.ErrorMatches, "cannot run \"no-such-action\" action: not defined")
   269  	c.Check(err, jc.Satisfies, runner.IsBadActionError)
   270  }
   271  
   272  func (s *FactorySuite) TestNewActionRunnerBadParams(c *gc.C) {
   273  	s.SetCharm(c, "dummy")
   274  	action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{
   275  		"outfile": 123,
   276  	})
   277  	c.Assert(err, jc.ErrorIsNil) // this will fail when state is done right
   278  	rnr, err := s.factory.NewActionRunner(action.Id())
   279  	c.Check(rnr, gc.IsNil)
   280  	c.Check(err, gc.ErrorMatches, "cannot run \"snapshot\" action: .*")
   281  	c.Check(err, jc.Satisfies, runner.IsBadActionError)
   282  }
   283  
   284  func (s *FactorySuite) TestNewActionRunnerMissingAction(c *gc.C) {
   285  	s.SetCharm(c, "dummy")
   286  	action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil)
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	_, err = s.unit.CancelAction(action)
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	rnr, err := s.factory.NewActionRunner(action.Id())
   291  	c.Check(rnr, gc.IsNil)
   292  	c.Check(err, gc.ErrorMatches, "action no longer available")
   293  	c.Check(err, gc.Equals, runner.ErrActionNotAvailable)
   294  }
   295  
   296  func (s *FactorySuite) TestNewActionRunnerUnauthAction(c *gc.C) {
   297  	s.SetCharm(c, "dummy")
   298  	otherUnit, err := s.service.AddUnit()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	action, err := s.State.EnqueueAction(otherUnit.Tag(), "snapshot", nil)
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	rnr, err := s.factory.NewActionRunner(action.Id())
   303  	c.Check(rnr, gc.IsNil)
   304  	c.Check(err, gc.ErrorMatches, "action no longer available")
   305  	c.Check(err, gc.Equals, runner.ErrActionNotAvailable)
   306  }