github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/deployer/simple_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package deployer_test
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"os"
    11  	"path/filepath"
    12  	"regexp"
    13  	"sort"
    14  
    15  	"github.com/juju/names"
    16  	jc "github.com/juju/testing/checkers"
    17  	gc "gopkg.in/check.v1"
    18  
    19  	"github.com/juju/juju/agent"
    20  	"github.com/juju/juju/agent/tools"
    21  	"github.com/juju/juju/state/multiwatcher"
    22  	"github.com/juju/juju/testing"
    23  	coretools "github.com/juju/juju/tools"
    24  	"github.com/juju/juju/version"
    25  	"github.com/juju/juju/worker/deployer"
    26  )
    27  
    28  type SimpleContextSuite struct {
    29  	SimpleToolsFixture
    30  }
    31  
    32  var _ = gc.Suite(&SimpleContextSuite{})
    33  
    34  func (s *SimpleContextSuite) SetUpTest(c *gc.C) {
    35  	s.SimpleToolsFixture.SetUp(c, c.MkDir())
    36  }
    37  
    38  func (s *SimpleContextSuite) TearDownTest(c *gc.C) {
    39  	s.SimpleToolsFixture.TearDown(c)
    40  }
    41  
    42  func (s *SimpleContextSuite) TestDeployRecall(c *gc.C) {
    43  	mgr0 := s.getContext(c)
    44  	units, err := mgr0.DeployedUnits()
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	c.Assert(units, gc.HasLen, 0)
    47  	s.assertUpstartCount(c, 0)
    48  
    49  	err = mgr0.DeployUnit("foo/123", "some-password")
    50  	c.Assert(err, jc.ErrorIsNil)
    51  	units, err = mgr0.DeployedUnits()
    52  	c.Assert(err, jc.ErrorIsNil)
    53  	c.Assert(units, gc.DeepEquals, []string{"foo/123"})
    54  	s.assertUpstartCount(c, 1)
    55  	s.checkUnitInstalled(c, "foo/123", "some-password")
    56  
    57  	err = mgr0.RecallUnit("foo/123")
    58  	c.Assert(err, jc.ErrorIsNil)
    59  	units, err = mgr0.DeployedUnits()
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	c.Assert(units, gc.HasLen, 0)
    62  	s.assertUpstartCount(c, 0)
    63  	s.checkUnitRemoved(c, "foo/123")
    64  }
    65  
    66  func (s *SimpleContextSuite) TestOldDeployedUnitsCanBeRecalled(c *gc.C) {
    67  	// After r1347 deployer tag is no longer part of the upstart conf filenames,
    68  	// now only the units' tags are used. This change is with the assumption only
    69  	// one deployer will be running on a machine (in the machine agent as a task,
    70  	// unlike before where there was one in the unit agent as well).
    71  	// This test ensures units deployed previously (or their upstart confs more
    72  	// specifically) can be detected and recalled by the deployer.
    73  
    74  	manager := s.getContext(c)
    75  
    76  	// No deployed units at first.
    77  	units, err := manager.DeployedUnits()
    78  	c.Assert(err, jc.ErrorIsNil)
    79  	c.Assert(units, gc.HasLen, 0)
    80  	s.assertUpstartCount(c, 0)
    81  
    82  	// Trying to recall any units will fail.
    83  	err = manager.RecallUnit("principal/1")
    84  	c.Assert(err, gc.ErrorMatches, `unit "principal/1" is not deployed`)
    85  
    86  	// Simulate some previously deployed units with the old
    87  	// upstart conf filename format (+deployer tags).
    88  	s.injectUnit(c, "jujud-machine-0:unit-mysql-0.conf", "unit-mysql-0")
    89  	s.assertUpstartCount(c, 1)
    90  	s.injectUnit(c, "jujud-unit-wordpress-0:unit-nrpe-0.conf", "unit-nrpe-0")
    91  	s.assertUpstartCount(c, 2)
    92  
    93  	// Make sure we can discover them.
    94  	units, err = manager.DeployedUnits()
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	c.Assert(units, gc.HasLen, 2)
    97  	sort.Strings(units)
    98  	c.Assert(units, gc.DeepEquals, []string{"mysql/0", "nrpe/0"})
    99  
   100  	// Deploy some units.
   101  	err = manager.DeployUnit("principal/1", "some-password")
   102  	c.Assert(err, jc.ErrorIsNil)
   103  	s.checkUnitInstalled(c, "principal/1", "some-password")
   104  	s.assertUpstartCount(c, 3)
   105  	err = manager.DeployUnit("subordinate/2", "fake-password")
   106  	c.Assert(err, jc.ErrorIsNil)
   107  	s.checkUnitInstalled(c, "subordinate/2", "fake-password")
   108  	s.assertUpstartCount(c, 4)
   109  
   110  	// Verify the newly deployed units are also discoverable.
   111  	units, err = manager.DeployedUnits()
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	c.Assert(units, gc.HasLen, 4)
   114  	sort.Strings(units)
   115  	c.Assert(units, gc.DeepEquals, []string{"mysql/0", "nrpe/0", "principal/1", "subordinate/2"})
   116  
   117  	// Recall all of them - should work ok.
   118  	unitCount := 4
   119  	for _, unitName := range units {
   120  		err = manager.RecallUnit(unitName)
   121  		c.Assert(err, jc.ErrorIsNil)
   122  		unitCount--
   123  		s.checkUnitRemoved(c, unitName)
   124  		s.assertUpstartCount(c, unitCount)
   125  	}
   126  
   127  	// Verify they're no longer discoverable.
   128  	units, err = manager.DeployedUnits()
   129  	c.Assert(err, jc.ErrorIsNil)
   130  	c.Assert(units, gc.HasLen, 0)
   131  }
   132  
   133  type SimpleToolsFixture struct {
   134  	dataDir  string
   135  	logDir   string
   136  	initDir  string
   137  	origPath string
   138  	binDir   string
   139  }
   140  
   141  var fakeJujud = "#!/bin/bash --norc\n# fake-jujud\nexit 0\n"
   142  
   143  func (fix *SimpleToolsFixture) SetUp(c *gc.C, dataDir string) {
   144  	fix.dataDir = dataDir
   145  	fix.initDir = c.MkDir()
   146  	fix.logDir = c.MkDir()
   147  	toolsDir := tools.SharedToolsDir(fix.dataDir, version.Current)
   148  	err := os.MkdirAll(toolsDir, 0755)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  	jujudPath := filepath.Join(toolsDir, "jujud")
   151  	err = ioutil.WriteFile(jujudPath, []byte(fakeJujud), 0755)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	toolsPath := filepath.Join(toolsDir, "downloaded-tools.txt")
   154  	testTools := coretools.Tools{Version: version.Current, URL: "http://testing.invalid/tools"}
   155  	data, err := json.Marshal(testTools)
   156  	c.Assert(err, jc.ErrorIsNil)
   157  	err = ioutil.WriteFile(toolsPath, data, 0644)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	fix.binDir = c.MkDir()
   160  	fix.origPath = os.Getenv("PATH")
   161  	os.Setenv("PATH", fix.binDir+":"+fix.origPath)
   162  	fix.makeBin(c, "status", `echo "blah stop/waiting"`)
   163  	fix.makeBin(c, "stopped-status", `echo "blah stop/waiting"`)
   164  	fix.makeBin(c, "started-status", `echo "blah start/running, process 666"`)
   165  	fix.makeBin(c, "start", "cp $(which started-status) $(which status)")
   166  	fix.makeBin(c, "stop", "cp $(which stopped-status) $(which status)")
   167  }
   168  
   169  func (fix *SimpleToolsFixture) TearDown(c *gc.C) {
   170  	os.Setenv("PATH", fix.origPath)
   171  }
   172  
   173  func (fix *SimpleToolsFixture) makeBin(c *gc.C, name, script string) {
   174  	path := filepath.Join(fix.binDir, name)
   175  	err := ioutil.WriteFile(path, []byte("#!/bin/bash --norc\n"+script), 0755)
   176  	c.Assert(err, jc.ErrorIsNil)
   177  }
   178  
   179  func (fix *SimpleToolsFixture) assertUpstartCount(c *gc.C, count int) {
   180  	fis, err := ioutil.ReadDir(fix.initDir)
   181  	c.Assert(err, jc.ErrorIsNil)
   182  	c.Assert(fis, gc.HasLen, count)
   183  }
   184  
   185  func (fix *SimpleToolsFixture) getContext(c *gc.C) *deployer.SimpleContext {
   186  	config := agentConfig(names.NewMachineTag("99"), fix.dataDir, fix.logDir)
   187  	return deployer.NewTestSimpleContext(config, fix.initDir, fix.logDir)
   188  }
   189  
   190  func (fix *SimpleToolsFixture) getContextForMachine(c *gc.C, machineTag names.Tag) *deployer.SimpleContext {
   191  	config := agentConfig(machineTag, fix.dataDir, fix.logDir)
   192  	return deployer.NewTestSimpleContext(config, fix.initDir, fix.logDir)
   193  }
   194  
   195  func (fix *SimpleToolsFixture) paths(tag names.Tag) (confPath, agentDir, toolsDir string) {
   196  	confName := fmt.Sprintf("jujud-%s.conf", tag)
   197  	confPath = filepath.Join(fix.initDir, confName)
   198  	agentDir = agent.Dir(fix.dataDir, tag)
   199  	toolsDir = tools.ToolsDir(fix.dataDir, tag.String())
   200  	return
   201  }
   202  
   203  func (fix *SimpleToolsFixture) checkUnitInstalled(c *gc.C, name, password string) {
   204  	tag := names.NewUnitTag(name)
   205  	uconfPath, _, toolsDir := fix.paths(tag)
   206  	uconfData, err := ioutil.ReadFile(uconfPath)
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	uconf := string(uconfData)
   209  
   210  	regex := regexp.MustCompile("(?m)(?:^\\s)*exec\\s.+$")
   211  	execs := regex.FindAllString(uconf, -1)
   212  
   213  	if nil == execs {
   214  		c.Fatalf("no command found in %s:\n%s", uconfPath, uconf)
   215  	} else if 1 > len(execs) {
   216  		c.Fatalf("Test is not built to handle more than one exec line.")
   217  	}
   218  
   219  	logPath := filepath.Join(fix.logDir, tag.String()+".log")
   220  	jujudPath := filepath.Join(toolsDir, "jujud")
   221  
   222  	for _, pat := range []string{
   223  		"^exec " + jujudPath + " unit ",
   224  		" --unit-name " + name + " ",
   225  		" >> " + logPath + " 2>&1$",
   226  	} {
   227  		match, err := regexp.MatchString(pat, execs[0])
   228  		c.Assert(err, jc.ErrorIsNil)
   229  		if !match {
   230  			c.Fatalf("failed to match:\n%s\nin:\n%s", pat, execs[0])
   231  		}
   232  	}
   233  
   234  	conf, err := agent.ReadConfig(agent.ConfigPath(fix.dataDir, tag))
   235  	c.Assert(err, jc.ErrorIsNil)
   236  	c.Assert(conf.Tag(), gc.Equals, tag)
   237  	c.Assert(conf.DataDir(), gc.Equals, fix.dataDir)
   238  
   239  	jujudData, err := ioutil.ReadFile(jujudPath)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(string(jujudData), gc.Equals, fakeJujud)
   242  }
   243  
   244  func (fix *SimpleToolsFixture) checkUnitRemoved(c *gc.C, name string) {
   245  	tag := names.NewUnitTag(name)
   246  	confPath, agentDir, toolsDir := fix.paths(tag)
   247  	for _, path := range []string{confPath, agentDir, toolsDir} {
   248  		_, err := ioutil.ReadFile(path)
   249  		if err == nil {
   250  			c.Logf("Warning: %q not removed as expected", path)
   251  		} else {
   252  			c.Assert(err, jc.Satisfies, os.IsNotExist)
   253  		}
   254  	}
   255  }
   256  
   257  func (fix *SimpleToolsFixture) injectUnit(c *gc.C, upstartConf, unitTag string) {
   258  	confPath := filepath.Join(fix.initDir, upstartConf)
   259  	err := ioutil.WriteFile(confPath, []byte("#!/bin/bash --norc\necho $0"), 0644)
   260  	c.Assert(err, jc.ErrorIsNil)
   261  	toolsDir := filepath.Join(fix.dataDir, "tools", unitTag)
   262  	err = os.MkdirAll(toolsDir, 0755)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  }
   265  
   266  type mockConfig struct {
   267  	agent.Config
   268  	tag               names.Tag
   269  	datadir           string
   270  	logdir            string
   271  	upgradedToVersion version.Number
   272  	jobs              []multiwatcher.MachineJob
   273  }
   274  
   275  func (mock *mockConfig) Tag() names.Tag {
   276  	return mock.tag
   277  }
   278  
   279  func (mock *mockConfig) DataDir() string {
   280  	return mock.datadir
   281  }
   282  
   283  func (mock *mockConfig) LogDir() string {
   284  	return mock.logdir
   285  }
   286  
   287  func (mock *mockConfig) Jobs() []multiwatcher.MachineJob {
   288  	return mock.jobs
   289  }
   290  
   291  func (mock *mockConfig) UpgradedToVersion() version.Number {
   292  	return mock.upgradedToVersion
   293  }
   294  
   295  func (mock *mockConfig) WriteUpgradedToVersion(newVersion version.Number) error {
   296  	mock.upgradedToVersion = newVersion
   297  	return nil
   298  }
   299  
   300  func (mock *mockConfig) Environment() names.EnvironTag {
   301  	return testing.EnvironmentTag
   302  }
   303  
   304  func (mock *mockConfig) CACert() string {
   305  	return testing.CACert
   306  }
   307  
   308  func (mock *mockConfig) Value(_ string) string {
   309  	return ""
   310  }
   311  
   312  func agentConfig(tag names.Tag, datadir, logdir string) agent.Config {
   313  	return &mockConfig{tag: tag, datadir: datadir, logdir: logdir}
   314  }