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 }