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