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