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