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