github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/service/systemd/service_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package systemd_test 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 11 "github.com/coreos/go-systemd/dbus" 12 "github.com/golang/mock/gomock" 13 "github.com/juju/errors" 14 jc "github.com/juju/testing/checkers" 15 "github.com/juju/utils/exec" 16 "github.com/juju/utils/shell" 17 gc "gopkg.in/check.v1" 18 "gopkg.in/juju/names.v2" 19 20 "github.com/juju/juju/juju/paths" 21 "github.com/juju/juju/service" 22 "github.com/juju/juju/service/common" 23 "github.com/juju/juju/service/systemd" 24 systemdtesting "github.com/juju/juju/service/systemd/testing" 25 coretesting "github.com/juju/juju/testing" 26 ) 27 28 var renderer = &shell.BashRenderer{} 29 30 const confStr = ` 31 [Unit] 32 Description=juju agent for %s 33 After=syslog.target 34 After=network.target 35 After=systemd-user-sessions.service 36 37 [Service] 38 ExecStart=%s 39 Restart=on-failure 40 41 [Install] 42 WantedBy=multi-user.target 43 44 ` 45 46 const jujud = "/var/lib/juju/bin/jujud" 47 48 var listCmdArg = exec.RunParams{ 49 Commands: `/bin/systemctl list-unit-files --no-legend --no-page -l -t service | grep -o -P '^\w[\S]*(?=\.service)'`, 50 } 51 52 var errFailure = errors.New("you-failed") 53 54 type initSystemSuite struct { 55 coretesting.BaseSuite 56 57 dataDir string 58 ch chan string 59 dBus *MockDBusAPI 60 fops *systemd.MockShimFileOps 61 exec *systemd.MockShimExec 62 63 name string 64 tag names.Tag 65 conf common.Conf 66 confStr string 67 } 68 69 var _ = gc.Suite(&initSystemSuite{}) 70 71 func (s *initSystemSuite) SetUpTest(c *gc.C) { 72 s.BaseSuite.SetUpTest(c) 73 74 dataDir, err := paths.DataDir("vivid") 75 c.Assert(err, jc.ErrorIsNil) 76 s.dataDir = dataDir 77 78 // Set up the service config. 79 tagStr := "machine-0" 80 tag, err := names.ParseTag(tagStr) 81 c.Assert(err, jc.ErrorIsNil) 82 s.tag = tag 83 s.name = "jujud-" + tagStr 84 s.conf = common.Conf{ 85 Desc: "juju agent for " + tagStr, 86 ExecStart: jujud + " " + tagStr, 87 } 88 } 89 90 func (s *initSystemSuite) patch(c *gc.C) *gomock.Controller { 91 ctrl := gomock.NewController(c) 92 93 s.dBus = NewMockDBusAPI(ctrl) 94 s.ch = systemd.PatchNewChan(s) 95 s.fops = systemd.PatchFileOps(s, ctrl) 96 s.exec = systemd.PatchExec(s, ctrl) 97 98 return ctrl 99 } 100 101 func (s *initSystemSuite) newService(c *gc.C) *systemd.Service { 102 var fac systemd.DBusAPIFactory 103 if s.dBus == nil { 104 fac = func() (systemd.DBusAPI, error) { 105 return nil, errors.New("Prior call to initSystemSuite.patch required before attempting DBusAPI connection") 106 } 107 } else { 108 fac = func() (systemd.DBusAPI, error) { return s.dBus, nil } 109 } 110 111 svc, err := systemd.NewService(s.name, s.conf, "/lib/systemd/system", fac, renderer.Join(s.dataDir, "init")) 112 c.Assert(err, jc.ErrorIsNil) 113 return svc 114 } 115 116 func (s *initSystemSuite) expectConf(c *gc.C, conf common.Conf) *gomock.Call { 117 data, err := systemd.Serialize(s.name, conf, renderer) 118 c.Assert(err, jc.ErrorIsNil) 119 120 return s.exec.EXPECT().RunCommands( 121 exec.RunParams{ 122 Commands: "cat /lib/systemd/system/jujud-machine-0/jujud-machine-0.service", 123 }, 124 ).Return(&exec.ExecResponse{Stdout: data}, nil) 125 } 126 127 func (s *initSystemSuite) newConfStr(name string) string { 128 return s.newConfStrCmd(name, "") 129 } 130 131 func (s *initSystemSuite) newConfStrCmd(name, cmd string) string { 132 tag := name[len("jujud-"):] 133 if cmd == "" { 134 cmd = jujud + " " + tag 135 } 136 return fmt.Sprintf(confStr[1:], tag, cmd) 137 } 138 139 func (s *initSystemSuite) newConfStrEnv(name, env string) string { 140 const replace = "[Service]\n" 141 result := s.newConfStr(name) 142 result = strings.Replace( 143 result, replace, 144 fmt.Sprintf("%sEnvironment=%s\n", replace, env), 145 1, 146 ) 147 return result 148 } 149 150 func (s *initSystemSuite) TestListServices(c *gc.C) { 151 ctrl := s.patch(c) 152 defer ctrl.Finish() 153 154 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{ 155 Stdout: []byte("jujud-machine-0\njujud-unit-wordpress-0"), 156 }, nil) 157 158 services, err := systemd.ListServices() 159 c.Assert(err, jc.ErrorIsNil) 160 c.Check(services, jc.SameContents, []string{"jujud-machine-0", "jujud-unit-wordpress-0"}) 161 } 162 163 func (s *initSystemSuite) TestListServicesEmpty(c *gc.C) { 164 ctrl := s.patch(c) 165 defer ctrl.Finish() 166 167 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil) 168 169 services, err := systemd.ListServices() 170 c.Assert(err, jc.ErrorIsNil) 171 c.Check(services, gc.HasLen, 0) 172 } 173 174 func (s *initSystemSuite) TestNewService(c *gc.C) { 175 svc := s.newService(c) 176 c.Check(svc.Service, jc.DeepEquals, common.Service{Name: s.name, Conf: s.conf}) 177 c.Check(svc.ConfName, gc.Equals, s.name+".service") 178 c.Check(svc.UnitName, gc.Equals, s.name+".service") 179 c.Check(svc.DirName, gc.Equals, fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)) 180 } 181 182 func (s *initSystemSuite) TestNewServiceLogfile(c *gc.C) { 183 s.conf.Logfile = "/var/log/juju/machine-0.log" 184 svc := s.newService(c) 185 186 user, group := systemd.SyslogUserGroup() 187 dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name) 188 script := ` 189 #!/usr/bin/env bash 190 191 # Set up logging. 192 touch '/var/log/juju/machine-0.log' 193 chown `[1:] + user + `:` + group + ` '/var/log/juju/machine-0.log' 194 chmod 0600 '/var/log/juju/machine-0.log' 195 exec >> '/var/log/juju/machine-0.log' 196 exec 2>&1 197 198 # Run the script. 199 ` + jujud + " machine-0" 200 201 c.Check(svc.Service, jc.DeepEquals, common.Service{ 202 Name: s.name, 203 Conf: common.Conf{ 204 Desc: s.conf.Desc, 205 ExecStart: dirName + "/exec-start.sh", 206 Logfile: "/var/log/juju/machine-0.log", 207 }, 208 }) 209 210 c.Check(svc.ConfName, gc.Equals, s.name+".service") 211 c.Check(svc.UnitName, gc.Equals, s.name+".service") 212 c.Check(svc.DirName, gc.Equals, dirName) 213 214 // This gives us a more readable output if they aren't equal. 215 c.Check(string(svc.Script), gc.Equals, script) 216 c.Check(strings.Split(string(svc.Script), "\n"), jc.DeepEquals, strings.Split(script, "\n")) 217 } 218 219 func (s *initSystemSuite) TestNewServiceEmptyConf(c *gc.C) { 220 svc, err := systemd.NewService(s.name, common.Conf{}, "/lib/systemd/system", systemd.NewDBusAPI, renderer.Join(s.dataDir, "init")) 221 c.Assert(err, gc.IsNil) 222 c.Check(svc.Service, jc.DeepEquals, common.Service{Name: s.name}) 223 c.Check(svc.ConfName, gc.Equals, s.name+".service") 224 c.Check(svc.UnitName, gc.Equals, s.name+".service") 225 c.Check(svc.DirName, gc.Equals, fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)) 226 } 227 228 func (s *initSystemSuite) TestNewServiceBasic(c *gc.C) { 229 s.conf.ExecStart = "/path/to/some/other/command" 230 svc := s.newService(c) 231 c.Check(svc.Service, jc.DeepEquals, common.Service{Name: s.name, Conf: s.conf}) 232 c.Check(svc.ConfName, gc.Equals, s.name+".service") 233 c.Check(svc.UnitName, gc.Equals, s.name+".service") 234 c.Check(svc.DirName, gc.Equals, fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name)) 235 } 236 237 func (s *initSystemSuite) TestNewServiceExtraScript(c *gc.C) { 238 s.conf.ExtraScript = "'/path/to/another/command'" 239 svc := s.newService(c) 240 241 dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name) 242 script := ` 243 #!/usr/bin/env bash 244 245 '/path/to/another/command' 246 `[1:] + jujud + " machine-0" 247 248 c.Check(svc.Service, jc.DeepEquals, common.Service{ 249 Name: s.name, 250 Conf: common.Conf{ 251 Desc: s.conf.Desc, 252 ExecStart: dirName + "/exec-start.sh", 253 }, 254 }) 255 256 c.Check(svc.ConfName, gc.Equals, s.name+".service") 257 c.Check(svc.UnitName, gc.Equals, s.name+".service") 258 c.Check(svc.DirName, gc.Equals, dirName) 259 c.Check(string(svc.Script), gc.Equals, script) 260 } 261 262 func (s *initSystemSuite) TestNewServiceMultiLine(c *gc.C) { 263 s.conf.ExecStart = "a\nb\nc" 264 svc := s.newService(c) 265 266 dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name) 267 script := ` 268 #!/usr/bin/env bash 269 270 a 271 b 272 c`[1:] 273 274 c.Check(svc.Service, jc.DeepEquals, common.Service{ 275 Name: s.name, 276 Conf: common.Conf{ 277 Desc: s.conf.Desc, 278 ExecStart: dirName + "/exec-start.sh", 279 }, 280 }) 281 282 c.Check(svc.ConfName, gc.Equals, s.name+".service") 283 c.Check(svc.UnitName, gc.Equals, s.name+".service") 284 c.Check(svc.DirName, gc.Equals, dirName) 285 286 // This gives us a more readable output if they aren't equal. 287 c.Check(string(svc.Script), gc.Equals, script) 288 } 289 290 func (s *initSystemSuite) TestInstalledTrue(c *gc.C) { 291 ctrl := s.patch(c) 292 defer ctrl.Finish() 293 294 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{ 295 Stdout: []byte("jujud-machine-0\njuju-mongod"), 296 }, nil) 297 298 installed, err := s.newService(c).Installed() 299 c.Assert(err, jc.ErrorIsNil) 300 c.Check(installed, jc.IsTrue) 301 } 302 303 func (s *initSystemSuite) TestInstalledFalse(c *gc.C) { 304 ctrl := s.patch(c) 305 defer ctrl.Finish() 306 307 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{ 308 Stdout: []byte("some-other-service"), 309 }, nil) 310 311 installed, err := s.newService(c).Installed() 312 c.Assert(err, jc.ErrorIsNil) 313 c.Check(installed, jc.IsFalse) 314 } 315 316 func (s *initSystemSuite) TestInstalledError(c *gc.C) { 317 ctrl := s.patch(c) 318 defer ctrl.Finish() 319 320 s.exec.EXPECT().RunCommands(listCmdArg).Return(nil, errFailure) 321 322 installed, err := s.newService(c).Installed() 323 c.Assert(errors.Cause(err), gc.Equals, errFailure) 324 c.Check(installed, jc.IsFalse) 325 } 326 327 func (s *initSystemSuite) TestExistsTrue(c *gc.C) { 328 ctrl := s.patch(c) 329 defer ctrl.Finish() 330 s.expectConf(c, s.conf) 331 332 exists, err := s.newService(c).Exists() 333 c.Assert(err, jc.ErrorIsNil) 334 c.Check(exists, jc.IsTrue) 335 } 336 337 func (s *initSystemSuite) TestExistsFalse(c *gc.C) { 338 ctrl := s.patch(c) 339 defer ctrl.Finish() 340 341 // We force the systemd API to return a slightly different conf. 342 // In this case we simply set Conf.Env, which s.conf does not set. 343 // This causes Service.Exists to return false. 344 s.expectConf(c, common.Conf{ 345 Desc: s.conf.Desc, 346 ExecStart: s.conf.ExecStart, 347 Env: map[string]string{"a": "b"}, 348 }) 349 350 exists, err := s.newService(c).Exists() 351 c.Assert(err, jc.ErrorIsNil) 352 c.Check(exists, jc.IsFalse) 353 } 354 355 func (s *initSystemSuite) TestExistsError(c *gc.C) { 356 ctrl := s.patch(c) 357 defer ctrl.Finish() 358 359 s.exec.EXPECT().RunCommands( 360 exec.RunParams{ 361 Commands: "cat /lib/systemd/system/jujud-machine-0/jujud-machine-0.service", 362 }, 363 ).Return(nil, errFailure) 364 365 exists, err := s.newService(c).Exists() 366 c.Assert(errors.Cause(err), gc.Equals, errFailure) 367 c.Check(exists, jc.IsFalse) 368 } 369 370 func (s *initSystemSuite) TestExistsEmptyConf(c *gc.C) { 371 svc := s.newService(c) 372 svc.Service.Conf = common.Conf{} 373 _, err := svc.Exists() 374 c.Check(err, gc.ErrorMatches, `.*no conf expected.*`) 375 } 376 377 func (s *initSystemSuite) TestRunningTrue(c *gc.C) { 378 ctrl := s.patch(c) 379 defer ctrl.Finish() 380 381 gomock.InOrder( 382 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 383 {Name: "jujud-machine-0.service", LoadState: "loaded", ActiveState: "active"}, 384 {Name: "juju-mongod.service", LoadState: "loaded", ActiveState: "active"}, 385 }, nil), 386 s.dBus.EXPECT().Close(), 387 ) 388 389 running, err := s.newService(c).Running() 390 c.Assert(err, jc.ErrorIsNil) 391 c.Check(running, jc.IsTrue) 392 } 393 394 func (s *initSystemSuite) TestRunningFalse(c *gc.C) { 395 ctrl := s.patch(c) 396 defer ctrl.Finish() 397 398 gomock.InOrder( 399 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 400 {Name: "jujud-machine-0.service", LoadState: "loaded", ActiveState: "inactive"}, 401 {Name: "juju-mongod.service", LoadState: "loaded", ActiveState: "active"}, 402 }, nil), 403 s.dBus.EXPECT().Close(), 404 ) 405 406 running, err := s.newService(c).Running() 407 c.Assert(err, jc.ErrorIsNil) 408 c.Check(running, jc.IsFalse) 409 } 410 411 func (s *initSystemSuite) TestRunningNotEnabled(c *gc.C) { 412 ctrl := s.patch(c) 413 defer ctrl.Finish() 414 415 gomock.InOrder( 416 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 417 {Name: "random-thing.service", LoadState: "loaded", ActiveState: "active"}, 418 }, nil), 419 s.dBus.EXPECT().Close(), 420 ) 421 422 running, err := s.newService(c).Running() 423 c.Assert(err, jc.ErrorIsNil) 424 c.Check(running, jc.IsFalse) 425 } 426 427 func (s *initSystemSuite) TestRunningError(c *gc.C) { 428 ctrl := s.patch(c) 429 defer ctrl.Finish() 430 431 gomock.InOrder( 432 s.dBus.EXPECT().ListUnits().Return(nil, errFailure), 433 s.dBus.EXPECT().Close(), 434 ) 435 436 _, err := s.newService(c).Running() 437 c.Check(errors.Cause(err), gc.Equals, errFailure) 438 } 439 440 func (s *initSystemSuite) TestStart(c *gc.C) { 441 ctrl := s.patch(c) 442 defer ctrl.Finish() 443 444 svc := s.newService(c) 445 446 gomock.InOrder( 447 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{ 448 Stdout: []byte("jujud-machine-0\njuju-mongod"), 449 }, nil), 450 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 451 {Name: svc.UnitName, LoadState: "loaded", ActiveState: "inactive"}, 452 }, nil), 453 s.dBus.EXPECT().Close(), 454 455 // Equality check for the channel fails here, so we use Any(). 456 // We know this is safe, because we notify on the channel we got from 457 // the patched call and everything proceeds happily. 458 s.dBus.EXPECT().StartUnit(svc.UnitName, "fail", gomock.Any()).Return(1, nil).Do( 459 func(_ ...interface{}) { s.ch <- "done" }, 460 ), 461 s.dBus.EXPECT().Close(), 462 ) 463 464 err := svc.Start() 465 c.Assert(err, jc.ErrorIsNil) 466 } 467 468 func (s *initSystemSuite) TestStartAlreadyRunning(c *gc.C) { 469 ctrl := s.patch(c) 470 defer ctrl.Finish() 471 472 svc := s.newService(c) 473 474 gomock.InOrder( 475 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{ 476 Stdout: []byte("jujud-machine-0\njuju-mongod"), 477 }, nil), 478 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 479 {Name: svc.UnitName, LoadState: "loaded", ActiveState: "active"}, 480 }, nil), 481 s.dBus.EXPECT().Close(), 482 ) 483 484 err := svc.Start() 485 c.Assert(err, jc.ErrorIsNil) 486 } 487 488 func (s *initSystemSuite) TestStartNotInstalled(c *gc.C) { 489 ctrl := s.patch(c) 490 defer ctrl.Finish() 491 492 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil) 493 494 err := s.newService(c).Start() 495 c.Check(err, jc.Satisfies, errors.IsNotFound) 496 } 497 498 func (s *initSystemSuite) TestStop(c *gc.C) { 499 ctrl := s.patch(c) 500 defer ctrl.Finish() 501 502 svc := s.newService(c) 503 504 gomock.InOrder( 505 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 506 {Name: svc.UnitName, LoadState: "loaded", ActiveState: "active"}, 507 }, nil), 508 s.dBus.EXPECT().Close(), 509 510 // Equality check for the channel fails here, so we use Any(). 511 // We know this is safe, because we notify on the channel we got from 512 // the patched call and everything proceeds happily. 513 s.dBus.EXPECT().StopUnit(svc.UnitName, "fail", gomock.Any()).Return(1, nil).Do( 514 func(_ ...interface{}) { s.ch <- "done" }, 515 ), 516 s.dBus.EXPECT().Close(), 517 ) 518 519 err := svc.Stop() 520 c.Assert(err, jc.ErrorIsNil) 521 } 522 523 func (s *initSystemSuite) TestStopNotRunning(c *gc.C) { 524 ctrl := s.patch(c) 525 defer ctrl.Finish() 526 527 svc := s.newService(c) 528 529 gomock.InOrder( 530 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 531 {Name: svc.UnitName, LoadState: "loaded", ActiveState: "inactive"}, 532 }, nil), 533 s.dBus.EXPECT().Close(), 534 ) 535 536 err := svc.Stop() 537 c.Assert(err, jc.ErrorIsNil) 538 } 539 540 func (s *initSystemSuite) TestStopNotInstalled(c *gc.C) { 541 ctrl := s.patch(c) 542 defer ctrl.Finish() 543 544 gomock.InOrder( 545 s.dBus.EXPECT().ListUnits().Return(nil, nil), 546 s.dBus.EXPECT().Close(), 547 ) 548 549 err := s.newService(c).Stop() 550 c.Assert(err, jc.ErrorIsNil) 551 } 552 553 func (s *initSystemSuite) TestRemove(c *gc.C) { 554 ctrl := s.patch(c) 555 defer ctrl.Finish() 556 557 svc := s.newService(c) 558 559 gomock.InOrder( 560 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte(svc.Name())}, nil), 561 s.dBus.EXPECT().DisableUnitFiles([]string{svc.UnitName}, false).Return(nil, nil), 562 s.dBus.EXPECT().Reload().Return(nil), 563 s.fops.EXPECT().RemoveAll(svc.DirName).Return(nil), 564 s.dBus.EXPECT().Close(), 565 ) 566 567 err := svc.Remove() 568 c.Assert(err, jc.ErrorIsNil) 569 } 570 571 func (s *initSystemSuite) TestRemoveNotInstalled(c *gc.C) { 572 ctrl := s.patch(c) 573 defer ctrl.Finish() 574 575 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil) 576 577 err := s.newService(c).Remove() 578 c.Assert(err, jc.ErrorIsNil) 579 } 580 581 func (s *initSystemSuite) TestInstall(c *gc.C) { 582 ctrl := s.patch(c) 583 defer ctrl.Finish() 584 585 dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name) 586 fileName := fmt.Sprintf("%s/%s.service", dirName, s.name) 587 588 gomock.InOrder( 589 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil), 590 s.fops.EXPECT().MkdirAll(dirName).Return(nil), 591 s.fops.EXPECT().CreateFile(fileName, []byte(s.newConfStr(s.name)), os.FileMode(0644)).Return(nil), 592 s.dBus.EXPECT().LinkUnitFiles([]string{fileName}, false, true).Return(nil, nil), 593 s.dBus.EXPECT().Reload().Return(nil), 594 s.dBus.EXPECT().EnableUnitFiles([]string{fileName}, false, true).Return(true, nil, nil), 595 s.dBus.EXPECT().Close(), 596 ) 597 598 err := s.newService(c).Install() 599 c.Assert(err, jc.ErrorIsNil) 600 } 601 602 func (s *initSystemSuite) TestInstallAlreadyInstalled(c *gc.C) { 603 ctrl := s.patch(c) 604 defer ctrl.Finish() 605 606 s.expectConf(c, s.conf) 607 svc := s.newService(c) 608 609 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte(svc.Name())}, nil) 610 611 err := svc.Install() 612 c.Assert(err, jc.ErrorIsNil) 613 } 614 615 func (s *initSystemSuite) TestInstallZombie(c *gc.C) { 616 ctrl := s.patch(c) 617 defer ctrl.Finish() 618 619 // We force the systemd API to return a slightly different conf. 620 // In this case we simply set a different Env value between the 621 // conf we are installing and the conf returned by the systemd API. 622 // This causes Service.Exists to return false. 623 conf := common.Conf{ 624 Desc: s.conf.Desc, 625 ExecStart: s.conf.ExecStart, 626 Env: map[string]string{"a": "b"}, 627 } 628 s.expectConf(c, conf) 629 conf.Env["a"] = "c" 630 svc, err := systemd.NewService(s.name, conf, "/lib/systemd/system", func() (systemd.DBusAPI, error) { return s.dBus, nil }, renderer.Join(s.dataDir, "init")) 631 632 dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name) 633 fileName := fmt.Sprintf("%s/%s.service", dirName, s.name) 634 635 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte(svc.Name())}, nil).Times(2) 636 s.dBus.EXPECT().Close().Times(3) 637 638 s.dBus.EXPECT().ListUnits().Return([]dbus.UnitStatus{ 639 {Name: svc.Name(), LoadState: "loaded", ActiveState: "active"}, 640 }, nil) 641 s.dBus.EXPECT().DisableUnitFiles([]string{svc.UnitName}, false).Return(nil, nil) 642 s.dBus.EXPECT().Reload().Return(nil) 643 s.fops.EXPECT().RemoveAll(svc.DirName).Return(nil) 644 s.fops.EXPECT().MkdirAll(dirName).Return(nil) 645 s.fops.EXPECT().CreateFile(fileName, []byte(s.newConfStrEnv(s.name, `"a=c"`)), os.FileMode(0644)).Return(nil) 646 s.dBus.EXPECT().LinkUnitFiles([]string{fileName}, false, true).Return(nil, nil) 647 s.dBus.EXPECT().Reload().Return(nil) 648 s.dBus.EXPECT().EnableUnitFiles([]string{fileName}, false, true).Return(true, nil, nil) 649 650 err = svc.Install() 651 c.Assert(err, jc.ErrorIsNil) 652 } 653 654 func (s *initSystemSuite) TestInstallMultiLine(c *gc.C) { 655 ctrl := s.patch(c) 656 defer ctrl.Finish() 657 658 dirName := fmt.Sprintf("%s/%s", "/lib/systemd/system", s.name) 659 fileName := fmt.Sprintf("%s/%s.service", dirName, s.name) 660 scriptPath := fmt.Sprintf("%s/exec-start.sh", dirName) 661 cmd := "a\nb\nc" 662 663 svc := s.newService(c) 664 svc.Service.Conf.ExecStart = scriptPath 665 svc.Script = []byte(cmd) 666 667 gomock.InOrder( 668 s.exec.EXPECT().RunCommands(listCmdArg).Return(&exec.ExecResponse{Stdout: []byte("")}, nil), 669 s.fops.EXPECT().MkdirAll(dirName).Return(nil), 670 s.fops.EXPECT().CreateFile(scriptPath, []byte(cmd), os.FileMode(0755)).Return(nil), 671 s.fops.EXPECT().CreateFile(fileName, []byte(s.newConfStrCmd(s.name, scriptPath)), os.FileMode(0644)).Return(nil), 672 s.dBus.EXPECT().LinkUnitFiles([]string{fileName}, false, true).Return(nil, nil), 673 s.dBus.EXPECT().Reload().Return(nil), 674 s.dBus.EXPECT().EnableUnitFiles([]string{fileName}, false, true).Return(true, nil, nil), 675 s.dBus.EXPECT().Close(), 676 ) 677 678 err := svc.Install() 679 c.Assert(err, jc.ErrorIsNil) 680 } 681 682 func (s *initSystemSuite) TestInstallEmptyConf(c *gc.C) { 683 svc := s.newService(c) 684 svc.Service.Conf = common.Conf{} 685 err := svc.Install() 686 c.Check(err, gc.ErrorMatches, `.*missing conf.*`) 687 } 688 689 func (s *initSystemSuite) TestInstallCommands(c *gc.C) { 690 name := "jujud-machine-0" 691 commands, err := s.newService(c).InstallCommands() 692 c.Assert(err, jc.ErrorIsNil) 693 694 test := systemdtesting.WriteConfTest{ 695 Service: name, 696 DataDir: "/lib/systemd/system", 697 Expected: s.newConfStr(name), 698 } 699 test.CheckCommands(c, commands) 700 } 701 702 func (s *initSystemSuite) TestInstallCommandsLogfile(c *gc.C) { 703 name := "jujud-machine-0" 704 s.conf.Logfile = "/var/log/juju/machine-0.log" 705 svc := s.newService(c) 706 commands, err := svc.InstallCommands() 707 c.Assert(err, jc.ErrorIsNil) 708 709 user, group := systemd.SyslogUserGroup() 710 test := systemdtesting.WriteConfTest{ 711 Service: name, 712 DataDir: "/lib/systemd/system", 713 Expected: strings.Replace( 714 s.newConfStr(name), 715 "ExecStart=/var/lib/juju/bin/jujud machine-0", 716 "ExecStart=/lib/systemd/system/jujud-machine-0/exec-start.sh", 717 -1), 718 Script: ` 719 # Set up logging. 720 touch '/var/log/juju/machine-0.log' 721 chown `[1:] + user + `:` + group + ` '/var/log/juju/machine-0.log' 722 chmod 0600 '/var/log/juju/machine-0.log' 723 exec >> '/var/log/juju/machine-0.log' 724 exec 2>&1 725 726 # Run the script. 727 ` + jujud + " machine-0", 728 } 729 730 test.CheckCommands(c, commands) 731 } 732 733 func (s *initSystemSuite) TestInstallCommandsShutdown(c *gc.C) { 734 name := "juju-shutdown-job" 735 conf, err := service.ShutdownAfterConf("cloud-final") 736 c.Assert(err, jc.ErrorIsNil) 737 738 svc, err := systemd.NewService(name, conf, "/lib/systemd/system", systemd.NewDBusAPI, renderer.Join(s.dataDir, "init")) 739 c.Assert(err, jc.ErrorIsNil) 740 741 commands, err := svc.InstallCommands() 742 c.Assert(err, jc.ErrorIsNil) 743 744 test := systemdtesting.WriteConfTest{ 745 Service: name, 746 DataDir: "/lib/systemd/system", 747 Expected: ` 748 [Unit] 749 Description=juju shutdown job 750 After=syslog.target 751 After=network.target 752 After=systemd-user-sessions.service 753 After=cloud-final 754 755 [Service] 756 ExecStart=/sbin/shutdown -h now 757 ExecStopPost=/bin/systemctl disable juju-shutdown-job.service 758 759 [Install] 760 WantedBy=multi-user.target 761 `[1:], 762 } 763 764 test.CheckCommands(c, commands) 765 } 766 767 func (s *initSystemSuite) TestInstallCommandsEmptyConf(c *gc.C) { 768 svc := s.newService(c) 769 svc.Service.Conf = common.Conf{} 770 _, err := svc.InstallCommands() 771 c.Check(err, gc.ErrorMatches, `.*missing conf.*`) 772 } 773 774 func (s *initSystemSuite) TestStartCommands(c *gc.C) { 775 commands, err := s.newService(c).StartCommands() 776 c.Assert(err, jc.ErrorIsNil) 777 c.Check(commands, jc.DeepEquals, []string{"/bin/systemctl start jujud-machine-0.service"}) 778 }