github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/runner/context/util_test.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package context_test
     5  
     6  import (
     7  	"path/filepath"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/juju/clock/testclock"
    12  	"github.com/juju/proxy"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/charm.v6"
    17  	"gopkg.in/juju/names.v2"
    18  
    19  	"github.com/juju/juju/api"
    20  	"github.com/juju/juju/api/block"
    21  	"github.com/juju/juju/api/uniter"
    22  	"github.com/juju/juju/core/instance"
    23  	"github.com/juju/juju/juju/testing"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/state"
    26  	"github.com/juju/juju/state/multiwatcher"
    27  	"github.com/juju/juju/storage"
    28  	"github.com/juju/juju/worker/uniter/runner/context"
    29  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    30  	runnertesting "github.com/juju/juju/worker/uniter/runner/testing"
    31  )
    32  
    33  var noProxies = proxy.Settings{}
    34  var apiAddrs = []string{"a1:123", "a2:123"}
    35  var expectedAPIAddrs = strings.Join(apiAddrs, " ")
    36  
    37  // HookContextSuite contains shared setup for various other test suites. Test
    38  // methods should not be added to this type, because they'll get run repeatedly.
    39  type HookContextSuite struct {
    40  	testing.JujuConnSuite
    41  	application *state.Application
    42  	unit        *state.Unit
    43  	machine     *state.Machine
    44  	relch       *state.Charm
    45  	relunits    map[int]*state.RelationUnit
    46  	storage     *runnertesting.StorageContextAccessor
    47  	clock       *testclock.Clock
    48  
    49  	st             api.Connection
    50  	uniter         *uniter.State
    51  	apiUnit        *uniter.Unit
    52  	meteredAPIUnit *uniter.Unit
    53  	meteredCharm   *state.Charm
    54  	apiRelunits    map[int]*uniter.RelationUnit
    55  	BlockHelper
    56  }
    57  
    58  func (s *HookContextSuite) SetUpTest(c *gc.C) {
    59  	var err error
    60  	s.JujuConnSuite.SetUpTest(c)
    61  	s.BlockHelper = NewBlockHelper(s.APIState)
    62  	c.Assert(s.BlockHelper, gc.NotNil)
    63  	s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() })
    64  
    65  	// reset
    66  	s.machine = nil
    67  
    68  	sch := s.AddTestingCharm(c, "wordpress")
    69  	s.application = s.AddTestingApplication(c, "u", sch)
    70  	s.unit = s.AddUnit(c, s.application)
    71  
    72  	s.meteredCharm = s.AddTestingCharm(c, "metered")
    73  	meteredApplication := s.AddTestingApplication(c, "m", s.meteredCharm)
    74  	meteredUnit := s.addUnit(c, meteredApplication)
    75  	err = meteredUnit.SetCharmURL(s.meteredCharm.URL())
    76  	c.Assert(err, jc.ErrorIsNil)
    77  
    78  	password, err := utils.RandomPassword()
    79  	err = s.unit.SetPassword(password)
    80  	c.Assert(err, jc.ErrorIsNil)
    81  	s.st = s.OpenAPIAs(c, s.unit.Tag(), password)
    82  	s.uniter, err = s.st.Uniter()
    83  	c.Assert(err, jc.ErrorIsNil)
    84  	c.Assert(s.uniter, gc.NotNil)
    85  	s.apiUnit, err = s.uniter.Unit(s.unit.Tag().(names.UnitTag))
    86  	c.Assert(err, jc.ErrorIsNil)
    87  
    88  	err = meteredUnit.SetPassword(password)
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	meteredState := s.OpenAPIAs(c, meteredUnit.Tag(), password)
    91  	meteredUniter, err := meteredState.Uniter()
    92  	s.meteredAPIUnit, err = meteredUniter.Unit(meteredUnit.Tag().(names.UnitTag))
    93  	c.Assert(err, jc.ErrorIsNil)
    94  
    95  	// Note: The unit must always have a charm URL set, because this
    96  	// happens as part of the installation process (that happens
    97  	// before the initial install hook).
    98  	err = s.unit.SetCharmURL(sch.URL())
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	s.relch = s.AddTestingCharm(c, "mysql")
   101  	s.relunits = map[int]*state.RelationUnit{}
   102  	s.apiRelunits = map[int]*uniter.RelationUnit{}
   103  	s.AddContextRelation(c, "db0")
   104  	s.AddContextRelation(c, "db1")
   105  
   106  	storageData0 := names.NewStorageTag("data/0")
   107  	s.storage = &runnertesting.StorageContextAccessor{
   108  		map[names.StorageTag]*runnertesting.ContextStorage{
   109  			storageData0: {
   110  				storageData0,
   111  				storage.StorageKindBlock,
   112  				"/dev/sdb",
   113  			},
   114  		},
   115  	}
   116  
   117  	s.clock = testclock.NewClock(time.Time{})
   118  }
   119  
   120  func (s *HookContextSuite) GetContext(
   121  	c *gc.C, relId int, remoteName string,
   122  ) jujuc.Context {
   123  	uuid, err := utils.NewUUID()
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	return s.getHookContext(c, uuid.String(), relId, remoteName)
   126  }
   127  
   128  func (s *HookContextSuite) addUnit(c *gc.C, svc *state.Application) *state.Unit {
   129  	unit, err := svc.AddUnit(state.AddUnitParams{})
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	if s.machine != nil {
   132  		err = unit.AssignToMachine(s.machine)
   133  		c.Assert(err, jc.ErrorIsNil)
   134  		return unit
   135  	}
   136  
   137  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	machineId, err := unit.AssignedMachineId()
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	s.machine, err = s.State.Machine(machineId)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	zone := "a-zone"
   144  	hwc := instance.HardwareCharacteristics{
   145  		AvailabilityZone: &zone,
   146  	}
   147  	err = s.machine.SetProvisioned("i-exist", "", "fake_nonce", &hwc)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	return unit
   150  }
   151  
   152  func (s *HookContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit {
   153  	unit := s.addUnit(c, svc)
   154  	name := strings.Replace(unit.Name(), "/", "-", 1)
   155  	privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal)
   156  	err := s.machine.SetProviderAddresses(privateAddr)
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	return unit
   159  }
   160  
   161  func (s *HookContextSuite) AddContextRelation(c *gc.C, name string) {
   162  	s.AddTestingApplication(c, name, s.relch)
   163  	eps, err := s.State.InferEndpoints("u", name)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	rel, err := s.State.AddRelation(eps...)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	ru, err := rel.Unit(s.unit)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	err = ru.EnterScope(map[string]interface{}{"relation-name": name})
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	s.relunits[rel.Id()] = ru
   172  	apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag))
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	apiRelUnit, err := apiRel.Unit(s.apiUnit)
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	s.apiRelunits[rel.Id()] = apiRelUnit
   177  }
   178  
   179  func (s *HookContextSuite) getHookContext(c *gc.C, uuid string, relid int, remote string) *context.HookContext {
   180  	if relid != -1 {
   181  		_, found := s.apiRelunits[relid]
   182  		c.Assert(found, jc.IsTrue)
   183  	}
   184  	facade, err := s.st.Uniter()
   185  	c.Assert(err, jc.ErrorIsNil)
   186  
   187  	relctxs := map[int]*context.ContextRelation{}
   188  	for relId, relUnit := range s.apiRelunits {
   189  		cache := context.NewRelationCache(relUnit.ReadSettings, nil)
   190  		relctxs[relId] = context.NewContextRelation(relUnit, cache)
   191  	}
   192  
   193  	env, err := s.State.Model()
   194  	c.Assert(err, jc.ErrorIsNil)
   195  
   196  	context, err := context.NewHookContext(s.apiUnit, facade, "TestCtx", uuid,
   197  		env.Name(), relid, remote, relctxs, apiAddrs,
   198  		noProxies, noProxies, false, nil, nil, s.machine.Tag().(names.MachineTag),
   199  		runnertesting.NewRealPaths(c), s.clock)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	return context
   202  }
   203  
   204  func (s *HookContextSuite) getMeteredHookContext(c *gc.C, uuid string, relid int,
   205  	remote string, canAddMetrics bool, metrics *charm.Metrics, paths runnertesting.RealPaths) *context.HookContext {
   206  	if relid != -1 {
   207  		_, found := s.apiRelunits[relid]
   208  		c.Assert(found, jc.IsTrue)
   209  	}
   210  	facade, err := s.st.Uniter()
   211  	c.Assert(err, jc.ErrorIsNil)
   212  
   213  	relctxs := map[int]*context.ContextRelation{}
   214  	for relId, relUnit := range s.apiRelunits {
   215  		cache := context.NewRelationCache(relUnit.ReadSettings, nil)
   216  		relctxs[relId] = context.NewContextRelation(relUnit, cache)
   217  	}
   218  
   219  	context, err := context.NewHookContext(s.meteredAPIUnit, facade, "TestCtx", uuid,
   220  		"test-model-name", relid, remote, relctxs, apiAddrs,
   221  		noProxies, noProxies, canAddMetrics, metrics, nil, s.machine.Tag().(names.MachineTag),
   222  		paths, s.clock)
   223  	c.Assert(err, jc.ErrorIsNil)
   224  	return context
   225  }
   226  
   227  func (s *HookContextSuite) metricsDefinition(name string) *charm.Metrics {
   228  	return &charm.Metrics{Metrics: map[string]charm.Metric{name: {Type: charm.MetricTypeGauge, Description: "generated metric"}}}
   229  }
   230  
   231  func (s *HookContextSuite) AssertCoreContext(c *gc.C, ctx *context.HookContext) {
   232  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   233  	c.Assert(context.ContextMachineTag(ctx), jc.DeepEquals, names.NewMachineTag("0"))
   234  
   235  	expect, expectErr := s.unit.PrivateAddress()
   236  	actual, actualErr := ctx.PrivateAddress()
   237  	c.Assert(actual, gc.Equals, expect.Value)
   238  	c.Assert(actualErr, jc.DeepEquals, expectErr)
   239  
   240  	expect, expectErr = s.unit.PublicAddress()
   241  	actual, actualErr = ctx.PublicAddress()
   242  	c.Assert(actual, gc.Equals, expect.Value)
   243  	c.Assert(actualErr, jc.DeepEquals, expectErr)
   244  
   245  	env, err := s.State.Model()
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	name, uuid := context.ContextEnvInfo(ctx)
   248  	c.Assert(name, gc.Equals, env.Name())
   249  	c.Assert(uuid, gc.Equals, env.UUID())
   250  
   251  	ids, err := ctx.RelationIds()
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	c.Assert(ids, gc.HasLen, 2)
   254  
   255  	r, err := ctx.Relation(0)
   256  	c.Assert(err, jc.ErrorIsNil)
   257  	c.Assert(r.Name(), gc.Equals, "db")
   258  	c.Assert(r.FakeId(), gc.Equals, "db:0")
   259  
   260  	r, err = ctx.Relation(1)
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(r.Name(), gc.Equals, "db")
   263  	c.Assert(r.FakeId(), gc.Equals, "db:1")
   264  
   265  	az, err := ctx.AvailabilityZone()
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	c.Assert(az, gc.Equals, "a-zone")
   268  }
   269  
   270  func (s *HookContextSuite) AssertNotActionContext(c *gc.C, ctx *context.HookContext) {
   271  	actionData, err := ctx.ActionData()
   272  	c.Assert(actionData, gc.IsNil)
   273  	c.Assert(err, gc.ErrorMatches, "not running an action")
   274  }
   275  
   276  func (s *HookContextSuite) AssertActionContext(c *gc.C, ctx *context.HookContext) {
   277  	actionData, err := ctx.ActionData()
   278  	c.Assert(actionData, gc.NotNil)
   279  	c.Assert(err, jc.ErrorIsNil)
   280  }
   281  
   282  func (s *HookContextSuite) AssertNotStorageContext(c *gc.C, ctx *context.HookContext) {
   283  	storageAttachment, err := ctx.HookStorage()
   284  	c.Assert(storageAttachment, gc.IsNil)
   285  	c.Assert(err, gc.ErrorMatches, ".*")
   286  }
   287  
   288  func (s *HookContextSuite) AssertStorageContext(c *gc.C, ctx *context.HookContext, id string, attachment storage.StorageAttachmentInfo) {
   289  	fromCache, err := ctx.HookStorage()
   290  	c.Assert(err, jc.ErrorIsNil)
   291  	c.Assert(fromCache, gc.NotNil)
   292  	c.Assert(fromCache.Tag().Id(), gc.Equals, id)
   293  	c.Assert(fromCache.Kind(), gc.Equals, attachment.Kind)
   294  	c.Assert(fromCache.Location(), gc.Equals, attachment.Location)
   295  }
   296  
   297  func (s *HookContextSuite) AssertRelationContext(c *gc.C, ctx *context.HookContext, relId int, remoteUnit string) *context.ContextRelation {
   298  	actualRemoteUnit, _ := ctx.RemoteUnitName()
   299  	c.Assert(actualRemoteUnit, gc.Equals, remoteUnit)
   300  	rel, err := ctx.HookRelation()
   301  	c.Assert(err, jc.ErrorIsNil)
   302  	c.Assert(rel.Id(), gc.Equals, relId)
   303  	return rel.(*context.ContextRelation)
   304  }
   305  
   306  func (s *HookContextSuite) AssertNotRelationContext(c *gc.C, ctx *context.HookContext) {
   307  	rel, err := ctx.HookRelation()
   308  	c.Assert(rel, gc.IsNil)
   309  	c.Assert(err, gc.ErrorMatches, ".*")
   310  }
   311  
   312  type BlockHelper struct {
   313  	blockClient *block.Client
   314  }
   315  
   316  // NewBlockHelper creates a block switch used in testing
   317  // to manage desired juju blocks.
   318  func NewBlockHelper(st api.Connection) BlockHelper {
   319  	return BlockHelper{
   320  		blockClient: block.NewClient(st),
   321  	}
   322  }
   323  
   324  // on switches on desired block and
   325  // asserts that no errors were encountered.
   326  func (s *BlockHelper) on(c *gc.C, blockType multiwatcher.BlockType, msg string) {
   327  	c.Assert(s.blockClient.SwitchBlockOn(string(blockType), msg), gc.IsNil)
   328  }
   329  
   330  // BlockAllChanges switches changes block on.
   331  // This prevents all changes to juju environment.
   332  func (s *BlockHelper) BlockAllChanges(c *gc.C, msg string) {
   333  	s.on(c, multiwatcher.BlockChange, msg)
   334  }
   335  
   336  // BlockRemoveObject switches remove block on.
   337  // This prevents any object/entity removal on juju environment
   338  func (s *BlockHelper) BlockRemoveObject(c *gc.C, msg string) {
   339  	s.on(c, multiwatcher.BlockRemove, msg)
   340  }
   341  
   342  // BlockDestroyModel switches destroy block on.
   343  // This prevents juju environment destruction.
   344  func (s *BlockHelper) BlockDestroyModel(c *gc.C, msg string) {
   345  	s.on(c, multiwatcher.BlockDestroy, msg)
   346  }
   347  
   348  func (s *BlockHelper) Close() {
   349  	s.blockClient.Close()
   350  }
   351  
   352  // MockEnvPaths implements Paths for tests that don't need to actually touch
   353  // the filesystem.
   354  type MockEnvPaths struct{}
   355  
   356  func (MockEnvPaths) GetToolsDir() string {
   357  	return "path-to-tools"
   358  }
   359  
   360  func (MockEnvPaths) GetCharmDir() string {
   361  	return "path-to-charm"
   362  }
   363  
   364  func (MockEnvPaths) GetJujucSocket() string {
   365  	return "path-to-jujuc.socket"
   366  }
   367  
   368  func (MockEnvPaths) GetMetricsSpoolDir() string {
   369  	return "path-to-metrics-spool-dir"
   370  }
   371  
   372  func (MockEnvPaths) ComponentDir(name string) string {
   373  	return filepath.Join("path-to-base-dir", name)
   374  }