github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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  	jujutesting "github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils"
    14  	"github.com/juju/utils/proxy"
    15  	gc "gopkg.in/check.v1"
    16  	"gopkg.in/juju/charm.v6-unstable"
    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/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  	service  *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    *jujutesting.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.service = s.AddTestingService(c, "u", sch)
    70  	s.unit = s.AddUnit(c, s.service)
    71  
    72  	s.meteredCharm = s.AddTestingCharm(c, "metered")
    73  	meteredService := s.AddTestingService(c, "m", s.meteredCharm)
    74  	meteredUnit := s.addUnit(c, meteredService)
    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: &runnertesting.ContextStorage{
   110  				storageData0,
   111  				storage.StorageKindBlock,
   112  				"/dev/sdb",
   113  			},
   114  		},
   115  	}
   116  
   117  	s.clock = jujutesting.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(
   126  		c, uuid.String(), relId, remoteName, noProxies,
   127  	)
   128  }
   129  
   130  func (s *HookContextSuite) addUnit(c *gc.C, svc *state.Application) *state.Unit {
   131  	unit, err := svc.AddUnit()
   132  	c.Assert(err, jc.ErrorIsNil)
   133  	if s.machine != nil {
   134  		err = unit.AssignToMachine(s.machine)
   135  		c.Assert(err, jc.ErrorIsNil)
   136  		return unit
   137  	}
   138  
   139  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	machineId, err := unit.AssignedMachineId()
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	s.machine, err = s.State.Machine(machineId)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	zone := "a-zone"
   146  	hwc := instance.HardwareCharacteristics{
   147  		AvailabilityZone: &zone,
   148  	}
   149  	err = s.machine.SetProvisioned("i-exist", "fake_nonce", &hwc)
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	return unit
   152  }
   153  
   154  func (s *HookContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit {
   155  	unit := s.addUnit(c, svc)
   156  	name := strings.Replace(unit.Name(), "/", "-", 1)
   157  	privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal)
   158  	err := s.machine.SetProviderAddresses(privateAddr)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  	return unit
   161  }
   162  
   163  func (s *HookContextSuite) AddContextRelation(c *gc.C, name string) {
   164  	s.AddTestingService(c, name, s.relch)
   165  	eps, err := s.State.InferEndpoints("u", name)
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	rel, err := s.State.AddRelation(eps...)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	ru, err := rel.Unit(s.unit)
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	err = ru.EnterScope(map[string]interface{}{"relation-name": name})
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	s.relunits[rel.Id()] = ru
   174  	apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag))
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	apiRelUnit, err := apiRel.Unit(s.apiUnit)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.apiRelunits[rel.Id()] = apiRelUnit
   179  }
   180  
   181  func (s *HookContextSuite) getHookContext(c *gc.C, uuid string, relid int,
   182  	remote string, proxies proxy.Settings) *context.HookContext {
   183  	if relid != -1 {
   184  		_, found := s.apiRelunits[relid]
   185  		c.Assert(found, jc.IsTrue)
   186  	}
   187  	facade, err := s.st.Uniter()
   188  	c.Assert(err, jc.ErrorIsNil)
   189  
   190  	relctxs := map[int]*context.ContextRelation{}
   191  	for relId, relUnit := range s.apiRelunits {
   192  		cache := context.NewRelationCache(relUnit.ReadSettings, nil)
   193  		relctxs[relId] = context.NewContextRelation(relUnit, cache)
   194  	}
   195  
   196  	env, err := s.State.Model()
   197  	c.Assert(err, jc.ErrorIsNil)
   198  
   199  	context, err := context.NewHookContext(s.apiUnit, facade, "TestCtx", uuid,
   200  		env.Name(), relid, remote, relctxs, apiAddrs,
   201  		proxies, false, nil, nil, s.machine.Tag().(names.MachineTag),
   202  		runnertesting.NewRealPaths(c), s.clock)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	return context
   205  }
   206  
   207  func (s *HookContextSuite) getMeteredHookContext(c *gc.C, uuid string, relid int,
   208  	remote string, proxies proxy.Settings, canAddMetrics bool, metrics *charm.Metrics, paths runnertesting.RealPaths) *context.HookContext {
   209  	if relid != -1 {
   210  		_, found := s.apiRelunits[relid]
   211  		c.Assert(found, jc.IsTrue)
   212  	}
   213  	facade, err := s.st.Uniter()
   214  	c.Assert(err, jc.ErrorIsNil)
   215  
   216  	relctxs := map[int]*context.ContextRelation{}
   217  	for relId, relUnit := range s.apiRelunits {
   218  		cache := context.NewRelationCache(relUnit.ReadSettings, nil)
   219  		relctxs[relId] = context.NewContextRelation(relUnit, cache)
   220  	}
   221  
   222  	context, err := context.NewHookContext(s.meteredAPIUnit, facade, "TestCtx", uuid,
   223  		"test-model-name", relid, remote, relctxs, apiAddrs,
   224  		proxies, canAddMetrics, metrics, nil, s.machine.Tag().(names.MachineTag),
   225  		paths, s.clock)
   226  	c.Assert(err, jc.ErrorIsNil)
   227  	return context
   228  }
   229  
   230  func (s *HookContextSuite) metricsDefinition(name string) *charm.Metrics {
   231  	return &charm.Metrics{Metrics: map[string]charm.Metric{name: {Type: charm.MetricTypeGauge, Description: "generated metric"}}}
   232  }
   233  
   234  func (s *HookContextSuite) AssertCoreContext(c *gc.C, ctx *context.HookContext) {
   235  	c.Assert(ctx.UnitName(), gc.Equals, "u/0")
   236  	c.Assert(context.ContextMachineTag(ctx), jc.DeepEquals, names.NewMachineTag("0"))
   237  
   238  	expect, expectErr := s.unit.PrivateAddress()
   239  	actual, actualErr := ctx.PrivateAddress()
   240  	c.Assert(actual, gc.Equals, expect.Value)
   241  	c.Assert(actualErr, jc.DeepEquals, expectErr)
   242  
   243  	expect, expectErr = s.unit.PublicAddress()
   244  	actual, actualErr = ctx.PublicAddress()
   245  	c.Assert(actual, gc.Equals, expect.Value)
   246  	c.Assert(actualErr, jc.DeepEquals, expectErr)
   247  
   248  	env, err := s.State.Model()
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	name, uuid := context.ContextEnvInfo(ctx)
   251  	c.Assert(name, gc.Equals, env.Name())
   252  	c.Assert(uuid, gc.Equals, env.UUID())
   253  
   254  	ids, err := ctx.RelationIds()
   255  	c.Assert(err, jc.ErrorIsNil)
   256  	c.Assert(ids, gc.HasLen, 2)
   257  
   258  	r, err := ctx.Relation(0)
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	c.Assert(r.Name(), gc.Equals, "db")
   261  	c.Assert(r.FakeId(), gc.Equals, "db:0")
   262  
   263  	r, err = ctx.Relation(1)
   264  	c.Assert(err, jc.ErrorIsNil)
   265  	c.Assert(r.Name(), gc.Equals, "db")
   266  	c.Assert(r.FakeId(), gc.Equals, "db:1")
   267  
   268  	az, err := ctx.AvailabilityZone()
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	c.Assert(az, gc.Equals, "a-zone")
   271  }
   272  
   273  func (s *HookContextSuite) AssertNotActionContext(c *gc.C, ctx *context.HookContext) {
   274  	actionData, err := ctx.ActionData()
   275  	c.Assert(actionData, gc.IsNil)
   276  	c.Assert(err, gc.ErrorMatches, "not running an action")
   277  }
   278  
   279  func (s *HookContextSuite) AssertActionContext(c *gc.C, ctx *context.HookContext) {
   280  	actionData, err := ctx.ActionData()
   281  	c.Assert(actionData, gc.NotNil)
   282  	c.Assert(err, jc.ErrorIsNil)
   283  }
   284  
   285  func (s *HookContextSuite) AssertNotStorageContext(c *gc.C, ctx *context.HookContext) {
   286  	storageAttachment, err := ctx.HookStorage()
   287  	c.Assert(storageAttachment, gc.IsNil)
   288  	c.Assert(err, gc.ErrorMatches, ".*")
   289  }
   290  
   291  func (s *HookContextSuite) AssertStorageContext(c *gc.C, ctx *context.HookContext, id string, attachment storage.StorageAttachmentInfo) {
   292  	fromCache, err := ctx.HookStorage()
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	c.Assert(fromCache, gc.NotNil)
   295  	c.Assert(fromCache.Tag().Id(), gc.Equals, id)
   296  	c.Assert(fromCache.Kind(), gc.Equals, attachment.Kind)
   297  	c.Assert(fromCache.Location(), gc.Equals, attachment.Location)
   298  }
   299  
   300  func (s *HookContextSuite) AssertRelationContext(c *gc.C, ctx *context.HookContext, relId int, remoteUnit string) *context.ContextRelation {
   301  	actualRemoteUnit, _ := ctx.RemoteUnitName()
   302  	c.Assert(actualRemoteUnit, gc.Equals, remoteUnit)
   303  	rel, err := ctx.HookRelation()
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	c.Assert(rel.Id(), gc.Equals, relId)
   306  	return rel.(*context.ContextRelation)
   307  }
   308  
   309  func (s *HookContextSuite) AssertNotRelationContext(c *gc.C, ctx *context.HookContext) {
   310  	rel, err := ctx.HookRelation()
   311  	c.Assert(rel, gc.IsNil)
   312  	c.Assert(err, gc.ErrorMatches, ".*")
   313  }
   314  
   315  type BlockHelper struct {
   316  	blockClient *block.Client
   317  }
   318  
   319  // NewBlockHelper creates a block switch used in testing
   320  // to manage desired juju blocks.
   321  func NewBlockHelper(st api.Connection) BlockHelper {
   322  	return BlockHelper{
   323  		blockClient: block.NewClient(st),
   324  	}
   325  }
   326  
   327  // on switches on desired block and
   328  // asserts that no errors were encountered.
   329  func (s *BlockHelper) on(c *gc.C, blockType multiwatcher.BlockType, msg string) {
   330  	c.Assert(s.blockClient.SwitchBlockOn(string(blockType), msg), gc.IsNil)
   331  }
   332  
   333  // BlockAllChanges switches changes block on.
   334  // This prevents all changes to juju environment.
   335  func (s *BlockHelper) BlockAllChanges(c *gc.C, msg string) {
   336  	s.on(c, multiwatcher.BlockChange, msg)
   337  }
   338  
   339  // BlockRemoveObject switches remove block on.
   340  // This prevents any object/entity removal on juju environment
   341  func (s *BlockHelper) BlockRemoveObject(c *gc.C, msg string) {
   342  	s.on(c, multiwatcher.BlockRemove, msg)
   343  }
   344  
   345  // BlockDestroyModel switches destroy block on.
   346  // This prevents juju environment destruction.
   347  func (s *BlockHelper) BlockDestroyModel(c *gc.C, msg string) {
   348  	s.on(c, multiwatcher.BlockDestroy, msg)
   349  }
   350  
   351  func (s *BlockHelper) Close() {
   352  	s.blockClient.Close()
   353  }
   354  
   355  // StubMetricsRecorder implements the MetricsRecorder interface.
   356  type StubMetricsRecorder struct {
   357  	*jujutesting.Stub
   358  }
   359  
   360  // AddMetric implements the MetricsRecorder interface.
   361  func (s StubMetricsRecorder) AddMetric(key, value string, created time.Time) error {
   362  	s.AddCall("AddMetric", key, value, created)
   363  	return nil
   364  }
   365  
   366  func (mr *StubMetricsRecorder) IsDeclaredMetric(key string) bool {
   367  	mr.MethodCall(mr, "IsDeclaredMetric", key)
   368  	return true
   369  }
   370  
   371  // Close implements the MetricsRecorder interface.
   372  func (s StubMetricsRecorder) Close() error {
   373  	s.AddCall("Close")
   374  	return nil
   375  }
   376  
   377  // MockEnvPaths implements Paths for tests that don't need to actually touch
   378  // the filesystem.
   379  type MockEnvPaths struct{}
   380  
   381  func (MockEnvPaths) GetToolsDir() string {
   382  	return "path-to-tools"
   383  }
   384  
   385  func (MockEnvPaths) GetCharmDir() string {
   386  	return "path-to-charm"
   387  }
   388  
   389  func (MockEnvPaths) GetJujucSocket() string {
   390  	return "path-to-jujuc.socket"
   391  }
   392  
   393  func (MockEnvPaths) GetMetricsSpoolDir() string {
   394  	return "path-to-metrics-spool-dir"
   395  }
   396  
   397  func (MockEnvPaths) ComponentDir(name string) string {
   398  	return filepath.Join("path-to-base-dir", name)
   399  }