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