github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/hookstate/ctlcmd/services_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2017 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 ctlcmd_test 21 22 import ( 23 "context" 24 "fmt" 25 "sort" 26 27 . "gopkg.in/check.v1" 28 29 "github.com/snapcore/snapd/client" 30 "github.com/snapcore/snapd/dirs" 31 "github.com/snapcore/snapd/overlord/auth" 32 "github.com/snapcore/snapd/overlord/hookstate" 33 "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" 34 "github.com/snapcore/snapd/overlord/hookstate/hooktest" 35 "github.com/snapcore/snapd/overlord/servicestate" 36 "github.com/snapcore/snapd/overlord/snapstate" 37 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 38 "github.com/snapcore/snapd/overlord/state" 39 "github.com/snapcore/snapd/snap" 40 "github.com/snapcore/snapd/snap/snaptest" 41 "github.com/snapcore/snapd/store" 42 "github.com/snapcore/snapd/store/storetest" 43 "github.com/snapcore/snapd/systemd" 44 "github.com/snapcore/snapd/testutil" 45 ) 46 47 type fakeStore struct { 48 storetest.Store 49 } 50 51 func (f *fakeStore) SnapAction(_ context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 52 if assertQuery != nil { 53 panic("no assertion query support") 54 } 55 if actions[0].Action == "install" { 56 installs := make([]store.SnapActionResult, 0, len(actions)) 57 for _, a := range actions { 58 snapName, instanceKey := snap.SplitInstanceName(a.InstanceName) 59 if instanceKey != "" { 60 panic(fmt.Sprintf("unexpected instance name %q in snap install action", a.InstanceName)) 61 } 62 63 installs = append(installs, store.SnapActionResult{Info: &snap.Info{ 64 DownloadInfo: snap.DownloadInfo{ 65 Size: 1, 66 }, 67 SideInfo: snap.SideInfo{ 68 RealName: snapName, 69 Revision: snap.R(2), 70 }, 71 Architectures: []string{"all"}, 72 }}) 73 } 74 75 return installs, nil, nil 76 } 77 78 snaps := []store.SnapActionResult{{Info: &snap.Info{ 79 SideInfo: snap.SideInfo{ 80 RealName: "test-snap", 81 Revision: snap.R(2), 82 SnapID: "test-snap-id", 83 }, 84 Architectures: []string{"all"}, 85 }}, {Info: &snap.Info{ 86 SideInfo: snap.SideInfo{ 87 RealName: "other-snap", 88 Revision: snap.R(2), 89 SnapID: "other-snap-id", 90 }, 91 Architectures: []string{"all"}, 92 }}} 93 return snaps, nil, nil 94 } 95 96 type servicectlSuite struct { 97 testutil.BaseTest 98 st *state.State 99 fakeStore fakeStore 100 mockContext *hookstate.Context 101 mockHandler *hooktest.MockHandler 102 } 103 104 var _ = Suite(&servicectlSuite{}) 105 106 const testSnapYaml = `name: test-snap 107 version: 1.0 108 summary: test-snap 109 apps: 110 normal-app: 111 command: bin/dummy 112 test-service: 113 command: bin/service 114 daemon: simple 115 reload-command: bin/reload 116 another-service: 117 command: bin/service 118 daemon: simple 119 reload-command: bin/reload 120 ` 121 122 const otherSnapYaml = `name: other-snap 123 version: 1.0 124 summary: other-snap 125 apps: 126 test-service: 127 command: bin/service 128 daemon: simple 129 reload-command: bin/reload 130 ` 131 132 func mockServiceChangeFunc(testServiceControlInputs func(appInfos []*snap.AppInfo, inst *servicestate.Instruction)) func() { 133 return ctlcmd.MockServicestateControlFunc(func(st *state.State, appInfos []*snap.AppInfo, inst *servicestate.Instruction, flags *servicestate.Flags, context *hookstate.Context) ([]*state.TaskSet, error) { 134 testServiceControlInputs(appInfos, inst) 135 return nil, fmt.Errorf("forced error") 136 }) 137 } 138 139 func (s *servicectlSuite) SetUpTest(c *C) { 140 s.BaseTest.SetUpTest(c) 141 oldRoot := dirs.GlobalRootDir 142 dirs.SetRootDir(c.MkDir()) 143 144 testutil.MockCommand(c, "systemctl", "") 145 146 s.BaseTest.AddCleanup(func() { 147 dirs.SetRootDir(oldRoot) 148 }) 149 s.BaseTest.AddCleanup(snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})) 150 151 s.mockHandler = hooktest.NewMockHandler() 152 153 s.st = state.New(nil) 154 s.st.Lock() 155 defer s.st.Unlock() 156 157 snapstate.ReplaceStore(s.st, &s.fakeStore) 158 159 // mock installed snaps 160 info1 := snaptest.MockSnapCurrent(c, string(testSnapYaml), &snap.SideInfo{ 161 Revision: snap.R(1), 162 }) 163 info2 := snaptest.MockSnapCurrent(c, string(otherSnapYaml), &snap.SideInfo{ 164 Revision: snap.R(1), 165 }) 166 snapstate.Set(s.st, info1.InstanceName(), &snapstate.SnapState{ 167 Active: true, 168 Sequence: []*snap.SideInfo{ 169 { 170 RealName: info1.SnapName(), 171 Revision: info1.Revision, 172 SnapID: "test-snap-id", 173 }, 174 }, 175 Current: info1.Revision, 176 }) 177 snapstate.Set(s.st, info2.InstanceName(), &snapstate.SnapState{ 178 Active: true, 179 Sequence: []*snap.SideInfo{ 180 { 181 RealName: info2.SnapName(), 182 Revision: info2.Revision, 183 SnapID: "other-snap-id", 184 }, 185 }, 186 Current: info2.Revision, 187 }) 188 189 task := s.st.NewTask("test-task", "my test task") 190 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "test-hook"} 191 192 var err error 193 s.mockContext, err = hookstate.NewContext(task, task.State(), setup, s.mockHandler, "") 194 c.Assert(err, IsNil) 195 196 s.st.Set("seeded", true) 197 s.st.Set("refresh-privacy-key", "privacy-key") 198 s.AddCleanup(snapstatetest.UseFallbackDeviceModel()) 199 } 200 201 func (s *servicectlSuite) TestStopCommand(c *C) { 202 var serviceChangeFuncCalled bool 203 restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { 204 serviceChangeFuncCalled = true 205 c.Assert(appInfos, HasLen, 1) 206 c.Assert(appInfos[0].Name, Equals, "test-service") 207 c.Assert(inst, DeepEquals, &servicestate.Instruction{ 208 Action: "stop", 209 Names: []string{"test-snap.test-service"}, 210 StopOptions: client.StopOptions{ 211 Disable: false, 212 }, 213 }, 214 ) 215 }) 216 defer restore() 217 _, _, err := ctlcmd.Run(s.mockContext, []string{"stop", "test-snap.test-service"}, 0) 218 c.Assert(err, NotNil) 219 c.Check(err, ErrorMatches, "forced error") 220 c.Assert(serviceChangeFuncCalled, Equals, true) 221 } 222 223 func (s *servicectlSuite) TestStopCommandUnknownService(c *C) { 224 var serviceChangeFuncCalled bool 225 restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { 226 serviceChangeFuncCalled = true 227 }) 228 defer restore() 229 _, _, err := ctlcmd.Run(s.mockContext, []string{"stop", "test-snap.fooservice"}, 0) 230 c.Assert(err, NotNil) 231 c.Assert(err, ErrorMatches, `unknown service: "test-snap.fooservice"`) 232 c.Assert(serviceChangeFuncCalled, Equals, false) 233 } 234 235 func (s *servicectlSuite) TestStopCommandFailsOnOtherSnap(c *C) { 236 var serviceChangeFuncCalled bool 237 restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { 238 serviceChangeFuncCalled = true 239 }) 240 defer restore() 241 // verify that snapctl is not allowed to control services of other snaps (only the one of its hook) 242 _, _, err := ctlcmd.Run(s.mockContext, []string{"stop", "other-snap.test-service"}, 0) 243 c.Check(err, NotNil) 244 c.Assert(err, ErrorMatches, `unknown service: "other-snap.test-service"`) 245 c.Assert(serviceChangeFuncCalled, Equals, false) 246 } 247 248 func (s *servicectlSuite) TestStartCommand(c *C) { 249 var serviceChangeFuncCalled bool 250 restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { 251 serviceChangeFuncCalled = true 252 c.Assert(appInfos, HasLen, 1) 253 c.Assert(appInfos[0].Name, Equals, "test-service") 254 c.Assert(inst, DeepEquals, &servicestate.Instruction{ 255 Action: "start", 256 Names: []string{"test-snap.test-service"}, 257 StartOptions: client.StartOptions{ 258 Enable: false, 259 }, 260 }, 261 ) 262 }) 263 defer restore() 264 _, _, err := ctlcmd.Run(s.mockContext, []string{"start", "test-snap.test-service"}, 0) 265 c.Check(err, NotNil) 266 c.Check(err, ErrorMatches, "forced error") 267 c.Assert(serviceChangeFuncCalled, Equals, true) 268 } 269 270 func (s *servicectlSuite) TestRestartCommand(c *C) { 271 var serviceChangeFuncCalled bool 272 restore := mockServiceChangeFunc(func(appInfos []*snap.AppInfo, inst *servicestate.Instruction) { 273 serviceChangeFuncCalled = true 274 c.Assert(appInfos, HasLen, 1) 275 c.Assert(appInfos[0].Name, Equals, "test-service") 276 c.Assert(inst, DeepEquals, &servicestate.Instruction{ 277 Action: "restart", 278 Names: []string{"test-snap.test-service"}, 279 RestartOptions: client.RestartOptions{ 280 Reload: false, 281 }, 282 }, 283 ) 284 }) 285 defer restore() 286 _, _, err := ctlcmd.Run(s.mockContext, []string{"restart", "test-snap.test-service"}, 0) 287 c.Check(err, NotNil) 288 c.Check(err, ErrorMatches, "forced error") 289 c.Assert(serviceChangeFuncCalled, Equals, true) 290 } 291 292 func (s *servicectlSuite) TestConflictingChange(c *C) { 293 s.st.Lock() 294 task := s.st.NewTask("link-snap", "conflicting task") 295 snapsup := snapstate.SnapSetup{ 296 SideInfo: &snap.SideInfo{ 297 RealName: "test-snap", 298 SnapID: "test-snap-id-1", 299 Revision: snap.R(1), 300 }, 301 } 302 task.Set("snap-setup", snapsup) 303 chg := s.st.NewChange("conflicting change", "install change") 304 chg.AddTask(task) 305 s.st.Unlock() 306 307 _, _, err := ctlcmd.Run(s.mockContext, []string{"start", "test-snap.test-service"}, 0) 308 c.Check(err, NotNil) 309 c.Check(err, ErrorMatches, `snap "test-snap" has "conflicting change" change in progress`) 310 } 311 312 var ( 313 installTaskKinds = []string{ 314 "prerequisites", 315 "download-snap", 316 "validate-snap", 317 "mount-snap", 318 "copy-snap-data", 319 "setup-profiles", 320 "link-snap", 321 "auto-connect", 322 "set-auto-aliases", 323 "setup-aliases", 324 "run-hook[install]", 325 "start-snap-services", 326 "run-hook[configure]", 327 "run-hook[check-health]", 328 } 329 330 refreshTaskKinds = []string{ 331 "prerequisites", 332 "download-snap", 333 "validate-snap", 334 "mount-snap", 335 "run-hook[pre-refresh]", 336 "stop-snap-services", 337 "remove-aliases", 338 "unlink-current-snap", 339 "copy-snap-data", 340 "setup-profiles", 341 "link-snap", 342 "auto-connect", 343 "set-auto-aliases", 344 "setup-aliases", 345 "run-hook[post-refresh]", 346 "start-snap-services", 347 "cleanup", 348 "run-hook[configure]", 349 "run-hook[check-health]", 350 } 351 ) 352 353 func (s *servicectlSuite) TestQueuedCommands(c *C) { 354 s.st.Lock() 355 356 chg := s.st.NewChange("install change", "install change") 357 installed, tts, err := snapstate.InstallMany(s.st, []string{"one", "two"}, 0) 358 c.Assert(err, IsNil) 359 c.Check(installed, DeepEquals, []string{"one", "two"}) 360 c.Assert(tts, HasLen, 2) 361 c.Assert(taskKinds(tts[0].Tasks()), DeepEquals, installTaskKinds) 362 c.Assert(taskKinds(tts[1].Tasks()), DeepEquals, installTaskKinds) 363 chg.AddAll(tts[0]) 364 chg.AddAll(tts[1]) 365 366 s.st.Unlock() 367 368 for _, ts := range tts { 369 tsTasks := ts.Tasks() 370 // assumes configure task is last 371 task := tsTasks[len(tsTasks)-1] 372 c.Assert(task.Kind(), Equals, "run-hook") 373 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "configure"} 374 context, err := hookstate.NewContext(task, task.State(), setup, s.mockHandler, "") 375 c.Assert(err, IsNil) 376 377 _, _, err = ctlcmd.Run(context, []string{"stop", "test-snap.test-service"}, 0) 378 c.Check(err, IsNil) 379 _, _, err = ctlcmd.Run(context, []string{"start", "test-snap.test-service"}, 0) 380 c.Check(err, IsNil) 381 _, _, err = ctlcmd.Run(context, []string{"restart", "test-snap.test-service"}, 0) 382 c.Check(err, IsNil) 383 } 384 385 s.st.Lock() 386 defer s.st.Unlock() 387 388 expectedTaskKinds := append(installTaskKinds, "exec-command", "service-control", "exec-command", "service-control", "exec-command", "service-control") 389 checkLaneTasks := func(lane int) { 390 laneTasks := chg.LaneTasks(lane) 391 c.Assert(taskKinds(laneTasks), DeepEquals, expectedTaskKinds) 392 c.Check(laneTasks[12].Summary(), Matches, `Run configure hook of .* snap if present`) 393 c.Check(laneTasks[14].Summary(), Equals, "stop of [test-snap.test-service]") 394 c.Check(laneTasks[16].Summary(), Equals, "start of [test-snap.test-service]") 395 c.Check(laneTasks[18].Summary(), Equals, "restart of [test-snap.test-service]") 396 } 397 checkLaneTasks(1) 398 checkLaneTasks(2) 399 } 400 401 func (s *servicectlSuite) testQueueCommandsOrdering(c *C, finalTaskKind string) { 402 s.st.Lock() 403 404 chg := s.st.NewChange("seeding change", "seeding change") 405 finalTask := s.st.NewTask(finalTaskKind, "") 406 chg.AddTask(finalTask) 407 configure := s.st.NewTask("run-hook", "") 408 chg.AddTask(configure) 409 410 s.st.Unlock() 411 412 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "configure"} 413 context, err := hookstate.NewContext(configure, configure.State(), setup, s.mockHandler, "") 414 c.Assert(err, IsNil) 415 416 _, _, err = ctlcmd.Run(context, []string{"stop", "test-snap.test-service"}, 0) 417 c.Check(err, IsNil) 418 _, _, err = ctlcmd.Run(context, []string{"start", "test-snap.test-service"}, 0) 419 c.Check(err, IsNil) 420 421 s.st.Lock() 422 defer s.st.Unlock() 423 424 var finalWaitTasks []string 425 for _, t := range finalTask.WaitTasks() { 426 taskInfo := fmt.Sprintf("%s:%s", t.Kind(), t.Summary()) 427 finalWaitTasks = append(finalWaitTasks, taskInfo) 428 429 var wait []string 430 var hasRunHook bool 431 for _, wt := range t.WaitTasks() { 432 if wt.Kind() != "run-hook" { 433 taskInfo = fmt.Sprintf("%s:%s", wt.Kind(), wt.Summary()) 434 wait = append(wait, taskInfo) 435 } else { 436 hasRunHook = true 437 } 438 } 439 c.Assert(hasRunHook, Equals, true) 440 441 switch t.Kind() { 442 case "exec-command": 443 var argv []string 444 c.Assert(t.Get("argv", &argv), IsNil) 445 c.Check(argv, HasLen, 3) 446 switch argv[1] { 447 case "stop": 448 c.Check(wait, HasLen, 0) 449 case "start": 450 c.Check(wait, DeepEquals, []string{ 451 `exec-command:stop of [test-snap.test-service]`, 452 `service-control:Run service command "stop" for services ["test-service"] of snap "test-snap"`}) 453 default: 454 c.Fatalf("unexpected command: %q", argv[1]) 455 } 456 case "service-control": 457 var sa servicestate.ServiceAction 458 c.Assert(t.Get("service-action", &sa), IsNil) 459 c.Check(sa.Services, DeepEquals, []string{"test-service"}) 460 switch sa.Action { 461 case "stop": 462 c.Check(wait, DeepEquals, []string{ 463 "exec-command:stop of [test-snap.test-service]"}) 464 case "start": 465 c.Check(wait, DeepEquals, []string{ 466 "exec-command:start of [test-snap.test-service]", 467 "exec-command:stop of [test-snap.test-service]", 468 `service-control:Run service command "stop" for services ["test-service"] of snap "test-snap"`}) 469 } 470 default: 471 c.Fatalf("unexpected task: %s", t.Kind()) 472 } 473 474 } 475 c.Check(finalWaitTasks, DeepEquals, []string{ 476 `exec-command:stop of [test-snap.test-service]`, 477 `service-control:Run service command "stop" for services ["test-service"] of snap "test-snap"`, 478 `exec-command:start of [test-snap.test-service]`, 479 `service-control:Run service command "start" for services ["test-service"] of snap "test-snap"`}) 480 c.Check(finalTask.HaltTasks(), HasLen, 0) 481 } 482 483 func (s *servicectlSuite) TestQueuedCommandsRunBeforeMarkSeeded(c *C) { 484 s.testQueueCommandsOrdering(c, "mark-seeded") 485 } 486 487 func (s *servicectlSuite) TestQueuedCommandsRunBeforeSetModel(c *C) { 488 s.testQueueCommandsOrdering(c, "set-model") 489 } 490 491 func (s *servicectlSuite) TestQueuedCommandsUpdateMany(c *C) { 492 oldAutoAliases := snapstate.AutoAliases 493 snapstate.AutoAliases = func(*state.State, *snap.Info) (map[string]string, error) { 494 return nil, nil 495 } 496 defer func() { snapstate.AutoAliases = oldAutoAliases }() 497 498 s.st.Lock() 499 500 chg := s.st.NewChange("update many change", "update change") 501 installed, tts, err := snapstate.UpdateMany(context.Background(), s.st, []string{"test-snap", "other-snap"}, 0, nil) 502 c.Assert(err, IsNil) 503 sort.Strings(installed) 504 c.Check(installed, DeepEquals, []string{"other-snap", "test-snap"}) 505 c.Assert(tts, HasLen, 3) 506 c.Assert(taskKinds(tts[0].Tasks()), DeepEquals, refreshTaskKinds) 507 c.Assert(taskKinds(tts[1].Tasks()), DeepEquals, refreshTaskKinds) 508 c.Assert(taskKinds(tts[2].Tasks()), DeepEquals, []string{"check-rerefresh"}) 509 c.Assert(tts[2].Tasks()[0].Kind(), Equals, "check-rerefresh") 510 chg.AddAll(tts[0]) 511 chg.AddAll(tts[1]) 512 513 s.st.Unlock() 514 515 for _, ts := range tts[:2] { 516 tsTasks := ts.Tasks() 517 // assumes configure task is last 518 task := tsTasks[len(tsTasks)-1] 519 c.Assert(task.Kind(), Equals, "run-hook") 520 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "configure"} 521 context, err := hookstate.NewContext(task, task.State(), setup, s.mockHandler, "") 522 c.Assert(err, IsNil) 523 524 _, _, err = ctlcmd.Run(context, []string{"stop", "test-snap.test-service"}, 0) 525 c.Check(err, IsNil) 526 _, _, err = ctlcmd.Run(context, []string{"start", "test-snap.test-service"}, 0) 527 c.Check(err, IsNil) 528 _, _, err = ctlcmd.Run(context, []string{"restart", "test-snap.test-service"}, 0) 529 c.Check(err, IsNil) 530 } 531 532 s.st.Lock() 533 defer s.st.Unlock() 534 535 expectedTaskKinds := append(refreshTaskKinds, "exec-command", "service-control", "exec-command", "service-control", "exec-command", "service-control") 536 for i := 1; i <= 2; i++ { 537 laneTasks := chg.LaneTasks(i) 538 c.Assert(taskKinds(laneTasks), DeepEquals, expectedTaskKinds) 539 c.Check(laneTasks[17].Summary(), Matches, `Run configure hook of .* snap if present`) 540 c.Check(laneTasks[19].Summary(), Equals, "stop of [test-snap.test-service]") 541 c.Check(laneTasks[20].Summary(), Equals, `Run service command "stop" for services ["test-service"] of snap "test-snap"`) 542 c.Check(laneTasks[21].Summary(), Equals, "start of [test-snap.test-service]") 543 c.Check(laneTasks[22].Summary(), Equals, `Run service command "start" for services ["test-service"] of snap "test-snap"`) 544 c.Check(laneTasks[23].Summary(), Equals, "restart of [test-snap.test-service]") 545 c.Check(laneTasks[24].Summary(), Equals, `Run service command "restart" for services ["test-service"] of snap "test-snap"`) 546 } 547 } 548 549 func (s *servicectlSuite) TestQueuedCommandsSingleLane(c *C) { 550 s.st.Lock() 551 552 chg := s.st.NewChange("install change", "install change") 553 ts, err := snapstate.Install(context.Background(), s.st, "one", &snapstate.RevisionOptions{Revision: snap.R(1)}, 0, snapstate.Flags{}) 554 c.Assert(err, IsNil) 555 c.Assert(taskKinds(ts.Tasks()), DeepEquals, installTaskKinds) 556 chg.AddAll(ts) 557 558 s.st.Unlock() 559 560 tsTasks := ts.Tasks() 561 // assumes configure task is last 562 task := tsTasks[len(tsTasks)-1] 563 c.Assert(task.Kind(), Equals, "run-hook") 564 setup := &hookstate.HookSetup{Snap: "test-snap", Revision: snap.R(1), Hook: "configure"} 565 context, err := hookstate.NewContext(task, task.State(), setup, s.mockHandler, "") 566 c.Assert(err, IsNil) 567 568 _, _, err = ctlcmd.Run(context, []string{"stop", "test-snap.test-service"}, 0) 569 c.Check(err, IsNil) 570 _, _, err = ctlcmd.Run(context, []string{"start", "test-snap.test-service"}, 0) 571 c.Check(err, IsNil) 572 _, _, err = ctlcmd.Run(context, []string{"restart", "test-snap.test-service"}, 0) 573 c.Check(err, IsNil) 574 575 s.st.Lock() 576 defer s.st.Unlock() 577 578 laneTasks := chg.LaneTasks(0) 579 c.Assert(taskKinds(laneTasks), DeepEquals, append(installTaskKinds, "exec-command", "service-control", "exec-command", "service-control", "exec-command", "service-control")) 580 c.Check(laneTasks[12].Summary(), Matches, `Run configure hook of .* snap if present`) 581 c.Check(laneTasks[14].Summary(), Equals, "stop of [test-snap.test-service]") 582 c.Check(laneTasks[16].Summary(), Equals, "start of [test-snap.test-service]") 583 c.Check(laneTasks[18].Summary(), Equals, "restart of [test-snap.test-service]") 584 } 585 586 func (s *servicectlSuite) TestTwoServices(c *C) { 587 restore := systemd.MockSystemctl(func(args ...string) (buf []byte, err error) { 588 c.Assert(args[0], Equals, "show") 589 c.Check(args[2], Matches, `snap\.test-snap\.\w+-service\.service`) 590 return []byte(fmt.Sprintf(`Id=%s 591 Type=simple 592 ActiveState=active 593 UnitFileState=enabled 594 `, args[2])), nil 595 }) 596 defer restore() 597 598 stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"services"}, 0) 599 c.Assert(err, IsNil) 600 c.Check(string(stdout), Equals, ` 601 Service Startup Current Notes 602 test-snap.another-service enabled active - 603 test-snap.test-service enabled active - 604 `[1:]) 605 c.Check(string(stderr), Equals, "") 606 } 607 608 func (s *servicectlSuite) TestServices(c *C) { 609 restore := systemd.MockSystemctl(func(args ...string) (buf []byte, err error) { 610 c.Assert(args[0], Equals, "show") 611 c.Check(args[2], Equals, "snap.test-snap.test-service.service") 612 return []byte(`Id=snap.test-snap.test-service.service 613 Type=simple 614 ActiveState=active 615 UnitFileState=enabled 616 `), nil 617 }) 618 defer restore() 619 620 stdout, stderr, err := ctlcmd.Run(s.mockContext, []string{"services", "test-snap.test-service"}, 0) 621 c.Assert(err, IsNil) 622 c.Check(string(stdout), Equals, ` 623 Service Startup Current Notes 624 test-snap.test-service enabled active - 625 `[1:]) 626 c.Check(string(stderr), Equals, "") 627 }