github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"github.com/juju/clock/testclock"
    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/core/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  	model       *state.Model
    46  	application *state.Application
    47  	machine     *state.Machine
    48  	unit        *state.Unit
    49  	uniter      *uniter.State
    50  	apiUnit     *uniter.Unit
    51  	storage     *runnertesting.StorageContextAccessor
    52  
    53  	apiRelunits map[int]*uniter.RelationUnit
    54  	relch       *state.Charm
    55  	relunits    map[int]*state.RelationUnit
    56  }
    57  
    58  func (s *ContextSuite) SetUpTest(c *gc.C) {
    59  	s.JujuConnSuite.SetUpTest(c)
    60  
    61  	s.machine = nil
    62  
    63  	ch := s.AddTestingCharm(c, "wordpress")
    64  	s.application = s.AddTestingApplication(c, "u", ch)
    65  	s.unit = s.AddUnit(c, s.application)
    66  
    67  	storageData0 := names.NewStorageTag("data/0")
    68  	s.storage = &runnertesting.StorageContextAccessor{
    69  		map[names.StorageTag]*runnertesting.ContextStorage{
    70  			storageData0: {
    71  				storageData0,
    72  				storage.StorageKindBlock,
    73  				"/dev/sdb",
    74  			},
    75  		},
    76  	}
    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  	s.model, err = s.State.Model()
    88  	c.Assert(err, jc.ErrorIsNil)
    89  
    90  	s.paths = runnertesting.NewRealPaths(c)
    91  	s.membership = map[int][]string{}
    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(ch.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  	s.contextFactory, err = context.NewContextFactory(context.FactoryConfig{
   105  		State:            s.uniter,
   106  		UnitTag:          s.unit.Tag().(names.UnitTag),
   107  		Tracker:          runnertesting.FakeTracker{},
   108  		GetRelationInfos: s.getRelationInfos,
   109  		Storage:          s.storage,
   110  		Paths:            s.paths,
   111  		Clock:            testclock.NewClock(time.Time{}),
   112  	})
   113  	c.Assert(err, jc.ErrorIsNil)
   114  
   115  	factory, err := runner.NewFactory(
   116  		s.uniter,
   117  		s.paths,
   118  		s.contextFactory,
   119  	)
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	s.factory = factory
   122  }
   123  
   124  func (s *ContextSuite) AddContextRelation(c *gc.C, name string) {
   125  	s.AddTestingApplication(c, name, s.relch)
   126  	eps, err := s.State.InferEndpoints("u", name)
   127  	c.Assert(err, jc.ErrorIsNil)
   128  	rel, err := s.State.AddRelation(eps...)
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	ru, err := rel.Unit(s.unit)
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	err = ru.EnterScope(map[string]interface{}{"relation-name": name})
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	s.relunits[rel.Id()] = ru
   135  	apiRel, err := s.uniter.Relation(rel.Tag().(names.RelationTag))
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	apiRelUnit, err := apiRel.Unit(s.apiUnit)
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	s.apiRelunits[rel.Id()] = apiRelUnit
   140  }
   141  
   142  func (s *ContextSuite) AddUnit(c *gc.C, svc *state.Application) *state.Unit {
   143  	unit, err := svc.AddUnit(state.AddUnitParams{})
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	if s.machine != nil {
   146  		err = unit.AssignToMachine(s.machine)
   147  		c.Assert(err, jc.ErrorIsNil)
   148  		return unit
   149  	}
   150  
   151  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	machineId, err := unit.AssignedMachineId()
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	s.machine, err = s.State.Machine(machineId)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	zone := "a-zone"
   158  	hwc := instance.HardwareCharacteristics{
   159  		AvailabilityZone: &zone,
   160  	}
   161  	err = s.machine.SetProvisioned("i-exist", "", "fake_nonce", &hwc)
   162  	c.Assert(err, jc.ErrorIsNil)
   163  
   164  	name := strings.Replace(unit.Name(), "/", "-", 1)
   165  	privateAddr := network.NewScopedAddress(name+".testing.invalid", network.ScopeCloudLocal)
   166  	err = s.machine.SetProviderAddresses(privateAddr)
   167  	c.Assert(err, jc.ErrorIsNil)
   168  	return unit
   169  }
   170  
   171  func (s *ContextSuite) SetCharm(c *gc.C, name string) {
   172  	err := os.RemoveAll(s.paths.GetCharmDir())
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	err = fs.Copy(testcharms.Repo.CharmDirPath(name), s.paths.GetCharmDir())
   175  	c.Assert(err, jc.ErrorIsNil)
   176  }
   177  
   178  func (s *ContextSuite) getRelationInfos() map[int]*context.RelationInfo {
   179  	info := map[int]*context.RelationInfo{}
   180  	for relId, relUnit := range s.apiRelunits {
   181  		info[relId] = &context.RelationInfo{
   182  			RelationUnit: relUnit,
   183  			MemberNames:  s.membership[relId],
   184  		}
   185  	}
   186  	return info
   187  }
   188  
   189  // hookSpec supports makeCharm.
   190  type hookSpec struct {
   191  	// dir is the directory to create the hook in.
   192  	dir string
   193  	// name is the name of the hook.
   194  	name string
   195  	// perm is the file permissions of the hook.
   196  	perm os.FileMode
   197  	// code is the exit status of the hook.
   198  	code int
   199  	// stdout holds a string to print to stdout
   200  	stdout string
   201  	// stderr holds a string to print to stderr
   202  	stderr string
   203  	// background holds a string to print in the background after 0.2s.
   204  	background string
   205  }
   206  
   207  // makeCharm constructs a fake charm dir containing a single named hook
   208  // with permissions perm and exit code code. If output is non-empty,
   209  // the charm will write it to stdout and stderr, with each one prefixed
   210  // by name of the stream.
   211  func makeCharm(c *gc.C, spec hookSpec, charmDir string) {
   212  	dir := charmDir
   213  	if spec.dir != "" {
   214  		dir = filepath.Join(dir, spec.dir)
   215  		err := os.Mkdir(dir, 0755)
   216  		c.Assert(err, jc.ErrorIsNil)
   217  	}
   218  	c.Logf("openfile perm %v", spec.perm)
   219  	hook, err := os.OpenFile(
   220  		filepath.Join(dir, spec.name), os.O_CREATE|os.O_WRONLY, spec.perm,
   221  	)
   222  	c.Assert(err, jc.ErrorIsNil)
   223  	defer func() {
   224  		c.Assert(hook.Close(), gc.IsNil)
   225  	}()
   226  
   227  	printf := func(f string, a ...interface{}) {
   228  		_, err := fmt.Fprintf(hook, f+"\n", a...)
   229  		c.Assert(err, jc.ErrorIsNil)
   230  	}
   231  	if runtime.GOOS != "windows" {
   232  		printf("#!/bin/bash")
   233  	}
   234  	printf(echoPidScript)
   235  	if spec.stdout != "" {
   236  		printf("echo %s", spec.stdout)
   237  	}
   238  	if spec.stderr != "" {
   239  		printf("echo %s >&2", spec.stderr)
   240  	}
   241  	if spec.background != "" {
   242  		// Print something fairly quickly, then sleep for
   243  		// quite a long time - if the hook execution is
   244  		// blocking because of the background process,
   245  		// the hook execution will take much longer than
   246  		// expected.
   247  		printf("(sleep 0.2; echo %s; sleep 10) &", spec.background)
   248  	}
   249  	printf("exit %d", spec.code)
   250  }