github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/runner/util_test.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package runner_test
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/juju/names"
    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.v4"
    18  
    19  	"github.com/juju/juju/api"
    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/worker/uniter/runner"
    26  )
    27  
    28  var noProxies = proxy.Settings{}
    29  var apiAddrs = []string{"a1:123", "a2:123"}
    30  var expectedApiAddrs = strings.Join(apiAddrs, " ")
    31  
    32  // MockEnvPaths implements Paths for tests that don't need to actually touch
    33  // the filesystem.
    34  type MockEnvPaths struct{}
    35  
    36  func (MockEnvPaths) GetToolsDir() string {
    37  	return "path-to-tools"
    38  }
    39  
    40  func (MockEnvPaths) GetCharmDir() string {
    41  	return "path-to-charm"
    42  }
    43  
    44  func (MockEnvPaths) GetJujucSocket() string {
    45  	return "path-to-jujuc.socket"
    46  }
    47  
    48  // RealPaths implements Paths for tests that do touch the filesystem.
    49  type RealPaths struct {
    50  	tools  string
    51  	charm  string
    52  	socket string
    53  }
    54  
    55  func NewRealPaths(c *gc.C) RealPaths {
    56  	return RealPaths{
    57  		tools:  c.MkDir(),
    58  		charm:  c.MkDir(),
    59  		socket: filepath.Join(c.MkDir(), "jujuc.socket"),
    60  	}
    61  }
    62  
    63  func (p RealPaths) GetToolsDir() string {
    64  	return p.tools
    65  }
    66  
    67  func (p RealPaths) GetCharmDir() string {
    68  	return p.charm
    69  }
    70  
    71  func (p RealPaths) GetJujucSocket() string {
    72  	return p.socket
    73  }
    74  
    75  // HookContextSuite contains shared setup for various other test suites. Test
    76  // methods should not be added to this type, because they'll get run repeatedly.
    77  type HookContextSuite struct {
    78  	testing.JujuConnSuite
    79  	service  *state.Service
    80  	unit     *state.Unit
    81  	machine  *state.Machine
    82  	relch    *state.Charm
    83  	relunits map[int]*state.RelationUnit
    84  
    85  	st             *api.State
    86  	uniter         *uniter.State
    87  	apiUnit        *uniter.Unit
    88  	meteredApiUnit *uniter.Unit
    89  	apiRelunits    map[int]*uniter.RelationUnit
    90  }
    91  
    92  func (s *HookContextSuite) SetUpTest(c *gc.C) {
    93  	var err error
    94  	s.JujuConnSuite.SetUpTest(c)
    95  
    96  	// reset
    97  	s.machine = nil
    98  
    99  	sch := s.AddTestingCharm(c, "wordpress")
   100  	s.service = s.AddTestingService(c, "u", sch)
   101  	s.unit = s.AddUnit(c, s.service)
   102  
   103  	meteredCharm := s.AddTestingCharm(c, "metered")
   104  	meteredService := s.AddTestingService(c, "m", meteredCharm)
   105  	meteredUnit := s.addUnit(c, meteredService)
   106  	err = meteredUnit.SetCharmURL(meteredCharm.URL())
   107  	c.Assert(err, jc.ErrorIsNil)
   108  
   109  	password, err := utils.RandomPassword()
   110  	err = s.unit.SetPassword(password)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	s.st = s.OpenAPIAs(c, s.unit.Tag(), password)
   113  	s.uniter, err = s.st.Uniter()
   114  	c.Assert(err, jc.ErrorIsNil)
   115  	c.Assert(s.uniter, gc.NotNil)
   116  	s.apiUnit, err = s.uniter.Unit(s.unit.Tag().(names.UnitTag))
   117  	c.Assert(err, jc.ErrorIsNil)
   118  
   119  	err = meteredUnit.SetPassword(password)
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	meteredState := s.OpenAPIAs(c, meteredUnit.Tag(), password)
   122  	meteredUniter, err := meteredState.Uniter()
   123  	s.meteredApiUnit, err = meteredUniter.Unit(meteredUnit.Tag().(names.UnitTag))
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	// Note: The unit must always have a charm URL set, because this
   127  	// happens as part of the installation process (that happens
   128  	// before the initial install hook).
   129  	err = s.unit.SetCharmURL(sch.URL())
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	s.relch = s.AddTestingCharm(c, "mysql")
   132  	s.relunits = map[int]*state.RelationUnit{}
   133  	s.apiRelunits = map[int]*uniter.RelationUnit{}
   134  	s.AddContextRelation(c, "db0")
   135  	s.AddContextRelation(c, "db1")
   136  }
   137  
   138  func (s *HookContextSuite) addUnit(c *gc.C, svc *state.Service) *state.Unit {
   139  	unit, err := svc.AddUnit()
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	if s.machine == nil {
   142  		s.machine, err = s.State.AddMachine("quantal", state.JobHostUnits)
   143  		c.Assert(err, jc.ErrorIsNil)
   144  		zone := "a-zone"
   145  		hwc := instance.HardwareCharacteristics{
   146  			AvailabilityZone: &zone,
   147  		}
   148  		err = s.machine.SetProvisioned("i-exist", "fake_nonce", &hwc)
   149  		c.Assert(err, jc.ErrorIsNil)
   150  	}
   151  	err = unit.AssignToMachine(s.machine)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	return unit
   154  }
   155  
   156  func (s *HookContextSuite) AddUnit(c *gc.C, svc *state.Service) *state.Unit {
   157  	unit := s.addUnit(c, svc)
   158  	name := strings.Replace(unit.Name(), "/", "-", 1)
   159  	privateAddr := network.NewAddress(name+".testing.invalid", network.ScopeCloudLocal)
   160  	err := s.machine.SetAddresses(privateAddr)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	return unit
   163  }
   164  
   165  func (s *HookContextSuite) AddContextRelation(c *gc.C, name string) {
   166  	s.AddTestingService(c, name, s.relch)
   167  	eps, err := s.State.InferEndpoints("u", name)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	rel, err := s.State.AddRelation(eps...)
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	ru, err := rel.Unit(s.unit)
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	err = ru.EnterScope(map[string]interface{}{"relation-name": name})
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	s.relunits[rel.Id()] = ru
   176  	apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag))
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	apiRelUnit, err := apiRel.Unit(s.apiUnit)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	s.apiRelunits[rel.Id()] = apiRelUnit
   181  }
   182  
   183  func (s *HookContextSuite) getHookContext(c *gc.C, uuid string, relid int,
   184  	remote string, proxies proxy.Settings) *runner.HookContext {
   185  	if relid != -1 {
   186  		_, found := s.apiRelunits[relid]
   187  		c.Assert(found, jc.IsTrue)
   188  	}
   189  	facade, err := s.st.Uniter()
   190  	c.Assert(err, jc.ErrorIsNil)
   191  
   192  	relctxs := map[int]*runner.ContextRelation{}
   193  	for relId, relUnit := range s.apiRelunits {
   194  		cache := runner.NewRelationCache(relUnit.ReadSettings, nil)
   195  		relctxs[relId] = runner.NewContextRelation(relUnit, cache)
   196  	}
   197  
   198  	context, err := runner.NewHookContext(s.apiUnit, facade, "TestCtx", uuid,
   199  		"test-env-name", relid, remote, relctxs, apiAddrs, names.NewUserTag("owner"),
   200  		proxies, false, nil, nil, s.machine.Tag().(names.MachineTag))
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	return context
   203  }
   204  
   205  func (s *HookContextSuite) getMeteredHookContext(c *gc.C, uuid string, relid int,
   206  	remote string, proxies proxy.Settings, canAddMetrics bool, metrics *charm.Metrics) *runner.HookContext {
   207  	if relid != -1 {
   208  		_, found := s.apiRelunits[relid]
   209  		c.Assert(found, jc.IsTrue)
   210  	}
   211  	facade, err := s.st.Uniter()
   212  	c.Assert(err, jc.ErrorIsNil)
   213  
   214  	relctxs := map[int]*runner.ContextRelation{}
   215  	for relId, relUnit := range s.apiRelunits {
   216  		cache := runner.NewRelationCache(relUnit.ReadSettings, nil)
   217  		relctxs[relId] = runner.NewContextRelation(relUnit, cache)
   218  	}
   219  
   220  	context, err := runner.NewHookContext(s.meteredApiUnit, facade, "TestCtx", uuid,
   221  		"test-env-name", relid, remote, relctxs, apiAddrs, names.NewUserTag("owner"),
   222  		proxies, canAddMetrics, metrics, nil, s.machine.Tag().(names.MachineTag))
   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  // hookSpec supports makeCharm.
   232  type hookSpec struct {
   233  	// dir is the directory to create the hook in.
   234  	dir string
   235  	// name is the name of the hook.
   236  	name string
   237  	// perm is the file permissions of the hook.
   238  	perm os.FileMode
   239  	// code is the exit status of the hook.
   240  	code int
   241  	// stdout holds a string to print to stdout
   242  	stdout string
   243  	// stderr holds a string to print to stderr
   244  	stderr string
   245  	// background holds a string to print in the background after 0.2s.
   246  	background string
   247  }
   248  
   249  // makeCharm constructs a fake charm dir containing a single named hook
   250  // with permissions perm and exit code code. If output is non-empty,
   251  // the charm will write it to stdout and stderr, with each one prefixed
   252  // by name of the stream.
   253  func makeCharm(c *gc.C, spec hookSpec, charmDir string) {
   254  	dir := charmDir
   255  	if spec.dir != "" {
   256  		dir = filepath.Join(dir, spec.dir)
   257  		err := os.Mkdir(dir, 0755)
   258  		c.Assert(err, jc.ErrorIsNil)
   259  	}
   260  	c.Logf("openfile perm %v", spec.perm)
   261  	hook, err := os.OpenFile(
   262  		filepath.Join(dir, spec.name), os.O_CREATE|os.O_WRONLY, spec.perm,
   263  	)
   264  	c.Assert(err, jc.ErrorIsNil)
   265  	defer func() {
   266  		c.Assert(hook.Close(), gc.IsNil)
   267  	}()
   268  
   269  	printf := func(f string, a ...interface{}) {
   270  		_, err := fmt.Fprintf(hook, f+"\n", a...)
   271  		c.Assert(err, jc.ErrorIsNil)
   272  	}
   273  	printf("#!/bin/bash")
   274  	printf("echo $$ > pid")
   275  	if spec.stdout != "" {
   276  		printf("echo %s", spec.stdout)
   277  	}
   278  	if spec.stderr != "" {
   279  		printf("echo %s >&2", spec.stderr)
   280  	}
   281  	if spec.background != "" {
   282  		// Print something fairly quickly, then sleep for
   283  		// quite a long time - if the hook execution is
   284  		// blocking because of the background process,
   285  		// the hook execution will take much longer than
   286  		// expected.
   287  		printf("(sleep 0.2; echo %s; sleep 10) &", spec.background)
   288  	}
   289  	printf("exit %d", spec.code)
   290  }