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 }