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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package context_test
     5  
     6  import (
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils"
    14  	"github.com/juju/utils/fs"
    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/apiserver/params"
    20  	"github.com/juju/juju/core/leadership"
    21  	"github.com/juju/juju/state"
    22  	"github.com/juju/juju/storage"
    23  	"github.com/juju/juju/testcharms"
    24  	"github.com/juju/juju/worker/uniter/hook"
    25  	"github.com/juju/juju/worker/uniter/runner/context"
    26  	runnertesting "github.com/juju/juju/worker/uniter/runner/testing"
    27  )
    28  
    29  type ContextFactorySuite struct {
    30  	HookContextSuite
    31  	paths      runnertesting.RealPaths
    32  	factory    context.ContextFactory
    33  	membership map[int][]string
    34  }
    35  
    36  var _ = gc.Suite(&ContextFactorySuite{})
    37  
    38  func (s *ContextFactorySuite) SetUpTest(c *gc.C) {
    39  	s.HookContextSuite.SetUpTest(c)
    40  	s.paths = runnertesting.NewRealPaths(c)
    41  	s.membership = map[int][]string{}
    42  
    43  	contextFactory, err := context.NewContextFactory(
    44  		s.uniter,
    45  		s.unit.Tag().(names.UnitTag),
    46  		runnertesting.FakeTracker{},
    47  		s.getRelationInfos,
    48  		s.storage,
    49  		s.paths,
    50  		testing.NewClock(time.Time{}),
    51  	)
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	s.factory = contextFactory
    54  }
    55  
    56  func (s *ContextFactorySuite) setUpCacheMethods(c *gc.C) {
    57  	// The factory's caches are created lazily, so it doesn't have any at all to
    58  	// begin with. Creating and discarding a context lets us call updateCache
    59  	// without panicking. (IMO this is less invasive that making updateCache
    60  	// responsible for creating missing caches etc.)
    61  	_, err := s.factory.HookContext(hook.Info{Kind: hooks.Install})
    62  	c.Assert(err, jc.ErrorIsNil)
    63  }
    64  
    65  func (s *ContextFactorySuite) updateCache(relId int, unitName string, settings params.Settings) {
    66  	context.UpdateCachedSettings(s.factory, relId, unitName, settings)
    67  }
    68  
    69  func (s *ContextFactorySuite) getCache(relId int, unitName string) (params.Settings, bool) {
    70  	return context.CachedSettings(s.factory, relId, unitName)
    71  }
    72  
    73  func (s *ContextFactorySuite) SetCharm(c *gc.C, name string) {
    74  	err := os.RemoveAll(s.paths.GetCharmDir())
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.GetCharmDir())
    77  	c.Assert(err, jc.ErrorIsNil)
    78  }
    79  
    80  func (s *ContextFactorySuite) getRelationInfos() map[int]*context.RelationInfo {
    81  	info := map[int]*context.RelationInfo{}
    82  	for relId, relUnit := range s.apiRelunits {
    83  		info[relId] = &context.RelationInfo{
    84  			RelationUnit: relUnit,
    85  			MemberNames:  s.membership[relId],
    86  		}
    87  	}
    88  	return info
    89  }
    90  
    91  func (s *ContextFactorySuite) testLeadershipContextWiring(c *gc.C, createContext func() *context.HookContext) {
    92  	var stub testing.Stub
    93  	stub.SetErrors(errors.New("bam"))
    94  	restore := context.PatchNewLeadershipContext(
    95  		func(accessor context.LeadershipSettingsAccessor, tracker leadership.Tracker) context.LeadershipContext {
    96  			stub.AddCall("NewLeadershipContext", accessor, tracker)
    97  			return &StubLeadershipContext{Stub: &stub}
    98  		},
    99  	)
   100  	defer restore()
   101  
   102  	ctx := createContext()
   103  	isLeader, err := ctx.IsLeader()
   104  	c.Check(err, gc.ErrorMatches, "bam")
   105  	c.Check(isLeader, jc.IsFalse)
   106  
   107  	stub.CheckCalls(c, []testing.StubCall{{
   108  		FuncName: "NewLeadershipContext",
   109  		Args:     []interface{}{s.uniter.LeadershipSettings, runnertesting.FakeTracker{}},
   110  	}, {
   111  		FuncName: "IsLeader",
   112  	}})
   113  
   114  }
   115  
   116  func (s *ContextFactorySuite) TestNewHookContextLeadershipContext(c *gc.C) {
   117  	s.testLeadershipContextWiring(c, func() *context.HookContext {
   118  		ctx, err := s.factory.HookContext(hook.Info{Kind: hooks.ConfigChanged})
   119  		c.Assert(err, jc.ErrorIsNil)
   120  		return ctx
   121  	})
   122  }
   123  
   124  func (s *ContextFactorySuite) TestNewCommandContextLeadershipContext(c *gc.C) {
   125  	s.testLeadershipContextWiring(c, func() *context.HookContext {
   126  		ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: -1})
   127  		c.Assert(err, jc.ErrorIsNil)
   128  		return ctx
   129  	})
   130  }
   131  
   132  func (s *ContextFactorySuite) TestNewActionContextLeadershipContext(c *gc.C) {
   133  	s.testLeadershipContextWiring(c, func() *context.HookContext {
   134  		s.SetCharm(c, "dummy")
   135  		action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil)
   136  		c.Assert(err, jc.ErrorIsNil)
   137  
   138  		actionData := &context.ActionData{
   139  			Name:       action.Name(),
   140  			Tag:        names.NewActionTag(action.Id()),
   141  			Params:     action.Parameters(),
   142  			ResultsMap: map[string]interface{}{},
   143  		}
   144  
   145  		ctx, err := s.factory.ActionContext(actionData)
   146  		c.Assert(err, jc.ErrorIsNil)
   147  		return ctx
   148  	})
   149  }
   150  
   151  func (s *ContextFactorySuite) TestRelationHookContext(c *gc.C) {
   152  	hi := hook.Info{
   153  		Kind:       hooks.RelationBroken,
   154  		RelationId: 1,
   155  	}
   156  	ctx, err := s.factory.HookContext(hi)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	s.AssertCoreContext(c, ctx)
   159  	s.AssertNotActionContext(c, ctx)
   160  	s.AssertRelationContext(c, ctx, 1, "")
   161  	s.AssertNotStorageContext(c, ctx)
   162  }
   163  
   164  func (s *ContextFactorySuite) TestNewHookContextWithStorage(c *gc.C) {
   165  	// We need to set up a unit that has storage metadata defined.
   166  	ch := s.AddTestingCharm(c, "storage-block")
   167  	sCons := map[string]state.StorageConstraints{
   168  		"data": {Pool: "", Size: 1024, Count: 1},
   169  	}
   170  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons)
   171  	s.machine = nil // allocate a new machine
   172  	unit := s.AddUnit(c, service)
   173  
   174  	storageAttachments, err := s.State.UnitStorageAttachments(unit.UnitTag())
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	c.Assert(storageAttachments, gc.HasLen, 1)
   177  	storageTag := storageAttachments[0].StorageInstance()
   178  
   179  	volume, err := s.State.StorageInstanceVolume(storageTag)
   180  	c.Assert(err, jc.ErrorIsNil)
   181  	volumeTag := volume.VolumeTag()
   182  	machineTag := s.machine.MachineTag()
   183  
   184  	err = s.State.SetVolumeInfo(
   185  		volumeTag, state.VolumeInfo{
   186  			VolumeId: "vol-123",
   187  			Size:     456,
   188  		},
   189  	)
   190  	c.Assert(err, jc.ErrorIsNil)
   191  	err = s.State.SetVolumeAttachmentInfo(
   192  		machineTag, volumeTag, state.VolumeAttachmentInfo{
   193  			DeviceName: "sdb",
   194  		},
   195  	)
   196  	c.Assert(err, jc.ErrorIsNil)
   197  
   198  	password, err := utils.RandomPassword()
   199  	err = unit.SetPassword(password)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	st := s.OpenAPIAs(c, unit.Tag(), password)
   202  	uniter, err := st.Uniter()
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	contextFactory, err := context.NewContextFactory(
   206  		uniter,
   207  		unit.Tag().(names.UnitTag),
   208  		runnertesting.FakeTracker{},
   209  		s.getRelationInfos,
   210  		s.storage,
   211  		s.paths,
   212  		testing.NewClock(time.Time{}),
   213  	)
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	ctx, err := contextFactory.HookContext(hook.Info{
   216  		Kind:      hooks.StorageAttached,
   217  		StorageId: "data/0",
   218  	})
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	c.Assert(ctx.UnitName(), gc.Equals, "storage-block/0")
   221  	s.AssertStorageContext(c, ctx, "data/0", storage.StorageAttachmentInfo{
   222  		Kind:     storage.StorageKindBlock,
   223  		Location: "/dev/sdb",
   224  	})
   225  	s.AssertNotActionContext(c, ctx)
   226  	s.AssertNotRelationContext(c, ctx)
   227  }
   228  
   229  func (s *ContextFactorySuite) TestActionContext(c *gc.C) {
   230  	s.SetCharm(c, "dummy")
   231  	action, err := s.State.EnqueueAction(s.unit.Tag(), "snapshot", nil)
   232  	c.Assert(err, jc.ErrorIsNil)
   233  
   234  	actionData := &context.ActionData{
   235  		Name:       action.Name(),
   236  		Tag:        names.NewActionTag(action.Id()),
   237  		Params:     action.Parameters(),
   238  		ResultsMap: map[string]interface{}{},
   239  	}
   240  
   241  	ctx, err := s.factory.ActionContext(actionData)
   242  	c.Assert(err, jc.ErrorIsNil)
   243  
   244  	s.AssertCoreContext(c, ctx)
   245  	s.AssertActionContext(c, ctx)
   246  	s.AssertNotRelationContext(c, ctx)
   247  	s.AssertNotStorageContext(c, ctx)
   248  }
   249  
   250  func (s *ContextFactorySuite) TestCommandContext(c *gc.C) {
   251  	ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: -1})
   252  	c.Assert(err, jc.ErrorIsNil)
   253  
   254  	s.AssertCoreContext(c, ctx)
   255  	s.AssertNotActionContext(c, ctx)
   256  	s.AssertNotRelationContext(c, ctx)
   257  	s.AssertNotStorageContext(c, ctx)
   258  }
   259  
   260  func (s *ContextFactorySuite) TestCommandContextNoRelation(c *gc.C) {
   261  	ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: -1})
   262  	c.Assert(err, jc.ErrorIsNil)
   263  	s.AssertCoreContext(c, ctx)
   264  	s.AssertNotActionContext(c, ctx)
   265  	s.AssertNotRelationContext(c, ctx)
   266  	s.AssertNotStorageContext(c, ctx)
   267  }
   268  
   269  func (s *ContextFactorySuite) TestNewCommandContextForceNoRemoteUnit(c *gc.C) {
   270  	ctx, err := s.factory.CommandContext(context.CommandInfo{
   271  		RelationId: 0, ForceRemoteUnit: true,
   272  	})
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	s.AssertCoreContext(c, ctx)
   275  	s.AssertNotActionContext(c, ctx)
   276  	s.AssertRelationContext(c, ctx, 0, "")
   277  	s.AssertNotStorageContext(c, ctx)
   278  }
   279  
   280  func (s *ContextFactorySuite) TestNewCommandContextForceRemoteUnitMissing(c *gc.C) {
   281  	ctx, err := s.factory.CommandContext(context.CommandInfo{
   282  		RelationId: 0, RemoteUnitName: "blah/123", ForceRemoteUnit: true,
   283  	})
   284  	c.Assert(err, gc.IsNil)
   285  	s.AssertCoreContext(c, ctx)
   286  	s.AssertNotActionContext(c, ctx)
   287  	s.AssertRelationContext(c, ctx, 0, "blah/123")
   288  	s.AssertNotStorageContext(c, ctx)
   289  }
   290  
   291  func (s *ContextFactorySuite) TestNewCommandContextInferRemoteUnit(c *gc.C) {
   292  	s.membership[0] = []string{"foo/2"}
   293  	ctx, err := s.factory.CommandContext(context.CommandInfo{RelationId: 0})
   294  	c.Assert(err, jc.ErrorIsNil)
   295  	s.AssertCoreContext(c, ctx)
   296  	s.AssertNotActionContext(c, ctx)
   297  	s.AssertRelationContext(c, ctx, 0, "foo/2")
   298  	s.AssertNotStorageContext(c, ctx)
   299  }
   300  
   301  func (s *ContextFactorySuite) TestNewHookContextPrunesNonMemberCaches(c *gc.C) {
   302  
   303  	// Write cached member settings for a member and a non-member.
   304  	s.setUpCacheMethods(c)
   305  	s.membership[0] = []string{"rel0/0"}
   306  	s.updateCache(0, "rel0/0", params.Settings{"keep": "me"})
   307  	s.updateCache(0, "rel0/1", params.Settings{"drop": "me"})
   308  
   309  	ctx, err := s.factory.HookContext(hook.Info{Kind: hooks.Install})
   310  	c.Assert(err, jc.ErrorIsNil)
   311  
   312  	settings0, found := s.getCache(0, "rel0/0")
   313  	c.Assert(found, jc.IsTrue)
   314  	c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"})
   315  
   316  	settings1, found := s.getCache(0, "rel0/1")
   317  	c.Assert(found, jc.IsFalse)
   318  	c.Assert(settings1, gc.IsNil)
   319  
   320  	// Check the caches are being used by the context relations.
   321  	relCtx, err := ctx.Relation(0)
   322  	c.Assert(err, jc.ErrorIsNil)
   323  
   324  	// Verify that the settings really were cached by trying to look them up.
   325  	// Nothing's really in scope, so the call would fail if they weren't.
   326  	settings0, err = relCtx.ReadSettings("rel0/0")
   327  	c.Assert(err, jc.ErrorIsNil)
   328  	c.Assert(settings0, jc.DeepEquals, params.Settings{"keep": "me"})
   329  
   330  	// Verify that the non-member settings were purged by looking them up and
   331  	// checking for the expected error.
   332  	settings1, err = relCtx.ReadSettings("rel0/1")
   333  	c.Assert(settings1, gc.IsNil)
   334  	c.Assert(err, gc.ErrorMatches, "permission denied")
   335  }
   336  
   337  func (s *ContextFactorySuite) TestNewHookContextRelationJoinedUpdatesRelationContextAndCaches(c *gc.C) {
   338  	// Write some cached settings for r/0, so we can verify the cache gets cleared.
   339  	s.setUpCacheMethods(c)
   340  	s.membership[1] = []string{"r/0"}
   341  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   342  
   343  	ctx, err := s.factory.HookContext(hook.Info{
   344  		Kind:       hooks.RelationJoined,
   345  		RelationId: 1,
   346  		RemoteUnit: "r/0",
   347  	})
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	s.AssertCoreContext(c, ctx)
   350  	s.AssertNotActionContext(c, ctx)
   351  	s.AssertNotStorageContext(c, ctx)
   352  	rel := s.AssertRelationContext(c, ctx, 1, "r/0")
   353  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0"})
   354  	cached0, member := s.getCache(1, "r/0")
   355  	c.Assert(cached0, gc.IsNil)
   356  	c.Assert(member, jc.IsTrue)
   357  }
   358  
   359  func (s *ContextFactorySuite) TestNewHookContextRelationChangedUpdatesRelationContextAndCaches(c *gc.C) {
   360  	// Update member settings to have actual values, so we can check that
   361  	// the change for r/4 clears its cache but leaves r/0's alone.
   362  	s.setUpCacheMethods(c)
   363  	s.membership[1] = []string{"r/0", "r/4"}
   364  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   365  	s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
   366  
   367  	ctx, err := s.factory.HookContext(hook.Info{
   368  		Kind:       hooks.RelationChanged,
   369  		RelationId: 1,
   370  		RemoteUnit: "r/4",
   371  	})
   372  	c.Assert(err, jc.ErrorIsNil)
   373  	s.AssertCoreContext(c, ctx)
   374  	s.AssertNotActionContext(c, ctx)
   375  	s.AssertNotStorageContext(c, ctx)
   376  	rel := s.AssertRelationContext(c, ctx, 1, "r/4")
   377  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"})
   378  	cached0, member := s.getCache(1, "r/0")
   379  	c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"})
   380  	c.Assert(member, jc.IsTrue)
   381  	cached4, member := s.getCache(1, "r/4")
   382  	c.Assert(cached4, gc.IsNil)
   383  	c.Assert(member, jc.IsTrue)
   384  }
   385  
   386  func (s *ContextFactorySuite) TestNewHookContextRelationDepartedUpdatesRelationContextAndCaches(c *gc.C) {
   387  	// Update member settings to have actual values, so we can check that
   388  	// the depart for r/0 leaves r/4's cache alone (while discarding r/0's).
   389  	s.setUpCacheMethods(c)
   390  	s.membership[1] = []string{"r/0", "r/4"}
   391  	s.updateCache(1, "r/0", params.Settings{"foo": "bar"})
   392  	s.updateCache(1, "r/4", params.Settings{"baz": "qux"})
   393  
   394  	ctx, err := s.factory.HookContext(hook.Info{
   395  		Kind:       hooks.RelationDeparted,
   396  		RelationId: 1,
   397  		RemoteUnit: "r/0",
   398  	})
   399  	c.Assert(err, jc.ErrorIsNil)
   400  	s.AssertCoreContext(c, ctx)
   401  	s.AssertNotActionContext(c, ctx)
   402  	s.AssertNotStorageContext(c, ctx)
   403  	rel := s.AssertRelationContext(c, ctx, 1, "r/0")
   404  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/4"})
   405  	cached0, member := s.getCache(1, "r/0")
   406  	c.Assert(cached0, gc.IsNil)
   407  	c.Assert(member, jc.IsFalse)
   408  	cached4, member := s.getCache(1, "r/4")
   409  	c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"})
   410  	c.Assert(member, jc.IsTrue)
   411  }
   412  
   413  func (s *ContextFactorySuite) TestNewHookContextRelationBrokenRetainsCaches(c *gc.C) {
   414  	// Note that this is bizarre and unrealistic, because we would never usually
   415  	// run relation-broken on a non-empty relation. But verfying that the settings
   416  	// stick around allows us to verify that there's no special handling for that
   417  	// hook -- as there should not be, because the relation caches will be discarded
   418  	// for the *next* hook, which will be constructed with the current set of known
   419  	// relations and ignore everything else.
   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  	ctx, err := s.factory.HookContext(hook.Info{
   426  		Kind:       hooks.RelationBroken,
   427  		RelationId: 1,
   428  	})
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	rel := s.AssertRelationContext(c, ctx, 1, "")
   431  	c.Assert(rel.UnitNames(), jc.DeepEquals, []string{"r/0", "r/4"})
   432  	cached0, member := s.getCache(1, "r/0")
   433  	c.Assert(cached0, jc.DeepEquals, params.Settings{"foo": "bar"})
   434  	c.Assert(member, jc.IsTrue)
   435  	cached4, member := s.getCache(1, "r/4")
   436  	c.Assert(cached4, jc.DeepEquals, params.Settings{"baz": "qux"})
   437  	c.Assert(member, jc.IsTrue)
   438  }
   439  
   440  type StubLeadershipContext struct {
   441  	context.LeadershipContext
   442  	*testing.Stub
   443  }
   444  
   445  func (stub *StubLeadershipContext) IsLeader() (bool, error) {
   446  	stub.MethodCall(stub, "IsLeader")
   447  	return false, stub.NextErr()
   448  }