github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/runner/util_test.go (about)

     1  // Copyright 2015 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  	"runtime"
    11  	"strings"
    12  	"time"
    13  
    14  	jujutesting "github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils"
    17  	"github.com/juju/utils/fs"
    18  	gc "gopkg.in/check.v1"
    19  	"gopkg.in/juju/names.v2"
    20  
    21  	"github.com/juju/juju/api"
    22  	"github.com/juju/juju/api/uniter"
    23  	"github.com/juju/juju/instance"
    24  	"github.com/juju/juju/juju/testing"
    25  	"github.com/juju/juju/network"
    26  	"github.com/juju/juju/state"
    27  	"github.com/juju/juju/storage"
    28  	"github.com/juju/juju/testcharms"
    29  	"github.com/juju/juju/worker/uniter/runner"
    30  	"github.com/juju/juju/worker/uniter/runner/context"
    31  	runnertesting "github.com/juju/juju/worker/uniter/runner/testing"
    32  )
    33  
    34  var apiAddrs = []string{"a1:123", "a2:123"}
    35  
    36  type ContextSuite struct {
    37  	testing.JujuConnSuite
    38  
    39  	paths          runnertesting.RealPaths
    40  	factory        runner.Factory
    41  	contextFactory context.ContextFactory
    42  	membership     map[int][]string
    43  
    44  	st      api.Connection
    45  	service *state.Application
    46  	machine *state.Machine
    47  	unit    *state.Unit
    48  	uniter  *uniter.State
    49  	apiUnit *uniter.Unit
    50  	storage *runnertesting.StorageContextAccessor
    51  
    52  	apiRelunits map[int]*uniter.RelationUnit
    53  	relch       *state.Charm
    54  	relunits    map[int]*state.RelationUnit
    55  }
    56  
    57  func (s *ContextSuite) SetUpTest(c *gc.C) {
    58  	s.JujuConnSuite.SetUpTest(c)
    59  
    60  	s.machine = nil
    61  
    62  	ch := s.AddTestingCharm(c, "wordpress")
    63  	s.service = s.AddTestingService(c, "u", ch)
    64  	s.unit = s.AddUnit(c, s.service)
    65  
    66  	storageData0 := names.NewStorageTag("data/0")
    67  	s.storage = &runnertesting.StorageContextAccessor{
    68  		map[names.StorageTag]*runnertesting.ContextStorage{
    69  			storageData0: &runnertesting.ContextStorage{
    70  				storageData0,
    71  				storage.StorageKindBlock,
    72  				"/dev/sdb",
    73  			},
    74  		},
    75  	}
    76  
    77  	password, err := utils.RandomPassword()
    78  	err = s.unit.SetPassword(password)
    79  	c.Assert(err, jc.ErrorIsNil)
    80  	s.st = s.OpenAPIAs(c, s.unit.Tag(), password)
    81  	s.uniter, err = s.st.Uniter()
    82  	c.Assert(err, jc.ErrorIsNil)
    83  	c.Assert(s.uniter, gc.NotNil)
    84  	s.apiUnit, err = s.uniter.Unit(s.unit.Tag().(names.UnitTag))
    85  	c.Assert(err, jc.ErrorIsNil)
    86  
    87  	s.paths = runnertesting.NewRealPaths(c)
    88  	s.membership = map[int][]string{}
    89  
    90  	// Note: The unit must always have a charm URL set, because this
    91  	// happens as part of the installation process (that happens
    92  	// before the initial install hook).
    93  	err = s.unit.SetCharmURL(ch.URL())
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	s.relch = s.AddTestingCharm(c, "mysql")
    96  	s.relunits = map[int]*state.RelationUnit{}
    97  	s.apiRelunits = map[int]*uniter.RelationUnit{}
    98  	s.AddContextRelation(c, "db0")
    99  	s.AddContextRelation(c, "db1")
   100  
   101  	s.contextFactory, err = context.NewContextFactory(
   102  		s.uniter,
   103  		s.unit.Tag().(names.UnitTag),
   104  		runnertesting.FakeTracker{},
   105  		s.getRelationInfos,
   106  		s.storage,
   107  		s.paths,
   108  		jujutesting.NewClock(time.Time{}),
   109  	)
   110  	c.Assert(err, jc.ErrorIsNil)
   111  
   112  	factory, err := runner.NewFactory(
   113  		s.uniter,
   114  		s.paths,
   115  		s.contextFactory,
   116  	)
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	s.factory = factory
   119  }
   120  
   121  func (s *ContextSuite) AddContextRelation(c *gc.C, name string) {
   122  	s.AddTestingService(c, name, s.relch)
   123  	eps, err := s.State.InferEndpoints("u", name)
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	rel, err := s.State.AddRelation(eps...)
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	ru, err := rel.Unit(s.unit)
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	err = ru.EnterScope(map[string]interface{}{"relation-name": name})
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	s.relunits[rel.Id()] = ru
   132  	apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag))
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	apiRelUnit, err := apiRel.Unit(s.apiUnit)
   135  	c.Assert(err, jc.ErrorIsNil)
   136  	s.apiRelunits[rel.Id()] = apiRelUnit
   137  }
   138  
   139  func (s *ContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit {
   140  	unit, err := svc.AddUnit()
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	if s.machine != nil {
   143  		err = unit.AssignToMachine(s.machine)
   144  		c.Assert(err, jc.ErrorIsNil)
   145  		return unit
   146  	}
   147  
   148  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	machineId, err := unit.AssignedMachineId()
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	s.machine, err = s.State.Machine(machineId)
   153  	c.Assert(err, jc.ErrorIsNil)
   154  	zone := "a-zone"
   155  	hwc := instance.HardwareCharacteristics{
   156  		AvailabilityZone: &zone,
   157  	}
   158  	err = s.machine.SetProvisioned("i-exist", "fake_nonce", &hwc)
   159  	c.Assert(err, jc.ErrorIsNil)
   160  
   161  	name := strings.Replace(unit.Name(), "/", "-", 1)
   162  	privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal)
   163  	err = s.machine.SetProviderAddresses(privateAddr)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  	return unit
   166  }
   167  
   168  func (s *ContextSuite) SetCharm(c *gc.C, name string) {
   169  	err := os.RemoveAll(s.paths.GetCharmDir())
   170  	c.Assert(err, jc.ErrorIsNil)
   171  	err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.GetCharmDir())
   172  	c.Assert(err, jc.ErrorIsNil)
   173  }
   174  
   175  func (s *ContextSuite) getRelationInfos() map[int]*context.RelationInfo {
   176  	info := map[int]*context.RelationInfo{}
   177  	for relId, relUnit := range s.apiRelunits {
   178  		info[relId] = &context.RelationInfo{
   179  			RelationUnit: relUnit,
   180  			MemberNames:  s.membership[relId],
   181  		}
   182  	}
   183  	return info
   184  }
   185  
   186  // hookSpec supports makeCharm.
   187  type hookSpec struct {
   188  	// dir is the directory to create the hook in.
   189  	dir string
   190  	// name is the name of the hook.
   191  	name string
   192  	// perm is the file permissions of the hook.
   193  	perm os.FileMode
   194  	// code is the exit status of the hook.
   195  	code int
   196  	// stdout holds a string to print to stdout
   197  	stdout string
   198  	// stderr holds a string to print to stderr
   199  	stderr string
   200  	// background holds a string to print in the background after 0.2s.
   201  	background string
   202  }
   203  
   204  // makeCharm constructs a fake charm dir containing a single named hook
   205  // with permissions perm and exit code code. If output is non-empty,
   206  // the charm will write it to stdout and stderr, with each one prefixed
   207  // by name of the stream.
   208  func makeCharm(c *gc.C, spec hookSpec, charmDir string) {
   209  	dir := charmDir
   210  	if spec.dir != "" {
   211  		dir = filepath.Join(dir, spec.dir)
   212  		err := os.Mkdir(dir, 0755)
   213  		c.Assert(err, jc.ErrorIsNil)
   214  	}
   215  	c.Logf("openfile perm %v", spec.perm)
   216  	hook, err := os.OpenFile(
   217  		filepath.Join(dir, spec.name), os.O_CREATE|os.O_WRONLY, spec.perm,
   218  	)
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	defer func() {
   221  		c.Assert(hook.Close(), gc.IsNil)
   222  	}()
   223  
   224  	printf := func(f string, a ...interface{}) {
   225  		_, err := fmt.Fprintf(hook, f+"\n", a...)
   226  		c.Assert(err, jc.ErrorIsNil)
   227  	}
   228  	if runtime.GOOS != "windows" {
   229  		printf("#!/bin/bash")
   230  	}
   231  	printf(echoPidScript)
   232  	if spec.stdout != "" {
   233  		printf("echo %s", spec.stdout)
   234  	}
   235  	if spec.stderr != "" {
   236  		printf("echo %s >&2", spec.stderr)
   237  	}
   238  	if spec.background != "" {
   239  		// Print something fairly quickly, then sleep for
   240  		// quite a long time - if the hook execution is
   241  		// blocking because of the background process,
   242  		// the hook execution will take much longer than
   243  		// expected.
   244  		printf("(sleep 0.2; echo %s; sleep 10) &", spec.background)
   245  	}
   246  	printf("exit %d", spec.code)
   247  }