github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/service/discovery_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package service_test 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "runtime" 11 "strings" 12 13 "github.com/juju/errors" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils/exec" 16 "github.com/juju/utils/featureflag" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/feature" 20 "github.com/juju/juju/juju/osenv" 21 "github.com/juju/juju/service" 22 "github.com/juju/juju/service/common" 23 "github.com/juju/juju/service/systemd" 24 "github.com/juju/juju/service/upstart" 25 "github.com/juju/juju/service/windows" 26 "github.com/juju/juju/version" 27 ) 28 29 var maybeSystemd = service.InitSystemSystemd 30 31 func init() { 32 if featureflag.Enabled(feature.LegacyUpstart) { 33 maybeSystemd = service.InitSystemUpstart 34 } 35 } 36 37 const unknownExecutable = "/sbin/unknown/init/system" 38 39 type discoveryTest struct { 40 os version.OSType 41 series string 42 expected string 43 } 44 45 func (dt discoveryTest) version() version.Binary { 46 return version.Binary{ 47 OS: dt.os, 48 Series: dt.series, 49 } 50 } 51 52 func (dt discoveryTest) log(c *gc.C) { 53 c.Logf(" - testing {%q, %q}...", dt.os, dt.series) 54 } 55 56 func (dt discoveryTest) disableLocalDiscovery(c *gc.C, s *discoverySuite) { 57 s.PatchLocalDiscoveryDisable() 58 } 59 60 func (dt discoveryTest) disableVersionDiscovery(s *discoverySuite) { 61 dt.os = version.Unknown 62 dt.setVersion(s) 63 } 64 65 func (dt discoveryTest) setLocal(c *gc.C, s *discoverySuite) { 66 s.PatchLocalDiscoveryNoMatch(dt.expected) 67 } 68 69 func (dt discoveryTest) setVersion(s *discoverySuite) version.Binary { 70 vers := dt.version() 71 s.PatchVersion(vers) 72 return vers 73 } 74 75 func (dt discoveryTest) checkService(c *gc.C, svc service.Service, err error, name string, conf common.Conf) { 76 if dt.expected == "" { 77 c.Check(err, jc.Satisfies, errors.IsNotFound) 78 return 79 } 80 81 // Check the success case. 82 if !c.Check(err, jc.ErrorIsNil) { 83 return 84 } 85 switch dt.expected { 86 case service.InitSystemUpstart: 87 c.Check(svc, gc.FitsTypeOf, &upstart.Service{}) 88 case service.InitSystemSystemd: 89 c.Check(svc, gc.FitsTypeOf, &systemd.Service{}) 90 case service.InitSystemWindows: 91 c.Check(svc, gc.FitsTypeOf, &windows.Service{}) 92 default: 93 c.Errorf("unknown expected init system %q", dt.expected) 94 return 95 } 96 if svc == nil { 97 return 98 } 99 100 c.Check(svc.Name(), gc.Equals, name) 101 c.Check(svc.Conf(), jc.DeepEquals, conf) 102 } 103 104 func (dt discoveryTest) checkInitSystem(c *gc.C, name string, err error) { 105 if dt.expected == "" { 106 if !c.Check(err, jc.Satisfies, errors.IsNotFound) { 107 c.Logf("found init system %q", name) 108 } 109 } else { 110 c.Check(err, jc.ErrorIsNil) 111 c.Check(name, gc.Equals, dt.expected) 112 } 113 } 114 115 var discoveryTests = []discoveryTest{{ 116 os: version.Windows, 117 series: "win2012", 118 expected: service.InitSystemWindows, 119 }, { 120 os: version.Ubuntu, 121 series: "oneiric", 122 expected: "", 123 }, { 124 os: version.Ubuntu, 125 series: "precise", 126 expected: service.InitSystemUpstart, 127 }, { 128 os: version.Ubuntu, 129 series: "utopic", 130 expected: service.InitSystemUpstart, 131 }, { 132 os: version.Ubuntu, 133 series: "vivid", 134 expected: maybeSystemd, 135 }, { 136 os: version.CentOS, 137 series: "centos7", 138 expected: service.InitSystemSystemd, 139 }, { 140 os: version.Unknown, 141 expected: "", 142 }} 143 144 type discoverySuite struct { 145 service.BaseSuite 146 147 name string 148 conf common.Conf 149 } 150 151 var _ = gc.Suite(&discoverySuite{}) 152 153 func (s *discoverySuite) SetUpTest(c *gc.C) { 154 s.BaseSuite.SetUpTest(c) 155 156 s.name = "a-service" 157 s.conf = common.Conf{ 158 Desc: "some service", 159 ExecStart: "/path/to/some-command", 160 } 161 } 162 163 func (s *discoverySuite) unsetLegacyUpstart(c *gc.C) { 164 err := os.Setenv(osenv.JujuFeatureFlagEnvKey, "") 165 c.Assert(err, jc.ErrorIsNil) 166 featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey) 167 } 168 169 func (s *discoverySuite) setLegacyUpstart(c *gc.C) { 170 err := os.Setenv(osenv.JujuFeatureFlagEnvKey, feature.LegacyUpstart) 171 c.Assert(err, jc.ErrorIsNil) 172 featureflag.SetFlagsFromEnvironment(osenv.JujuFeatureFlagEnvKey) 173 } 174 175 func (s *discoverySuite) TestDiscoverServiceLocalHost(c *gc.C) { 176 var localInitSystem string 177 var err error 178 switch runtime.GOOS { 179 case "windows": 180 localInitSystem = service.InitSystemWindows 181 case "linux": 182 localInitSystem, err = service.VersionInitSystem(version.Current.Series) 183 } 184 c.Assert(err, gc.IsNil) 185 186 test := discoveryTest{ 187 os: version.Current.OS, 188 series: version.Current.Series, 189 expected: localInitSystem, 190 } 191 test.disableVersionDiscovery(s) 192 193 svc, err := service.DiscoverService(s.name, s.conf) 194 c.Assert(err, jc.ErrorIsNil) 195 196 test.checkService(c, svc, err, s.name, s.conf) 197 } 198 199 func (s *discoverySuite) TestDiscoverServiceVersionFallback(c *gc.C) { 200 for _, test := range discoveryTests { 201 test.log(c) 202 203 test.disableLocalDiscovery(c, s) 204 test.setVersion(s) 205 206 svc, err := service.DiscoverService(s.name, s.conf) 207 208 test.checkService(c, svc, err, s.name, s.conf) 209 } 210 } 211 212 func (s *discoverySuite) TestVersionInitSystem(c *gc.C) { 213 for _, test := range discoveryTests { 214 test.log(c) 215 initSystem, err := service.VersionInitSystem(test.series) 216 test.checkInitSystem(c, initSystem, err) 217 } 218 } 219 220 func (s *discoverySuite) TestVersionInitSystemLegacyUpstart(c *gc.C) { 221 s.setLegacyUpstart(c) 222 test := discoveryTest{ 223 os: version.Ubuntu, 224 series: "vivid", 225 expected: service.InitSystemUpstart, 226 } 227 vers := test.setVersion(s) 228 229 initSystem, err := service.VersionInitSystem(vers.Series) 230 231 test.checkInitSystem(c, initSystem, err) 232 } 233 234 func (s *discoverySuite) TestVersionInitSystemNoLegacyUpstart(c *gc.C) { 235 s.unsetLegacyUpstart(c) 236 test := discoveryTest{ 237 os: version.Ubuntu, 238 series: "vivid", 239 expected: service.InitSystemSystemd, 240 } 241 vers := test.setVersion(s) 242 243 initSystem, err := service.VersionInitSystem(vers.Series) 244 245 test.checkInitSystem(c, initSystem, err) 246 } 247 248 func (s *discoverySuite) TestDiscoverLocalInitSystemMatchFirst(c *gc.C) { 249 s.PatchLocalDiscovery( 250 service.NewDiscoveryCheck("initA", true, nil), 251 service.NewDiscoveryCheck("initB", false, nil), 252 ) 253 254 name, err := service.DiscoverLocalInitSystem() 255 c.Assert(err, jc.ErrorIsNil) 256 257 c.Check(name, gc.Equals, "initA") 258 } 259 260 func (s *discoverySuite) TestDiscoverLocalInitSystemErrorFirst(c *gc.C) { 261 failure := errors.New("<failed>") 262 s.PatchLocalDiscovery( 263 service.NewDiscoveryCheck("initA", false, failure), 264 service.NewDiscoveryCheck("initB", true, nil), 265 ) 266 267 name, err := service.DiscoverLocalInitSystem() 268 c.Assert(err, jc.ErrorIsNil) 269 270 c.Check(name, gc.Equals, "initB") 271 } 272 273 func (s *discoverySuite) TestDiscoverLocalInitSystemMatchFirstError(c *gc.C) { 274 failure := errors.New("<failed>") 275 s.PatchLocalDiscovery( 276 service.NewDiscoveryCheck("initA", true, failure), 277 service.NewDiscoveryCheck("initB", false, nil), 278 ) 279 280 name, err := service.DiscoverLocalInitSystem() 281 c.Assert(err, jc.ErrorIsNil) 282 283 c.Check(name, gc.Equals, "initA") 284 } 285 286 func (s *discoverySuite) TestDiscoverLocalInitSystemMatchSecond(c *gc.C) { 287 s.PatchLocalDiscovery( 288 service.NewDiscoveryCheck("initA", false, nil), 289 service.NewDiscoveryCheck("initB", true, nil), 290 ) 291 292 name, err := service.DiscoverLocalInitSystem() 293 c.Assert(err, jc.ErrorIsNil) 294 295 c.Check(name, gc.Equals, "initB") 296 } 297 298 func (s *discoverySuite) TestDiscoverLocalInitSystemMatchNone(c *gc.C) { 299 s.PatchLocalDiscovery( 300 service.NewDiscoveryCheck("initA", false, nil), 301 service.NewDiscoveryCheck("initB", false, nil), 302 ) 303 304 _, err := service.DiscoverLocalInitSystem() 305 306 c.Check(err, jc.Satisfies, errors.IsNotFound) 307 } 308 309 func (s *discoverySuite) TestDiscoverLocalInitSystemErrorMixed(c *gc.C) { 310 failure := errors.New("<failed>") 311 s.PatchLocalDiscovery( 312 service.NewDiscoveryCheck("initA", false, failure), 313 service.NewDiscoveryCheck("initB", false, nil), 314 ) 315 316 _, err := service.DiscoverLocalInitSystem() 317 318 c.Check(err, jc.Satisfies, errors.IsNotFound) 319 } 320 321 func (s *discoverySuite) TestDiscoverLocalInitSystemErrorAll(c *gc.C) { 322 failureA := errors.New("<failed A>") 323 failureB := errors.New("<failed B>") 324 s.PatchLocalDiscovery( 325 service.NewDiscoveryCheck("initA", false, failureA), 326 service.NewDiscoveryCheck("initB", false, failureB), 327 ) 328 329 _, err := service.DiscoverLocalInitSystem() 330 331 c.Check(err, jc.Satisfies, errors.IsNotFound) 332 } 333 334 func (s *discoverySuite) TestDiscoverInitSystemScriptBash(c *gc.C) { 335 if runtime.GOOS == "windows" { 336 c.Skip("not supported on windows") 337 } 338 339 script, filename := s.newDiscoverInitSystemScript(c) 340 script += filename 341 response, err := exec.RunCommands(exec.RunParams{ 342 Commands: script, 343 }) 344 c.Assert(err, jc.ErrorIsNil) 345 346 initSystem, err := service.DiscoverInitSystem() 347 c.Assert(err, jc.ErrorIsNil) 348 c.Check(response.Code, gc.Equals, 0) 349 c.Check(string(response.Stdout), gc.Equals, initSystem) 350 c.Check(string(response.Stderr), gc.Equals, "") 351 } 352 353 func (s *discoverySuite) TestDiscoverInitSystemScriptPosix(c *gc.C) { 354 if runtime.GOOS == "windows" { 355 c.Skip("not supported on windows") 356 } 357 358 script, filename := s.newDiscoverInitSystemScript(c) 359 script += "sh " + filename 360 response, err := exec.RunCommands(exec.RunParams{ 361 Commands: script, 362 }) 363 c.Assert(err, jc.ErrorIsNil) 364 365 initSystem, err := service.DiscoverInitSystem() 366 c.Assert(err, jc.ErrorIsNil) 367 c.Check(response.Code, gc.Equals, 0) 368 c.Check(string(response.Stdout), gc.Equals, initSystem) 369 c.Check(string(response.Stderr), gc.Equals, "") 370 } 371 372 func (s *discoverySuite) writeScript(c *gc.C, name, script string) (string, string) { 373 filename := filepath.Join(c.MkDir(), name) 374 commands := []string{ 375 fmt.Sprintf(` 376 cat > %s << 'EOF' 377 %s 378 EOF`[1:], filename, script), 379 "chmod 0755 " + filename, 380 } 381 writeScript := strings.Join(commands, "\n") + "\n" 382 return writeScript, filename 383 } 384 385 func (s *discoverySuite) newDiscoverInitSystemScript(c *gc.C) (string, string) { 386 script := service.DiscoverInitSystemScript() 387 return s.writeScript(c, "discover_init_system.sh", script) 388 } 389 390 func (s *discoverySuite) TestNewShellSelectCommandBash(c *gc.C) { 391 if runtime.GOOS == "windows" { 392 c.Skip("not supported on windows") 393 } 394 395 discoveryScript := service.DiscoverInitSystemScript() 396 handler := func(initSystem string) (string, bool) { 397 return "echo -n " + initSystem, true 398 } 399 script := "init_system=$(" + discoveryScript + ")\n" 400 // The script will fail with exit 1 if it cannot match in init system. 401 script += service.NewShellSelectCommand("init_system", "exit 1", handler) 402 response, err := exec.RunCommands(exec.RunParams{ 403 Commands: script, 404 }) 405 c.Assert(err, jc.ErrorIsNil) 406 407 initSystem, err := service.DiscoverInitSystem() 408 c.Assert(err, jc.ErrorIsNil) 409 c.Check(response.Code, gc.Equals, 0) 410 c.Check(string(response.Stdout), gc.Equals, initSystem) 411 c.Check(string(response.Stderr), gc.Equals, "") 412 } 413 414 func (s *discoverySuite) TestNewShellSelectCommandPosix(c *gc.C) { 415 if runtime.GOOS == "windows" { 416 c.Skip("not supported on windows") 417 } 418 419 discoveryScript := service.DiscoverInitSystemScript() 420 handler := func(initSystem string) (string, bool) { 421 return "echo -n " + initSystem, true 422 } 423 script := "init_system=$(" + discoveryScript + ")\n" 424 // The script will fail with exit 1 if it cannot match in init system. 425 script += service.NewShellSelectCommand("init_system", "exit 1", handler) 426 commands, filename := s.writeScript(c, "test_shell_select.sh", script) 427 commands += "sh " + filename 428 response, err := exec.RunCommands(exec.RunParams{ 429 Commands: script, 430 }) 431 c.Assert(err, jc.ErrorIsNil) 432 433 initSystem, err := service.DiscoverInitSystem() 434 c.Assert(err, jc.ErrorIsNil) 435 c.Check(response.Code, gc.Equals, 0) 436 c.Check(string(response.Stdout), gc.Equals, initSystem) 437 c.Check(string(response.Stderr), gc.Equals, "") 438 }