github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/systemd/systemd_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2015 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 systemd_test 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 "io" 27 "io/ioutil" 28 "os" 29 "path/filepath" 30 "strconv" 31 "testing" 32 "time" 33 34 . "gopkg.in/check.v1" 35 36 "github.com/snapcore/snapd/dirs" 37 "github.com/snapcore/snapd/gadget/quantity" 38 "github.com/snapcore/snapd/osutil" 39 "github.com/snapcore/snapd/osutil/squashfs" 40 "github.com/snapcore/snapd/sandbox/selinux" 41 . "github.com/snapcore/snapd/systemd" 42 "github.com/snapcore/snapd/testutil" 43 ) 44 45 type testreporter struct { 46 msgs []string 47 } 48 49 func (tr *testreporter) Notify(msg string) { 50 tr.msgs = append(tr.msgs, msg) 51 } 52 53 // Hook up check.v1 into the "go test" runner 54 func Test(t *testing.T) { TestingT(t) } 55 56 // systemd's testsuite 57 type SystemdTestSuite struct { 58 i int 59 argses [][]string 60 errors []error 61 outs [][]byte 62 63 j int 64 jns []string 65 jsvcs [][]string 66 jouts [][]byte 67 jerrs []error 68 jfollows []bool 69 70 rep *testreporter 71 72 restoreSystemctl func() 73 restoreJournalctl func() 74 restoreSELinux func() 75 } 76 77 var _ = Suite(&SystemdTestSuite{}) 78 79 func (s *SystemdTestSuite) SetUpTest(c *C) { 80 dirs.SetRootDir(c.MkDir()) 81 err := os.MkdirAll(dirs.SnapServicesDir, 0755) 82 c.Assert(err, IsNil) 83 c.Assert(os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755), IsNil) 84 85 // force UTC timezone, for reproducible timestamps 86 os.Setenv("TZ", "") 87 88 s.restoreSystemctl = MockSystemctl(s.myRun) 89 s.i = 0 90 s.argses = nil 91 s.errors = nil 92 s.outs = nil 93 94 s.restoreJournalctl = MockJournalctl(s.myJctl) 95 s.j = 0 96 s.jns = nil 97 s.jsvcs = nil 98 s.jouts = nil 99 s.jerrs = nil 100 s.jfollows = nil 101 102 s.rep = new(testreporter) 103 104 s.restoreSELinux = selinux.MockIsEnabled(func() (bool, error) { return false, nil }) 105 } 106 107 func (s *SystemdTestSuite) TearDownTest(c *C) { 108 s.restoreSystemctl() 109 s.restoreJournalctl() 110 s.restoreSELinux() 111 } 112 113 func (s *SystemdTestSuite) myRun(args ...string) (out []byte, err error) { 114 s.argses = append(s.argses, args) 115 if s.i < len(s.outs) { 116 out = s.outs[s.i] 117 } 118 if s.i < len(s.errors) { 119 err = s.errors[s.i] 120 } 121 s.i++ 122 return out, err 123 } 124 125 func (s *SystemdTestSuite) myJctl(svcs []string, n int, follow bool) (io.ReadCloser, error) { 126 var err error 127 var out []byte 128 129 s.jns = append(s.jns, strconv.Itoa(n)) 130 s.jsvcs = append(s.jsvcs, svcs) 131 s.jfollows = append(s.jfollows, follow) 132 133 if s.j < len(s.jouts) { 134 out = s.jouts[s.j] 135 } 136 if s.j < len(s.jerrs) { 137 err = s.jerrs[s.j] 138 } 139 s.j++ 140 141 if out == nil { 142 return nil, err 143 } 144 145 return ioutil.NopCloser(bytes.NewReader(out)), err 146 } 147 148 func (s *SystemdTestSuite) TestDaemonReload(c *C) { 149 err := New(SystemMode, s.rep).DaemonReload() 150 c.Assert(err, IsNil) 151 c.Assert(s.argses, DeepEquals, [][]string{{"daemon-reload"}}) 152 } 153 154 func (s *SystemdTestSuite) TestDaemonReexec(c *C) { 155 err := New(SystemMode, s.rep).DaemonReexec() 156 c.Assert(err, IsNil) 157 c.Assert(s.argses, DeepEquals, [][]string{{"daemon-reexec"}}) 158 } 159 160 func (s *SystemdTestSuite) TestStart(c *C) { 161 err := New(SystemMode, s.rep).Start("foo") 162 c.Assert(err, IsNil) 163 c.Check(s.argses, DeepEquals, [][]string{{"start", "foo"}}) 164 } 165 166 func (s *SystemdTestSuite) TestStartMany(c *C) { 167 err := New(SystemMode, s.rep).Start("foo", "bar", "baz") 168 c.Assert(err, IsNil) 169 c.Check(s.argses, DeepEquals, [][]string{{"start", "foo", "bar", "baz"}}) 170 } 171 172 func (s *SystemdTestSuite) TestStop(c *C) { 173 restore := MockStopDelays(time.Millisecond, 25*time.Second) 174 defer restore() 175 s.outs = [][]byte{ 176 nil, // for the "stop" itself 177 []byte("ActiveState=whatever\n"), 178 []byte("ActiveState=active\n"), 179 []byte("ActiveState=inactive\n"), 180 } 181 s.errors = []error{nil, nil, nil, nil, &Timeout{}} 182 err := New(SystemMode, s.rep).Stop("foo", 1*time.Second) 183 c.Assert(err, IsNil) 184 c.Assert(s.argses, HasLen, 4) 185 c.Check(s.argses[0], DeepEquals, []string{"stop", "foo"}) 186 c.Check(s.argses[1], DeepEquals, []string{"show", "--property=ActiveState", "foo"}) 187 c.Check(s.argses[1], DeepEquals, s.argses[2]) 188 c.Check(s.argses[1], DeepEquals, s.argses[3]) 189 } 190 191 func (s *SystemdTestSuite) TestStatus(c *C) { 192 s.outs = [][]byte{ 193 []byte(` 194 Type=simple 195 Id=foo.service 196 ActiveState=active 197 UnitFileState=enabled 198 199 Type=simple 200 Id=bar.service 201 ActiveState=reloading 202 UnitFileState=static 203 204 Type=potato 205 Id=baz.service 206 ActiveState=inactive 207 UnitFileState=disabled 208 209 Type= 210 Id=missing.service 211 ActiveState=inactive 212 UnitFileState= 213 `[1:]), 214 []byte(` 215 Id=some.timer 216 ActiveState=active 217 UnitFileState=enabled 218 219 Id=other.socket 220 ActiveState=active 221 UnitFileState=disabled 222 `[1:]), 223 } 224 s.errors = []error{nil} 225 out, err := New(SystemMode, s.rep).Status("foo.service", "bar.service", "baz.service", "missing.service", "some.timer", "other.socket") 226 c.Assert(err, IsNil) 227 c.Check(out, DeepEquals, []*UnitStatus{ 228 { 229 Daemon: "simple", 230 UnitName: "foo.service", 231 Active: true, 232 Enabled: true, 233 Installed: true, 234 }, { 235 Daemon: "simple", 236 UnitName: "bar.service", 237 Active: true, 238 Enabled: true, 239 Installed: true, 240 }, { 241 Daemon: "potato", 242 UnitName: "baz.service", 243 Active: false, 244 Enabled: false, 245 Installed: true, 246 }, { 247 Daemon: "", 248 UnitName: "missing.service", 249 Active: false, 250 Enabled: false, 251 Installed: false, 252 }, { 253 UnitName: "some.timer", 254 Active: true, 255 Enabled: true, 256 Installed: true, 257 }, { 258 UnitName: "other.socket", 259 Active: true, 260 Enabled: false, 261 Installed: true, 262 }, 263 }) 264 c.Check(s.rep.msgs, IsNil) 265 c.Assert(s.argses, DeepEquals, [][]string{ 266 {"show", "--property=Id,ActiveState,UnitFileState,Type", "foo.service", "bar.service", "baz.service", "missing.service"}, 267 {"show", "--property=Id,ActiveState,UnitFileState", "some.timer", "other.socket"}, 268 }) 269 } 270 271 func (s *SystemdTestSuite) TestStatusBadNumberOfValues(c *C) { 272 s.outs = [][]byte{ 273 []byte(` 274 Type=simple 275 Id=foo.service 276 ActiveState=active 277 UnitFileState=enabled 278 279 Type=simple 280 Id=foo.service 281 ActiveState=active 282 UnitFileState=enabled 283 `[1:]), 284 } 285 s.errors = []error{nil} 286 out, err := New(SystemMode, s.rep).Status("foo.service") 287 c.Check(err, ErrorMatches, "cannot get unit status: expected 1 results, got 2") 288 c.Check(out, IsNil) 289 c.Check(s.rep.msgs, IsNil) 290 } 291 292 func (s *SystemdTestSuite) TestStatusBadLine(c *C) { 293 s.outs = [][]byte{ 294 []byte(` 295 Type=simple 296 Id=foo.service 297 ActiveState=active 298 UnitFileState=enabled 299 Potatoes 300 `[1:]), 301 } 302 s.errors = []error{nil} 303 out, err := New(SystemMode, s.rep).Status("foo.service") 304 c.Assert(err, ErrorMatches, `.* bad line "Potatoes" .*`) 305 c.Check(out, IsNil) 306 } 307 308 func (s *SystemdTestSuite) TestStatusBadId(c *C) { 309 s.outs = [][]byte{ 310 []byte(` 311 Type=simple 312 Id=bar.service 313 ActiveState=active 314 UnitFileState=enabled 315 `[1:]), 316 } 317 s.errors = []error{nil} 318 out, err := New(SystemMode, s.rep).Status("foo.service") 319 c.Assert(err, ErrorMatches, `.* queried status of "foo.service" but got status of "bar.service"`) 320 c.Check(out, IsNil) 321 } 322 323 func (s *SystemdTestSuite) TestStatusBadField(c *C) { 324 s.outs = [][]byte{ 325 []byte(` 326 Type=simple 327 Id=foo.service 328 ActiveState=active 329 UnitFileState=enabled 330 Potatoes=false 331 `[1:]), 332 } 333 s.errors = []error{nil} 334 out, err := New(SystemMode, s.rep).Status("foo.service") 335 c.Assert(err, ErrorMatches, `.* unexpected field "Potatoes" .*`) 336 c.Check(out, IsNil) 337 } 338 339 func (s *SystemdTestSuite) TestStatusMissingRequiredFieldService(c *C) { 340 s.outs = [][]byte{ 341 []byte(` 342 Id=foo.service 343 ActiveState=active 344 `[1:]), 345 } 346 s.errors = []error{nil} 347 out, err := New(SystemMode, s.rep).Status("foo.service") 348 c.Assert(err, ErrorMatches, `.* missing UnitFileState, Type .*`) 349 c.Check(out, IsNil) 350 } 351 352 func (s *SystemdTestSuite) TestStatusMissingRequiredFieldTimer(c *C) { 353 s.outs = [][]byte{ 354 []byte(` 355 Id=foo.timer 356 ActiveState=active 357 `[1:]), 358 } 359 s.errors = []error{nil} 360 out, err := New(SystemMode, s.rep).Status("foo.timer") 361 c.Assert(err, ErrorMatches, `.* missing UnitFileState .*`) 362 c.Check(out, IsNil) 363 } 364 365 func (s *SystemdTestSuite) TestStatusDupeField(c *C) { 366 s.outs = [][]byte{ 367 []byte(` 368 Type=simple 369 Id=foo.service 370 ActiveState=active 371 ActiveState=active 372 UnitFileState=enabled 373 `[1:]), 374 } 375 s.errors = []error{nil} 376 out, err := New(SystemMode, s.rep).Status("foo.service") 377 c.Assert(err, ErrorMatches, `.* duplicate field "ActiveState" .*`) 378 c.Check(out, IsNil) 379 } 380 381 func (s *SystemdTestSuite) TestStatusEmptyField(c *C) { 382 s.outs = [][]byte{ 383 []byte(` 384 Type=simple 385 Id= 386 ActiveState=active 387 UnitFileState=enabled 388 `[1:]), 389 } 390 s.errors = []error{nil} 391 out, err := New(SystemMode, s.rep).Status("foo.service") 392 c.Assert(err, ErrorMatches, `.* empty field "Id" .*`) 393 c.Check(out, IsNil) 394 } 395 396 func (s *SystemdTestSuite) TestStopTimeout(c *C) { 397 restore := MockStopDelays(time.Millisecond, 25*time.Second) 398 defer restore() 399 err := New(SystemMode, s.rep).Stop("foo", 10*time.Millisecond) 400 c.Assert(err, FitsTypeOf, &Timeout{}) 401 c.Assert(len(s.rep.msgs) > 0, Equals, true) 402 c.Check(s.rep.msgs[0], Equals, "Waiting for foo to stop.") 403 } 404 405 func (s *SystemdTestSuite) TestDisable(c *C) { 406 err := New(SystemMode, s.rep).Disable("foo") 407 c.Assert(err, IsNil) 408 c.Check(s.argses, DeepEquals, [][]string{{"disable", "foo"}}) 409 } 410 411 func (s *SystemdTestSuite) TestUnderRootDisable(c *C) { 412 err := NewUnderRoot("xyzzy", SystemMode, s.rep).Disable("foo") 413 c.Assert(err, IsNil) 414 c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "disable", "foo"}}) 415 } 416 417 func (s *SystemdTestSuite) TestAvailable(c *C) { 418 err := Available() 419 c.Assert(err, IsNil) 420 c.Check(s.argses, DeepEquals, [][]string{{"--version"}}) 421 } 422 423 func (s *SystemdTestSuite) TestVersion(c *C) { 424 s.outs = [][]byte{ 425 []byte("systemd 223\n+PAM\n"), 426 []byte("systemd 245 (245.4-4ubuntu3)\n+PAM +AUDIT +SELINUX +IMA\n"), 427 // error cases 428 []byte("foo 223\n+PAM\n"), 429 []byte(""), 430 []byte("systemd abc\n+PAM\n"), 431 } 432 433 v, err := Version() 434 c.Assert(err, IsNil) 435 c.Check(v, Equals, 223) 436 437 v, err = Version() 438 c.Assert(err, IsNil) 439 c.Check(v, Equals, 245) 440 441 _, err = Version() 442 c.Assert(err, ErrorMatches, `cannot parse systemd version: expected "systemd", got "foo"`) 443 444 _, err = Version() 445 c.Assert(err, ErrorMatches, `cannot read systemd version: <nil>`) 446 447 _, err = Version() 448 c.Assert(err, ErrorMatches, `cannot convert systemd version to number: abc`) 449 450 c.Check(s.argses, DeepEquals, [][]string{ 451 {"--version"}, 452 {"--version"}, 453 {"--version"}, 454 {"--version"}, 455 {"--version"}, 456 }) 457 } 458 459 func (s *SystemdTestSuite) TestEnable(c *C) { 460 err := New(SystemMode, s.rep).Enable("foo") 461 c.Assert(err, IsNil) 462 c.Check(s.argses, DeepEquals, [][]string{{"enable", "foo"}}) 463 } 464 465 func (s *SystemdTestSuite) TestEnableUnderRoot(c *C) { 466 err := NewUnderRoot("xyzzy", SystemMode, s.rep).Enable("foo") 467 c.Assert(err, IsNil) 468 c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "enable", "foo"}}) 469 } 470 471 func (s *SystemdTestSuite) TestMask(c *C) { 472 err := New(SystemMode, s.rep).Mask("foo") 473 c.Assert(err, IsNil) 474 c.Check(s.argses, DeepEquals, [][]string{{"mask", "foo"}}) 475 } 476 477 func (s *SystemdTestSuite) TestMaskUnderRoot(c *C) { 478 err := NewUnderRoot("xyzzy", SystemMode, s.rep).Mask("foo") 479 c.Assert(err, IsNil) 480 c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "mask", "foo"}}) 481 } 482 483 func (s *SystemdTestSuite) TestUnmask(c *C) { 484 err := New(SystemMode, s.rep).Unmask("foo") 485 c.Assert(err, IsNil) 486 c.Check(s.argses, DeepEquals, [][]string{{"unmask", "foo"}}) 487 } 488 489 func (s *SystemdTestSuite) TestUnmaskUnderRoot(c *C) { 490 err := NewUnderRoot("xyzzy", SystemMode, s.rep).Unmask("foo") 491 c.Assert(err, IsNil) 492 c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "unmask", "foo"}}) 493 } 494 495 func (s *SystemdTestSuite) TestRestart(c *C) { 496 restore := MockStopDelays(time.Millisecond, 25*time.Second) 497 defer restore() 498 s.outs = [][]byte{ 499 nil, // for the "stop" itself 500 []byte("ActiveState=inactive\n"), 501 nil, // for the "start" 502 } 503 s.errors = []error{nil, nil, nil, nil, &Timeout{}} 504 err := New(SystemMode, s.rep).Restart("foo", 100*time.Millisecond) 505 c.Assert(err, IsNil) 506 c.Check(s.argses, HasLen, 3) 507 c.Check(s.argses[0], DeepEquals, []string{"stop", "foo"}) 508 c.Check(s.argses[1], DeepEquals, []string{"show", "--property=ActiveState", "foo"}) 509 c.Check(s.argses[2], DeepEquals, []string{"start", "foo"}) 510 } 511 512 func (s *SystemdTestSuite) TestKill(c *C) { 513 c.Assert(New(SystemMode, s.rep).Kill("foo", "HUP", ""), IsNil) 514 c.Check(s.argses, DeepEquals, [][]string{{"kill", "foo", "-s", "HUP", "--kill-who=all"}}) 515 } 516 517 func (s *SystemdTestSuite) TestIsTimeout(c *C) { 518 c.Check(IsTimeout(os.ErrInvalid), Equals, false) 519 c.Check(IsTimeout(&Timeout{}), Equals, true) 520 } 521 522 func (s *SystemdTestSuite) TestLogErrJctl(c *C) { 523 s.jerrs = []error{&Timeout{}} 524 525 reader, err := New(SystemMode, s.rep).LogReader([]string{"foo"}, 24, false) 526 c.Check(err, NotNil) 527 c.Check(reader, IsNil) 528 c.Check(s.jns, DeepEquals, []string{"24"}) 529 c.Check(s.jsvcs, DeepEquals, [][]string{{"foo"}}) 530 c.Check(s.jfollows, DeepEquals, []bool{false}) 531 c.Check(s.j, Equals, 1) 532 } 533 534 func (s *SystemdTestSuite) TestLogs(c *C) { 535 expected := `{"a": 1} 536 {"a": 2} 537 ` 538 s.jouts = [][]byte{[]byte(expected)} 539 540 reader, err := New(SystemMode, s.rep).LogReader([]string{"foo"}, 24, false) 541 c.Check(err, IsNil) 542 logs, err := ioutil.ReadAll(reader) 543 c.Assert(err, IsNil) 544 c.Check(string(logs), Equals, expected) 545 c.Check(s.jns, DeepEquals, []string{"24"}) 546 c.Check(s.jsvcs, DeepEquals, [][]string{{"foo"}}) 547 c.Check(s.jfollows, DeepEquals, []bool{false}) 548 c.Check(s.j, Equals, 1) 549 } 550 551 // mustJSONMarshal panic's if the value cannot be marshaled 552 func mustJSONMarshal(v interface{}) *json.RawMessage { 553 b, err := json.Marshal(v) 554 if err != nil { 555 panic(fmt.Sprintf("couldn't marshal json in test fixture: %v", err)) 556 } 557 msg := json.RawMessage(b) 558 return &msg 559 } 560 561 func (s *SystemdTestSuite) TestLogPIDWithNonTrivialKeyValues(c *C) { 562 l1 := Log{ 563 "_PID": mustJSONMarshal([]string{}), 564 } 565 l2 := Log{ 566 "_PID": mustJSONMarshal(6), 567 } 568 l3 := Log{ 569 "_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}), 570 } 571 l4 := Log{ 572 "SYSLOG_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}), 573 } 574 l5 := Log{ 575 "_PID": mustJSONMarshal("42"), 576 "SYSLOG_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}), 577 } 578 l6 := Log{ 579 "_PID": mustJSONMarshal([]string{"42"}), 580 "SYSLOG_PID": mustJSONMarshal([]string{"pid1", "pid2", "pid3"}), 581 } 582 l7 := Log{ 583 "_PID": mustJSONMarshal([]string{"42", "42"}), 584 "SYSLOG_PID": mustJSONMarshal([]string{"singlepid"}), 585 } 586 c.Check(Log{}.PID(), Equals, "-") 587 c.Check(l1.PID(), Equals, "-") 588 c.Check(l2.PID(), Equals, "-") 589 c.Check(l3.PID(), Equals, "-") 590 c.Check(l4.PID(), Equals, "-") 591 // things starting with underscore are "trusted", so we trust 592 // them more than the user-settable ones: 593 c.Check(l5.PID(), Equals, "42") 594 c.Check(l6.PID(), Equals, "42") 595 c.Check(l7.PID(), Equals, "singlepid") 596 } 597 598 func (s *SystemdTestSuite) TestLogsMessageWithNonUniqueKeys(c *C) { 599 600 tt := []struct { 601 msg *json.RawMessage 602 exp string 603 comment string 604 }{ 605 { 606 mustJSONMarshal("m1"), 607 "m1", 608 "simple string", 609 }, 610 { 611 mustJSONMarshal("Я"), 612 "Я", 613 "simple utf-8 string", 614 }, 615 { 616 mustJSONMarshal([]rune{65, 66, 67, 192, 69}), 617 "ABC\xc0E", 618 "invalid utf-8 bytes", 619 }, 620 { 621 mustJSONMarshal(""), 622 "", 623 "empty string", 624 }, 625 { 626 mustJSONMarshal([]string{"m1", "m2", "m3"}), 627 "m1\nm2\nm3", 628 "slice of strings", 629 }, 630 { 631 // this is just "hello" in ascii 632 mustJSONMarshal([]rune{104, 101, 108, 108, 111}), 633 "hello", 634 "rune arrays are converted to strings", 635 }, 636 { 637 // this is "hello\r" in ascii, the \r char is unprintable 638 mustJSONMarshal([]rune{104, 101, 108, 108, 111, 13}), 639 "hello\r", 640 "rune arrays are converted to strings", 641 }, 642 { 643 // this is "hel" and "lo" in ascii 644 mustJSONMarshal([][]rune{ 645 {104, 101, 108}, 646 {108, 111}, 647 }), 648 "hel\nlo", 649 "arrays of rune arrays are converted to arrays of strings", 650 }, 651 { 652 // this is invalid utf-8 string followed by a valid one 653 mustJSONMarshal([][]byte{ 654 {65, 66, 67, 192, 69}, 655 {104, 101, 108, 108, 111}, 656 }), 657 "ABC\xc0E\nhello", 658 "arrays of bytes, some are invalid utf-8 strings", 659 }, 660 { 661 mustJSONMarshal(5), 662 "- (error decoding original message: unsupported JSON encoding format)", 663 "invalid message format of raw scalar number", 664 }, 665 { 666 mustJSONMarshal(map[string]int{"hello": 1}), 667 "- (error decoding original message: unsupported JSON encoding format)", 668 "invalid message format of map object", 669 }, 670 } 671 672 // trivial case 673 c.Check(Log{}.Message(), Equals, "-") 674 675 // case where the JSON has a "null" JSON value for the key, which happens if 676 // the actual message is too large for journald to send 677 // we can't use the mustJSONMarshal helper for this in the test table 678 // because that gets decoded by Go differently than a verbatim nil here, it 679 // gets interpreted as the empty string rather than nil directly 680 c.Check(Log{"MESSAGE": nil}.Message(), Equals, "- (error decoding original message: message key \"MESSAGE\" truncated)") 681 682 for _, t := range tt { 683 if t.msg == nil { 684 685 } 686 c.Check(Log{ 687 "MESSAGE": t.msg, 688 }.Message(), Equals, t.exp, Commentf(t.comment)) 689 } 690 } 691 692 func (s *SystemdTestSuite) TestLogSID(c *C) { 693 c.Check(Log{}.SID(), Equals, "-") 694 c.Check(Log{"SYSLOG_IDENTIFIER": mustJSONMarshal("abcdef")}.SID(), Equals, "abcdef") 695 c.Check(Log{"SYSLOG_IDENTIFIER": mustJSONMarshal([]string{"abcdef"})}.SID(), Equals, "abcdef") 696 // multiple string values are not supported 697 c.Check(Log{"SYSLOG_IDENTIFIER": mustJSONMarshal([]string{"abc", "def"})}.SID(), Equals, "-") 698 699 } 700 701 func (s *SystemdTestSuite) TestLogPID(c *C) { 702 c.Check(Log{}.PID(), Equals, "-") 703 c.Check(Log{"_PID": mustJSONMarshal("99")}.PID(), Equals, "99") 704 c.Check(Log{"SYSLOG_PID": mustJSONMarshal("99")}.PID(), Equals, "99") 705 // things starting with underscore are "trusted", so we trust 706 // them more than the user-settable ones: 707 c.Check(Log{ 708 "_PID": mustJSONMarshal("42"), 709 "SYSLOG_PID": mustJSONMarshal("99"), 710 }.PID(), Equals, "42") 711 } 712 713 func (s *SystemdTestSuite) TestTime(c *C) { 714 t, err := Log{}.Time() 715 c.Check(t.IsZero(), Equals, true) 716 c.Check(err, ErrorMatches, "key \"__REALTIME_TIMESTAMP\" missing from message") 717 718 // multiple timestampe keys mean we don't have a timestamp 719 t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal([]string{"1", "2"})}.Time() 720 c.Check(t.IsZero(), Equals, true) 721 c.Check(err, ErrorMatches, `no timestamp`) 722 723 t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal("what")}.Time() 724 c.Check(t.IsZero(), Equals, true) 725 c.Check(err, ErrorMatches, `timestamp not a decimal number: "what"`) 726 727 t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal("0")}.Time() 728 c.Check(err, IsNil) 729 c.Check(t.String(), Equals, "1970-01-01 00:00:00 +0000 UTC") 730 731 t, err = Log{"__REALTIME_TIMESTAMP": mustJSONMarshal("42")}.Time() 732 c.Check(err, IsNil) 733 c.Check(t.String(), Equals, "1970-01-01 00:00:00.000042 +0000 UTC") 734 } 735 736 func (s *SystemdTestSuite) TestMountUnitPath(c *C) { 737 c.Assert(MountUnitPath("/apps/hello/1.1"), Equals, filepath.Join(dirs.SnapServicesDir, "apps-hello-1.1.mount")) 738 } 739 740 func makeMockFile(c *C, path string) { 741 err := os.MkdirAll(filepath.Dir(path), 0755) 742 c.Assert(err, IsNil) 743 err = ioutil.WriteFile(path, nil, 0644) 744 c.Assert(err, IsNil) 745 } 746 747 func (s *SystemdTestSuite) TestAddMountUnit(c *C) { 748 rootDir := dirs.GlobalRootDir 749 750 restore := squashfs.MockNeedsFuse(false) 751 defer restore() 752 753 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 754 makeMockFile(c, mockSnapPath) 755 756 mountUnitName, err := NewUnderRoot(rootDir, SystemMode, nil).AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs") 757 c.Assert(err, IsNil) 758 defer os.Remove(mountUnitName) 759 760 c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(` 761 [Unit] 762 Description=Mount unit for foo, revision 42 763 Before=snapd.service 764 After=zfs-mount.service 765 766 [Mount] 767 What=%s 768 Where=/snap/snapname/123 769 Type=squashfs 770 Options=nodev,ro,x-gdu.hide,x-gvfs-hide 771 LazyUnmount=yes 772 773 [Install] 774 WantedBy=multi-user.target 775 `[1:], mockSnapPath)) 776 777 c.Assert(s.argses, DeepEquals, [][]string{ 778 {"daemon-reload"}, 779 {"--root", rootDir, "enable", "snap-snapname-123.mount"}, 780 {"start", "snap-snapname-123.mount"}, 781 }) 782 } 783 784 func (s *SystemdTestSuite) TestAddMountUnitForDirs(c *C) { 785 restore := squashfs.MockNeedsFuse(false) 786 defer restore() 787 788 // a directory instead of a file produces a different output 789 snapDir := c.MkDir() 790 mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foodir", "x1", snapDir, "/snap/snapname/x1", "squashfs") 791 c.Assert(err, IsNil) 792 defer os.Remove(mountUnitName) 793 794 c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(` 795 [Unit] 796 Description=Mount unit for foodir, revision x1 797 Before=snapd.service 798 After=zfs-mount.service 799 800 [Mount] 801 What=%s 802 Where=/snap/snapname/x1 803 Type=none 804 Options=nodev,ro,x-gdu.hide,x-gvfs-hide,bind 805 LazyUnmount=yes 806 807 [Install] 808 WantedBy=multi-user.target 809 `[1:], snapDir)) 810 811 c.Assert(s.argses, DeepEquals, [][]string{ 812 {"daemon-reload"}, 813 {"enable", "snap-snapname-x1.mount"}, 814 {"start", "snap-snapname-x1.mount"}, 815 }) 816 } 817 818 func (s *SystemdTestSuite) TestWriteSELinuxMountUnit(c *C) { 819 restore := selinux.MockIsEnabled(func() (bool, error) { return true, nil }) 820 defer restore() 821 restore = selinux.MockIsEnforcing(func() (bool, error) { return true, nil }) 822 defer restore() 823 restore = squashfs.MockNeedsFuse(false) 824 defer restore() 825 826 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 827 err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755) 828 c.Assert(err, IsNil) 829 err = ioutil.WriteFile(mockSnapPath, nil, 0644) 830 c.Assert(err, IsNil) 831 832 mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs") 833 c.Assert(err, IsNil) 834 defer os.Remove(mountUnitName) 835 836 c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(` 837 [Unit] 838 Description=Mount unit for foo, revision 42 839 Before=snapd.service 840 After=zfs-mount.service 841 842 [Mount] 843 What=%s 844 Where=/snap/snapname/123 845 Type=squashfs 846 Options=nodev,context=system_u:object_r:snappy_snap_t:s0,ro,x-gdu.hide,x-gvfs-hide 847 LazyUnmount=yes 848 849 [Install] 850 WantedBy=multi-user.target 851 `[1:], mockSnapPath)) 852 } 853 854 func (s *SystemdTestSuite) TestFuseInContainer(c *C) { 855 if !osutil.FileExists("/dev/fuse") { 856 c.Skip("No /dev/fuse on the system") 857 } 858 859 systemdCmd := testutil.MockCommand(c, "systemd-detect-virt", ` 860 echo lxc 861 exit 0 862 `) 863 defer systemdCmd.Restore() 864 865 fuseCmd := testutil.MockCommand(c, "squashfuse", ` 866 exit 0 867 `) 868 defer fuseCmd.Restore() 869 870 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 871 err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755) 872 c.Assert(err, IsNil) 873 err = ioutil.WriteFile(mockSnapPath, nil, 0644) 874 c.Assert(err, IsNil) 875 876 mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "x1", mockSnapPath, "/snap/snapname/123", "squashfs") 877 c.Assert(err, IsNil) 878 defer os.Remove(mountUnitName) 879 880 c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(` 881 [Unit] 882 Description=Mount unit for foo, revision x1 883 Before=snapd.service 884 After=zfs-mount.service 885 886 [Mount] 887 What=%s 888 Where=/snap/snapname/123 889 Type=fuse.squashfuse 890 Options=nodev,ro,x-gdu.hide,x-gvfs-hide,allow_other 891 LazyUnmount=yes 892 893 [Install] 894 WantedBy=multi-user.target 895 `[1:], mockSnapPath)) 896 } 897 898 func (s *SystemdTestSuite) TestFuseOutsideContainer(c *C) { 899 systemdCmd := testutil.MockCommand(c, "systemd-detect-virt", ` 900 echo none 901 exit 0 902 `) 903 defer systemdCmd.Restore() 904 905 fuseCmd := testutil.MockCommand(c, "squashfuse", ` 906 exit 0 907 `) 908 defer fuseCmd.Restore() 909 910 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 911 err := os.MkdirAll(filepath.Dir(mockSnapPath), 0755) 912 c.Assert(err, IsNil) 913 err = ioutil.WriteFile(mockSnapPath, nil, 0644) 914 c.Assert(err, IsNil) 915 916 mountUnitName, err := New(SystemMode, nil).AddMountUnitFile("foo", "x1", mockSnapPath, "/snap/snapname/123", "squashfs") 917 c.Assert(err, IsNil) 918 defer os.Remove(mountUnitName) 919 920 c.Assert(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(` 921 [Unit] 922 Description=Mount unit for foo, revision x1 923 Before=snapd.service 924 After=zfs-mount.service 925 926 [Mount] 927 What=%s 928 Where=/snap/snapname/123 929 Type=squashfs 930 Options=nodev,ro,x-gdu.hide,x-gvfs-hide 931 LazyUnmount=yes 932 933 [Install] 934 WantedBy=multi-user.target 935 `[1:], mockSnapPath)) 936 } 937 938 func (s *SystemdTestSuite) TestJctl(c *C) { 939 var args []string 940 var err error 941 MockOsutilStreamCommand(func(name string, myargs ...string) (io.ReadCloser, error) { 942 c.Check(cap(myargs) <= len(myargs)+2, Equals, true, Commentf("cap:%d, len:%d", cap(myargs), len(myargs))) 943 args = myargs 944 return nil, nil 945 }) 946 947 _, err = Jctl([]string{"foo", "bar"}, 10, false) 948 c.Assert(err, IsNil) 949 c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "-n", "10", "-u", "foo", "-u", "bar"}) 950 _, err = Jctl([]string{"foo", "bar", "baz"}, 99, true) 951 c.Assert(err, IsNil) 952 c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "-n", "99", "-f", "-u", "foo", "-u", "bar", "-u", "baz"}) 953 _, err = Jctl([]string{"foo", "bar"}, -1, false) 954 c.Assert(err, IsNil) 955 c.Check(args, DeepEquals, []string{"-o", "json", "--no-pager", "--no-tail", "-u", "foo", "-u", "bar"}) 956 } 957 958 func (s *SystemdTestSuite) TestIsActiveUnderRoot(c *C) { 959 sysErr := &Error{} 960 // manpage states that systemctl returns exit code 3 for inactive 961 // services, however we should check any non-0 exit status 962 sysErr.SetExitCode(1) 963 sysErr.SetMsg([]byte("inactive\n")) 964 s.errors = []error{sysErr} 965 966 _, err := NewUnderRoot("xyzzy", SystemMode, s.rep).IsActive("foo") 967 c.Assert(err, IsNil) 968 c.Check(s.argses, DeepEquals, [][]string{{"--root", "xyzzy", "is-active", "foo"}}) 969 } 970 971 func (s *SystemdTestSuite) TestIsActiveIsInactive(c *C) { 972 sysErr := &Error{} 973 // manpage states that systemctl returns exit code 3 for inactive 974 // services, however we should check any non-0 exit status 975 sysErr.SetExitCode(1) 976 sysErr.SetMsg([]byte("inactive\n")) 977 s.errors = []error{sysErr} 978 979 active, err := New(SystemMode, s.rep).IsActive("foo") 980 c.Assert(active, Equals, false) 981 c.Assert(err, IsNil) 982 c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}}) 983 } 984 985 func (s *SystemdTestSuite) TestIsActiveIsInactiveAlternativeMessage(c *C) { 986 sysErr := &Error{} 987 // on Centos 7, with systemd 219 we see "unknown" returned when querying the 988 // active state for a slice unit which does not exist, check that we handle 989 // this case properly as well 990 sysErr.SetExitCode(3) 991 sysErr.SetMsg([]byte("unknown\n")) 992 s.errors = []error{sysErr} 993 994 active, err := New(SystemMode, s.rep).IsActive("foo") 995 c.Assert(active, Equals, false) 996 c.Assert(err, IsNil) 997 c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}}) 998 } 999 1000 func (s *SystemdTestSuite) TestIsActiveIsFailed(c *C) { 1001 sysErr := &Error{} 1002 // seen in the wild to be reported for a 'failed' service 1003 sysErr.SetExitCode(3) 1004 sysErr.SetMsg([]byte("failed\n")) 1005 s.errors = []error{sysErr} 1006 1007 active, err := New(SystemMode, s.rep).IsActive("foo") 1008 c.Assert(active, Equals, false) 1009 c.Assert(err, IsNil) 1010 c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}}) 1011 } 1012 1013 func (s *SystemdTestSuite) TestIsActiveIsActive(c *C) { 1014 s.errors = []error{nil} 1015 1016 active, err := New(SystemMode, s.rep).IsActive("foo") 1017 c.Assert(active, Equals, true) 1018 c.Assert(err, IsNil) 1019 c.Check(s.argses, DeepEquals, [][]string{{"is-active", "foo"}}) 1020 } 1021 1022 func (s *SystemdTestSuite) TestIsActiveUnexpectedErr(c *C) { 1023 sysErr := &Error{} 1024 sysErr.SetExitCode(1) 1025 sysErr.SetMsg([]byte("random-failure\n")) 1026 s.errors = []error{sysErr} 1027 1028 active, err := NewUnderRoot("xyzzy", SystemMode, s.rep).IsActive("foo") 1029 c.Assert(active, Equals, false) 1030 c.Assert(err, ErrorMatches, ".* failed with exit status 1: random-failure\n") 1031 } 1032 1033 func makeMockMountUnit(c *C, mountDir string) string { 1034 mountUnit := MountUnitPath(dirs.StripRootDir(mountDir)) 1035 err := ioutil.WriteFile(mountUnit, nil, 0644) 1036 c.Assert(err, IsNil) 1037 return mountUnit 1038 } 1039 1040 // FIXME: also test for the "IsMounted" case 1041 func (s *SystemdTestSuite) TestRemoveMountUnit(c *C) { 1042 rootDir := dirs.GlobalRootDir 1043 1044 restore := osutil.MockMountInfo("") 1045 defer restore() 1046 1047 mountDir := rootDir + "/snap/foo/42" 1048 mountUnit := makeMockMountUnit(c, mountDir) 1049 err := NewUnderRoot(rootDir, SystemMode, nil).RemoveMountUnitFile(mountDir) 1050 1051 c.Assert(err, IsNil) 1052 // the file is gone 1053 c.Check(osutil.FileExists(mountUnit), Equals, false) 1054 // and the unit is disabled and the daemon reloaded 1055 c.Check(s.argses, DeepEquals, [][]string{ 1056 {"--root", rootDir, "disable", "snap-foo-42.mount"}, 1057 {"daemon-reload"}, 1058 }) 1059 } 1060 1061 func (s *SystemdTestSuite) TestDaemonReloadMutex(c *C) { 1062 s.testDaemonReloadMutex(c, Systemd.DaemonReload) 1063 } 1064 1065 func (s *SystemdTestSuite) testDaemonReloadMutex(c *C, reload func(Systemd) error) { 1066 rootDir := dirs.GlobalRootDir 1067 sysd := NewUnderRoot(rootDir, SystemMode, nil) 1068 1069 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 1070 makeMockFile(c, mockSnapPath) 1071 1072 // create a go-routine that will try to daemon-reload like crazy 1073 stopCh := make(chan bool, 1) 1074 stoppedCh := make(chan bool, 1) 1075 go func() { 1076 for { 1077 sysd.DaemonReload() 1078 select { 1079 case <-stopCh: 1080 close(stoppedCh) 1081 return 1082 default: 1083 //pass 1084 } 1085 } 1086 }() 1087 1088 // And now add a mount unit file while the go-routine tries to 1089 // daemon-reload. This will be serialized, if not this would 1090 // panic because systemd.daemonReloadNoLock ensures the lock is 1091 // taken when this happens. 1092 _, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/foo/42", "squashfs") 1093 c.Assert(err, IsNil) 1094 close(stopCh) 1095 <-stoppedCh 1096 } 1097 1098 func (s *SystemdTestSuite) TestDaemonReexecMutex(c *C) { 1099 s.testDaemonReloadMutex(c, Systemd.DaemonReexec) 1100 } 1101 1102 func (s *SystemdTestSuite) TestUserMode(c *C) { 1103 rootDir := dirs.GlobalRootDir 1104 sysd := NewUnderRoot(rootDir, UserMode, nil) 1105 1106 c.Assert(sysd.Enable("foo"), IsNil) 1107 c.Check(s.argses[0], DeepEquals, []string{"--user", "--root", rootDir, "enable", "foo"}) 1108 c.Assert(sysd.Start("foo"), IsNil) 1109 c.Check(s.argses[1], DeepEquals, []string{"--user", "start", "foo"}) 1110 } 1111 1112 func (s *SystemdTestSuite) TestGlobalUserMode(c *C) { 1113 rootDir := dirs.GlobalRootDir 1114 sysd := NewUnderRoot(rootDir, GlobalUserMode, nil) 1115 1116 c.Assert(sysd.Enable("foo"), IsNil) 1117 c.Check(s.argses[0], DeepEquals, []string{"--user", "--global", "--root", rootDir, "enable", "foo"}) 1118 c.Assert(sysd.Disable("foo"), IsNil) 1119 c.Check(s.argses[1], DeepEquals, []string{"--user", "--global", "--root", rootDir, "disable", "foo"}) 1120 c.Assert(sysd.Mask("foo"), IsNil) 1121 c.Check(s.argses[2], DeepEquals, []string{"--user", "--global", "--root", rootDir, "mask", "foo"}) 1122 c.Assert(sysd.Unmask("foo"), IsNil) 1123 c.Check(s.argses[3], DeepEquals, []string{"--user", "--global", "--root", rootDir, "unmask", "foo"}) 1124 _, err := sysd.IsEnabled("foo") 1125 c.Check(err, IsNil) 1126 c.Check(s.argses[4], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "foo"}) 1127 1128 // Commands that don't make sense for GlobalUserMode panic 1129 c.Check(sysd.DaemonReload, Panics, "cannot call daemon-reload with GlobalUserMode") 1130 c.Check(sysd.DaemonReexec, Panics, "cannot call daemon-reexec with GlobalUserMode") 1131 c.Check(func() { sysd.Start("foo") }, Panics, "cannot call start with GlobalUserMode") 1132 c.Check(func() { sysd.StartNoBlock("foo") }, Panics, "cannot call start with GlobalUserMode") 1133 c.Check(func() { sysd.Stop("foo", 0) }, Panics, "cannot call stop with GlobalUserMode") 1134 c.Check(func() { sysd.Restart("foo", 0) }, Panics, "cannot call restart with GlobalUserMode") 1135 c.Check(func() { sysd.Kill("foo", "HUP", "") }, Panics, "cannot call kill with GlobalUserMode") 1136 c.Check(func() { sysd.IsActive("foo") }, Panics, "cannot call is-active with GlobalUserMode") 1137 } 1138 1139 func (s *SystemdTestSuite) TestStatusGlobalUserMode(c *C) { 1140 output := []byte("enabled\ndisabled\nstatic\n") 1141 sysdErr := &Error{} 1142 sysdErr.SetExitCode(1) 1143 sysdErr.SetMsg(output) 1144 1145 s.outs = [][]byte{output, nil, output} 1146 s.errors = []error{nil, sysdErr, nil} 1147 1148 rootDir := dirs.GlobalRootDir 1149 sysd := NewUnderRoot(rootDir, GlobalUserMode, nil) 1150 sts, err := sysd.Status("foo", "bar", "baz") 1151 c.Check(err, IsNil) 1152 c.Check(sts, DeepEquals, []*UnitStatus{ 1153 {UnitName: "foo", Enabled: true}, 1154 {UnitName: "bar", Enabled: false}, 1155 {UnitName: "baz", Enabled: true}, 1156 }) 1157 c.Check(s.argses[0], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "foo", "bar", "baz"}) 1158 1159 // Output is collected if systemctl has a non-zero exit status 1160 sts, err = sysd.Status("one", "two", "three") 1161 c.Check(err, IsNil) 1162 c.Check(sts, DeepEquals, []*UnitStatus{ 1163 {UnitName: "one", Enabled: true}, 1164 {UnitName: "two", Enabled: false}, 1165 {UnitName: "three", Enabled: true}, 1166 }) 1167 c.Check(s.argses[1], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "one", "two", "three"}) 1168 1169 // An error is returned if the wrong number of statuses are returned 1170 sts, err = sysd.Status("one") 1171 c.Check(err, ErrorMatches, "cannot get enabled status of services: expected 1 results, got 3") 1172 c.Check(sts, IsNil) 1173 c.Check(s.argses[2], DeepEquals, []string{"--user", "--global", "--root", rootDir, "is-enabled", "one"}) 1174 } 1175 1176 const unitTemplate = ` 1177 [Unit] 1178 Description=Mount unit for foo, revision 42 1179 Before=snapd.service 1180 After=zfs-mount.service 1181 1182 [Mount] 1183 What=%s 1184 Where=/snap/snapname/123 1185 Type=%s 1186 Options=%s 1187 LazyUnmount=yes 1188 1189 [Install] 1190 WantedBy=multi-user.target 1191 ` 1192 1193 func (s *SystemdTestSuite) TestPreseedModeAddMountUnit(c *C) { 1194 sysd := NewEmulationMode(dirs.GlobalRootDir) 1195 1196 restore := squashfs.MockNeedsFuse(false) 1197 defer restore() 1198 1199 mockMountCmd := testutil.MockCommand(c, "mount", "") 1200 defer mockMountCmd.Restore() 1201 1202 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 1203 makeMockFile(c, mockSnapPath) 1204 1205 mountUnitName, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs") 1206 c.Assert(err, IsNil) 1207 defer os.Remove(mountUnitName) 1208 1209 // systemd was not called 1210 c.Check(s.argses, HasLen, 0) 1211 // mount was called 1212 c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "squashfs", mockSnapPath, "/snap/snapname/123", "-o", "nodev,ro,x-gdu.hide,x-gvfs-hide"}) 1213 // unit was enabled with a symlink 1214 c.Check(osutil.IsSymlink(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", mountUnitName)), Equals, true) 1215 c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(unitTemplate[1:], mockSnapPath, "squashfs", "nodev,ro,x-gdu.hide,x-gvfs-hide")) 1216 } 1217 1218 func (s *SystemdTestSuite) TestPreseedModeAddMountUnitWithFuse(c *C) { 1219 sysd := NewEmulationMode(dirs.GlobalRootDir) 1220 1221 restore := MockSquashFsType(func() (string, []string) { return "fuse.squashfuse", []string{"a,b,c"} }) 1222 defer restore() 1223 1224 mockMountCmd := testutil.MockCommand(c, "mount", "") 1225 defer mockMountCmd.Restore() 1226 1227 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 1228 makeMockFile(c, mockSnapPath) 1229 1230 mountUnitName, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs") 1231 c.Assert(err, IsNil) 1232 defer os.Remove(mountUnitName) 1233 1234 c.Check(mockMountCmd.Calls()[0], DeepEquals, []string{"mount", "-t", "fuse.squashfuse", mockSnapPath, "/snap/snapname/123", "-o", "nodev,a,b,c"}) 1235 c.Check(filepath.Join(dirs.SnapServicesDir, mountUnitName), testutil.FileEquals, fmt.Sprintf(unitTemplate[1:], mockSnapPath, "squashfs", "nodev,ro,x-gdu.hide,x-gvfs-hide")) 1236 } 1237 1238 func (s *SystemdTestSuite) TestPreseedModeMountError(c *C) { 1239 sysd := NewEmulationMode(dirs.GlobalRootDir) 1240 1241 restore := squashfs.MockNeedsFuse(false) 1242 defer restore() 1243 1244 mockMountCmd := testutil.MockCommand(c, "mount", `echo "some failure"; exit 1`) 1245 defer mockMountCmd.Restore() 1246 1247 mockSnapPath := filepath.Join(c.MkDir(), "/var/lib/snappy/snaps/foo_1.0.snap") 1248 makeMockFile(c, mockSnapPath) 1249 1250 _, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "squashfs") 1251 c.Assert(err, ErrorMatches, `cannot mount .*/var/lib/snappy/snaps/foo_1.0.snap \(squashfs\) at /snap/snapname/123 in preseed mode: exit status 1; some failure\n`) 1252 } 1253 1254 func (s *SystemdTestSuite) TestPreseedModeRemoveMountUnit(c *C) { 1255 mountDir := dirs.GlobalRootDir + "/snap/foo/42" 1256 1257 restore := MockOsutilIsMounted(func(path string) (bool, error) { 1258 c.Check(path, Equals, mountDir) 1259 return true, nil 1260 }) 1261 defer restore() 1262 1263 mockUmountCmd := testutil.MockCommand(c, "umount", "") 1264 defer mockUmountCmd.Restore() 1265 1266 sysd := NewEmulationMode(dirs.GlobalRootDir) 1267 1268 mountUnit := makeMockMountUnit(c, mountDir) 1269 symlinkPath := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", filepath.Base(mountUnit)) 1270 c.Assert(os.Symlink(mountUnit, symlinkPath), IsNil) 1271 c.Assert(sysd.RemoveMountUnitFile(mountDir), IsNil) 1272 1273 // the file is gone 1274 c.Check(osutil.FileExists(mountUnit), Equals, false) 1275 // unit symlink is gone 1276 c.Check(osutil.IsSymlink(symlinkPath), Equals, false) 1277 // and systemd was not called 1278 c.Check(s.argses, HasLen, 0) 1279 // umount was called 1280 c.Check(mockUmountCmd.Calls(), DeepEquals, [][]string{{"umount", "-d", "-l", mountDir}}) 1281 } 1282 1283 func (s *SystemdTestSuite) TestPreseedModeRemoveMountUnitUnmounted(c *C) { 1284 mountDir := dirs.GlobalRootDir + "/snap/foo/42" 1285 1286 restore := MockOsutilIsMounted(func(path string) (bool, error) { 1287 c.Check(path, Equals, mountDir) 1288 return false, nil 1289 }) 1290 defer restore() 1291 1292 mockUmountCmd := testutil.MockCommand(c, "umount", "") 1293 defer mockUmountCmd.Restore() 1294 1295 sysd := NewEmulationMode(dirs.GlobalRootDir) 1296 mountUnit := makeMockMountUnit(c, mountDir) 1297 symlinkPath := filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants", filepath.Base(mountUnit)) 1298 c.Assert(os.Symlink(mountUnit, symlinkPath), IsNil) 1299 1300 c.Assert(sysd.RemoveMountUnitFile(mountDir), IsNil) 1301 1302 // the file is gone 1303 c.Check(osutil.FileExists(mountUnit), Equals, false) 1304 // unit symlink is gone 1305 c.Check(osutil.IsSymlink(symlinkPath), Equals, false) 1306 // and systemd was not called 1307 c.Check(s.argses, HasLen, 0) 1308 // umount was not called 1309 c.Check(mockUmountCmd.Calls(), HasLen, 0) 1310 } 1311 1312 func (s *SystemdTestSuite) TestPreseedModeBindmountNotSupported(c *C) { 1313 sysd := NewEmulationMode(dirs.GlobalRootDir) 1314 1315 restore := squashfs.MockNeedsFuse(false) 1316 defer restore() 1317 1318 mockSnapPath := c.MkDir() 1319 1320 _, err := sysd.AddMountUnitFile("foo", "42", mockSnapPath, "/snap/snapname/123", "") 1321 c.Assert(err, ErrorMatches, `bind-mounted directory is not supported in emulation mode`) 1322 } 1323 1324 func (s *SystemdTestSuite) TestEnableInEmulationMode(c *C) { 1325 sysd := NewEmulationMode("/path") 1326 c.Assert(sysd.Enable("foo"), IsNil) 1327 1328 sysd = NewEmulationMode("") 1329 c.Assert(sysd.Enable("bar"), IsNil) 1330 c.Check(s.argses, DeepEquals, [][]string{ 1331 {"--root", "/path", "enable", "foo"}, 1332 {"--root", dirs.GlobalRootDir, "enable", "bar"}}) 1333 } 1334 1335 func (s *SystemdTestSuite) TestDisableInEmulationMode(c *C) { 1336 sysd := NewEmulationMode("/path") 1337 c.Assert(sysd.Disable("foo"), IsNil) 1338 1339 c.Check(s.argses, DeepEquals, [][]string{ 1340 {"--root", "/path", "disable", "foo"}}) 1341 } 1342 1343 func (s *SystemdTestSuite) TestMaskInEmulationMode(c *C) { 1344 sysd := NewEmulationMode("/path") 1345 c.Assert(sysd.Mask("foo"), IsNil) 1346 1347 c.Check(s.argses, DeepEquals, [][]string{ 1348 {"--root", "/path", "mask", "foo"}}) 1349 } 1350 1351 func (s *SystemdTestSuite) TestUnmaskInEmulationMode(c *C) { 1352 sysd := NewEmulationMode("/path") 1353 c.Assert(sysd.Unmask("foo"), IsNil) 1354 1355 c.Check(s.argses, DeepEquals, [][]string{ 1356 {"--root", "/path", "unmask", "foo"}}) 1357 } 1358 1359 func (s *SystemdTestSuite) TestMountHappy(c *C) { 1360 sysd := New(SystemMode, nil) 1361 1362 cmd := testutil.MockCommand(c, "systemd-mount", "") 1363 defer cmd.Restore() 1364 1365 c.Assert(sysd.Mount("foo", "bar"), IsNil) 1366 c.Check(cmd.Calls(), DeepEquals, [][]string{ 1367 {"systemd-mount", "foo", "bar"}, 1368 }) 1369 cmd.ForgetCalls() 1370 c.Assert(sysd.Mount("foo", "bar", "-o", "bind"), IsNil) 1371 c.Check(cmd.Calls(), DeepEquals, [][]string{ 1372 {"systemd-mount", "-o", "bind", "foo", "bar"}, 1373 }) 1374 } 1375 1376 func (s *SystemdTestSuite) TestMountErr(c *C) { 1377 sysd := New(SystemMode, nil) 1378 1379 cmd := testutil.MockCommand(c, "systemd-mount", `echo "failed"; exit 111`) 1380 defer cmd.Restore() 1381 1382 err := sysd.Mount("foo", "bar") 1383 c.Assert(err, ErrorMatches, "failed") 1384 c.Check(cmd.Calls(), DeepEquals, [][]string{ 1385 {"systemd-mount", "foo", "bar"}, 1386 }) 1387 } 1388 1389 func (s *SystemdTestSuite) TestUmountHappy(c *C) { 1390 sysd := New(SystemMode, nil) 1391 1392 cmd := testutil.MockCommand(c, "systemd-mount", "") 1393 defer cmd.Restore() 1394 1395 c.Assert(sysd.Umount("bar"), IsNil) 1396 c.Check(cmd.Calls(), DeepEquals, [][]string{ 1397 {"systemd-mount", "--umount", "bar"}, 1398 }) 1399 } 1400 1401 func (s *SystemdTestSuite) TestUmountErr(c *C) { 1402 sysd := New(SystemMode, nil) 1403 1404 cmd := testutil.MockCommand(c, "systemd-mount", `echo "failed"; exit 111`) 1405 defer cmd.Restore() 1406 1407 err := sysd.Umount("bar") 1408 c.Assert(err, ErrorMatches, "failed") 1409 c.Check(cmd.Calls(), DeepEquals, [][]string{ 1410 {"systemd-mount", "--umount", "bar"}, 1411 }) 1412 } 1413 1414 func (s *SystemdTestSuite) TestCurrentUsageFamilyReallyInvalid(c *C) { 1415 s.outs = [][]byte{ 1416 []byte(`gahstringsarehard`), 1417 []byte(`gahstringsarehard`), 1418 } 1419 sysd := New(SystemMode, s.rep) 1420 _, err := sysd.CurrentMemoryUsage("bar.service") 1421 c.Assert(err, ErrorMatches, `invalid property format from systemd for MemoryCurrent \(got gahstringsarehard\)`) 1422 _, err = sysd.CurrentTasksCount("bar.service") 1423 c.Assert(err, ErrorMatches, `invalid property format from systemd for TasksCurrent \(got gahstringsarehard\)`) 1424 c.Check(s.argses, DeepEquals, [][]string{ 1425 {"show", "--property", "MemoryCurrent", "bar.service"}, 1426 {"show", "--property", "TasksCurrent", "bar.service"}, 1427 }) 1428 } 1429 1430 func (s *SystemdTestSuite) TestCurrentUsageFamilyInactive(c *C) { 1431 s.outs = [][]byte{ 1432 []byte(`MemoryCurrent=[not set]`), 1433 []byte(`TasksCurrent=[not set]`), 1434 } 1435 sysd := New(SystemMode, s.rep) 1436 _, err := sysd.CurrentMemoryUsage("bar.service") 1437 c.Assert(err, ErrorMatches, "memory usage unavailable") 1438 _, err = sysd.CurrentTasksCount("bar.service") 1439 c.Assert(err, ErrorMatches, "tasks count unavailable") 1440 c.Check(s.argses, DeepEquals, [][]string{ 1441 {"show", "--property", "MemoryCurrent", "bar.service"}, 1442 {"show", "--property", "TasksCurrent", "bar.service"}, 1443 }) 1444 } 1445 1446 func (s *SystemdTestSuite) TestCurrentUsageFamilyInvalid(c *C) { 1447 s.outs = [][]byte{ 1448 []byte(`MemoryCurrent=blahhhhhhhhhhhhhh`), 1449 []byte(`TasksCurrent=blahhhhhhhhhhhhhh`), 1450 } 1451 sysd := New(SystemMode, s.rep) 1452 _, err := sysd.CurrentMemoryUsage("bar.service") 1453 c.Assert(err, ErrorMatches, `invalid property value from systemd for MemoryCurrent: cannot parse "blahhhhhhhhhhhhhh" as an integer`) 1454 _, err = sysd.CurrentTasksCount("bar.service") 1455 c.Assert(err, ErrorMatches, `invalid property value from systemd for TasksCurrent: cannot parse "blahhhhhhhhhhhhhh" as an integer`) 1456 c.Check(s.argses, DeepEquals, [][]string{ 1457 {"show", "--property", "MemoryCurrent", "bar.service"}, 1458 {"show", "--property", "TasksCurrent", "bar.service"}, 1459 }) 1460 } 1461 1462 func (s *SystemdTestSuite) TestCurrentUsageFamilyHappy(c *C) { 1463 s.outs = [][]byte{ 1464 []byte(`MemoryCurrent=1024`), 1465 []byte(`MemoryCurrent=18446744073709551615`), // special value from systemd bug 1466 []byte(`TasksCurrent=10`), 1467 } 1468 sysd := New(SystemMode, s.rep) 1469 memUsage, err := sysd.CurrentMemoryUsage("bar.service") 1470 c.Assert(err, IsNil) 1471 c.Assert(memUsage, Equals, quantity.SizeKiB) 1472 memUsage, err = sysd.CurrentMemoryUsage("bar.service") 1473 c.Assert(err, IsNil) 1474 const sixteenExb = quantity.Size(1<<64 - 1) 1475 c.Assert(memUsage, Equals, sixteenExb) 1476 tasksUsage, err := sysd.CurrentTasksCount("bar.service") 1477 c.Assert(tasksUsage, Equals, uint64(10)) 1478 c.Assert(err, IsNil) 1479 c.Check(s.argses, DeepEquals, [][]string{ 1480 {"show", "--property", "MemoryCurrent", "bar.service"}, 1481 {"show", "--property", "MemoryCurrent", "bar.service"}, 1482 {"show", "--property", "TasksCurrent", "bar.service"}, 1483 }) 1484 } 1485 1486 func (s *SystemdTestSuite) TestInactiveEnterTimestampZero(c *C) { 1487 s.outs = [][]byte{ 1488 []byte(`InactiveEnterTimestamp=`), 1489 } 1490 sysd := New(SystemMode, s.rep) 1491 stamp, err := sysd.InactiveEnterTimestamp("bar.service") 1492 c.Assert(err, IsNil) 1493 c.Check(s.argses, DeepEquals, [][]string{ 1494 {"show", "--property", "InactiveEnterTimestamp", "bar.service"}, 1495 }) 1496 c.Check(stamp.IsZero(), Equals, true) 1497 } 1498 1499 func (s *SystemdTestSuite) TestInactiveEnterTimestampValidWhitespace(c *C) { 1500 s.outs = [][]byte{ 1501 []byte(`InactiveEnterTimestamp=Fri 2021-04-16 15:32:21 UTC 1502 `), 1503 } 1504 1505 stamp, err := New(SystemMode, s.rep).InactiveEnterTimestamp("bar.service") 1506 c.Assert(err, IsNil) 1507 c.Check(s.argses, DeepEquals, [][]string{ 1508 {"show", "--property", "InactiveEnterTimestamp", "bar.service"}, 1509 }) 1510 c.Check(stamp.Equal(time.Date(2021, time.April, 16, 15, 32, 21, 0, time.UTC)), Equals, true) 1511 } 1512 1513 func (s *SystemdTestSuite) TestInactiveEnterTimestampValid(c *C) { 1514 s.outs = [][]byte{ 1515 []byte(`InactiveEnterTimestamp=Fri 2021-04-16 15:32:21 UTC`), 1516 } 1517 1518 stamp, err := New(SystemMode, s.rep).InactiveEnterTimestamp("bar.service") 1519 c.Assert(err, IsNil) 1520 c.Check(s.argses, DeepEquals, [][]string{ 1521 {"show", "--property", "InactiveEnterTimestamp", "bar.service"}, 1522 }) 1523 c.Check(stamp.Equal(time.Date(2021, time.April, 16, 15, 32, 21, 0, time.UTC)), Equals, true) 1524 } 1525 1526 func (s *SystemdTestSuite) TestInactiveEnterTimestampFailure(c *C) { 1527 s.outs = [][]byte{ 1528 []byte(`mocked failure`), 1529 } 1530 s.errors = []error{ 1531 fmt.Errorf("mocked failure"), 1532 } 1533 stamp, err := New(SystemMode, s.rep).InactiveEnterTimestamp("bar.service") 1534 c.Assert(err, ErrorMatches, "mocked failure") 1535 c.Check(stamp.IsZero(), Equals, true) 1536 } 1537 1538 func (s *SystemdTestSuite) TestInactiveEnterTimestampMalformed(c *C) { 1539 s.outs = [][]byte{ 1540 []byte(`InactiveEnterTimestamp`), 1541 []byte(``), 1542 []byte(`some random garbage 1543 with newlines`), 1544 } 1545 sysd := New(SystemMode, s.rep) 1546 for i := 0; i < len(s.outs); i++ { 1547 s.argses = nil 1548 stamp, err := sysd.InactiveEnterTimestamp("bar.service") 1549 c.Assert(err.Error(), testutil.Contains, `invalid property format from systemd for InactiveEnterTimestamp (got`) 1550 c.Check(s.argses, DeepEquals, [][]string{ 1551 {"show", "--property", "InactiveEnterTimestamp", "bar.service"}, 1552 }) 1553 c.Check(stamp.IsZero(), Equals, true) 1554 } 1555 } 1556 1557 func (s *SystemdTestSuite) TestInactiveEnterTimestampMalformedMore(c *C) { 1558 s.outs = [][]byte{ 1559 []byte(`InactiveEnterTimestamp=0`), // 0 is valid for InactiveEnterTimestampMonotonic 1560 } 1561 sysd := New(SystemMode, s.rep) 1562 1563 stamp, err := sysd.InactiveEnterTimestamp("bar.service") 1564 1565 c.Assert(err, ErrorMatches, `internal error: systemctl time output \(0\) is malformed`) 1566 c.Check(s.argses, DeepEquals, [][]string{ 1567 {"show", "--property", "InactiveEnterTimestamp", "bar.service"}, 1568 }) 1569 c.Check(stamp.IsZero(), Equals, true) 1570 } 1571 1572 type systemdErrorSuite struct{} 1573 1574 var _ = Suite(&systemdErrorSuite{}) 1575 1576 func (s *systemdErrorSuite) TestErrorStringNormalError(c *C) { 1577 systemctl := testutil.MockCommand(c, "systemctl", `echo "I fail"; exit 11`) 1578 defer systemctl.Restore() 1579 1580 _, err := Version() 1581 c.Check(err, ErrorMatches, `systemctl command \[--version\] failed with exit status 11: I fail\n`) 1582 } 1583 1584 func (s *systemdErrorSuite) TestErrorStringNoOutput(c *C) { 1585 systemctl := testutil.MockCommand(c, "systemctl", `exit 22`) 1586 defer systemctl.Restore() 1587 1588 _, err := Version() 1589 c.Check(err, ErrorMatches, `systemctl command \[--version\] failed with exit status 22`) 1590 } 1591 1592 func (s *systemdErrorSuite) TestErrorStringNoSystemctl(c *C) { 1593 oldPath := os.Getenv("PATH") 1594 os.Setenv("PATH", "/xxx") 1595 defer func() { os.Setenv("PATH", oldPath) }() 1596 1597 _, err := Version() 1598 c.Check(err, ErrorMatches, `systemctl command \[--version\] failed with: exec: "systemctl": executable file not found in \$PATH`) 1599 }