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