github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/names"
    13  	"github.com/juju/testing"
    14  	jc "github.com/juju/testing/checkers"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/fs"
    17  	gc "gopkg.in/check.v1"
    18  	"gopkg.in/juju/charm.v5/hooks"
    19  
    20  	"github.com/juju/juju/apiserver/params"
    21  	"github.com/juju/juju/state"
    22  	"github.com/juju/juju/storage"
    23  	"github.com/juju/juju/testcharms"
    24  	"github.com/juju/juju/worker/leadership"
    25  	"github.com/juju/juju/worker/uniter/hook"
    26  	"github.com/juju/juju/worker/uniter/runner"
    27  )
    28  
    29  type FactorySuite struct {
    30  	HookContextSuite
    31  	paths      RealPaths
    32  	factory    runner.Factory
    33  	membership map[int][]string
    34  }
    35  
    36  var _ = gc.Suite(&FactorySuite{})
    37  
    38  type fakeTracker struct {
    39  	leadership.Tracker
    40  }
    41  
    42  func (fakeTracker) ServiceName() string {
    43  	return "service-name"
    44  }
    45  
    46  func (s *FactorySuite) SetUpTest(c *gc.C) {
    47  	s.HookContextSuite.SetUpTest(c)
    48  	s.paths = NewRealPaths(c)
    49  	s.membership = map[int][]string{}
    50  	factory, err := runner.NewFactory(
    51  		s.uniter,
    52  		s.unit.Tag().(names.UnitTag),
    53  		fakeTracker{},
    54  		s.getRelationInfos,
    55  		s.storage,
    56  		s.paths,
    57  	)
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	s.factory = factory
    60  }
    61  
    62  func (s *FactorySuite) SetCharm(c *gc.C, name string) {
    63  	err := os.RemoveAll(s.paths.charm)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.charm)
    66  	c.Assert(err, jc.ErrorIsNil)
    67  }
    68  
    69  func (s *FactorySuite) getRelationInfos() map[int]*runner.RelationInfo {
    70  	info := map[int]*runner.RelationInfo{}
    71  	for relId, relUnit := range s.apiRelunits {
    72  		info[relId] = &runner.RelationInfo{
    73  			RelationUnit: relUnit,
    74  			MemberNames:  s.membership[relId],
    75  		}
    76  	}
    77  	return info
    78  }
    79  
    80  func (s *FactorySuite) setUpCacheMethods(c *gc.C) {
    81  	// The factory's caches are created lazily, so it doesn't have any at all to
    82  	// begin with. Creating and discarding a context lets us call updateCache
    83  	// without panicking. (IMO this is less invasive that making updateCache
    84  	// responsible for creating missing caches etc.)
    85  	_, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install})
    86  	c.Assert(err, jc.ErrorIsNil)
    87  }
    88  
    89  func (s *FactorySuite) updateCache(relId int, unitName string, settings params.Settings) {
    90  	runner.UpdateCachedSettings(s.factory, relId, unitName, settings)
    91  }
    92  
    93  func (s *FactorySuite) getCache(relId int, unitName string) (params.Settings, bool) {
    94  	return runner.CachedSettings(s.factory, relId, unitName)
    95  }
    96  
    97  func (s *FactorySuite) AssertPaths(c *gc.C, rnr runner.Runner) {
    98  	c.Assert(runner.RunnerPaths(rnr), gc.DeepEquals, s.paths)
    99  }
   100  
   101  func (s *FactorySuite) AssertCoreContext(c *gc.C, ctx runner.Context) {
   102  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   103  	c.Assert(ctx.OwnerTag(), gc.Equals, s.service.GetOwnerTag())
   104  	c.Assert(runner.ContextMachineTag(ctx), jc.DeepEquals, names.NewMachineTag("0"))
   105  
   106  	expect, expectOK := s.unit.PrivateAddress()
   107  	actual, actualOK := ctx.PrivateAddress()
   108  	c.Assert(actual, gc.Equals, expect)
   109  	c.Assert(actualOK, gc.Equals, expectOK)
   110  
   111  	expect, expectOK = s.unit.PublicAddress()
   112  	actual, actualOK = ctx.PublicAddress()
   113  	c.Assert(actual, gc.Equals, expect)
   114  	c.Assert(actualOK, gc.Equals, expectOK)
   115  
   116  	env, err := s.State.Environment()
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	name, uuid := runner.ContextEnvInfo(ctx)
   119  	c.Assert(name, gc.Equals, env.Name())
   120  	c.Assert(uuid, gc.Equals, env.UUID())
   121  
   122  	c.Assert(ctx.RelationIds(), gc.HasLen, 2)
   123  
   124  	r, found := ctx.Relation(0)
   125  	c.Assert(found, jc.IsTrue)
   126  	c.Assert(r.Name(), gc.Equals, "db")
   127  	c.Assert(r.FakeId(), gc.Equals, "db:0")
   128  
   129  	r, found = ctx.Relation(1)
   130  	c.Assert(found, jc.IsTrue)
   131  	c.Assert(r.Name(), gc.Equals, "db")
   132  	c.Assert(r.FakeId(), gc.Equals, "db:1")
   133  }
   134  
   135  func (s *FactorySuite) AssertNotActionContext(c *gc.C, ctx runner.Context) {
   136  	actionData, err := ctx.ActionData()
   137  	c.Assert(actionData, gc.IsNil)
   138  	c.Assert(err, gc.ErrorMatches, "not running an action")
   139  }
   140  
   141  func (s *FactorySuite) AssertNotStorageContext(c *gc.C, ctx runner.Context) {
   142  	storageAttachment, ok := ctx.HookStorage()
   143  	c.Assert(storageAttachment, gc.IsNil)
   144  	c.Assert(ok, jc.IsFalse)
   145  }
   146  
   147  func (s *FactorySuite) AssertStorageContext(c *gc.C, ctx runner.Context, id string, attachment storage.StorageAttachmentInfo) {
   148  	fromCache, ok := ctx.HookStorage()
   149  	c.Assert(ok, jc.IsTrue)
   150  	c.Assert(fromCache, gc.NotNil)
   151  	c.Assert(fromCache.Tag().Id(), gc.Equals, id)
   152  	c.Assert(fromCache.Kind(), gc.Equals, attachment.Kind)
   153  	c.Assert(fromCache.Location(), gc.Equals, attachment.Location)
   154  }
   155  
   156  func (s *FactorySuite) AssertRelationContext(c *gc.C, ctx runner.Context, relId int, remoteUnit string) *runner.ContextRelation {
   157  	actualRemoteUnit, _ := ctx.RemoteUnitName()
   158  	c.Assert(actualRemoteUnit, gc.Equals, remoteUnit)
   159  	rel, found := ctx.HookRelation()
   160  	c.Assert(found, jc.IsTrue)
   161  	c.Assert(rel.Id(), gc.Equals, relId)
   162  	return rel.(*runner.ContextRelation)
   163  }
   164  
   165  func (s *FactorySuite) AssertNotRelationContext(c *gc.C, ctx runner.Context) {
   166  	rel, found := ctx.HookRelation()
   167  	c.Assert(rel, gc.IsNil)
   168  	c.Assert(found, jc.IsFalse)
   169  }
   170  
   171  func (s *FactorySuite) TestNewCommandRunnerNoRelation(c *gc.C) {
   172  	rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: -1})
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	s.AssertPaths(c, rnr)
   175  	ctx := rnr.Context()
   176  	s.AssertCoreContext(c, ctx)
   177  	s.AssertNotActionContext(c, ctx)
   178  	s.AssertNotRelationContext(c, ctx)
   179  	s.AssertNotStorageContext(c, ctx)
   180  }
   181  
   182  func (s *FactorySuite) TestNewCommandRunnerRelationIdDoesNotExist(c *gc.C) {
   183  	for _, value := range []bool{true, false} {
   184  		_, err := s.factory.NewCommandRunner(runner.CommandInfo{
   185  			RelationId: 12, ForceRemoteUnit: value,
   186  		})
   187  		c.Check(err, gc.ErrorMatches, `unknown relation id: 12`)
   188  	}
   189  }
   190  
   191  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInvalid(c *gc.C) {
   192  	for _, value := range []bool{true, false} {
   193  		_, err := s.factory.NewCommandRunner(runner.CommandInfo{
   194  			RelationId: 0, RemoteUnitName: "blah", ForceRemoteUnit: value,
   195  		})
   196  		c.Check(err, gc.ErrorMatches, `invalid remote unit: blah`)
   197  	}
   198  }
   199  
   200  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitInappropriate(c *gc.C) {
   201  	for _, value := range []bool{true, false} {
   202  		_, err := s.factory.NewCommandRunner(runner.CommandInfo{
   203  			RelationId: -1, RemoteUnitName: "blah/123", ForceRemoteUnit: value,
   204  		})
   205  		c.Check(err, gc.ErrorMatches, `remote unit provided without a relation: blah/123`)
   206  	}
   207  }
   208  
   209  func (s *FactorySuite) TestNewCommandRunnerEmptyRelation(c *gc.C) {
   210  	_, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 1})
   211  	c.Check(err, gc.ErrorMatches, `cannot infer remote unit in empty relation 1`)
   212  }
   213  
   214  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitAmbiguous(c *gc.C) {
   215  	s.membership[1] = []string{"foo/0", "foo/1"}
   216  	_, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 1})
   217  	c.Check(err, gc.ErrorMatches, `ambiguous remote unit; possibilities are \[foo/0 foo/1\]`)
   218  }
   219  
   220  func (s *FactorySuite) TestNewCommandRunnerRemoteUnitMissing(c *gc.C) {
   221  	s.membership[0] = []string{"foo/0", "foo/1"}
   222  	_, err := s.factory.NewCommandRunner(runner.CommandInfo{
   223  		RelationId: 0, RemoteUnitName: "blah/123",
   224  	})
   225  	c.Check(err, gc.ErrorMatches, `unknown remote unit blah/123; possibilities are \[foo/0 foo/1\]`)
   226  }
   227  
   228  func (s *FactorySuite) TestNewCommandRunnerForceNoRemoteUnit(c *gc.C) {
   229  	rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{
   230  		RelationId: 0, ForceRemoteUnit: true,
   231  	})
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	s.AssertPaths(c, rnr)
   234  	ctx := rnr.Context()
   235  	s.AssertCoreContext(c, ctx)
   236  	s.AssertNotActionContext(c, ctx)
   237  	s.AssertRelationContext(c, ctx, 0, "")
   238  	s.AssertNotStorageContext(c, ctx)
   239  }
   240  
   241  func (s *FactorySuite) TestNewCommandRunnerForceRemoteUnitMissing(c *gc.C) {
   242  	rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{
   243  		RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true,
   244  	})
   245  	c.Assert(err, gc.IsNil)
   246  	ctx := rnr.Context()
   247  	s.AssertCoreContext(c, ctx)
   248  	s.AssertNotActionContext(c, ctx)
   249  	s.AssertRelationContext(c, ctx, 0, "blah/123")
   250  	s.AssertNotStorageContext(c, ctx)
   251  }
   252  
   253  func (s *FactorySuite) TestNewCommandRunnerInferRemoteUnit(c *gc.C) {
   254  	s.membership[0] = []string{"foo/2"}
   255  	rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: 0})
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	s.AssertPaths(c, rnr)
   258  	ctx := rnr.Context()
   259  	s.AssertCoreContext(c, ctx)
   260  	s.AssertNotActionContext(c, ctx)
   261  	s.AssertRelationContext(c, ctx, 0, "foo/2")
   262  	s.AssertNotStorageContext(c, ctx)
   263  }
   264  
   265  func (s *FactorySuite) TestNewHookRunner(c *gc.C) {
   266  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.ConfigChanged})
   267  	c.Assert(err, jc.ErrorIsNil)
   268  	s.AssertPaths(c, rnr)
   269  	ctx := rnr.Context()
   270  	s.AssertCoreContext(c, ctx)
   271  	s.AssertNotActionContext(c, ctx)
   272  	s.AssertNotRelationContext(c, ctx)
   273  	s.AssertNotStorageContext(c, ctx)
   274  }
   275  
   276  func (s *FactorySuite) TestNewHookRunnerWithBadHook(c *gc.C) {
   277  	rnr, err := s.factory.NewHookRunner(hook.Info{})
   278  	c.Assert(rnr, gc.IsNil)
   279  	c.Assert(err, gc.ErrorMatches, `unknown hook kind ""`)
   280  }
   281  
   282  func (s *FactorySuite) TestNewHookRunnerWithStorage(c *gc.C) {
   283  	// We need to set up a unit that has storage metadata defined.
   284  	ch := s.AddTestingCharm(c, "storage-block")
   285  	sCons := map[string]state.StorageConstraints{
   286  		"data": {Pool: "", Size: 1024, Count: 1},
   287  	}
   288  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons)
   289  	s.machine = nil // allocate a new machine
   290  	unit := s.AddUnit(c, service)
   291  
   292  	storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	c.Assert(storageAttachments, gc.HasLen, 1)
   295  	storageTag := storageAttachments[0].StorageInstance()
   296  
   297  	volume, err := s.State.StorageInstanceVolume(storageTag)
   298  	c.Assert(err, jc.ErrorIsNil)
   299  	volumeTag := volume.VolumeTag()
   300  	machineTag := s.machine.MachineTag()
   301  
   302  	err = s.State.SetVolumeInfo(
   303  		volumeTag, state.VolumeInfo{
   304  			VolumeId: "vol-123",
   305  			Size:     456,
   306  		},
   307  	)
   308  	c.Assert(err, jc.ErrorIsNil)
   309  	err = s.State.SetVolumeAttachmentInfo(
   310  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   311  			DeviceName: "sdb",
   312  		},
   313  	)
   314  	c.Assert(err, jc.ErrorIsNil)
   315  
   316  	password, err := utils.RandomPassword()
   317  	err = unit.SetPassword(password)
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	st := s.OpenAPIAs(c, unit.Tag(), password)
   320  	uniter, err := st.Uniter()
   321  	c.Assert(err, jc.ErrorIsNil)
   322  
   323  	factory, err := runner.NewFactory(
   324  		uniter,
   325  		unit.Tag().(names.UnitTag),
   326  		fakeTracker{},
   327  		s.getRelationInfos,
   328  		s.storage,
   329  		s.paths,
   330  	)
   331  	c.Assert(err, jc.ErrorIsNil)
   332  
   333  	rnr, err := factory.NewHookRunner(hook.Info{
   334  		Kind:      hooks.StorageAttached,
   335  		StorageId: "data/0",
   336  	})
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	s.AssertPaths(c, rnr)
   339  	ctx := rnr.Context()
   340  	c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0")
   341  	s.AssertStorageContext(c, ctx, "data/0", storage.StorageAttachmentInfo{
   342  		Kind:     storage.StorageKindBlock,
   343  		Location: "/dev/sdb",
   344  	})
   345  	s.AssertNotActionContext(c, ctx)
   346  	s.AssertNotRelationContext(c, ctx)
   347  }
   348  
   349  func (s *FactorySuite) TestNewHookRunnerWithRelation(c *gc.C) {
   350  	rnr, err := s.factory.NewHookRunner(hook.Info{
   351  		Kind:       hooks.RelationBroken,
   352  		RelationId: 1,
   353  	})
   354  	c.Assert(err, jc.ErrorIsNil)
   355  	s.AssertPaths(c, rnr)
   356  	ctx := rnr.Context()
   357  	s.AssertCoreContext(c, ctx)
   358  	s.AssertNotActionContext(c, ctx)
   359  	s.AssertRelationContext(c, ctx, 1, "")
   360  	s.AssertNotStorageContext(c, ctx)
   361  }
   362  
   363  func (s *FactorySuite) TestNewHookRunnerPrunesNonMemberCaches(c *gc.C) {
   364  
   365  	// Write cached member settings for a member and a non-member.
   366  	s.setUpCacheMethods(c)
   367  	s.membership[0] = []string{"rel0/0"}
   368  	s.updateCache(0, "rel0/0", params.Settings{"keep": "me"})
   369  	s.updateCache(0, "rel0/1", params.Settings{"drop": "me"})
   370  
   371  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install})
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	s.AssertPaths(c, rnr)
   374  	ctx := rnr.Context()
   375  
   376  	settings0, found := s.getCache(0, "rel0/0")
   377  	c.Assert(found, jc.IsTrue)
   378  	c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"})
   379  
   380  	settings1, found := s.getCache(0, "rel0/1")
   381  	c.Assert(found, jc.IsFalse)
   382  	c.Assert(settings1, gc.IsNil)
   383  
   384  	// Check the caches are being used by the context relations.
   385  	relCtx, found := ctx.Relation(0)
   386  	c.Assert(found, jc.IsTrue)
   387  
   388  	// Verify that the settings really were cached by trying to look them up.
   389  	// Nothing's really in scope, so the call would fail if they weren't.
   390  	settings0, err = relCtx.ReadSettings("rel0/0")
   391  	c.Assert(err, jc.ErrorIsNil)
   392  	c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"})
   393  
   394  	// Verify that the non-member settings were purged by looking them up and
   395  	// checking for the expected error.
   396  	settings1, err = relCtx.ReadSettings("rel0/1")
   397  	c.Assert(settings1, gc.IsNil)
   398  	c.Assert(err, gc.ErrorMatches, "permission denied")
   399  }
   400  
   401  func (s *FactorySuite) TestNewHookRunnerRelationJoinedUpdatesRelationContextAndCaches(c *gc.C) {
   402  	// Write some cached settings for r/0, so we can verify the cache gets cleared.
   403  	s.setUpCacheMethods(c)
   404  	s.membership[1] = []string{"r/0"}
   405  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   406  
   407  	rnr, err := s.factory.NewHookRunner(hook.Info{
   408  		Kind:       hooks.RelationJoined,
   409  		RelationId: 1,
   410  		RemoteUnit: "r/0",
   411  	})
   412  	c.Assert(err, jc.ErrorIsNil)
   413  	s.AssertPaths(c, rnr)
   414  	ctx := rnr.Context()
   415  	s.AssertCoreContext(c, ctx)
   416  	s.AssertNotActionContext(c, ctx)
   417  	s.AssertNotStorageContext(c, ctx)
   418  	rel := s.AssertRelationContext(c, ctx, 1, "r/0")
   419  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0"})
   420  	cached0, member := s.getCache(1, "r/0")
   421  	c.Assert(cached0, gc.IsNil)
   422  	c.Assert(member, jc.IsTrue)
   423  }
   424  
   425  func (s *FactorySuite) TestNewHookRunnerRelationChangedUpdatesRelationContextAndCaches(c *gc.C) {
   426  	// Update member settings to have actual values, so we can check that
   427  	// the change for r/4 clears its cache but leaves r/0's alone.
   428  	s.setUpCacheMethods(c)
   429  	s.membership[1] = []string{"r/0", "r/4"}
   430  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   431  	s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
   432  
   433  	rnr, err := s.factory.NewHookRunner(hook.Info{
   434  		Kind:       hooks.RelationChanged,
   435  		RelationId: 1,
   436  		RemoteUnit: "r/4",
   437  	})
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	s.AssertPaths(c, rnr)
   440  	ctx := rnr.Context()
   441  	s.AssertCoreContext(c, ctx)
   442  	s.AssertNotActionContext(c, ctx)
   443  	s.AssertNotStorageContext(c, ctx)
   444  	rel := s.AssertRelationContext(c, ctx, 1, "r/4")
   445  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"})
   446  	cached0, member := s.getCache(1, "r/0")
   447  	c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"})
   448  	c.Assert(member, jc.IsTrue)
   449  	cached4, member := s.getCache(1, "r/4")
   450  	c.Assert(cached4, gc.IsNil)
   451  	c.Assert(member, jc.IsTrue)
   452  }
   453  
   454  func (s *FactorySuite) TestNewHookRunnerRelationDepartedUpdatesRelationContextAndCaches(c *gc.C) {
   455  	// Update member settings to have actual values, so we can check that
   456  	// the depart for r/0 leaves r/4's cache alone (while discarding r/0's).
   457  	s.setUpCacheMethods(c)
   458  	s.membership[1] = []string{"r/0", "r/4"}
   459  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   460  	s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
   461  
   462  	rnr, err := s.factory.NewHookRunner(hook.Info{
   463  		Kind:       hooks.RelationDeparted,
   464  		RelationId: 1,
   465  		RemoteUnit: "r/0",
   466  	})
   467  	c.Assert(err, jc.ErrorIsNil)
   468  	s.AssertPaths(c, rnr)
   469  	ctx := rnr.Context()
   470  	s.AssertCoreContext(c, ctx)
   471  	s.AssertNotActionContext(c, ctx)
   472  	s.AssertNotStorageContext(c, ctx)
   473  	rel := s.AssertRelationContext(c, ctx, 1, "r/0")
   474  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/4"})
   475  	cached0, member := s.getCache(1, "r/0")
   476  	c.Assert(cached0, gc.IsNil)
   477  	c.Assert(member, jc.IsFalse)
   478  	cached4, member := s.getCache(1, "r/4")
   479  	c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"})
   480  	c.Assert(member, jc.IsTrue)
   481  }
   482  
   483  func (s *FactorySuite) TestNewHookRunnerRelationBrokenRetainsCaches(c *gc.C) {
   484  	// Note that this is bizarre and unrealistic, because we would never usually
   485  	// run relation-broken on a non-empty relation. But verfying that the settings
   486  	// stick around allows us to verify that there's no special handling for that
   487  	// hook -- as there should not be, because the relation caches will be discarded
   488  	// for the *next* hook, which will be constructed with the current set of known
   489  	// relations and ignore everything else.
   490  	s.setUpCacheMethods(c)
   491  	s.membership[1] = []string{"r/0", "r/4"}
   492  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   493  	s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
   494  
   495  	rnr, err := s.factory.NewHookRunner(hook.Info{
   496  		Kind:       hooks.RelationBroken,
   497  		RelationId: 1,
   498  	})
   499  	c.Assert(err, jc.ErrorIsNil)
   500  	s.AssertPaths(c, rnr)
   501  	ctx := rnr.Context()
   502  	rel := s.AssertRelationContext(c, ctx, 1, "")
   503  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"})
   504  	cached0, member := s.getCache(1, "r/0")
   505  	c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"})
   506  	c.Assert(member, jc.IsTrue)
   507  	cached4, member := s.getCache(1, "r/4")
   508  	c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"})
   509  	c.Assert(member, jc.IsTrue)
   510  }
   511  
   512  func (s *FactorySuite) TestNewHookRunnerWithBadRelation(c *gc.C) {
   513  	rnr, err := s.factory.NewHookRunner(hook.Info{
   514  		Kind:       hooks.RelationBroken,
   515  		RelationId: 12345,
   516  	})
   517  	c.Assert(rnr, gc.IsNil)
   518  	c.Assert(err, gc.ErrorMatches, `unknown relation id: 12345`)
   519  }
   520  
   521  func (s *FactorySuite) TestNewHookRunnerMetricsDisabledHook(c *gc.C) {
   522  	s.SetCharm(c, "metered")
   523  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.Install})
   524  	c.Assert(err, jc.ErrorIsNil)
   525  	s.AssertPaths(c, rnr)
   526  	ctx := rnr.Context()
   527  	err = ctx.AddMetric("key", "value", time.Now())
   528  	c.Assert(err, gc.ErrorMatches, "metrics disabled")
   529  }
   530  
   531  func (s *FactorySuite) TestNewHookRunnerMetricsDisabledUndeclared(c *gc.C) {
   532  	s.SetCharm(c, "mysql")
   533  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics})
   534  	c.Assert(err, jc.ErrorIsNil)
   535  	s.AssertPaths(c, rnr)
   536  	ctx := rnr.Context()
   537  	err = ctx.AddMetric("key", "value", time.Now())
   538  	c.Assert(err, gc.ErrorMatches, "metrics disabled")
   539  }
   540  
   541  func (s *FactorySuite) TestNewHookRunnerMetricsDeclarationError(c *gc.C) {
   542  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics})
   543  	c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
   544  	c.Assert(rnr, gc.IsNil)
   545  }
   546  
   547  func (s *FactorySuite) TestNewHookRunnerMetricsEnabled(c *gc.C) {
   548  	s.SetCharm(c, "metered")
   549  
   550  	rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.CollectMetrics})
   551  	c.Assert(err, jc.ErrorIsNil)
   552  	s.AssertPaths(c, rnr)
   553  	ctx := rnr.Context()
   554  	err = ctx.AddMetric("pings", "0.5", time.Now())
   555  	c.Assert(err, jc.ErrorIsNil)
   556  }
   557  
   558  func (s *FactorySuite) TestNewActionRunnerGood(c *gc.C) {
   559  	s.SetCharm(c, "dummy")
   560  	action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{
   561  		"outfile": "/some/file.bz2",
   562  	})
   563  	c.Assert(err, jc.ErrorIsNil)
   564  	rnr, err := s.factory.NewActionRunner(action.Id())
   565  	c.Assert(err, jc.ErrorIsNil)
   566  	s.AssertPaths(c, rnr)
   567  	ctx := rnr.Context()
   568  	data, err := ctx.ActionData()
   569  	c.Assert(err, jc.ErrorIsNil)
   570  	c.Assert(data, jc.DeepEquals, &runner.ActionData{
   571  		ActionName: "snapshot",
   572  		ActionTag:  action.ActionTag(),
   573  		ActionParams: map[string]interface{}{
   574  			"outfile": "/some/file.bz2",
   575  		},
   576  		ResultsMap: map[string]interface{}{},
   577  	})
   578  	vars := ctx.HookVars(s.paths)
   579  	c.Assert(len(vars) > 0, jc.IsTrue, gc.Commentf("expected HookVars but found none"))
   580  	combined := strings.Join(vars, "|")
   581  	c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_NAME=snapshot(\|.*|$)`)
   582  	c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_UUID=`+action.Id()+`(\|.*|$)`)
   583  	c.Assert(combined, gc.Matches, `(^|.*\|)JUJU_ACTION_TAG=`+action.Tag().String()+`(\|.*|$)`)
   584  }
   585  
   586  func (s *FactorySuite) TestNewActionRunnerBadCharm(c *gc.C) {
   587  	rnr, err := s.factory.NewActionRunner("irrelevant")
   588  	c.Assert(rnr, gc.IsNil)
   589  	c.Assert(errors.Cause(err), jc.Satisfies, os.IsNotExist)
   590  	c.Assert(err, gc.Not(jc.Satisfies), runner.IsBadActionError)
   591  }
   592  
   593  func (s *FactorySuite) TestNewActionRunnerBadName(c *gc.C) {
   594  	s.SetCharm(c, "dummy")
   595  	action, err := s.State.EnqueueAction(s.unit.Tag(), "no-such-action", nil)
   596  	c.Assert(err, jc.ErrorIsNil) // this will fail when using AddAction on unit
   597  	rnr, err := s.factory.NewActionRunner(action.Id())
   598  	c.Check(rnr, gc.IsNil)
   599  	c.Check(err, gc.ErrorMatches, "cannot run \"no-such-action\" action: not defined")
   600  	c.Check(err, jc.Satisfies, runner.IsBadActionError)
   601  }
   602  
   603  func (s *FactorySuite) TestNewActionRunnerBadParams(c *gc.C) {
   604  	s.SetCharm(c, "dummy")
   605  	action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", map[string]interface{}{
   606  		"outfile": 123,
   607  	})
   608  	c.Assert(err, jc.ErrorIsNil) // this will fail when state is done right
   609  	rnr, err := s.factory.NewActionRunner(action.Id())
   610  	c.Check(rnr, gc.IsNil)
   611  	c.Check(err, gc.ErrorMatches, "cannot run \"snapshot\" action: .*")
   612  	c.Check(err, jc.Satisfies, runner.IsBadActionError)
   613  }
   614  
   615  func (s *FactorySuite) TestNewActionRunnerMissingAction(c *gc.C) {
   616  	s.SetCharm(c, "dummy")
   617  	action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil)
   618  	c.Assert(err, jc.ErrorIsNil)
   619  	_, err = s.unit.CancelAction(action)
   620  	c.Assert(err, jc.ErrorIsNil)
   621  	rnr, err := s.factory.NewActionRunner(action.Id())
   622  	c.Check(rnr, gc.IsNil)
   623  	c.Check(err, gc.ErrorMatches, "action no longer available")
   624  	c.Check(err, gc.Equals, runner.ErrActionNotAvailable)
   625  }
   626  
   627  func (s *FactorySuite) TestNewActionRunnerUnauthAction(c *gc.C) {
   628  	s.SetCharm(c, "dummy")
   629  	otherUnit, err := s.service.AddUnit()
   630  	c.Assert(err, jc.ErrorIsNil)
   631  	action, err := s.State.EnqueueAction(otherUnit.Tag(), "snapshot", nil)
   632  	c.Assert(err, jc.ErrorIsNil)
   633  	rnr, err := s.factory.NewActionRunner(action.Id())
   634  	c.Check(rnr, gc.IsNil)
   635  	c.Check(err, gc.ErrorMatches, "action no longer available")
   636  	c.Check(err, gc.Equals, runner.ErrActionNotAvailable)
   637  }
   638  
   639  func (s *FactorySuite) testLeadershipContextWiring(c *gc.C, createRunner func() runner.Runner) {
   640  	var stub testing.Stub
   641  	stub.SetErrors(errors.New("bam"))
   642  	restore := runner.PatchNewLeadershipContext(
   643  		func(accessor runner.LeadershipSettingsAccessor, tracker leadership.Tracker) runner.LeadershipContext {
   644  			stub.AddCall("NewLeadershipContext", accessor, tracker)
   645  			return &StubLeadershipContext{Stub: &stub}
   646  		},
   647  	)
   648  	defer restore()
   649  
   650  	rnr := createRunner()
   651  	isLeader, err := rnr.Context().IsLeader()
   652  	c.Check(err, gc.ErrorMatches, "bam")
   653  	c.Check(isLeader, jc.IsFalse)
   654  
   655  	stub.CheckCalls(c, []testing.StubCall{{
   656  		FuncName: "NewLeadershipContext",
   657  		Args:     []interface{}{s.uniter.LeadershipSettings, fakeTracker{}},
   658  	}, {
   659  		FuncName: "IsLeader",
   660  	}})
   661  
   662  }
   663  
   664  func (s *FactorySuite) TestNewHookRunnerLeadershipContext(c *gc.C) {
   665  	s.testLeadershipContextWiring(c, func() runner.Runner {
   666  		rnr, err := s.factory.NewHookRunner(hook.Info{Kind: hooks.ConfigChanged})
   667  		c.Assert(err, jc.ErrorIsNil)
   668  		return rnr
   669  	})
   670  }
   671  
   672  func (s *FactorySuite) TestNewActionRunnerLeadershipContext(c *gc.C) {
   673  	s.testLeadershipContextWiring(c, func() runner.Runner {
   674  		s.SetCharm(c, "dummy")
   675  		action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil)
   676  		c.Assert(err, jc.ErrorIsNil)
   677  		rnr, err := s.factory.NewActionRunner(action.Id())
   678  		c.Assert(err, jc.ErrorIsNil)
   679  		return rnr
   680  	})
   681  }
   682  
   683  func (s *FactorySuite) TestNewCommandRunnerLeadershipContext(c *gc.C) {
   684  	s.testLeadershipContextWiring(c, func() runner.Runner {
   685  		rnr, err := s.factory.NewCommandRunner(runner.CommandInfo{RelationId: -1})
   686  		c.Assert(err, jc.ErrorIsNil)
   687  		return rnr
   688  	})
   689  }
   690  
   691  type StubLeadershipContext struct {
   692  	runner.LeadershipContext
   693  	*testing.Stub
   694  }
   695  
   696  func (stub *StubLeadershipContext) IsLeader() (bool, error) {
   697  	stub.MethodCall(stub, "IsLeader")
   698  	return false, stub.NextErr()
   699  }