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