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