github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/ifacestate/helpers_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2018 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 ifacestate_test 21 22 import ( 23 "errors" 24 "fmt" 25 "io/ioutil" 26 "os" 27 "path/filepath" 28 "strings" 29 30 . "gopkg.in/check.v1" 31 32 "github.com/snapcore/snapd/dirs" 33 "github.com/snapcore/snapd/interfaces" 34 "github.com/snapcore/snapd/interfaces/ifacetest" 35 "github.com/snapcore/snapd/logger" 36 "github.com/snapcore/snapd/osutil" 37 "github.com/snapcore/snapd/overlord" 38 "github.com/snapcore/snapd/overlord/ifacestate" 39 "github.com/snapcore/snapd/overlord/snapstate" 40 "github.com/snapcore/snapd/overlord/state" 41 "github.com/snapcore/snapd/snap" 42 "github.com/snapcore/snapd/snap/snaptest" 43 "github.com/snapcore/snapd/timings" 44 ) 45 46 type helpersSuite struct { 47 st *state.State 48 } 49 50 var _ = Suite(&helpersSuite{}) 51 52 func (s *helpersSuite) SetUpTest(c *C) { 53 s.st = state.New(nil) 54 dirs.SetRootDir(c.MkDir()) 55 } 56 57 func (s *helpersSuite) TearDownTest(c *C) { 58 dirs.SetRootDir("") 59 } 60 61 func (s *helpersSuite) TestIdentityMapper(c *C) { 62 var m ifacestate.SnapMapper = &ifacestate.IdentityMapper{} 63 64 // Nothing is altered. 65 c.Assert(m.RemapSnapFromState("example"), Equals, "example") 66 c.Assert(m.RemapSnapToState("example"), Equals, "example") 67 c.Assert(m.RemapSnapFromRequest("example"), Equals, "example") 68 69 c.Assert(m.SystemSnapName(), Equals, "unknown") 70 } 71 72 func (s *helpersSuite) TestCoreCoreSystemMapper(c *C) { 73 var m ifacestate.SnapMapper = &ifacestate.CoreCoreSystemMapper{} 74 75 // Snaps are not renamed when interacting with the state. 76 c.Assert(m.RemapSnapFromState("core"), Equals, "core") 77 c.Assert(m.RemapSnapToState("core"), Equals, "core") 78 79 // The "core" snap is renamed to the "system" in API response 80 // and back in the API requests. 81 c.Assert(m.RemapSnapFromRequest("system"), Equals, "core") 82 83 // Other snap names are unchanged. 84 c.Assert(m.RemapSnapFromState("potato"), Equals, "potato") 85 c.Assert(m.RemapSnapToState("potato"), Equals, "potato") 86 c.Assert(m.RemapSnapFromRequest("potato"), Equals, "potato") 87 88 c.Assert(m.SystemSnapName(), Equals, "core") 89 } 90 91 func (s *helpersSuite) TestCoreSnapdSystemMapper(c *C) { 92 var m ifacestate.SnapMapper = &ifacestate.CoreSnapdSystemMapper{} 93 94 // The "snapd" snap is renamed to the "core" in when saving the state 95 // and back when loading the state. 96 c.Assert(m.RemapSnapFromState("core"), Equals, "snapd") 97 c.Assert(m.RemapSnapToState("snapd"), Equals, "core") 98 99 // The "snapd" snap is renamed to the "system" in API response and back in 100 // the API requests. 101 c.Assert(m.RemapSnapFromRequest("system"), Equals, "snapd") 102 103 // The "core" snap is also renamed to "snapd" in API requests, for 104 // compatibility. 105 c.Assert(m.RemapSnapFromRequest("core"), Equals, "snapd") 106 107 // Other snap names are unchanged. 108 c.Assert(m.RemapSnapFromState("potato"), Equals, "potato") 109 c.Assert(m.RemapSnapToState("potato"), Equals, "potato") 110 c.Assert(m.RemapSnapFromRequest("potato"), Equals, "potato") 111 112 c.Assert(m.SystemSnapName(), Equals, "snapd") 113 } 114 115 // caseMapper implements SnapMapper to use upper case internally and lower case externally. 116 type caseMapper struct{} 117 118 func (m *caseMapper) RemapSnapFromState(snapName string) string { 119 return strings.ToUpper(snapName) 120 } 121 122 func (m *caseMapper) RemapSnapToState(snapName string) string { 123 return strings.ToLower(snapName) 124 } 125 126 func (m *caseMapper) RemapSnapFromRequest(snapName string) string { 127 return strings.ToUpper(snapName) 128 } 129 130 func (m *caseMapper) SystemSnapName() string { 131 return "unknown" 132 } 133 134 func (s *helpersSuite) TestMappingFunctions(c *C) { 135 restore := ifacestate.MockSnapMapper(&caseMapper{}) 136 defer restore() 137 138 c.Assert(ifacestate.RemapSnapFromState("example"), Equals, "EXAMPLE") 139 c.Assert(ifacestate.RemapSnapToState("EXAMPLE"), Equals, "example") 140 c.Assert(ifacestate.RemapSnapFromRequest("example"), Equals, "EXAMPLE") 141 c.Assert(ifacestate.SystemSnapName(), Equals, "unknown") 142 } 143 144 func (s *helpersSuite) TestGetConns(c *C) { 145 s.st.Lock() 146 defer s.st.Unlock() 147 s.st.Set("conns", map[string]interface{}{ 148 "app:network core:network": map[string]interface{}{ 149 "auto": true, 150 "interface": "network", 151 "slot-static": map[string]interface{}{ 152 "number": int(78), 153 }, 154 }, 155 }) 156 157 restore := ifacestate.MockSnapMapper(&caseMapper{}) 158 defer restore() 159 160 conns, err := ifacestate.GetConns(s.st) 161 c.Assert(err, IsNil) 162 for id, connState := range conns { 163 c.Assert(id, Equals, "APP:network CORE:network") 164 c.Assert(connState.Auto, Equals, true) 165 c.Assert(connState.Interface, Equals, "network") 166 c.Assert(connState.StaticSlotAttrs["number"], Equals, int64(78)) 167 } 168 } 169 170 func (s *helpersSuite) TestSetConns(c *C) { 171 s.st.Lock() 172 defer s.st.Unlock() 173 174 restore := ifacestate.MockSnapMapper(&caseMapper{}) 175 defer restore() 176 177 // This has upper-case data internally, see export_test.go 178 ifacestate.SetConns(s.st, ifacestate.UpperCaseConnState()) 179 var conns map[string]interface{} 180 err := s.st.Get("conns", &conns) 181 c.Assert(err, IsNil) 182 c.Assert(conns, DeepEquals, map[string]interface{}{ 183 "app:network core:network": map[string]interface{}{ 184 "auto": true, 185 "interface": "network", 186 }}) 187 } 188 189 func (s *helpersSuite) TestHotplugTaskHelpers(c *C) { 190 s.st.Lock() 191 defer s.st.Unlock() 192 193 t := s.st.NewTask("foo", "") 194 _, _, err := ifacestate.GetHotplugAttrs(t) 195 c.Assert(err, ErrorMatches, `internal error: cannot get interface name from hotplug task: no state entry for key`) 196 197 t.Set("interface", "x") 198 _, _, err = ifacestate.GetHotplugAttrs(t) 199 c.Assert(err, ErrorMatches, `internal error: cannot get hotplug key from hotplug task: no state entry for key`) 200 201 ifacestate.SetHotplugAttrs(t, "iface", "key") 202 203 var iface string 204 var key snap.HotplugKey 205 c.Assert(t.Get("hotplug-key", &key), IsNil) 206 c.Assert(key, DeepEquals, snap.HotplugKey("key")) 207 208 c.Assert(t.Get("interface", &iface), IsNil) 209 c.Assert(iface, Equals, "iface") 210 211 iface, key, err = ifacestate.GetHotplugAttrs(t) 212 c.Assert(err, IsNil) 213 c.Assert(key, DeepEquals, snap.HotplugKey("key")) 214 c.Assert(iface, Equals, "iface") 215 } 216 217 func (s *helpersSuite) TestHotplugSlotInfo(c *C) { 218 s.st.Lock() 219 defer s.st.Unlock() 220 221 slots, err := ifacestate.GetHotplugSlots(s.st) 222 c.Assert(err, IsNil) 223 c.Assert(slots, HasLen, 0) 224 225 defs := map[string]*ifacestate.HotplugSlotInfo{} 226 defs["foo"] = &ifacestate.HotplugSlotInfo{ 227 Name: "foo", 228 Interface: "iface", 229 StaticAttrs: map[string]interface{}{"attr": "value"}, 230 HotplugKey: "key", 231 } 232 ifacestate.SetHotplugSlots(s.st, defs) 233 234 var data map[string]interface{} 235 c.Assert(s.st.Get("hotplug-slots", &data), IsNil) 236 c.Assert(data, DeepEquals, map[string]interface{}{ 237 "foo": map[string]interface{}{ 238 "name": "foo", 239 "interface": "iface", 240 "static-attrs": map[string]interface{}{"attr": "value"}, 241 "hotplug-key": "key", 242 "hotplug-gone": false, 243 }}) 244 245 slots, err = ifacestate.GetHotplugSlots(s.st) 246 c.Assert(err, IsNil) 247 c.Assert(slots, DeepEquals, defs) 248 } 249 250 func (s *helpersSuite) TestFindConnsForHotplugKey(c *C) { 251 st := s.st 252 st.Lock() 253 defer st.Unlock() 254 255 // Set conns in the state and get them via GetConns to avoid having to 256 // know the internals of connState struct. 257 st.Set("conns", map[string]interface{}{ 258 "snap1:plug1 core:slot1": map[string]interface{}{ 259 "interface": "iface1", 260 "hotplug-key": "key1", 261 }, 262 "snap1:plug2 core:slot2": map[string]interface{}{ 263 "interface": "iface2", 264 "hotplug-key": "key1", 265 }, 266 "snap1:plug3 core:slot3": map[string]interface{}{ 267 "interface": "iface2", 268 "hotplug-key": "key2", 269 }, 270 "snap2:plug1 core:slot1": map[string]interface{}{ 271 "interface": "iface2", 272 "hotplug-key": "key2", 273 }, 274 }) 275 276 conns, err := ifacestate.GetConns(st) 277 c.Assert(err, IsNil) 278 279 hotplugConns := ifacestate.FindConnsForHotplugKey(conns, "iface1", "key1") 280 c.Assert(hotplugConns, DeepEquals, []string{"snap1:plug1 core:slot1"}) 281 282 hotplugConns = ifacestate.FindConnsForHotplugKey(conns, "iface2", "key2") 283 c.Assert(hotplugConns, DeepEquals, []string{"snap1:plug3 core:slot3", "snap2:plug1 core:slot1"}) 284 285 hotplugConns = ifacestate.FindConnsForHotplugKey(conns, "unknown", "key1") 286 c.Assert(hotplugConns, HasLen, 0) 287 } 288 289 func (s *helpersSuite) TestCheckIsSystemSnapPresentWithCore(c *C) { 290 restore := ifacestate.MockSnapMapper(&ifacestate.CoreCoreSystemMapper{}) 291 defer restore() 292 293 // no core snap yet 294 c.Assert(ifacestate.CheckSystemSnapIsPresent(s.st), Equals, false) 295 296 s.st.Lock() 297 298 // add "core" snap 299 sideInfo := &snap.SideInfo{Revision: snap.R(1)} 300 snapInfo := snaptest.MockSnapInstance(c, "", coreSnapYaml, sideInfo) 301 sideInfo.RealName = snapInfo.SnapName() 302 303 snapstate.Set(s.st, snapInfo.InstanceName(), &snapstate.SnapState{ 304 Active: true, 305 Sequence: []*snap.SideInfo{sideInfo}, 306 Current: sideInfo.Revision, 307 SnapType: string(snapInfo.Type()), 308 InstanceKey: snapInfo.InstanceKey, 309 }) 310 s.st.Unlock() 311 312 c.Assert(ifacestate.CheckSystemSnapIsPresent(s.st), Equals, true) 313 } 314 315 var snapdYaml = `name: snapd 316 version: 1.0 317 ` 318 319 func (s *helpersSuite) TestCheckIsSystemSnapPresentWithSnapd(c *C) { 320 restore := ifacestate.MockSnapMapper(&ifacestate.CoreSnapdSystemMapper{}) 321 defer restore() 322 323 // no snapd snap yet 324 c.Assert(ifacestate.CheckSystemSnapIsPresent(s.st), Equals, false) 325 326 s.st.Lock() 327 328 // "snapd" snap 329 sideInfo := &snap.SideInfo{Revision: snap.R(1)} 330 snapInfo := snaptest.MockSnapInstance(c, "", snapdYaml, sideInfo) 331 sideInfo.RealName = snapInfo.SnapName() 332 333 snapstate.Set(s.st, snapInfo.InstanceName(), &snapstate.SnapState{ 334 Active: true, 335 Sequence: []*snap.SideInfo{sideInfo}, 336 Current: sideInfo.Revision, 337 SnapType: string(snapInfo.Type()), 338 InstanceKey: snapInfo.InstanceKey, 339 }) 340 341 inf, err := ifacestate.SystemSnapInfo(s.st) 342 c.Assert(err, IsNil) 343 c.Assert(inf.InstanceName(), Equals, "snapd") 344 345 s.st.Unlock() 346 347 c.Assert(ifacestate.CheckSystemSnapIsPresent(s.st), Equals, true) 348 } 349 350 // Check what happens with system-key when security profile regeneration fails. 351 func (s *helpersSuite) TestSystemKeyAndFailingProfileRegeneration(c *C) { 352 dirs.SetRootDir(c.MkDir()) 353 defer dirs.SetRootDir("") 354 355 // Create a fake security backend with failing Setup method and mock all 356 // security backends away so that we only use this special one. Note that 357 // the backend is given a non-empty name as the interface manager skips 358 // test backends with empty name for convenience. 359 backend := &ifacetest.TestSecurityBackend{ 360 BackendName: "BROKEN", 361 SetupCallback: func(snapInfo *snap.Info, opts interfaces.ConfinementOptions, repo *interfaces.Repository) error { 362 return errors.New("FAILED") 363 }, 364 } 365 restore := ifacestate.MockSecurityBackends([]interfaces.SecurityBackend{backend}) 366 defer restore() 367 368 // Create a mock overlord, mainly to have state. 369 ovld := overlord.Mock() 370 st := ovld.State() 371 372 // Put a fake snap in the state, we need to setup security for at least one 373 // snap to give the fake security backend a chance to fail. 374 yamlText := ` 375 name: test-snapd-canary 376 version: 1 377 apps: 378 test-snapd-canary: 379 command: bin/canary 380 ` 381 si := &snap.SideInfo{Revision: snap.R(1), RealName: "test-snapd-canary"} 382 snapInfo := snaptest.MockSnap(c, yamlText, si) 383 st.Lock() 384 snapst := &snapstate.SnapState{ 385 SnapType: string(snap.TypeApp), 386 Sequence: []*snap.SideInfo{si}, 387 Active: true, 388 Current: snap.R(1), 389 } 390 snapstate.Set(st, snapInfo.InstanceName(), snapst) 391 st.Unlock() 392 393 // Pretend that security profiles are out of date and mock the 394 // function that writes the new system key with one always panics. 395 restore = ifacestate.MockProfilesNeedRegeneration(func() bool { return true }) 396 defer restore() 397 restore = ifacestate.MockWriteSystemKey(func() error { panic("should not attempt to write system key") }) 398 defer restore() 399 // Put a fake system key in place, we just want to see that file being removed. 400 err := os.MkdirAll(filepath.Dir(dirs.SnapSystemKeyFile), 0755) 401 c.Assert(err, IsNil) 402 err = ioutil.WriteFile(dirs.SnapSystemKeyFile, []byte("system-key"), 0755) 403 c.Assert(err, IsNil) 404 405 // Put up a fake logger to capture logged messages. 406 log, restore := logger.MockLogger() 407 defer restore() 408 409 // Construct and start up the interface manager. 410 mgr, err := ifacestate.Manager(st, nil, ovld.TaskRunner(), nil, nil) 411 c.Assert(err, IsNil) 412 err = mgr.StartUp() 413 c.Assert(err, IsNil) 414 415 // Check that system key is not on disk. 416 c.Check(log.String(), Matches, `.*cannot regenerate BROKEN profiles\n.*FAILED.*\n`) 417 c.Check(osutil.FileExists(dirs.SnapSystemKeyFile), Equals, false) 418 } 419 420 func mockSnaps(c *C, st *state.State) { 421 // Put fake snaps in the state 422 for _, name := range []string{"foo", "bar"} { 423 yamlText := ` 424 name: %NAME% 425 version: 1 426 apps: 427 test: 428 command: bin/test 429 ` 430 si := &snap.SideInfo{Revision: snap.R(1), RealName: name} 431 snapInfo := snaptest.MockSnap(c, strings.Replace(yamlText, "%NAME%", name, -1), si) 432 st.Lock() 433 snapst := &snapstate.SnapState{ 434 SnapType: string(snap.TypeApp), 435 Sequence: []*snap.SideInfo{si}, 436 Active: true, 437 Current: snap.R(1), 438 } 439 snapstate.Set(st, snapInfo.InstanceName(), snapst) 440 st.Unlock() 441 } 442 } 443 444 func (s *helpersSuite) TestProfileRegenerationSetupMany(c *C) { 445 dirs.SetRootDir(c.MkDir()) 446 defer dirs.SetRootDir("") 447 448 var setupManyCalls int 449 var writeKey bool 450 451 // Create a fake security backend 452 backend := &ifacetest.TestSecurityBackendSetupMany{ 453 TestSecurityBackend: ifacetest.TestSecurityBackend{BackendName: "fake"}, 454 SetupManyCallback: func(snaps []*snap.Info, confinement func(snapName string) interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) []error { 455 c.Check(snaps, HasLen, 2) 456 setupManyCalls++ 457 return nil 458 }, 459 } 460 restore := ifacestate.MockSecurityBackends([]interfaces.SecurityBackend{backend}) 461 defer restore() 462 463 // Create a mock overlord, mainly to have state. 464 ovld := overlord.Mock() 465 st := ovld.State() 466 467 mockSnaps(c, st) 468 469 // Pretend that security profiles are out of date. 470 restore = ifacestate.MockProfilesNeedRegeneration(func() bool { return true }) 471 defer restore() 472 restore = ifacestate.MockWriteSystemKey(func() error { 473 writeKey = true 474 return nil 475 }) 476 defer restore() 477 478 // Construct and start up the interface manager. 479 mgr, err := ifacestate.Manager(st, nil, ovld.TaskRunner(), nil, nil) 480 c.Assert(err, IsNil) 481 err = mgr.StartUp() 482 c.Assert(err, IsNil) 483 484 c.Check(writeKey, Equals, true) 485 c.Check(setupManyCalls, Equals, 1) 486 } 487 488 func (s *helpersSuite) TestProfileRegenerationSetupManyFailsSystemKeyNotWritten(c *C) { 489 dirs.SetRootDir(c.MkDir()) 490 defer dirs.SetRootDir("") 491 492 var setupManyCalls int 493 var writeKey bool 494 495 // Create a fake security backend 496 backend := &ifacetest.TestSecurityBackendSetupMany{ 497 TestSecurityBackend: ifacetest.TestSecurityBackend{BackendName: "fake"}, 498 SetupManyCallback: func(snaps []*snap.Info, confinement func(snapName string) interfaces.ConfinementOptions, repo *interfaces.Repository, tm timings.Measurer) []error { 499 c.Check(snaps, HasLen, 2) 500 setupManyCalls++ 501 return []error{fmt.Errorf("FAILED")} 502 }, 503 } 504 restore := ifacestate.MockSecurityBackends([]interfaces.SecurityBackend{backend}) 505 defer restore() 506 507 // Put up a fake logger to capture logged messages. 508 log, restoreLog := logger.MockLogger() 509 defer restoreLog() 510 511 // Create a mock overlord, mainly to have state. 512 ovld := overlord.Mock() 513 st := ovld.State() 514 515 mockSnaps(c, st) 516 517 // Pretend that security profiles are out of date. 518 restore = ifacestate.MockProfilesNeedRegeneration(func() bool { return true }) 519 defer restore() 520 restore = ifacestate.MockWriteSystemKey(func() error { 521 writeKey = true 522 return nil 523 }) 524 defer restore() 525 526 // Construct and start up the interface manager. 527 mgr, err := ifacestate.Manager(st, nil, ovld.TaskRunner(), nil, nil) 528 c.Assert(err, IsNil) 529 err = mgr.StartUp() 530 c.Assert(err, IsNil) 531 532 // Check that system key is not on disk. 533 c.Check(writeKey, Equals, false) 534 c.Check(setupManyCalls, Equals, 1) 535 c.Check(log.String(), Matches, ".*cannot regenerate fake profiles\n.*FAILED\n") 536 } 537 538 func (s *helpersSuite) TestIsHotplugChange(c *C) { 539 s.st.Lock() 540 defer s.st.Unlock() 541 542 chg := s.st.NewChange("foo", "") 543 c.Assert(ifacestate.IsHotplugChange(chg), Equals, false) 544 545 chg = s.st.NewChange("hotplugfoo", "") 546 c.Assert(ifacestate.IsHotplugChange(chg), Equals, false) 547 548 chg = s.st.NewChange("hotplug-foo", "") 549 c.Assert(ifacestate.IsHotplugChange(chg), Equals, true) 550 } 551 552 func (s *helpersSuite) TestGetHotplugChangeAttrs(c *C) { 553 s.st.Lock() 554 defer s.st.Unlock() 555 556 chg := s.st.NewChange("none-set", "") 557 _, _, err := ifacestate.GetHotplugChangeAttrs(chg) 558 c.Assert(err, ErrorMatches, `internal error: hotplug-key not set on change "none-set"`) 559 560 chg = s.st.NewChange("foo", "") 561 chg.Set("hotplug-seq", 1) 562 _, _, err = ifacestate.GetHotplugChangeAttrs(chg) 563 c.Assert(err, ErrorMatches, `internal error: hotplug-key not set on change "foo"`) 564 565 chg = s.st.NewChange("bar", "") 566 chg.Set("hotplug-key", "2222") 567 _, _, err = ifacestate.GetHotplugChangeAttrs(chg) 568 c.Assert(err, ErrorMatches, `internal error: hotplug-seq not set on change "bar"`) 569 570 chg = s.st.NewChange("baz", "") 571 chg.Set("hotplug-key", "1234") 572 chg.Set("hotplug-seq", 7) 573 574 seq, key, err := ifacestate.GetHotplugChangeAttrs(chg) 575 c.Assert(err, IsNil) 576 c.Check(key, DeepEquals, snap.HotplugKey("1234")) 577 c.Check(seq, Equals, 7) 578 } 579 580 func (s *helpersSuite) TestSetHotplugChangeAttrs(c *C) { 581 s.st.Lock() 582 defer s.st.Unlock() 583 584 chg := s.st.NewChange("foo", "") 585 ifacestate.SetHotplugChangeAttrs(chg, 12, "abcd") 586 587 var seq int 588 var hotplugKey string 589 c.Assert(chg.Get("hotplug-seq", &seq), IsNil) 590 c.Assert(chg.Get("hotplug-key", &hotplugKey), IsNil) 591 c.Check(seq, Equals, 12) 592 c.Check(hotplugKey, Equals, "abcd") 593 } 594 595 func (s *helpersSuite) TestAllocHotplugSeq(c *C) { 596 s.st.Lock() 597 defer s.st.Unlock() 598 599 var stateSeq int 600 601 // sanity 602 c.Assert(s.st.Get("hotplug-seq", &stateSeq), Equals, state.ErrNoState) 603 604 seq, err := ifacestate.AllocHotplugSeq(s.st) 605 c.Assert(err, IsNil) 606 c.Assert(seq, Equals, 1) 607 608 seq, err = ifacestate.AllocHotplugSeq(s.st) 609 c.Assert(err, IsNil) 610 c.Assert(seq, Equals, 2) 611 612 c.Assert(s.st.Get("hotplug-seq", &stateSeq), IsNil) 613 c.Check(stateSeq, Equals, 2) 614 } 615 616 func (s *helpersSuite) TestAddHotplugSeqWaitTask(c *C) { 617 s.st.Lock() 618 defer s.st.Unlock() 619 620 chg := s.st.NewChange("foo", "") 621 t1 := s.st.NewTask("task1", "") 622 t2 := s.st.NewTask("task2", "") 623 chg.AddTask(t1) 624 chg.AddTask(t2) 625 626 ifacestate.AddHotplugSeqWaitTask(chg, "1234", 1) 627 // hotplug change got an extra task 628 c.Assert(chg.Tasks(), HasLen, 3) 629 seq, key, err := ifacestate.GetHotplugChangeAttrs(chg) 630 c.Assert(err, IsNil) 631 c.Check(seq, Equals, 1) 632 c.Check(key, DeepEquals, snap.HotplugKey("1234")) 633 634 var seqTask *state.Task 635 for _, t := range chg.Tasks() { 636 if t.Kind() == "hotplug-seq-wait" { 637 seqTask = t 638 break 639 } 640 } 641 c.Assert(seqTask, NotNil) 642 643 // existing tasks wait for the hotplug-seq-wait task 644 c.Assert(t1.WaitTasks(), HasLen, 1) 645 c.Assert(t1.WaitTasks()[0].ID(), Equals, seqTask.ID()) 646 c.Assert(t2.WaitTasks(), HasLen, 1) 647 c.Assert(t2.WaitTasks()[0].ID(), Equals, seqTask.ID()) 648 } 649 650 func (s *helpersSuite) TestAddHotplugSlot(c *C) { 651 s.st.Lock() 652 defer s.st.Unlock() 653 654 var beforePrepareSlotCalled int 655 repo := interfaces.NewRepository() 656 iface := &ifacetest.TestInterface{ 657 InterfaceName: "test", 658 BeforePrepareSlotCallback: func(*snap.SlotInfo) error { 659 beforePrepareSlotCalled += 1 660 return nil 661 }, 662 } 663 repo.AddInterface(iface) 664 665 stateSlots, err := ifacestate.GetHotplugSlots(s.st) 666 c.Assert(err, IsNil) 667 c.Check(stateSlots, HasLen, 0) 668 669 si := &snap.SideInfo{Revision: snap.R(1)} 670 coreInfo := snaptest.MockSnap(c, coreSnapYaml, si) 671 672 slot := &snap.SlotInfo{ 673 Name: "slot", 674 Label: "label", 675 Snap: coreInfo, 676 Interface: "test", 677 Attrs: map[string]interface{}{"foo": "bar"}, 678 HotplugKey: "key", 679 } 680 c.Assert(ifacestate.AddHotplugSlot(s.st, repo, stateSlots, iface, slot), IsNil) 681 c.Assert(beforePrepareSlotCalled, Equals, 1) 682 683 // same slot cannot be re-added to repo 684 c.Assert(ifacestate.AddHotplugSlot(s.st, repo, stateSlots, iface, slot), ErrorMatches, `cannot add hotplug slot "slot" for interface test: snap "core" has slots conflicting on name "slot"`) 685 686 stateSlots, err = ifacestate.GetHotplugSlots(s.st) 687 c.Assert(err, IsNil) 688 c.Assert(stateSlots, HasLen, 1) 689 690 stateSlot := stateSlots["slot"] 691 c.Assert(stateSlot, NotNil) 692 c.Check(stateSlot, DeepEquals, &ifacestate.HotplugSlotInfo{ 693 Name: "slot", 694 Interface: "test", 695 StaticAttrs: map[string]interface{}{"foo": "bar"}, 696 HotplugKey: "key", 697 HotplugGone: false}) 698 } 699 700 func (s *helpersSuite) TestAddHotplugSlotValidationErrors(c *C) { 701 s.st.Lock() 702 defer s.st.Unlock() 703 704 repo := interfaces.NewRepository() 705 iface := &ifacetest.TestInterface{ 706 InterfaceName: "test", 707 BeforePrepareSlotCallback: func(slot *snap.SlotInfo) error { return fmt.Errorf("fail") }, 708 } 709 repo.AddInterface(iface) 710 711 stateSlots, err := ifacestate.GetHotplugSlots(s.st) 712 c.Assert(err, IsNil) 713 c.Check(stateSlots, HasLen, 0) 714 715 si := &snap.SideInfo{Revision: snap.R(1)} 716 coreInfo := snaptest.MockSnap(c, coreSnapYaml, si) 717 718 slot := &snap.SlotInfo{ 719 Name: "slot", 720 Snap: coreInfo, 721 Interface: "test", 722 } 723 // hotplug key missing 724 c.Assert(ifacestate.AddHotplugSlot(s.st, repo, stateSlots, iface, slot), ErrorMatches, `internal error: cannot store slot "slot", not a hotplug slot`) 725 slot.HotplugKey = "key" 726 727 // sanitization failure 728 c.Assert(ifacestate.AddHotplugSlot(s.st, repo, stateSlots, iface, slot), ErrorMatches, `cannot sanitize hotplug slot \"slot\" for interface test: fail`) 729 } 730 731 func (s *helpersSuite) TestDiscardLateBackendViaSnapstate(c *C) { 732 s.st.Lock() 733 defer s.st.Unlock() 734 dirs.SetRootDir(c.MkDir()) 735 defer dirs.SetRootDir("") 736 737 // security profiles do not need regeneration when crating the manager 738 restore := ifacestate.MockProfilesNeedRegeneration(func() bool { return false }) 739 defer restore() 740 741 backend := &ifacetest.TestSecurityBackendDiscardingLate{ 742 RemoveLateCallback: func(snapName string, rev snap.Revision, typ snap.Type) error { 743 if snapName == "this-fails" { 744 return fmt.Errorf("remove late fails") 745 } 746 return nil 747 }, 748 } 749 restore = ifacestate.MockSecurityBackends([]interfaces.SecurityBackend{backend}) 750 defer restore() 751 752 // mock overlord 753 ovld := overlord.Mock() 754 st := ovld.State() 755 // manager 756 mgr, err := ifacestate.Manager(st, nil, ovld.TaskRunner(), nil, nil) 757 c.Assert(err, IsNil) 758 // installs the ifacemgr helper 759 err = mgr.StartUp() 760 c.Assert(err, IsNil) 761 762 // call via the snapstate hook 763 err = snapstate.SecurityProfilesRemoveLate("snapd", snap.R(1234), snap.TypeSnapd) 764 c.Assert(err, IsNil) 765 err = snapstate.SecurityProfilesRemoveLate("this-fails", snap.R(12), snap.TypeApp) 766 c.Assert(err, ErrorMatches, "remove late fails") 767 c.Check(backend.RemoveLateCalledFor, DeepEquals, [][]string{ 768 {"snapd", "1234", "snapd"}, 769 {"this-fails", "12", "app"}, 770 }) 771 }