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