github.com/stolowski/snapd@v0.0.0-20210407085831-115137ce5a22/wrappers/services_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package wrappers_test 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "os" 26 "path/filepath" 27 "regexp" 28 "sort" 29 "strings" 30 "time" 31 32 . "gopkg.in/check.v1" 33 34 "github.com/snapcore/snapd/dirs" 35 // imported to ensure actual interfaces are defined, 36 // in production this is guaranteed by ifacestate 37 _ "github.com/snapcore/snapd/interfaces/builtin" 38 "github.com/snapcore/snapd/osutil" 39 "github.com/snapcore/snapd/progress" 40 "github.com/snapcore/snapd/snap" 41 "github.com/snapcore/snapd/snap/snaptest" 42 "github.com/snapcore/snapd/strutil" 43 "github.com/snapcore/snapd/systemd" 44 "github.com/snapcore/snapd/testutil" 45 "github.com/snapcore/snapd/timings" 46 "github.com/snapcore/snapd/usersession/agent" 47 "github.com/snapcore/snapd/wrappers" 48 ) 49 50 type servicesTestSuite struct { 51 testutil.DBusTest 52 53 tempdir string 54 55 sysdLog [][]string 56 57 systemctlRestorer, delaysRestorer func() 58 59 perfTimings timings.Measurer 60 61 agent *agent.SessionAgent 62 } 63 64 var _ = Suite(&servicesTestSuite{}) 65 66 func (s *servicesTestSuite) SetUpTest(c *C) { 67 s.DBusTest.SetUpTest(c) 68 s.tempdir = c.MkDir() 69 s.sysdLog = nil 70 dirs.SetRootDir(s.tempdir) 71 72 s.systemctlRestorer = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 73 s.sysdLog = append(s.sysdLog, cmd) 74 return []byte("ActiveState=inactive\n"), nil 75 }) 76 s.delaysRestorer = systemd.MockStopDelays(time.Millisecond, 25*time.Second) 77 s.perfTimings = timings.New(nil) 78 79 xdgRuntimeDir := fmt.Sprintf("%s/%d", dirs.XdgRuntimeDirBase, os.Getuid()) 80 err := os.MkdirAll(xdgRuntimeDir, 0700) 81 c.Assert(err, IsNil) 82 s.agent, err = agent.New() 83 c.Assert(err, IsNil) 84 s.agent.Start() 85 } 86 87 func (s *servicesTestSuite) TearDownTest(c *C) { 88 if s.agent != nil { 89 err := s.agent.Stop() 90 c.Check(err, IsNil) 91 } 92 s.systemctlRestorer() 93 s.delaysRestorer() 94 dirs.SetRootDir("") 95 s.DBusTest.TearDownTest(c) 96 } 97 98 func (s *servicesTestSuite) TestAddSnapServicesAndRemove(c *C) { 99 info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) 100 svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") 101 102 err := wrappers.AddSnapServices(info, nil, progress.Null) 103 c.Assert(err, IsNil) 104 c.Check(s.sysdLog, DeepEquals, [][]string{ 105 {"daemon-reload"}, 106 }) 107 108 s.sysdLog = nil 109 110 flags := &wrappers.StartServicesFlags{Enable: true} 111 err = wrappers.StartServices(info.Services(), nil, flags, progress.Null, s.perfTimings) 112 c.Assert(err, IsNil) 113 c.Check(s.sysdLog, DeepEquals, [][]string{ 114 {"enable", filepath.Base(svcFile)}, 115 {"start", filepath.Base(svcFile)}, 116 }) 117 118 content, err := ioutil.ReadFile(svcFile) 119 c.Assert(err, IsNil) 120 121 dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") 122 c.Assert(string(content), Equals, fmt.Sprintf(`[Unit] 123 # Auto-generated, DO NOT EDIT 124 Description=Service for snap application hello-snap.svc1 125 Requires=%[1]s 126 Wants=network.target 127 After=%[1]s network.target snapd.apparmor.service 128 X-Snappy=yes 129 130 [Service] 131 EnvironmentFile=-/etc/environment 132 ExecStart=/usr/bin/snap run hello-snap.svc1 133 SyslogIdentifier=hello-snap.svc1 134 Restart=on-failure 135 WorkingDirectory=%[2]s/var/snap/hello-snap/12 136 ExecStop=/usr/bin/snap run --command=stop hello-snap.svc1 137 ExecStopPost=/usr/bin/snap run --command=post-stop hello-snap.svc1 138 TimeoutStopSec=30 139 Type=forking 140 141 [Install] 142 WantedBy=multi-user.target 143 `, 144 systemd.EscapeUnitNamePath(dir), 145 dirs.GlobalRootDir, 146 )) 147 148 s.sysdLog = nil 149 err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) 150 c.Assert(err, IsNil) 151 c.Assert(s.sysdLog, HasLen, 2) 152 c.Check(s.sysdLog, DeepEquals, [][]string{ 153 {"stop", filepath.Base(svcFile)}, 154 {"show", "--property=ActiveState", "snap.hello-snap.svc1.service"}, 155 }) 156 157 s.sysdLog = nil 158 err = wrappers.RemoveSnapServices(info, progress.Null) 159 c.Assert(err, IsNil) 160 c.Check(osutil.FileExists(svcFile), Equals, false) 161 c.Assert(s.sysdLog, HasLen, 2) 162 c.Check(s.sysdLog[0], DeepEquals, []string{"disable", filepath.Base(svcFile)}) 163 c.Check(s.sysdLog[1], DeepEquals, []string{"daemon-reload"}) 164 } 165 166 func (s *servicesTestSuite) TestAddSnapServicesWithInterfaceSnippets(c *C) { 167 tt := []struct { 168 comment string 169 plugSnippet string 170 }{ 171 // just single bare interfaces with no attributes 172 { 173 "docker-support", 174 ` 175 plugs: 176 - docker-support`, 177 }, 178 { 179 "k8s-support", 180 ` 181 plugs: 182 - kubernetes-support`, 183 }, 184 { 185 "lxd-support", 186 ` 187 plugs: 188 - lxd-support 189 `, 190 }, 191 { 192 "greengrass-support", 193 ` 194 plugs: 195 - greengrass-support 196 `, 197 }, 198 199 // multiple interfaces that require Delegate=true, but only one is 200 // generated 201 202 { 203 "multiple interfaces that require Delegate=true", 204 ` 205 plugs: 206 - docker-support 207 - kubernetes-support`, 208 }, 209 210 // interfaces with flavor attributes 211 212 { 213 "k8s-support with kubelet", 214 ` 215 plugs: 216 - kubelet 217 plugs: 218 kubelet: 219 interface: kubernetes-support 220 flavor: kubelet 221 `, 222 }, 223 { 224 "k8s-support with kubeproxy", 225 ` 226 plugs: 227 - kubeproxy 228 plugs: 229 kubeproxy: 230 interface: kubernetes-support 231 flavor: kubeproxy 232 `, 233 }, 234 { 235 "greengrass-support with legacy-container flavor", 236 ` 237 plugs: 238 - greengrass 239 plugs: 240 greengrass: 241 interface: greengrass-support 242 flavor: legacy-container 243 `, 244 }, 245 } 246 247 for _, t := range tt { 248 comment := Commentf(t.comment) 249 info := snaptest.MockSnap(c, packageHello+` 250 svc1: 251 daemon: simple 252 `+t.plugSnippet, 253 &snap.SideInfo{Revision: snap.R(12)}) 254 svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") 255 256 err := wrappers.AddSnapServices(info, nil, progress.Null) 257 c.Assert(err, IsNil, comment) 258 c.Check(s.sysdLog, DeepEquals, [][]string{ 259 {"daemon-reload"}, 260 }, comment) 261 262 content, err := ioutil.ReadFile(svcFile) 263 c.Assert(err, IsNil, comment) 264 265 dir := filepath.Join(dirs.SnapMountDir, "hello-snap", "12.mount") 266 c.Assert(string(content), Equals, fmt.Sprintf(`[Unit] 267 # Auto-generated, DO NOT EDIT 268 Description=Service for snap application hello-snap.svc1 269 Requires=%[1]s 270 Wants=network.target 271 After=%[1]s network.target snapd.apparmor.service 272 X-Snappy=yes 273 274 [Service] 275 EnvironmentFile=-/etc/environment 276 ExecStart=/usr/bin/snap run hello-snap.svc1 277 SyslogIdentifier=hello-snap.svc1 278 Restart=on-failure 279 WorkingDirectory=%[2]s/var/snap/hello-snap/12 280 TimeoutStopSec=30 281 Type=simple 282 Delegate=true 283 284 [Install] 285 WantedBy=multi-user.target 286 `, 287 systemd.EscapeUnitNamePath(dir), 288 dirs.GlobalRootDir, 289 ), comment) 290 291 s.sysdLog = nil 292 err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) 293 c.Assert(err, IsNil, comment) 294 c.Assert(s.sysdLog, HasLen, 2, comment) 295 c.Check(s.sysdLog, DeepEquals, [][]string{ 296 {"stop", filepath.Base(svcFile)}, 297 {"show", "--property=ActiveState", "snap.hello-snap.svc1.service"}, 298 }, comment) 299 300 s.sysdLog = nil 301 err = wrappers.RemoveSnapServices(info, progress.Null) 302 c.Assert(err, IsNil, comment) 303 c.Check(osutil.FileExists(svcFile), Equals, false, comment) 304 c.Assert(s.sysdLog, HasLen, 2, comment) 305 c.Check(s.sysdLog, DeepEquals, [][]string{ 306 {"disable", filepath.Base(svcFile)}, 307 {"daemon-reload"}, 308 }, comment) 309 310 s.sysdLog = nil 311 } 312 } 313 314 func (s *servicesTestSuite) TestAddSnapServicesAndRemoveUserDaemons(c *C) { 315 info := snaptest.MockSnap(c, packageHello+` 316 svc1: 317 daemon: simple 318 daemon-scope: user 319 `, &snap.SideInfo{Revision: snap.R(12)}) 320 svcFile := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.service") 321 322 err := wrappers.AddSnapServices(info, nil, progress.Null) 323 c.Assert(err, IsNil) 324 c.Check(s.sysdLog, DeepEquals, [][]string{ 325 {"--user", "daemon-reload"}, 326 }) 327 328 content, err := ioutil.ReadFile(svcFile) 329 c.Assert(err, IsNil) 330 331 expected := "ExecStart=/usr/bin/snap run hello-snap.svc1" 332 c.Check(string(content), Matches, "(?ms).*^"+regexp.QuoteMeta(expected)) // check.v1 adds ^ and $ around the regexp provided 333 334 s.sysdLog = nil 335 err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) 336 c.Assert(err, IsNil) 337 c.Assert(s.sysdLog, HasLen, 2) 338 c.Check(s.sysdLog, DeepEquals, [][]string{ 339 {"--user", "stop", filepath.Base(svcFile)}, 340 {"--user", "show", "--property=ActiveState", "snap.hello-snap.svc1.service"}, 341 }) 342 343 s.sysdLog = nil 344 err = wrappers.RemoveSnapServices(info, progress.Null) 345 c.Assert(err, IsNil) 346 c.Check(osutil.FileExists(svcFile), Equals, false) 347 c.Assert(s.sysdLog, HasLen, 2) 348 c.Check(s.sysdLog, DeepEquals, [][]string{ 349 {"--user", "--global", "disable", filepath.Base(svcFile)}, 350 {"--user", "daemon-reload"}, 351 }) 352 } 353 354 var snapdYaml = `name: snapd 355 version: 1.0 356 type: snapd 357 ` 358 359 func (s *servicesTestSuite) TestRemoveSnapWithSocketsRemovesSocketsService(c *C) { 360 info := snaptest.MockSnap(c, packageHello+` 361 svc1: 362 daemon: simple 363 plugs: [network-bind] 364 sockets: 365 sock1: 366 listen-stream: $SNAP_DATA/sock1.socket 367 socket-mode: 0666 368 sock2: 369 listen-stream: $SNAP_COMMON/sock2.socket 370 `, &snap.SideInfo{Revision: snap.R(12)}) 371 372 err := wrappers.AddSnapServices(info, nil, progress.Null) 373 c.Assert(err, IsNil) 374 375 err = wrappers.StopServices(info.Services(), nil, "", &progress.Null, s.perfTimings) 376 c.Assert(err, IsNil) 377 378 err = wrappers.RemoveSnapServices(info, &progress.Null) 379 c.Assert(err, IsNil) 380 381 app := info.Apps["svc1"] 382 c.Assert(app.Sockets, HasLen, 2) 383 for _, socket := range app.Sockets { 384 c.Check(osutil.FileExists(socket.File()), Equals, false) 385 } 386 } 387 388 func (s *servicesTestSuite) TestRemoveSnapPackageFallbackToKill(c *C) { 389 restore := wrappers.MockKillWait(time.Millisecond) 390 defer restore() 391 392 var sysdLog [][]string 393 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 394 // filter out the "systemctl show" that 395 // StopServices generates 396 if cmd[0] != "show" { 397 sysdLog = append(sysdLog, cmd) 398 } 399 return []byte("ActiveState=active\n"), nil 400 }) 401 defer r() 402 403 info := snaptest.MockSnap(c, `name: wat 404 version: 42 405 apps: 406 wat: 407 command: wat 408 stop-timeout: 20ms 409 daemon: forking 410 `, &snap.SideInfo{Revision: snap.R(11)}) 411 412 err := wrappers.AddSnapServices(info, nil, progress.Null) 413 c.Assert(err, IsNil) 414 415 sysdLog = nil 416 417 svcFName := "snap.wat.wat.service" 418 419 err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) 420 c.Assert(err, IsNil) 421 422 c.Check(sysdLog, DeepEquals, [][]string{ 423 {"stop", svcFName}, 424 // check kill invocations 425 {"kill", svcFName, "-s", "TERM", "--kill-who=all"}, 426 {"kill", svcFName, "-s", "KILL", "--kill-who=all"}, 427 }) 428 } 429 430 func (s *servicesTestSuite) TestRemoveSnapPackageUserDaemonStopFailure(c *C) { 431 var sysdLog [][]string 432 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 433 // filter out the "systemctl --user show" that 434 // StopServices generates 435 if cmd[0] == "--user" && cmd[1] != "show" { 436 sysdLog = append(sysdLog, cmd) 437 } 438 if cmd[0] == "--user" && cmd[1] == "stop" { 439 return nil, fmt.Errorf("user unit stop failed") 440 } 441 return []byte("ActiveState=active\n"), nil 442 }) 443 defer r() 444 445 info := snaptest.MockSnap(c, `name: wat 446 version: 42 447 apps: 448 wat: 449 command: wat 450 stop-timeout: 20ms 451 daemon: forking 452 daemon-scope: user 453 `, &snap.SideInfo{Revision: snap.R(11)}) 454 455 err := wrappers.AddSnapServices(info, nil, progress.Null) 456 c.Assert(err, IsNil) 457 458 sysdLog = nil 459 460 svcFName := "snap.wat.wat.service" 461 462 err = wrappers.StopServices(info.Services(), nil, "", progress.Null, s.perfTimings) 463 c.Check(err, ErrorMatches, "some user services failed to stop") 464 c.Check(sysdLog, DeepEquals, [][]string{ 465 {"--user", "stop", svcFName}, 466 }) 467 } 468 469 func (s *servicesTestSuite) TestServicesEnableState(c *C) { 470 info := snaptest.MockSnap(c, packageHello+` 471 svc2: 472 command: bin/hello 473 daemon: forking 474 svc3: 475 command: bin/hello 476 daemon: simple 477 daemon-scope: user 478 `, &snap.SideInfo{Revision: snap.R(12)}) 479 svc1File := "snap.hello-snap.svc1.service" 480 svc2File := "snap.hello-snap.svc2.service" 481 482 s.systemctlRestorer() 483 r := testutil.MockCommand(c, "systemctl", `#!/bin/sh 484 if [ "$1" = "--root" ]; then 485 # shifting by 2 also drops the temp dir arg to --root 486 shift 2 487 fi 488 489 case "$1" in 490 is-enabled) 491 case "$2" in 492 "snap.hello-snap.svc1.service") 493 echo "disabled" 494 exit 1 495 ;; 496 "snap.hello-snap.svc2.service") 497 echo "enabled" 498 exit 0 499 ;; 500 *) 501 echo "unexpected is-enabled of service $2" 502 exit 2 503 ;; 504 esac 505 ;; 506 *) 507 echo "unexpected op $*" 508 exit 2 509 esac 510 511 exit 1 512 `) 513 defer r.Restore() 514 515 states, err := wrappers.ServicesEnableState(info, progress.Null) 516 c.Assert(err, IsNil) 517 518 c.Assert(states, DeepEquals, map[string]bool{ 519 "svc1": false, 520 "svc2": true, 521 }) 522 523 // the calls could be out of order in the list, since iterating over a map 524 // is non-deterministic, so manually check each call 525 c.Assert(r.Calls(), HasLen, 2) 526 for _, call := range r.Calls() { 527 c.Assert(call, HasLen, 3) 528 c.Assert(call[:2], DeepEquals, []string{"systemctl", "is-enabled"}) 529 switch call[2] { 530 case svc1File, svc2File: 531 default: 532 c.Errorf("unknown service for systemctl call: %s", call[2]) 533 } 534 } 535 } 536 537 func (s *servicesTestSuite) TestServicesEnableStateFail(c *C) { 538 info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) 539 svc1File := "snap.hello-snap.svc1.service" 540 541 s.systemctlRestorer() 542 r := testutil.MockCommand(c, "systemctl", `#!/bin/sh 543 if [ "$1" = "--root" ]; then 544 # shifting by 2 also drops the temp dir arg to --root 545 shift 2 546 fi 547 548 case "$1" in 549 is-enabled) 550 case "$2" in 551 "snap.hello-snap.svc1.service") 552 echo "whoops" 553 exit 1 554 ;; 555 *) 556 echo "unexpected is-enabled of service $2" 557 exit 2 558 ;; 559 esac 560 ;; 561 *) 562 echo "unexpected op $*" 563 exit 2 564 esac 565 566 exit 1 567 `) 568 defer r.Restore() 569 570 _, err := wrappers.ServicesEnableState(info, progress.Null) 571 c.Assert(err, ErrorMatches, ".*is-enabled snap.hello-snap.svc1.service\\] failed with exit status 1: whoops\n.*") 572 573 c.Assert(r.Calls(), DeepEquals, [][]string{ 574 {"systemctl", "is-enabled", svc1File}, 575 }) 576 } 577 578 func (s *servicesTestSuite) TestAddSnapServicesWithDisabledServices(c *C) { 579 info := snaptest.MockSnap(c, packageHello+` 580 svc2: 581 command: bin/hello 582 daemon: forking 583 `, &snap.SideInfo{Revision: snap.R(12)}) 584 585 s.systemctlRestorer() 586 r := testutil.MockCommand(c, "systemctl", `#!/bin/sh 587 if [ "$1" = "--root" ]; then 588 shift 2 589 fi 590 591 case "$1" in 592 enable) 593 case "$2" in 594 "snap.hello-snap.svc1.service") 595 echo "unexpected enable of disabled service $2" 596 exit 1 597 ;; 598 "snap.hello-snap.svc2.service") 599 exit 0 600 ;; 601 *) 602 echo "unexpected enable of service $2" 603 exit 1 604 ;; 605 esac 606 ;; 607 start) 608 case "$2" in 609 "snap.hello-snap.svc2.service") 610 exit 0 611 ;; 612 *) 613 echo "unexpected start of service $2" 614 exit 1 615 ;; 616 esac 617 ;; 618 daemon-reload) 619 exit 0 620 ;; 621 *) 622 echo "unexpected op $*" 623 exit 2 624 esac 625 exit 2 626 `) 627 defer r.Restore() 628 629 // svc1 will be disabled 630 disabledSvcs := []string{"svc1"} 631 632 err := wrappers.AddSnapServices(info, nil, progress.Null) 633 c.Assert(err, IsNil) 634 635 c.Assert(r.Calls(), DeepEquals, [][]string{ 636 {"systemctl", "daemon-reload"}, 637 }) 638 639 r.ForgetCalls() 640 641 flags := &wrappers.StartServicesFlags{Enable: true} 642 err = wrappers.StartServices(info.Services(), disabledSvcs, flags, progress.Null, s.perfTimings) 643 c.Assert(err, IsNil) 644 645 // only svc2 should be enabled 646 c.Assert(r.Calls(), DeepEquals, [][]string{ 647 {"systemctl", "enable", "snap.hello-snap.svc2.service"}, 648 {"systemctl", "start", "snap.hello-snap.svc2.service"}, 649 }) 650 } 651 652 func (s *servicesTestSuite) TestAddSnapServicesWithPreseed(c *C) { 653 opts := &wrappers.AddSnapServicesOptions{Preseeding: true} 654 655 info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) 656 657 s.systemctlRestorer() 658 r := testutil.MockCommand(c, "systemctl", "exit 1") 659 defer r.Restore() 660 661 err := wrappers.AddSnapServices(info, opts, progress.Null) 662 c.Assert(err, IsNil) 663 664 // file was created 665 svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.*.service")) 666 c.Check(svcFiles, HasLen, 1) 667 668 // but systemctl was not called 669 c.Assert(r.Calls(), HasLen, 0) 670 } 671 672 func (s *servicesTestSuite) TestStopServicesWithSockets(c *C) { 673 var sysServices, userServices []string 674 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 675 if cmd[0] == "stop" { 676 sysServices = append(sysServices, cmd[1]) 677 } else if cmd[0] == "--user" && cmd[1] == "stop" { 678 userServices = append(userServices, cmd[2]) 679 } 680 return []byte("ActiveState=inactive\n"), nil 681 }) 682 defer r() 683 684 info := snaptest.MockSnap(c, packageHello+` 685 svc1: 686 daemon: simple 687 plugs: [network-bind] 688 sockets: 689 sock1: 690 listen-stream: $SNAP_COMMON/sock1.socket 691 socket-mode: 0666 692 sock2: 693 listen-stream: $SNAP_DATA/sock2.socket 694 svc2: 695 daemon: simple 696 daemon-scope: user 697 plugs: [network-bind] 698 sockets: 699 sock1: 700 listen-stream: $SNAP_USER_COMMON/sock1.socket 701 socket-mode: 0666 702 sock2: 703 listen-stream: $SNAP_USER_DATA/sock2.socket 704 `, &snap.SideInfo{Revision: snap.R(12)}) 705 706 err := wrappers.AddSnapServices(info, nil, progress.Null) 707 c.Assert(err, IsNil) 708 709 sysServices = nil 710 userServices = nil 711 712 err = wrappers.StopServices(info.Services(), nil, "", &progress.Null, s.perfTimings) 713 c.Assert(err, IsNil) 714 715 sort.Strings(sysServices) 716 c.Check(sysServices, DeepEquals, []string{ 717 "snap.hello-snap.svc1.service", "snap.hello-snap.svc1.sock1.socket", "snap.hello-snap.svc1.sock2.socket"}) 718 sort.Strings(userServices) 719 c.Check(userServices, DeepEquals, []string{ 720 "snap.hello-snap.svc2.service", "snap.hello-snap.svc2.sock1.socket", "snap.hello-snap.svc2.sock2.socket"}) 721 } 722 723 func (s *servicesTestSuite) TestStartServices(c *C) { 724 info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) 725 svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") 726 727 flags := &wrappers.StartServicesFlags{Enable: true} 728 err := wrappers.StartServices(info.Services(), nil, flags, &progress.Null, s.perfTimings) 729 c.Assert(err, IsNil) 730 731 c.Check(s.sysdLog, DeepEquals, [][]string{ 732 {"enable", filepath.Base(svcFile)}, 733 {"start", filepath.Base(svcFile)}, 734 }) 735 } 736 737 func (s *servicesTestSuite) TestStartServicesUserDaemons(c *C) { 738 info := snaptest.MockSnap(c, packageHello+` 739 svc1: 740 daemon: simple 741 daemon-scope: user 742 `, &snap.SideInfo{Revision: snap.R(12)}) 743 svcFile := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.service") 744 745 flags := &wrappers.StartServicesFlags{Enable: true} 746 err := wrappers.StartServices(info.Services(), nil, flags, &progress.Null, s.perfTimings) 747 c.Assert(err, IsNil) 748 749 c.Assert(s.sysdLog, DeepEquals, [][]string{ 750 {"--user", "--global", "enable", filepath.Base(svcFile)}, 751 {"--user", "start", filepath.Base(svcFile)}, 752 }) 753 } 754 755 func (s *servicesTestSuite) TestStartServicesEnabledConditional(c *C) { 756 info := snaptest.MockSnap(c, packageHello, &snap.SideInfo{Revision: snap.R(12)}) 757 svcFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.service") 758 759 flags := &wrappers.StartServicesFlags{} 760 c.Check(wrappers.StartServices(info.Services(), nil, flags, progress.Null, s.perfTimings), IsNil) 761 c.Check(s.sysdLog, DeepEquals, [][]string{{"start", filepath.Base(svcFile)}}) 762 } 763 764 func (s *servicesTestSuite) TestNoStartDisabledServices(c *C) { 765 svc2Name := "snap.hello-snap.svc2.service" 766 767 info := snaptest.MockSnap(c, packageHello+` 768 svc2: 769 command: bin/hello 770 daemon: simple 771 `, &snap.SideInfo{Revision: snap.R(12)}) 772 773 s.systemctlRestorer() 774 r := testutil.MockCommand(c, "systemctl", `#!/bin/sh 775 if [ "$1" = "--root" ]; then 776 shift 2 777 fi 778 779 case "$1" in 780 start) 781 if [ "$2" = "snap.hello-snap.svc2.service" ]; then 782 exit 0 783 fi 784 echo "unexpected start of service $2" 785 exit 1 786 ;; 787 enable) 788 if [ "$2" = "snap.hello-snap.svc2.service" ]; then 789 exit 0 790 fi 791 echo "unexpected enable of service $2" 792 exit 1 793 ;; 794 *) 795 echo "unexpected call $*" 796 exit 2 797 esac 798 `) 799 defer r.Restore() 800 801 flags := &wrappers.StartServicesFlags{Enable: true} 802 err := wrappers.StartServices(info.Services(), []string{"svc1"}, flags, &progress.Null, s.perfTimings) 803 c.Assert(err, IsNil) 804 c.Assert(r.Calls(), DeepEquals, [][]string{ 805 {"systemctl", "enable", svc2Name}, 806 {"systemctl", "start", svc2Name}, 807 }) 808 } 809 810 func (s *servicesTestSuite) TestAddSnapMultiServicesFailCreateCleanup(c *C) { 811 // sanity check: there are no service files 812 svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) 813 c.Check(svcFiles, HasLen, 0) 814 815 info := snaptest.MockSnap(c, packageHello+` 816 svc2: 817 daemon: potato 818 `, &snap.SideInfo{Revision: snap.R(12)}) 819 820 err := wrappers.AddSnapServices(info, nil, progress.Null) 821 c.Assert(err, ErrorMatches, ".*potato.*") 822 823 // the services are cleaned up 824 svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) 825 c.Check(svcFiles, HasLen, 0) 826 827 // *either* the first service failed validation, and nothing 828 // was done, *or* the second one failed, and the first one was 829 // enabled before the second failed, and disabled after. 830 if len(s.sysdLog) > 0 { 831 // the second service failed validation 832 c.Check(s.sysdLog, DeepEquals, [][]string{ 833 {"daemon-reload"}, 834 }) 835 } 836 } 837 838 func (s *servicesTestSuite) TestMultiServicesFailEnableCleanup(c *C) { 839 var sysdLog [][]string 840 svc1Name := "snap.hello-snap.svc1.service" 841 svc2Name := "snap.hello-snap.svc2.service" 842 numEnables := 0 843 844 // sanity check: there are no service files 845 svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) 846 c.Check(svcFiles, HasLen, 0) 847 848 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 849 sysdLog = append(sysdLog, cmd) 850 sdcmd := cmd[0] 851 if sdcmd == "show" { 852 return []byte("ActiveState=inactive"), nil 853 } 854 if len(cmd) >= 2 { 855 sdcmd = cmd[len(cmd)-2] 856 } 857 switch sdcmd { 858 case "enable": 859 numEnables++ 860 switch numEnables { 861 case 1: 862 if cmd[len(cmd)-1] == svc2Name { 863 // the services are being iterated in the "wrong" order 864 svc1Name, svc2Name = svc2Name, svc1Name 865 } 866 return nil, nil 867 case 2: 868 return nil, fmt.Errorf("failed") 869 default: 870 panic("expected no more than 2 enables") 871 } 872 case "disable", "daemon-reload", "stop": 873 return nil, nil 874 default: 875 panic("unexpected systemctl command " + sdcmd) 876 } 877 }) 878 defer r() 879 880 info := snaptest.MockSnap(c, packageHello+` 881 svc2: 882 command: bin/hello 883 daemon: simple 884 `, &snap.SideInfo{Revision: snap.R(12)}) 885 886 err := wrappers.AddSnapServices(info, nil, progress.Null) 887 c.Assert(err, IsNil) 888 889 svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) 890 c.Check(svcFiles, HasLen, 2) 891 892 flags := &wrappers.StartServicesFlags{Enable: true} 893 err = wrappers.StartServices(info.Services(), nil, flags, progress.Null, s.perfTimings) 894 c.Assert(err, ErrorMatches, "failed") 895 896 c.Check(sysdLog, DeepEquals, [][]string{ 897 {"daemon-reload"}, // from AddSnapServices 898 {"enable", svc1Name}, 899 {"enable", svc2Name}, // this one fails 900 {"disable", svc1Name}, 901 }) 902 } 903 904 func (s *servicesTestSuite) TestAddSnapMultiServicesStartFailOnSystemdReloadCleanup(c *C) { 905 // this test might be overdoing it (it's mostly covering the same ground as the previous one), but ... :-) 906 var sysdLog [][]string 907 908 // sanity check: there are no service files 909 svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) 910 c.Check(svcFiles, HasLen, 0) 911 912 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 913 sysdLog = append(sysdLog, cmd) 914 if cmd[0] == "daemon-reload" { 915 return nil, fmt.Errorf("failed") 916 } 917 c.Fatalf("unexpected systemctl call") 918 return nil, nil 919 920 }) 921 defer r() 922 923 info := snaptest.MockSnap(c, packageHello+` 924 svc2: 925 command: bin/hello 926 daemon: simple 927 `, &snap.SideInfo{Revision: snap.R(12)}) 928 929 err := wrappers.AddSnapServices(info, nil, progress.Null) 930 c.Assert(err, ErrorMatches, "failed") 931 932 // the services are cleaned up 933 svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapServicesDir, "snap.hello-snap.*.service")) 934 c.Check(svcFiles, HasLen, 0) 935 c.Check(sysdLog, DeepEquals, [][]string{ 936 {"daemon-reload"}, // this one fails 937 {"daemon-reload"}, // reload as part of cleanup after removal 938 }) 939 } 940 941 func (s *servicesTestSuite) TestAddSnapMultiUserServicesFailEnableCleanup(c *C) { 942 var sysdLog [][]string 943 944 // sanity check: there are no service files 945 svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service")) 946 c.Check(svcFiles, HasLen, 0) 947 948 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 949 sysdLog = append(sysdLog, cmd) 950 if len(cmd) >= 1 && cmd[0] == "--user" { 951 cmd = cmd[1:] 952 } 953 if len(cmd) >= 1 && cmd[0] == "--global" { 954 cmd = cmd[1:] 955 } 956 sdcmd := cmd[0] 957 if len(cmd) >= 2 { 958 sdcmd = cmd[len(cmd)-2] 959 } 960 switch sdcmd { 961 case "daemon-reload": 962 return nil, fmt.Errorf("failed") 963 default: 964 panic("unexpected systemctl command " + sdcmd) 965 } 966 }) 967 defer r() 968 969 info := snaptest.MockSnap(c, packageHello+` 970 svc1: 971 command: bin/hello 972 daemon: simple 973 daemon-scope: user 974 svc2: 975 command: bin/hello 976 daemon: simple 977 daemon-scope: user 978 `, &snap.SideInfo{Revision: snap.R(12)}) 979 980 err := wrappers.AddSnapServices(info, nil, progress.Null) 981 c.Assert(err, ErrorMatches, "cannot reload daemon: failed") 982 983 // the services are cleaned up 984 svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service")) 985 c.Check(svcFiles, HasLen, 0) 986 c.Check(sysdLog, DeepEquals, [][]string{ 987 {"--user", "daemon-reload"}, 988 {"--user", "daemon-reload"}, 989 }) 990 } 991 992 func (s *servicesTestSuite) TestAddSnapMultiUserServicesStartFailOnSystemdReloadCleanup(c *C) { 993 // this test might be overdoing it (it's mostly covering the same ground as the previous one), but ... :-) 994 var sysdLog [][]string 995 svc1Name := "snap.hello-snap.svc1.service" 996 svc2Name := "snap.hello-snap.svc2.service" 997 998 // sanity check: there are no service files 999 svcFiles, _ := filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service")) 1000 c.Check(svcFiles, HasLen, 0) 1001 1002 first := true 1003 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1004 sysdLog = append(sysdLog, cmd) 1005 if len(cmd) < 3 { 1006 return nil, fmt.Errorf("failed") 1007 } 1008 if first { 1009 first = false 1010 if cmd[len(cmd)-1] == svc2Name { 1011 // the services are being iterated in the "wrong" order 1012 svc1Name, svc2Name = svc2Name, svc1Name 1013 } 1014 } 1015 return nil, nil 1016 1017 }) 1018 defer r() 1019 1020 info := snaptest.MockSnap(c, packageHello+` 1021 svc1: 1022 command: bin/hello 1023 daemon: simple 1024 daemon-scope: user 1025 svc2: 1026 command: bin/hello 1027 daemon: simple 1028 daemon-scope: user 1029 `, &snap.SideInfo{Revision: snap.R(12)}) 1030 1031 err := wrappers.AddSnapServices(info, nil, progress.Null) 1032 c.Assert(err, ErrorMatches, "cannot reload daemon: failed") 1033 1034 // the services are cleaned up 1035 svcFiles, _ = filepath.Glob(filepath.Join(dirs.SnapUserServicesDir, "snap.hello-snap.*.service")) 1036 c.Check(svcFiles, HasLen, 0) 1037 c.Check(sysdLog, DeepEquals, [][]string{ 1038 {"--user", "daemon-reload"}, // this one fails 1039 {"--user", "daemon-reload"}, // so does this one :-) 1040 }) 1041 } 1042 1043 func (s *servicesTestSuite) TestAddSnapSocketFiles(c *C) { 1044 info := snaptest.MockSnap(c, packageHello+` 1045 svc1: 1046 daemon: simple 1047 plugs: [network-bind] 1048 sockets: 1049 sock1: 1050 listen-stream: $SNAP_COMMON/sock1.socket 1051 socket-mode: 0666 1052 sock2: 1053 listen-stream: $SNAP_DATA/sock2.socket 1054 sock3: 1055 listen-stream: $XDG_RUNTIME_DIR/sock3.socket 1056 1057 `, &snap.SideInfo{Revision: snap.R(12)}) 1058 1059 sock1File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock1.socket") 1060 sock2File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock2.socket") 1061 sock3File := filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc1.sock3.socket") 1062 1063 err := wrappers.AddSnapServices(info, nil, progress.Null) 1064 c.Assert(err, IsNil) 1065 1066 expected := fmt.Sprintf( 1067 `[Socket] 1068 Service=snap.hello-snap.svc1.service 1069 FileDescriptorName=sock1 1070 ListenStream=%s 1071 SocketMode=0666 1072 1073 `, filepath.Join(s.tempdir, "/var/snap/hello-snap/common/sock1.socket")) 1074 c.Check(sock1File, testutil.FileContains, expected) 1075 1076 expected = fmt.Sprintf( 1077 `[Socket] 1078 Service=snap.hello-snap.svc1.service 1079 FileDescriptorName=sock2 1080 ListenStream=%s 1081 1082 `, filepath.Join(s.tempdir, "/var/snap/hello-snap/12/sock2.socket")) 1083 c.Check(sock2File, testutil.FileContains, expected) 1084 1085 expected = fmt.Sprintf( 1086 `[Socket] 1087 Service=snap.hello-snap.svc1.service 1088 FileDescriptorName=sock3 1089 ListenStream=%s 1090 1091 `, filepath.Join(s.tempdir, "/run/user/0/snap.hello-snap/sock3.socket")) 1092 c.Check(sock3File, testutil.FileContains, expected) 1093 } 1094 1095 func (s *servicesTestSuite) TestAddSnapUserSocketFiles(c *C) { 1096 info := snaptest.MockSnap(c, packageHello+` 1097 svc1: 1098 daemon: simple 1099 daemon-scope: user 1100 plugs: [network-bind] 1101 sockets: 1102 sock1: 1103 listen-stream: $SNAP_USER_COMMON/sock1.socket 1104 socket-mode: 0666 1105 sock2: 1106 listen-stream: $SNAP_USER_DATA/sock2.socket 1107 sock3: 1108 listen-stream: $XDG_RUNTIME_DIR/sock3.socket 1109 `, &snap.SideInfo{Revision: snap.R(12)}) 1110 1111 sock1File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock1.socket") 1112 sock2File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock2.socket") 1113 sock3File := filepath.Join(s.tempdir, "/etc/systemd/user/snap.hello-snap.svc1.sock3.socket") 1114 1115 err := wrappers.AddSnapServices(info, nil, progress.Null) 1116 c.Assert(err, IsNil) 1117 1118 expected := `[Socket] 1119 Service=snap.hello-snap.svc1.service 1120 FileDescriptorName=sock1 1121 ListenStream=%h/snap/hello-snap/common/sock1.socket 1122 SocketMode=0666 1123 1124 ` 1125 c.Check(sock1File, testutil.FileContains, expected) 1126 1127 expected = `[Socket] 1128 Service=snap.hello-snap.svc1.service 1129 FileDescriptorName=sock2 1130 ListenStream=%h/snap/hello-snap/12/sock2.socket 1131 1132 ` 1133 c.Check(sock2File, testutil.FileContains, expected) 1134 1135 expected = `[Socket] 1136 Service=snap.hello-snap.svc1.service 1137 FileDescriptorName=sock3 1138 ListenStream=%t/snap.hello-snap/sock3.socket 1139 1140 ` 1141 c.Check(sock3File, testutil.FileContains, expected) 1142 } 1143 1144 func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanup(c *C) { 1145 var sysdLog [][]string 1146 svc1Name := "snap.hello-snap.svc1.service" 1147 svc2Name := "snap.hello-snap.svc2.service" 1148 1149 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1150 sysdLog = append(sysdLog, cmd) 1151 if len(cmd) >= 2 && cmd[0] == "start" { 1152 name := cmd[len(cmd)-1] 1153 if name == svc2Name { 1154 return nil, fmt.Errorf("failed") 1155 } 1156 } 1157 return []byte("ActiveState=inactive\n"), nil 1158 }) 1159 defer r() 1160 1161 info := snaptest.MockSnap(c, packageHello+` 1162 svc2: 1163 command: bin/hello 1164 daemon: simple 1165 `, &snap.SideInfo{Revision: snap.R(12)}) 1166 1167 svcs := info.Services() 1168 c.Assert(svcs, HasLen, 2) 1169 if svcs[0].Name == "svc2" { 1170 svcs[0], svcs[1] = svcs[1], svcs[0] 1171 } 1172 1173 flags := &wrappers.StartServicesFlags{Enable: true} 1174 err := wrappers.StartServices(svcs, nil, flags, &progress.Null, s.perfTimings) 1175 c.Assert(err, ErrorMatches, "failed") 1176 c.Assert(sysdLog, HasLen, 10, Commentf("len: %v calls: %v", len(sysdLog), sysdLog)) 1177 c.Check(sysdLog, DeepEquals, [][]string{ 1178 {"enable", svc1Name}, 1179 {"enable", svc2Name}, 1180 {"start", svc1Name}, 1181 {"start", svc2Name}, // one of the services fails 1182 {"stop", svc2Name}, 1183 {"show", "--property=ActiveState", svc2Name}, 1184 {"stop", svc1Name}, 1185 {"show", "--property=ActiveState", svc1Name}, 1186 {"disable", svc1Name}, 1187 {"disable", svc2Name}, 1188 }, Commentf("calls: %v", sysdLog)) 1189 } 1190 1191 func (s *servicesTestSuite) TestStartSnapMultiServicesFailStartCleanupWithSockets(c *C) { 1192 var sysdLog [][]string 1193 svc1Name := "snap.hello-snap.svc1.service" 1194 svc2Name := "snap.hello-snap.svc2.service" 1195 svc2SocketName := "snap.hello-snap.svc2.sock1.socket" 1196 svc3Name := "snap.hello-snap.svc3.service" 1197 svc3SocketName := "snap.hello-snap.svc3.sock1.socket" 1198 1199 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1200 sysdLog = append(sysdLog, cmd) 1201 c.Logf("call: %v", cmd) 1202 if len(cmd) >= 2 && cmd[0] == "start" && cmd[1] == svc3SocketName { 1203 // svc2 socket fails 1204 return nil, fmt.Errorf("failed") 1205 } 1206 return []byte("ActiveState=inactive\n"), nil 1207 }) 1208 defer r() 1209 1210 info := snaptest.MockSnap(c, packageHello+` 1211 svc2: 1212 command: bin/hello 1213 daemon: simple 1214 sockets: 1215 sock1: 1216 listen-stream: $SNAP_COMMON/sock1.socket 1217 socket-mode: 0666 1218 svc3: 1219 command: bin/hello 1220 daemon: simple 1221 sockets: 1222 sock1: 1223 listen-stream: $SNAP_COMMON/sock1.socket 1224 socket-mode: 0666 1225 `, &snap.SideInfo{Revision: snap.R(12)}) 1226 1227 // ensure desired order 1228 apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]} 1229 1230 flags := &wrappers.StartServicesFlags{Enable: true} 1231 err := wrappers.StartServices(apps, nil, flags, &progress.Null, s.perfTimings) 1232 c.Assert(err, ErrorMatches, "failed") 1233 c.Logf("sysdlog: %v", sysdLog) 1234 c.Assert(sysdLog, HasLen, 18, Commentf("len: %v calls: %v", len(sysdLog), sysdLog)) 1235 c.Check(sysdLog, DeepEquals, [][]string{ 1236 {"enable", svc1Name}, 1237 {"enable", svc2SocketName}, 1238 {"start", svc2SocketName}, 1239 {"enable", svc3SocketName}, 1240 {"start", svc3SocketName}, // start failed, what follows is the cleanup 1241 {"stop", svc3SocketName}, 1242 {"show", "--property=ActiveState", svc3SocketName}, 1243 {"stop", svc3Name}, 1244 {"show", "--property=ActiveState", svc3Name}, 1245 {"disable", svc3SocketName}, 1246 {"stop", svc2SocketName}, 1247 {"show", "--property=ActiveState", svc2SocketName}, 1248 {"stop", svc2Name}, 1249 {"show", "--property=ActiveState", svc2Name}, 1250 {"disable", svc2SocketName}, 1251 {"stop", svc1Name}, 1252 {"show", "--property=ActiveState", svc1Name}, 1253 {"disable", svc1Name}, 1254 }, Commentf("calls: %v", sysdLog)) 1255 } 1256 1257 func (s *servicesTestSuite) TestStartSnapMultiUserServicesFailStartCleanup(c *C) { 1258 var sysdLog [][]string 1259 svc1Name := "snap.hello-snap.svc1.service" 1260 svc2Name := "snap.hello-snap.svc2.service" 1261 1262 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1263 sysdLog = append(sysdLog, cmd) 1264 if len(cmd) >= 3 && cmd[0] == "--user" && cmd[1] == "start" { 1265 name := cmd[len(cmd)-1] 1266 if name == svc2Name { 1267 return nil, fmt.Errorf("failed") 1268 } 1269 } 1270 return []byte("ActiveState=inactive\n"), nil 1271 }) 1272 defer r() 1273 1274 info := snaptest.MockSnap(c, packageHello+` 1275 svc1: 1276 command: bin/hello 1277 daemon: simple 1278 daemon-scope: user 1279 svc2: 1280 command: bin/hello 1281 daemon: simple 1282 daemon-scope: user 1283 `, &snap.SideInfo{Revision: snap.R(12)}) 1284 1285 svcs := info.Services() 1286 c.Assert(svcs, HasLen, 2) 1287 if svcs[0].Name == "svc2" { 1288 svcs[0], svcs[1] = svcs[1], svcs[0] 1289 } 1290 flags := &wrappers.StartServicesFlags{Enable: true} 1291 err := wrappers.StartServices(svcs, nil, flags, &progress.Null, s.perfTimings) 1292 c.Assert(err, ErrorMatches, "some user services failed to start") 1293 c.Assert(sysdLog, HasLen, 12, Commentf("len: %v calls: %v", len(sysdLog), sysdLog)) 1294 c.Check(sysdLog, DeepEquals, [][]string{ 1295 {"--user", "--global", "enable", svc1Name}, 1296 {"--user", "--global", "enable", svc2Name}, 1297 {"--user", "start", svc1Name}, 1298 {"--user", "start", svc2Name}, // one of the services fails 1299 // session agent attempts to stop the non-failed services 1300 {"--user", "stop", svc1Name}, 1301 {"--user", "show", "--property=ActiveState", svc1Name}, 1302 // StartServices ensures everything is stopped 1303 {"--user", "stop", svc2Name}, 1304 {"--user", "show", "--property=ActiveState", svc2Name}, 1305 {"--user", "stop", svc1Name}, 1306 {"--user", "show", "--property=ActiveState", svc1Name}, 1307 {"--user", "--global", "disable", svc1Name}, 1308 {"--user", "--global", "disable", svc2Name}, 1309 }, Commentf("calls: %v", sysdLog)) 1310 } 1311 1312 func (s *servicesTestSuite) TestStartSnapServicesKeepsOrder(c *C) { 1313 var sysdLog [][]string 1314 svc1Name := "snap.services-snap.svc1.service" 1315 svc2Name := "snap.services-snap.svc2.service" 1316 svc3Name := "snap.services-snap.svc3.service" 1317 1318 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1319 sysdLog = append(sysdLog, cmd) 1320 return []byte("ActiveState=inactive\n"), nil 1321 }) 1322 defer r() 1323 1324 info := snaptest.MockSnap(c, `name: services-snap 1325 apps: 1326 svc1: 1327 daemon: simple 1328 before: [svc3] 1329 svc2: 1330 daemon: simple 1331 after: [svc1] 1332 svc3: 1333 daemon: simple 1334 before: [svc2] 1335 `, &snap.SideInfo{Revision: snap.R(12)}) 1336 1337 svcs := info.Services() 1338 c.Assert(svcs, HasLen, 3) 1339 1340 sorted, err := snap.SortServices(svcs) 1341 c.Assert(err, IsNil) 1342 1343 flags := &wrappers.StartServicesFlags{Enable: true} 1344 err = wrappers.StartServices(sorted, nil, flags, &progress.Null, s.perfTimings) 1345 c.Assert(err, IsNil) 1346 c.Assert(sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(sysdLog), sysdLog)) 1347 c.Check(sysdLog, DeepEquals, [][]string{ 1348 {"enable", svc1Name}, 1349 {"enable", svc3Name}, 1350 {"enable", svc2Name}, 1351 {"start", svc1Name}, 1352 {"start", svc3Name}, 1353 {"start", svc2Name}, 1354 }, Commentf("calls: %v", sysdLog)) 1355 1356 // change the order 1357 sorted[1], sorted[0] = sorted[0], sorted[1] 1358 1359 // we should observe the calls done in the same order as services 1360 err = wrappers.StartServices(sorted, nil, flags, &progress.Null, s.perfTimings) 1361 c.Assert(err, IsNil) 1362 c.Assert(sysdLog, HasLen, 12, Commentf("len: %v calls: %v", len(sysdLog), sysdLog)) 1363 c.Check(sysdLog[6:], DeepEquals, [][]string{ 1364 {"enable", svc3Name}, 1365 {"enable", svc1Name}, 1366 {"enable", svc2Name}, 1367 {"start", svc3Name}, 1368 {"start", svc1Name}, 1369 {"start", svc2Name}, 1370 }, Commentf("calls: %v", sysdLog)) 1371 } 1372 1373 func (s *servicesTestSuite) TestServiceAfterBefore(c *C) { 1374 snapYaml := packageHello + ` 1375 svc2: 1376 daemon: forking 1377 after: [svc1] 1378 svc3: 1379 daemon: forking 1380 before: [svc4] 1381 after: [svc2] 1382 svc4: 1383 daemon: forking 1384 after: 1385 - svc1 1386 - svc2 1387 - svc3 1388 ` 1389 info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)}) 1390 1391 checks := []struct { 1392 file string 1393 kind string 1394 matches []string 1395 }{{ 1396 file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service"), 1397 kind: "After", 1398 matches: []string{info.Apps["svc1"].ServiceName()}, 1399 }, { 1400 file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"), 1401 kind: "After", 1402 matches: []string{info.Apps["svc2"].ServiceName()}, 1403 }, { 1404 file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"), 1405 kind: "Before", 1406 matches: []string{info.Apps["svc4"].ServiceName()}, 1407 }, { 1408 file: filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"), 1409 kind: "After", 1410 matches: []string{ 1411 info.Apps["svc1"].ServiceName(), 1412 info.Apps["svc2"].ServiceName(), 1413 info.Apps["svc3"].ServiceName(), 1414 }, 1415 }} 1416 1417 err := wrappers.AddSnapServices(info, nil, progress.Null) 1418 c.Assert(err, IsNil) 1419 1420 for _, check := range checks { 1421 content, err := ioutil.ReadFile(check.file) 1422 c.Assert(err, IsNil) 1423 1424 for _, m := range check.matches { 1425 c.Check(string(content), Matches, 1426 // match: 1427 // ... 1428 // After=other.mount some.target foo.service bar.service 1429 // Before=foo.service bar.service 1430 // ... 1431 // but not: 1432 // Foo=something After=foo.service Bar=something else 1433 // or: 1434 // After=foo.service 1435 // bar.service 1436 // or: 1437 // After= foo.service bar.service 1438 "(?ms).*^(?U)"+check.kind+"=.*\\s?"+regexp.QuoteMeta(m)+"\\s?[^=]*$") 1439 } 1440 } 1441 1442 } 1443 1444 func (s *servicesTestSuite) TestServiceWatchdog(c *C) { 1445 snapYaml := packageHello + ` 1446 svc2: 1447 daemon: forking 1448 watchdog-timeout: 12s 1449 svc3: 1450 daemon: forking 1451 watchdog-timeout: 0s 1452 svc4: 1453 daemon: forking 1454 ` 1455 info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)}) 1456 1457 err := wrappers.AddSnapServices(info, nil, progress.Null) 1458 c.Assert(err, IsNil) 1459 1460 content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service")) 1461 c.Assert(err, IsNil) 1462 c.Check(strings.Contains(string(content), "\nWatchdogSec=12\n"), Equals, true) 1463 1464 noWatchdog := []string{ 1465 filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service"), 1466 filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc4.service"), 1467 } 1468 for _, svcPath := range noWatchdog { 1469 content, err := ioutil.ReadFile(svcPath) 1470 c.Assert(err, IsNil) 1471 c.Check(strings.Contains(string(content), "WatchdogSec="), Equals, false) 1472 } 1473 } 1474 1475 func (s *servicesTestSuite) TestStopServiceEndure(c *C) { 1476 const surviveYaml = `name: survive-snap 1477 version: 1.0 1478 apps: 1479 survivor: 1480 command: bin/survivor 1481 refresh-mode: endure 1482 daemon: simple 1483 ` 1484 info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)}) 1485 survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.survivor.service") 1486 1487 err := wrappers.AddSnapServices(info, nil, progress.Null) 1488 c.Assert(err, IsNil) 1489 c.Check(s.sysdLog, DeepEquals, [][]string{ 1490 {"daemon-reload"}, 1491 }) 1492 s.sysdLog = nil 1493 1494 apps := []*snap.AppInfo{info.Apps["survivor"]} 1495 flags := &wrappers.StartServicesFlags{Enable: true} 1496 err = wrappers.StartServices(apps, nil, flags, progress.Null, s.perfTimings) 1497 c.Assert(err, IsNil) 1498 1499 c.Check(s.sysdLog, DeepEquals, [][]string{ 1500 {"enable", filepath.Base(survivorFile)}, 1501 {"start", filepath.Base(survivorFile)}, 1502 }) 1503 1504 s.sysdLog = nil 1505 err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRefresh, progress.Null, s.perfTimings) 1506 c.Assert(err, IsNil) 1507 c.Assert(s.sysdLog, HasLen, 0) 1508 1509 s.sysdLog = nil 1510 err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRemove, progress.Null, s.perfTimings) 1511 c.Assert(err, IsNil) 1512 c.Check(s.sysdLog, DeepEquals, [][]string{ 1513 {"stop", filepath.Base(survivorFile)}, 1514 {"show", "--property=ActiveState", "snap.survive-snap.survivor.service"}, 1515 }) 1516 1517 } 1518 1519 func (s *servicesTestSuite) TestStopServiceSigs(c *C) { 1520 r := wrappers.MockKillWait(1 * time.Millisecond) 1521 defer r() 1522 1523 survivorFile := filepath.Join(s.tempdir, "/etc/systemd/system/snap.survive-snap.srv.service") 1524 for _, t := range []struct { 1525 mode string 1526 expectedSig string 1527 expectedWho string 1528 }{ 1529 {mode: "sigterm", expectedSig: "TERM", expectedWho: "main"}, 1530 {mode: "sigterm-all", expectedSig: "TERM", expectedWho: "all"}, 1531 {mode: "sighup", expectedSig: "HUP", expectedWho: "main"}, 1532 {mode: "sighup-all", expectedSig: "HUP", expectedWho: "all"}, 1533 {mode: "sigusr1", expectedSig: "USR1", expectedWho: "main"}, 1534 {mode: "sigusr1-all", expectedSig: "USR1", expectedWho: "all"}, 1535 {mode: "sigusr2", expectedSig: "USR2", expectedWho: "main"}, 1536 {mode: "sigusr2-all", expectedSig: "USR2", expectedWho: "all"}, 1537 } { 1538 surviveYaml := fmt.Sprintf(`name: survive-snap 1539 version: 1.0 1540 apps: 1541 srv: 1542 command: bin/survivor 1543 stop-mode: %s 1544 daemon: simple 1545 `, t.mode) 1546 info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)}) 1547 1548 s.sysdLog = nil 1549 err := wrappers.AddSnapServices(info, nil, progress.Null) 1550 c.Assert(err, IsNil) 1551 1552 c.Check(s.sysdLog, DeepEquals, [][]string{ 1553 {"daemon-reload"}, 1554 }) 1555 s.sysdLog = nil 1556 1557 var apps []*snap.AppInfo 1558 for _, a := range info.Apps { 1559 apps = append(apps, a) 1560 } 1561 flags := &wrappers.StartServicesFlags{Enable: true} 1562 err = wrappers.StartServices(apps, nil, flags, progress.Null, s.perfTimings) 1563 c.Assert(err, IsNil) 1564 c.Check(s.sysdLog, DeepEquals, [][]string{ 1565 {"enable", filepath.Base(survivorFile)}, 1566 {"start", filepath.Base(survivorFile)}, 1567 }) 1568 1569 s.sysdLog = nil 1570 err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRefresh, progress.Null, s.perfTimings) 1571 c.Assert(err, IsNil) 1572 c.Check(s.sysdLog, DeepEquals, [][]string{ 1573 {"stop", filepath.Base(survivorFile)}, 1574 {"show", "--property=ActiveState", "snap.survive-snap.srv.service"}, 1575 }, Commentf("failure in %s", t.mode)) 1576 1577 s.sysdLog = nil 1578 err = wrappers.StopServices(info.Services(), nil, snap.StopReasonRemove, progress.Null, s.perfTimings) 1579 c.Assert(err, IsNil) 1580 switch t.expectedWho { 1581 case "all": 1582 c.Check(s.sysdLog, DeepEquals, [][]string{ 1583 {"stop", filepath.Base(survivorFile)}, 1584 {"show", "--property=ActiveState", "snap.survive-snap.srv.service"}, 1585 }) 1586 case "main": 1587 c.Check(s.sysdLog, DeepEquals, [][]string{ 1588 {"stop", filepath.Base(survivorFile)}, 1589 {"show", "--property=ActiveState", "snap.survive-snap.srv.service"}, 1590 {"kill", filepath.Base(survivorFile), "-s", "TERM", "--kill-who=all"}, 1591 {"kill", filepath.Base(survivorFile), "-s", "KILL", "--kill-who=all"}, 1592 }) 1593 default: 1594 panic("not reached") 1595 } 1596 } 1597 1598 } 1599 1600 func (s *servicesTestSuite) TestStartSnapSocketEnableStart(c *C) { 1601 svc1Name := "snap.hello-snap.svc1.service" 1602 // svc2Name := "snap.hello-snap.svc2.service" 1603 svc2Sock := "snap.hello-snap.svc2.sock.socket" 1604 svc3Sock := "snap.hello-snap.svc3.sock.socket" 1605 1606 info := snaptest.MockSnap(c, packageHello+` 1607 svc2: 1608 command: bin/hello 1609 daemon: simple 1610 sockets: 1611 sock: 1612 listen-stream: $SNAP_COMMON/sock1.socket 1613 svc3: 1614 command: bin/hello 1615 daemon: simple 1616 daemon-scope: user 1617 sockets: 1618 sock: 1619 listen-stream: $SNAP_USER_COMMON/sock1.socket 1620 `, &snap.SideInfo{Revision: snap.R(12)}) 1621 1622 // fix the apps order to make the test stable 1623 apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]} 1624 flags := &wrappers.StartServicesFlags{Enable: true} 1625 err := wrappers.StartServices(apps, nil, flags, &progress.Null, s.perfTimings) 1626 c.Assert(err, IsNil) 1627 c.Assert(s.sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog)) 1628 c.Check(s.sysdLog, DeepEquals, [][]string{ 1629 {"enable", svc1Name}, 1630 {"enable", svc2Sock}, 1631 {"start", svc2Sock}, 1632 {"--user", "--global", "enable", svc3Sock}, 1633 {"--user", "start", svc3Sock}, 1634 {"start", svc1Name}, 1635 }, Commentf("calls: %v", s.sysdLog)) 1636 } 1637 1638 func (s *servicesTestSuite) TestStartSnapTimerEnableStart(c *C) { 1639 svc1Name := "snap.hello-snap.svc1.service" 1640 // svc2Name := "snap.hello-snap.svc2.service" 1641 svc2Timer := "snap.hello-snap.svc2.timer" 1642 svc3Timer := "snap.hello-snap.svc3.timer" 1643 1644 info := snaptest.MockSnap(c, packageHello+` 1645 svc2: 1646 command: bin/hello 1647 daemon: simple 1648 timer: 10:00-12:00 1649 svc3: 1650 command: bin/hello 1651 daemon: simple 1652 daemon-scope: user 1653 timer: 10:00-12:00 1654 `, &snap.SideInfo{Revision: snap.R(12)}) 1655 1656 // fix the apps order to make the test stable 1657 apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]} 1658 flags := &wrappers.StartServicesFlags{Enable: true} 1659 err := wrappers.StartServices(apps, nil, flags, &progress.Null, s.perfTimings) 1660 c.Assert(err, IsNil) 1661 c.Assert(s.sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog)) 1662 c.Check(s.sysdLog, DeepEquals, [][]string{ 1663 {"enable", svc1Name}, 1664 {"enable", svc2Timer}, 1665 {"start", svc2Timer}, 1666 {"--user", "--global", "enable", svc3Timer}, 1667 {"--user", "start", svc3Timer}, 1668 {"start", svc1Name}, 1669 }, Commentf("calls: %v", s.sysdLog)) 1670 } 1671 1672 func (s *servicesTestSuite) TestStartSnapTimerCleanup(c *C) { 1673 var sysdLog [][]string 1674 svc1Name := "snap.hello-snap.svc1.service" 1675 svc2Name := "snap.hello-snap.svc2.service" 1676 svc2Timer := "snap.hello-snap.svc2.timer" 1677 1678 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1679 sysdLog = append(sysdLog, cmd) 1680 if len(cmd) >= 2 && cmd[0] == "start" && cmd[1] == svc2Timer { 1681 return nil, fmt.Errorf("failed") 1682 } 1683 return []byte("ActiveState=inactive\n"), nil 1684 }) 1685 defer r() 1686 1687 info := snaptest.MockSnap(c, packageHello+` 1688 svc2: 1689 command: bin/hello 1690 daemon: simple 1691 timer: 10:00-12:00 1692 `, &snap.SideInfo{Revision: snap.R(12)}) 1693 1694 // fix the apps order to make the test stable 1695 apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"]} 1696 flags := &wrappers.StartServicesFlags{Enable: true} 1697 err := wrappers.StartServices(apps, nil, flags, &progress.Null, s.perfTimings) 1698 c.Assert(err, ErrorMatches, "failed") 1699 c.Assert(sysdLog, HasLen, 11, Commentf("len: %v calls: %v", len(sysdLog), sysdLog)) 1700 c.Check(sysdLog, DeepEquals, [][]string{ 1701 {"enable", svc1Name}, 1702 {"enable", svc2Timer}, 1703 {"start", svc2Timer}, // this call fails 1704 {"stop", svc2Timer}, 1705 {"show", "--property=ActiveState", svc2Timer}, 1706 {"stop", svc2Name}, 1707 {"show", "--property=ActiveState", svc2Name}, 1708 {"disable", svc2Timer}, 1709 {"stop", svc1Name}, 1710 {"show", "--property=ActiveState", svc1Name}, 1711 {"disable", svc1Name}, 1712 }, Commentf("calls: %v", sysdLog)) 1713 } 1714 1715 func (s *servicesTestSuite) TestAddRemoveSnapWithTimersAddsRemovesTimerFiles(c *C) { 1716 info := snaptest.MockSnap(c, packageHello+` 1717 svc2: 1718 command: bin/hello 1719 daemon: simple 1720 timer: 10:00-12:00 1721 `, &snap.SideInfo{Revision: snap.R(12)}) 1722 1723 err := wrappers.AddSnapServices(info, nil, progress.Null) 1724 c.Assert(err, IsNil) 1725 1726 app := info.Apps["svc2"] 1727 c.Assert(app.Timer, NotNil) 1728 1729 c.Check(osutil.FileExists(app.Timer.File()), Equals, true) 1730 c.Check(osutil.FileExists(app.ServiceFile()), Equals, true) 1731 1732 err = wrappers.StopServices(info.Services(), nil, "", &progress.Null, s.perfTimings) 1733 c.Assert(err, IsNil) 1734 1735 err = wrappers.RemoveSnapServices(info, &progress.Null) 1736 c.Assert(err, IsNil) 1737 1738 c.Check(osutil.FileExists(app.Timer.File()), Equals, false) 1739 c.Check(osutil.FileExists(app.ServiceFile()), Equals, false) 1740 } 1741 1742 func (s *servicesTestSuite) TestFailedAddSnapCleansUp(c *C) { 1743 info := snaptest.MockSnap(c, packageHello+` 1744 svc2: 1745 command: bin/hello 1746 daemon: simple 1747 timer: 10:00-12:00 1748 svc3: 1749 command: bin/hello 1750 daemon: simple 1751 plugs: [network-bind] 1752 sockets: 1753 sock1: 1754 listen-stream: $SNAP_COMMON/sock1.socket 1755 socket-mode: 0666 1756 `, &snap.SideInfo{Revision: snap.R(12)}) 1757 1758 calls := 0 1759 r := systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 1760 if len(cmd) == 1 && cmd[0] == "daemon-reload" && calls == 0 { 1761 // only fail the first systemd daemon-reload call, the 1762 // second one is at the end of cleanup 1763 calls += 1 1764 return nil, fmt.Errorf("failed") 1765 } 1766 return []byte("ActiveState=inactive\n"), nil 1767 }) 1768 defer r() 1769 1770 err := wrappers.AddSnapServices(info, nil, progress.Null) 1771 c.Assert(err, NotNil) 1772 1773 c.Logf("services dir: %v", dirs.SnapServicesDir) 1774 matches, err := filepath.Glob(dirs.SnapServicesDir + "/*") 1775 c.Assert(err, IsNil) 1776 c.Assert(matches, HasLen, 0, Commentf("the following autogenerated files were left behind: %v", matches)) 1777 } 1778 1779 func (s *servicesTestSuite) TestAddServicesDidReload(c *C) { 1780 const base = `name: hello-snap 1781 version: 1.10 1782 summary: hello 1783 description: Hello... 1784 apps: 1785 ` 1786 onlyServices := snaptest.MockSnap(c, base+` 1787 svc1: 1788 command: bin/hello 1789 daemon: simple 1790 `, &snap.SideInfo{Revision: snap.R(12)}) 1791 1792 onlySockets := snaptest.MockSnap(c, base+` 1793 svc1: 1794 command: bin/hello 1795 daemon: simple 1796 plugs: [network-bind] 1797 sockets: 1798 sock1: 1799 listen-stream: $SNAP_COMMON/sock1.socket 1800 socket-mode: 0666 1801 `, &snap.SideInfo{Revision: snap.R(12)}) 1802 1803 onlyTimers := snaptest.MockSnap(c, base+` 1804 svc1: 1805 command: bin/hello 1806 daemon: oneshot 1807 timer: 10:00-12:00 1808 `, &snap.SideInfo{Revision: snap.R(12)}) 1809 1810 for i, info := range []*snap.Info{onlyServices, onlySockets, onlyTimers} { 1811 s.sysdLog = nil 1812 err := wrappers.AddSnapServices(info, nil, progress.Null) 1813 c.Assert(err, IsNil) 1814 reloads := 0 1815 c.Logf("calls: %v", s.sysdLog) 1816 for _, call := range s.sysdLog { 1817 if strutil.ListContains(call, "daemon-reload") { 1818 reloads += 1 1819 } 1820 } 1821 c.Check(reloads >= 1, Equals, true, Commentf("test-case %v did not reload services as expected", i)) 1822 } 1823 } 1824 1825 func (s *servicesTestSuite) TestSnapServicesActivation(c *C) { 1826 const snapYaml = `name: hello-snap 1827 version: 1.10 1828 summary: hello 1829 description: Hello... 1830 apps: 1831 svc1: 1832 command: bin/hello 1833 daemon: simple 1834 plugs: [network-bind] 1835 sockets: 1836 sock1: 1837 listen-stream: $SNAP_COMMON/sock1.socket 1838 socket-mode: 0666 1839 svc2: 1840 command: bin/hello 1841 daemon: oneshot 1842 timer: 10:00-12:00 1843 svc3: 1844 command: bin/hello 1845 daemon: simple 1846 ` 1847 svc1Socket := "snap.hello-snap.svc1.sock1.socket" 1848 svc2Timer := "snap.hello-snap.svc2.timer" 1849 svc3Name := "snap.hello-snap.svc3.service" 1850 1851 info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)}) 1852 1853 // fix the apps order to make the test stable 1854 err := wrappers.AddSnapServices(info, nil, progress.Null) 1855 c.Assert(err, IsNil) 1856 c.Check(s.sysdLog, DeepEquals, [][]string{ 1857 {"daemon-reload"}, 1858 }) 1859 s.sysdLog = nil 1860 1861 apps := []*snap.AppInfo{info.Apps["svc1"], info.Apps["svc2"], info.Apps["svc3"]} 1862 flags := &wrappers.StartServicesFlags{Enable: true} 1863 err = wrappers.StartServices(apps, nil, flags, progress.Null, s.perfTimings) 1864 c.Assert(err, IsNil) 1865 1866 c.Assert(s.sysdLog, HasLen, 6, Commentf("len: %v calls: %v", len(s.sysdLog), s.sysdLog)) 1867 c.Check(s.sysdLog, DeepEquals, [][]string{ 1868 {"enable", svc3Name}, 1869 {"enable", svc1Socket}, 1870 {"start", svc1Socket}, 1871 {"enable", svc2Timer}, 1872 {"start", svc2Timer}, 1873 {"start", svc3Name}, 1874 }, Commentf("calls: %v", s.sysdLog)) 1875 } 1876 1877 func (s *servicesTestSuite) TestServiceRestartDelay(c *C) { 1878 snapYaml := packageHello + ` 1879 svc2: 1880 daemon: forking 1881 restart-delay: 12s 1882 svc3: 1883 daemon: forking 1884 ` 1885 info := snaptest.MockSnap(c, snapYaml, &snap.SideInfo{Revision: snap.R(12)}) 1886 1887 err := wrappers.AddSnapServices(info, nil, progress.Null) 1888 c.Assert(err, IsNil) 1889 1890 content, err := ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc2.service")) 1891 c.Assert(err, IsNil) 1892 c.Check(strings.Contains(string(content), "\nRestartSec=12\n"), Equals, true) 1893 1894 content, err = ioutil.ReadFile(filepath.Join(s.tempdir, "/etc/systemd/system/snap.hello-snap.svc3.service")) 1895 c.Assert(err, IsNil) 1896 c.Check(strings.Contains(string(content), "RestartSec="), Equals, false) 1897 } 1898 1899 func (s *servicesTestSuite) TestAddRemoveSnapServiceWithSnapd(c *C) { 1900 info := makeMockSnapdSnap(c) 1901 1902 err := wrappers.AddSnapServices(info, nil, progress.Null) 1903 c.Check(err, ErrorMatches, "internal error: adding explicit services for snapd snap is unexpected") 1904 1905 err = wrappers.RemoveSnapServices(info, progress.Null) 1906 c.Check(err, ErrorMatches, "internal error: removing explicit services for snapd snap is unexpected") 1907 } 1908 1909 func (s *servicesTestSuite) TestReloadOrRestart(c *C) { 1910 const surviveYaml = `name: test-snap 1911 version: 1.0 1912 apps: 1913 foo: 1914 command: bin/foo 1915 daemon: simple 1916 ` 1917 info := snaptest.MockSnap(c, surviveYaml, &snap.SideInfo{Revision: snap.R(1)}) 1918 srvFile := "snap.test-snap.foo.service" 1919 1920 err := wrappers.AddSnapServices(info, nil, progress.Null) 1921 c.Assert(err, IsNil) 1922 1923 s.sysdLog = nil 1924 flags := &wrappers.RestartServicesFlags{Reload: true} 1925 c.Assert(wrappers.RestartServices(info.Services(), flags, progress.Null, s.perfTimings), IsNil) 1926 c.Assert(err, IsNil) 1927 c.Check(s.sysdLog, DeepEquals, [][]string{ 1928 {"reload-or-restart", srvFile}, 1929 }) 1930 1931 s.sysdLog = nil 1932 flags.Reload = false 1933 c.Assert(wrappers.RestartServices(info.Services(), flags, progress.Null, s.perfTimings), IsNil) 1934 c.Check(s.sysdLog, DeepEquals, [][]string{ 1935 {"stop", srvFile}, 1936 {"show", "--property=ActiveState", srvFile}, 1937 {"start", srvFile}, 1938 }) 1939 1940 s.sysdLog = nil 1941 c.Assert(wrappers.RestartServices(info.Services(), nil, progress.Null, s.perfTimings), IsNil) 1942 c.Check(s.sysdLog, DeepEquals, [][]string{ 1943 {"stop", srvFile}, 1944 {"show", "--property=ActiveState", srvFile}, 1945 {"start", srvFile}, 1946 }) 1947 } 1948 1949 func (s *servicesTestSuite) TestStopAndDisableServices(c *C) { 1950 info := snaptest.MockSnap(c, packageHello+` 1951 svc1: 1952 daemon: simple 1953 `, &snap.SideInfo{Revision: snap.R(12)}) 1954 svcFile := "snap.hello-snap.svc1.service" 1955 1956 err := wrappers.AddSnapServices(info, nil, progress.Null) 1957 c.Assert(err, IsNil) 1958 1959 s.sysdLog = nil 1960 flags := &wrappers.StopServicesFlags{Disable: true} 1961 err = wrappers.StopServices(info.Services(), flags, "", progress.Null, s.perfTimings) 1962 c.Assert(err, IsNil) 1963 c.Check(s.sysdLog, DeepEquals, [][]string{ 1964 {"stop", svcFile}, 1965 {"show", "--property=ActiveState", svcFile}, 1966 {"disable", svcFile}, 1967 }) 1968 }