github.com/rigado/snapd@v2.42.5-go-mod+incompatible/wrappers/services_gen_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 "os" 25 "os/exec" 26 "path/filepath" 27 "strings" 28 "time" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/snap" 34 "github.com/snapcore/snapd/snap/snaptest" 35 "github.com/snapcore/snapd/testutil" 36 "github.com/snapcore/snapd/timeout" 37 "github.com/snapcore/snapd/timeutil" 38 "github.com/snapcore/snapd/wrappers" 39 ) 40 41 type servicesWrapperGenSuite struct { 42 testutil.BaseTest 43 } 44 45 var _ = Suite(&servicesWrapperGenSuite{}) 46 47 const expectedServiceFmt = `[Unit] 48 # Auto-generated, DO NOT EDIT 49 Description=Service for snap application snap.app 50 Requires=%s-snap-44.mount 51 Wants=network.target 52 After=%s-snap-44.mount network.target 53 X-Snappy=yes 54 55 [Service] 56 ExecStart=/usr/bin/snap run snap.app 57 SyslogIdentifier=snap.app 58 Restart=%s 59 WorkingDirectory=/var/snap/snap/44 60 ExecStop=/usr/bin/snap run --command=stop snap.app 61 ExecReload=/usr/bin/snap run --command=reload snap.app 62 ExecStopPost=/usr/bin/snap run --command=post-stop snap.app 63 TimeoutStopSec=10 64 Type=%s 65 66 [Install] 67 WantedBy=multi-user.target 68 ` 69 70 var ( 71 mountUnitPrefix = strings.Replace(dirs.SnapMountDir[1:], "/", "-", -1) 72 ) 73 74 var ( 75 expectedAppService = fmt.Sprintf(expectedServiceFmt, mountUnitPrefix, mountUnitPrefix, "on-failure", "simple") 76 expectedDbusService = fmt.Sprintf(expectedServiceFmt, mountUnitPrefix, mountUnitPrefix, "on-failure", "dbus\nBusName=foo.bar.baz") 77 expectedOneshotService = fmt.Sprintf(expectedServiceFmt, mountUnitPrefix, mountUnitPrefix, "no", "oneshot\nRemainAfterExit=yes") 78 ) 79 80 var ( 81 expectedServiceWrapperFmt = `[Unit] 82 # Auto-generated, DO NOT EDIT 83 Description=Service for snap application xkcd-webserver.xkcd-webserver 84 Requires=%s-xkcd\x2dwebserver-44.mount 85 Wants=network.target 86 After=%s-xkcd\x2dwebserver-44.mount network.target 87 X-Snappy=yes 88 89 [Service] 90 ExecStart=/usr/bin/snap run xkcd-webserver 91 SyslogIdentifier=xkcd-webserver.xkcd-webserver 92 Restart=on-failure 93 WorkingDirectory=/var/snap/xkcd-webserver/44 94 ExecStop=/usr/bin/snap run --command=stop xkcd-webserver 95 ExecReload=/usr/bin/snap run --command=reload xkcd-webserver 96 ExecStopPost=/usr/bin/snap run --command=post-stop xkcd-webserver 97 TimeoutStopSec=30 98 Type=%s 99 %s` 100 expectedTypeForkingWrapper = fmt.Sprintf(expectedServiceWrapperFmt, mountUnitPrefix, mountUnitPrefix, "forking", "\n[Install]\nWantedBy=multi-user.target\n") 101 ) 102 103 func (s *servicesWrapperGenSuite) SetUpTest(c *C) { 104 s.BaseTest.SetUpTest(c) 105 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 106 } 107 108 func (s *servicesWrapperGenSuite) TearDownTest(c *C) { 109 s.BaseTest.TearDownTest(c) 110 } 111 112 func (s *servicesWrapperGenSuite) TestGenerateSnapServiceFile(c *C) { 113 yamlText := ` 114 name: snap 115 version: 1.0 116 apps: 117 app: 118 command: bin/start 119 stop-command: bin/stop 120 reload-command: bin/reload 121 post-stop-command: bin/stop --post 122 stop-timeout: 10s 123 daemon: simple 124 ` 125 info, err := snap.InfoFromSnapYaml([]byte(yamlText)) 126 c.Assert(err, IsNil) 127 info.Revision = snap.R(44) 128 app := info.Apps["app"] 129 130 generatedWrapper, err := wrappers.GenerateSnapServiceFile(app) 131 c.Assert(err, IsNil) 132 c.Check(string(generatedWrapper), Equals, expectedAppService) 133 } 134 135 func (s *servicesWrapperGenSuite) TestGenerateSnapServiceFileWithStartTimeout(c *C) { 136 yamlText := ` 137 name: snap 138 version: 1.0 139 apps: 140 app: 141 command: bin/start 142 start-timeout: 10m 143 daemon: simple 144 ` 145 info, err := snap.InfoFromSnapYaml([]byte(yamlText)) 146 c.Assert(err, IsNil) 147 info.Revision = snap.R(44) 148 app := info.Apps["app"] 149 150 generatedWrapper, err := wrappers.GenerateSnapServiceFile(app) 151 c.Assert(err, IsNil) 152 c.Check(string(generatedWrapper), testutil.Contains, "\nTimeoutStartSec=600\n") 153 } 154 155 func (s *servicesWrapperGenSuite) TestGenerateSnapServiceFileRestart(c *C) { 156 yamlTextTemplate := ` 157 name: snap 158 apps: 159 app: 160 daemon: simple 161 restart-condition: %s 162 ` 163 for name, cond := range snap.RestartMap { 164 yamlText := fmt.Sprintf(yamlTextTemplate, cond) 165 166 info, err := snap.InfoFromSnapYaml([]byte(yamlText)) 167 c.Assert(err, IsNil) 168 info.Revision = snap.R(44) 169 app := info.Apps["app"] 170 171 generatedWrapper, err := wrappers.GenerateSnapServiceFile(app) 172 c.Assert(err, IsNil) 173 wrapperText := string(generatedWrapper) 174 if cond == snap.RestartNever { 175 c.Check(wrapperText, Matches, 176 `(?ms).*^Restart=no$.*`, Commentf(name)) 177 } else { 178 c.Check(wrapperText, Matches, 179 `(?ms).*^Restart=`+name+`$.*`, Commentf(name)) 180 } 181 } 182 } 183 184 func (s *servicesWrapperGenSuite) TestGenerateSnapServiceFileTypeForking(c *C) { 185 service := &snap.AppInfo{ 186 Snap: &snap.Info{ 187 SuggestedName: "xkcd-webserver", 188 Version: "0.3.4", 189 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 190 }, 191 Name: "xkcd-webserver", 192 Command: "bin/foo start", 193 StopCommand: "bin/foo stop", 194 ReloadCommand: "bin/foo reload", 195 PostStopCommand: "bin/foo post-stop", 196 StopTimeout: timeout.DefaultTimeout, 197 Daemon: "forking", 198 } 199 200 generatedWrapper, err := wrappers.GenerateSnapServiceFile(service) 201 c.Assert(err, IsNil) 202 c.Assert(string(generatedWrapper), Equals, expectedTypeForkingWrapper) 203 } 204 205 func (s *servicesWrapperGenSuite) TestGenerateSnapServiceFileIllegalChars(c *C) { 206 service := &snap.AppInfo{ 207 Snap: &snap.Info{ 208 SuggestedName: "xkcd-webserver", 209 Version: "0.3.4", 210 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 211 }, 212 Name: "xkcd-webserver", 213 Command: "bin/foo start\n", 214 StopCommand: "bin/foo stop", 215 ReloadCommand: "bin/foo reload", 216 PostStopCommand: "bin/foo post-stop", 217 StopTimeout: timeout.DefaultTimeout, 218 Daemon: "simple", 219 } 220 221 _, err := wrappers.GenerateSnapServiceFile(service) 222 c.Assert(err, NotNil) 223 } 224 225 func (s *servicesWrapperGenSuite) TestGenServiceFileWithBusName(c *C) { 226 227 yamlText := ` 228 name: snap 229 version: 1.0 230 apps: 231 app: 232 command: bin/start 233 stop-command: bin/stop 234 reload-command: bin/reload 235 post-stop-command: bin/stop --post 236 stop-timeout: 10s 237 bus-name: foo.bar.baz 238 daemon: dbus 239 ` 240 241 info, err := snap.InfoFromSnapYaml([]byte(yamlText)) 242 c.Assert(err, IsNil) 243 info.Revision = snap.R(44) 244 app := info.Apps["app"] 245 246 generatedWrapper, err := wrappers.GenerateSnapServiceFile(app) 247 c.Assert(err, IsNil) 248 249 c.Assert(string(generatedWrapper), Equals, expectedDbusService) 250 } 251 252 func (s *servicesWrapperGenSuite) TestGenOneshotServiceFile(c *C) { 253 254 info := snaptest.MockInfo(c, ` 255 name: snap 256 version: 1.0 257 apps: 258 app: 259 command: bin/start 260 stop-command: bin/stop 261 reload-command: bin/reload 262 post-stop-command: bin/stop --post 263 stop-timeout: 10s 264 daemon: oneshot 265 `, &snap.SideInfo{Revision: snap.R(44)}) 266 267 app := info.Apps["app"] 268 269 generatedWrapper, err := wrappers.GenerateSnapServiceFile(app) 270 c.Assert(err, IsNil) 271 272 c.Assert(string(generatedWrapper), Equals, expectedOneshotService) 273 } 274 275 func (s *servicesWrapperGenSuite) TestGenerateSnapServiceWithSockets(c *C) { 276 const sock1ExpectedFmt = `[Unit] 277 # Auto-generated, DO NOT EDIT 278 Description=Socket sock1 for snap application some-snap.app 279 Requires=%s-some\x2dsnap-44.mount 280 After=%s-some\x2dsnap-44.mount 281 X-Snappy=yes 282 283 [Socket] 284 Service=snap.some-snap.app.service 285 FileDescriptorName=sock1 286 ListenStream=%s/sock1.socket 287 SocketMode=0666 288 289 [Install] 290 WantedBy=sockets.target 291 ` 292 const sock2ExpectedFmt = `[Unit] 293 # Auto-generated, DO NOT EDIT 294 Description=Socket sock2 for snap application some-snap.app 295 Requires=%s-some\x2dsnap-44.mount 296 After=%s-some\x2dsnap-44.mount 297 X-Snappy=yes 298 299 [Socket] 300 Service=snap.some-snap.app.service 301 FileDescriptorName=sock2 302 ListenStream=%s/sock2.socket 303 304 [Install] 305 WantedBy=sockets.target 306 ` 307 308 si := &snap.Info{ 309 SuggestedName: "some-snap", 310 Version: "1.0", 311 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 312 } 313 service := &snap.AppInfo{ 314 Snap: si, 315 Name: "app", 316 Command: "bin/foo start", 317 Daemon: "simple", 318 Plugs: map[string]*snap.PlugInfo{"network-bind": {}}, 319 Sockets: map[string]*snap.SocketInfo{ 320 "sock1": { 321 Name: "sock1", 322 ListenStream: "$SNAP_DATA/sock1.socket", 323 SocketMode: 0666, 324 }, 325 "sock2": { 326 Name: "sock2", 327 ListenStream: "$SNAP_DATA/sock2.socket", 328 }, 329 }, 330 } 331 service.Sockets["sock1"].App = service 332 service.Sockets["sock2"].App = service 333 334 sock1Path := filepath.Join(dirs.SnapServicesDir, "snap.some-snap.app.sock1.socket") 335 sock2Path := filepath.Join(dirs.SnapServicesDir, "snap.some-snap.app.sock2.socket") 336 sock1Expected := fmt.Sprintf(sock1ExpectedFmt, mountUnitPrefix, mountUnitPrefix, si.DataDir()) 337 sock2Expected := fmt.Sprintf(sock2ExpectedFmt, mountUnitPrefix, mountUnitPrefix, si.DataDir()) 338 339 generatedWrapper, err := wrappers.GenerateSnapServiceFile(service) 340 c.Assert(err, IsNil) 341 c.Assert(strings.Contains(string(generatedWrapper), "[Install]"), Equals, false) 342 c.Assert(strings.Contains(string(generatedWrapper), "WantedBy=multi-user.target"), Equals, false) 343 344 generatedSockets, err := wrappers.GenerateSnapSocketFiles(service) 345 c.Assert(err, IsNil) 346 c.Assert(generatedSockets, Not(IsNil)) 347 c.Assert(*generatedSockets, HasLen, 2) 348 c.Assert(*generatedSockets, DeepEquals, map[string][]byte{ 349 sock1Path: []byte(sock1Expected), 350 sock2Path: []byte(sock2Expected), 351 }) 352 } 353 354 func (s *servicesWrapperGenSuite) TestServiceAfterBefore(c *C) { 355 const expectedServiceFmt = `[Unit] 356 # Auto-generated, DO NOT EDIT 357 Description=Service for snap application snap.app 358 Requires=%s-snap-44.mount 359 Wants=network.target 360 After=%s-snap-44.mount network.target %s 361 Before=%s 362 X-Snappy=yes 363 364 [Service] 365 ExecStart=/usr/bin/snap run snap.app 366 SyslogIdentifier=snap.app 367 Restart=%s 368 WorkingDirectory=/var/snap/snap/44 369 TimeoutStopSec=30 370 Type=%s 371 372 [Install] 373 WantedBy=multi-user.target 374 ` 375 376 service := &snap.AppInfo{ 377 Snap: &snap.Info{ 378 SuggestedName: "snap", 379 Version: "0.3.4", 380 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 381 Apps: map[string]*snap.AppInfo{ 382 "foo": { 383 Name: "foo", 384 Snap: &snap.Info{SuggestedName: "snap"}, 385 Daemon: "forking", 386 }, 387 "bar": { 388 Name: "bar", 389 Snap: &snap.Info{SuggestedName: "snap"}, 390 Daemon: "forking", 391 }, 392 "zed": { 393 Name: "zed", 394 Snap: &snap.Info{SuggestedName: "snap"}, 395 Daemon: "forking", 396 }, 397 "baz": { 398 Name: "baz", 399 Snap: &snap.Info{SuggestedName: "snap"}, 400 Daemon: "forking", 401 }, 402 }, 403 }, 404 Name: "app", 405 Command: "bin/foo start", 406 Daemon: "simple", 407 StopTimeout: timeout.DefaultTimeout, 408 } 409 410 for _, tc := range []struct { 411 after []string 412 before []string 413 generatedAfter string 414 generatedBefore string 415 }{{ 416 after: []string{"bar", "zed"}, 417 generatedAfter: "snap.snap.bar.service snap.snap.zed.service", 418 before: []string{"foo", "baz"}, 419 generatedBefore: "snap.snap.foo.service snap.snap.baz.service", 420 }, { 421 after: []string{"bar"}, 422 generatedAfter: "snap.snap.bar.service", 423 before: []string{"foo"}, 424 generatedBefore: "snap.snap.foo.service", 425 }, 426 } { 427 c.Logf("tc: %v", tc) 428 service.After = tc.after 429 service.Before = tc.before 430 generatedWrapper, err := wrappers.GenerateSnapServiceFile(service) 431 c.Assert(err, IsNil) 432 433 expectedService := fmt.Sprintf(expectedServiceFmt, mountUnitPrefix, mountUnitPrefix, 434 tc.generatedAfter, tc.generatedBefore, "on-failure", "simple") 435 c.Assert(string(generatedWrapper), Equals, expectedService) 436 } 437 } 438 439 func (s *servicesWrapperGenSuite) TestServiceTimerUnit(c *C) { 440 const expectedServiceFmt = `[Unit] 441 # Auto-generated, DO NOT EDIT 442 Description=Timer app for snap application snap.app 443 Requires=%s-snap-44.mount 444 After=%s-snap-44.mount 445 X-Snappy=yes 446 447 [Timer] 448 Unit=snap.snap.app.service 449 OnCalendar=*-*-* 10:00 450 OnCalendar=*-*-* 11:00 451 452 [Install] 453 WantedBy=timers.target 454 ` 455 456 expectedService := fmt.Sprintf(expectedServiceFmt, mountUnitPrefix, mountUnitPrefix) 457 service := &snap.AppInfo{ 458 Snap: &snap.Info{ 459 SuggestedName: "snap", 460 Version: "0.3.4", 461 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 462 }, 463 Name: "app", 464 Command: "bin/foo start", 465 Daemon: "simple", 466 StopTimeout: timeout.DefaultTimeout, 467 Timer: &snap.TimerInfo{ 468 Timer: "10:00-12:00/2", 469 }, 470 } 471 service.Timer.App = service 472 473 generatedWrapper, err := wrappers.GenerateSnapTimerFile(service) 474 c.Assert(err, IsNil) 475 476 c.Logf("timer: \n%v\n", string(generatedWrapper)) 477 c.Assert(string(generatedWrapper), Equals, expectedService) 478 } 479 480 func (s *servicesWrapperGenSuite) TestServiceTimerUnitBadTimer(c *C) { 481 service := &snap.AppInfo{ 482 Snap: &snap.Info{ 483 SuggestedName: "snap", 484 Version: "0.3.4", 485 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 486 }, 487 Name: "app", 488 Command: "bin/foo start", 489 Daemon: "simple", 490 StopTimeout: timeout.DefaultTimeout, 491 Timer: &snap.TimerInfo{ 492 Timer: "bad-timer", 493 }, 494 } 495 service.Timer.App = service 496 497 generatedWrapper, err := wrappers.GenerateSnapTimerFile(service) 498 c.Assert(err, ErrorMatches, `cannot parse "bad-timer": "bad" is not a valid weekday`) 499 c.Assert(generatedWrapper, IsNil) 500 } 501 502 func (s *servicesWrapperGenSuite) TestServiceTimerServiceUnit(c *C) { 503 const expectedServiceFmt = `[Unit] 504 # Auto-generated, DO NOT EDIT 505 Description=Service for snap application snap.app 506 Requires=%s-snap-44.mount 507 Wants=network.target 508 After=%s-snap-44.mount network.target 509 X-Snappy=yes 510 511 [Service] 512 ExecStart=/usr/bin/snap run --timer="10:00-12:00,,mon,23:00~01:00/2" snap.app 513 SyslogIdentifier=snap.app 514 Restart=%s 515 WorkingDirectory=/var/snap/snap/44 516 TimeoutStopSec=30 517 Type=%s 518 519 [Install] 520 WantedBy=multi-user.target 521 ` 522 523 expectedService := fmt.Sprintf(expectedServiceFmt, mountUnitPrefix, mountUnitPrefix, "on-failure", "simple") 524 service := &snap.AppInfo{ 525 Snap: &snap.Info{ 526 SuggestedName: "snap", 527 Version: "0.3.4", 528 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 529 }, 530 Name: "app", 531 Command: "bin/foo start", 532 Daemon: "simple", 533 StopTimeout: timeout.DefaultTimeout, 534 Timer: &snap.TimerInfo{ 535 Timer: "10:00-12:00,,mon,23:00~01:00/2", 536 }, 537 } 538 539 generatedWrapper, err := wrappers.GenerateSnapServiceFile(service) 540 c.Assert(err, IsNil) 541 542 c.Logf("service: \n%v\n", string(generatedWrapper)) 543 c.Assert(string(generatedWrapper), Equals, expectedService) 544 } 545 546 func (s *servicesWrapperGenSuite) TestTimerGenerateSchedules(c *C) { 547 systemdAnalyzePath, _ := exec.LookPath("systemd-analyze") 548 if systemdAnalyzePath != "" { 549 // systemd-analyze is in the path, but it will fail if the 550 // daemon is not running (as it happens in LP builds) and writes 551 // the following to stderr: 552 // Failed to create bus connection: No such file or directory 553 cmd := exec.Command(systemdAnalyzePath, "calendar", "12:00") 554 err := cmd.Run() 555 if err != nil { 556 // turns out it's not usable, disable extra verification 557 fmt.Fprintln(os.Stderr, `WARNING: systemd-analyze not usable, cannot validate a known schedule "12:00"`) 558 systemdAnalyzePath = "" 559 } 560 } 561 562 if systemdAnalyzePath == "" { 563 fmt.Fprintln(os.Stderr, "WARNING: generated schedules will not be validated by systemd-analyze") 564 } 565 566 for _, t := range []struct { 567 in string 568 expected []string 569 randomized bool 570 }{{ 571 in: "9:00-11:00,,20:00-22:00", 572 expected: []string{"*-*-* 09:00", "*-*-* 20:00"}, 573 }, { 574 in: "9:00-11:00/2,,20:00", 575 expected: []string{"*-*-* 09:00", "*-*-* 10:00", "*-*-* 20:00"}, 576 }, { 577 in: "9:00~11:00/2,,20:00", 578 expected: []string{`\*-\*-\* 09:[0-5][0-9]`, `\*-\*-\* 10:[0-5][0-9]`, `\*-\*-\* 20:00`}, 579 randomized: true, 580 }, { 581 in: "mon,10:00,,fri,15:00", 582 expected: []string{"Mon *-*-* 10:00", "Fri *-*-* 15:00"}, 583 }, { 584 in: "mon-fri,10:00-11:00", 585 expected: []string{"Mon,Tue,Wed,Thu,Fri *-*-* 10:00"}, 586 }, { 587 in: "fri-mon,10:00-11:00", 588 expected: []string{"Fri,Sat,Sun,Mon *-*-* 10:00"}, 589 }, { 590 in: "mon5,10:00", 591 expected: []string{"Mon *-*~7/1 10:00"}, 592 }, { 593 in: "mon2,10:00", 594 expected: []string{"Mon *-*-8..14/1 10:00"}, 595 }, { 596 in: "mon2,mon1,10:00", 597 expected: []string{"Mon *-*-8..14/1 10:00", "Mon *-*-1..7/1 10:00"}, 598 }, { 599 // NOTE: non-representable, assumes that service runner does the 600 // filtering of when to run the timer 601 in: "mon1-mon3,10:00", 602 expected: []string{"*-*-1..21/1 10:00"}, 603 }, { 604 in: "mon,10:00~12:00,,fri,15:00", 605 expected: []string{`Mon \*-\*-\* 1[01]:[0-5][0-9]`, `Fri \*-\*-\* 15:00`}, 606 randomized: true, 607 }, { 608 in: "23:00~24:00/4", 609 expected: []string{`\*-\*-\* 23:[01][0-9]`, `\*-\*-\* 23:[12][0-9]`, `\*-\*-\* 23:[34][0-9]`, `*-*-* 23:[45][0-9]`}, 610 randomized: true, 611 }, { 612 in: "23:00~01:00/4", 613 expected: []string{`\*-\*-\* 23:[0-2][0-9]`, `\*-\*-\* 23:[3-5][0-9]`, `\*-\*-\* 00:[0-2][0-9]`, `\*-\*-\* 00:[3-5][0-9]`}, 614 randomized: true, 615 }, { 616 in: "23:00-01:00/4", 617 expected: []string{`*-*-* 23:00`, `*-*-* 23:30`, `*-*-* 00:00`, `*-*-* 00:30`}, 618 }, { 619 in: "24:00", 620 expected: []string{`*-*-* 00:00`}, 621 }} { 622 c.Logf("trying %+v", t) 623 624 schedule, err := timeutil.ParseSchedule(t.in) 625 c.Check(err, IsNil) 626 627 timer := wrappers.GenerateOnCalendarSchedules(schedule) 628 c.Check(timer, Not(IsNil)) 629 if !t.randomized { 630 c.Check(timer, DeepEquals, t.expected) 631 } else { 632 c.Assert(timer, HasLen, len(t.expected)) 633 for i := range timer { 634 c.Check(timer[i], Matches, t.expected[i]) 635 } 636 } 637 638 if systemdAnalyzePath != "" { 639 cmd := exec.Command(systemdAnalyzePath, append([]string{"calendar"}, timer...)...) 640 out, err := cmd.CombinedOutput() 641 c.Check(err, IsNil, Commentf("systemd-analyze failed with output:\n%s", string(out))) 642 } 643 } 644 } 645 646 func (s *servicesWrapperGenSuite) TestKillModeSig(c *C) { 647 for _, rm := range []string{"sigterm", "sighup", "sigusr1", "sigusr2"} { 648 service := &snap.AppInfo{ 649 Snap: &snap.Info{ 650 SuggestedName: "snap", 651 Version: "0.3.4", 652 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 653 }, 654 Name: "app", 655 Command: "bin/foo start", 656 Daemon: "simple", 657 StopMode: snap.StopModeType(rm), 658 } 659 660 generatedWrapper, err := wrappers.GenerateSnapServiceFile(service) 661 c.Assert(err, IsNil) 662 663 c.Check(string(generatedWrapper), Equals, fmt.Sprintf(`[Unit] 664 # Auto-generated, DO NOT EDIT 665 Description=Service for snap application snap.app 666 Requires=%s-snap-44.mount 667 Wants=network.target 668 After=%s-snap-44.mount network.target 669 X-Snappy=yes 670 671 [Service] 672 ExecStart=/usr/bin/snap run snap.app 673 SyslogIdentifier=snap.app 674 Restart=on-failure 675 WorkingDirectory=/var/snap/snap/44 676 TimeoutStopSec=30 677 Type=simple 678 KillMode=process 679 KillSignal=%s 680 681 [Install] 682 WantedBy=multi-user.target 683 `, mountUnitPrefix, mountUnitPrefix, strings.ToUpper(rm))) 684 } 685 } 686 687 func (s *servicesWrapperGenSuite) TestRestartDelay(c *C) { 688 service := &snap.AppInfo{ 689 Snap: &snap.Info{ 690 SuggestedName: "snap", 691 Version: "0.3.4", 692 SideInfo: snap.SideInfo{Revision: snap.R(44)}, 693 }, 694 Name: "app", 695 Command: "bin/foo start", 696 Daemon: "simple", 697 RestartDelay: timeout.Timeout(20 * time.Second), 698 } 699 700 generatedWrapper, err := wrappers.GenerateSnapServiceFile(service) 701 c.Assert(err, IsNil) 702 703 c.Check(string(generatedWrapper), Equals, fmt.Sprintf(`[Unit] 704 # Auto-generated, DO NOT EDIT 705 Description=Service for snap application snap.app 706 Requires=%s-snap-44.mount 707 Wants=network.target 708 After=%s-snap-44.mount network.target 709 X-Snappy=yes 710 711 [Service] 712 ExecStart=/usr/bin/snap run snap.app 713 SyslogIdentifier=snap.app 714 Restart=on-failure 715 RestartSec=20 716 WorkingDirectory=/var/snap/snap/44 717 TimeoutStopSec=30 718 Type=simple 719 720 [Install] 721 WantedBy=multi-user.target 722 `, mountUnitPrefix, mountUnitPrefix)) 723 }