github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/overlord/managers_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2019 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 overlord_test 21 22 // test the various managers and their operation together through overlord 23 24 import ( 25 "bytes" 26 "context" 27 "encoding/json" 28 "errors" 29 "fmt" 30 "io" 31 "io/ioutil" 32 "net/http" 33 "net/http/httptest" 34 "net/url" 35 "os" 36 "path/filepath" 37 "sort" 38 "strings" 39 "time" 40 41 . "gopkg.in/check.v1" 42 "gopkg.in/tomb.v2" 43 "gopkg.in/yaml.v2" 44 45 "github.com/snapcore/snapd/asserts" 46 "github.com/snapcore/snapd/asserts/assertstest" 47 "github.com/snapcore/snapd/asserts/sysdb" 48 "github.com/snapcore/snapd/boot" 49 "github.com/snapcore/snapd/boot/boottest" 50 "github.com/snapcore/snapd/bootloader" 51 "github.com/snapcore/snapd/bootloader/bootloadertest" 52 "github.com/snapcore/snapd/client" 53 "github.com/snapcore/snapd/dirs" 54 "github.com/snapcore/snapd/gadget" 55 "github.com/snapcore/snapd/interfaces" 56 "github.com/snapcore/snapd/osutil" 57 "github.com/snapcore/snapd/overlord" 58 "github.com/snapcore/snapd/overlord/assertstate" 59 "github.com/snapcore/snapd/overlord/assertstate/assertstatetest" 60 "github.com/snapcore/snapd/overlord/auth" 61 "github.com/snapcore/snapd/overlord/configstate/config" 62 "github.com/snapcore/snapd/overlord/devicestate" 63 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 64 "github.com/snapcore/snapd/overlord/hookstate" 65 "github.com/snapcore/snapd/overlord/hookstate/ctlcmd" 66 "github.com/snapcore/snapd/overlord/ifacestate" 67 "github.com/snapcore/snapd/overlord/snapshotstate" 68 "github.com/snapcore/snapd/overlord/snapstate" 69 "github.com/snapcore/snapd/overlord/state" 70 "github.com/snapcore/snapd/release" 71 "github.com/snapcore/snapd/snap" 72 "github.com/snapcore/snapd/snap/snapfile" 73 "github.com/snapcore/snapd/snap/snaptest" 74 "github.com/snapcore/snapd/store" 75 "github.com/snapcore/snapd/systemd" 76 "github.com/snapcore/snapd/testutil" 77 ) 78 79 var ( 80 settleTimeout = testutil.HostScaledTimeout(45 * time.Second) 81 aggressiveSettleTimeout = testutil.HostScaledTimeout(50 * time.Millisecond) 82 connectRetryTimeout = testutil.HostScaledTimeout(70 * time.Millisecond) 83 ) 84 85 type automaticSnapshotCall struct { 86 InstanceName string 87 SnapConfig map[string]interface{} 88 Usernames []string 89 } 90 91 type baseMgrsSuite struct { 92 testutil.BaseTest 93 94 tempdir string 95 96 storeSigning *assertstest.StoreStack 97 brands *assertstest.SigningAccounts 98 99 devAcct *asserts.Account 100 101 serveIDtoName map[string]string 102 serveSnapPath map[string]string 103 serveRevision map[string]string 104 serveOldPaths map[string][]string 105 serveOldRevs map[string][]string 106 107 hijackServeSnap func(http.ResponseWriter) 108 109 checkDeviceAndAuthContext func(store.DeviceAndAuthContext) 110 expectedSerial string 111 expectedStore string 112 sessionMacaroon string 113 114 o *overlord.Overlord 115 116 failNextDownload string 117 118 automaticSnapshots []automaticSnapshotCall 119 } 120 121 var ( 122 _ = Suite(&mgrsSuite{}) 123 _ = Suite(&storeCtxSetupSuite{}) 124 ) 125 126 var ( 127 brandPrivKey, _ = assertstest.GenerateKey(752) 128 129 develPrivKey, _ = assertstest.GenerateKey(752) 130 131 deviceKey, _ = assertstest.GenerateKey(752) 132 ) 133 134 func verifyLastTasksetIsRerefresh(c *C, tts []*state.TaskSet) { 135 ts := tts[len(tts)-1] 136 c.Assert(ts.Tasks(), HasLen, 1) 137 c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh") 138 } 139 140 func (s *baseMgrsSuite) SetUpTest(c *C) { 141 s.BaseTest.SetUpTest(c) 142 143 s.tempdir = c.MkDir() 144 dirs.SetRootDir(s.tempdir) 145 s.AddCleanup(func() { dirs.SetRootDir("") }) 146 147 // needed for system key generation 148 s.AddCleanup(osutil.MockMountInfo("")) 149 150 err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) 151 c.Assert(err, IsNil) 152 153 // needed by hooks 154 s.AddCleanup(testutil.MockCommand(c, "snap", "").Restore) 155 156 restoreCheckFreeSpace := snapstate.MockOsutilCheckFreeSpace(func(string, uint64) error { return nil }) 157 s.AddCleanup(restoreCheckFreeSpace) 158 159 oldSetupInstallHook := snapstate.SetupInstallHook 160 oldSetupRemoveHook := snapstate.SetupRemoveHook 161 snapstate.SetupRemoveHook = hookstate.SetupRemoveHook 162 snapstate.SetupInstallHook = hookstate.SetupInstallHook 163 s.AddCleanup(func() { 164 snapstate.SetupRemoveHook = oldSetupRemoveHook 165 snapstate.SetupInstallHook = oldSetupInstallHook 166 }) 167 168 s.automaticSnapshots = nil 169 r := snapshotstate.MockBackendSave(func(_ context.Context, id uint64, si *snap.Info, cfg map[string]interface{}, usernames []string) (*client.Snapshot, error) { 170 s.automaticSnapshots = append(s.automaticSnapshots, automaticSnapshotCall{InstanceName: si.InstanceName(), SnapConfig: cfg, Usernames: usernames}) 171 return nil, nil 172 }) 173 s.AddCleanup(r) 174 175 s.AddCleanup(ifacestate.MockConnectRetryTimeout(connectRetryTimeout)) 176 177 os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") 178 s.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") }) 179 180 // create a fake systemd environment 181 os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755) 182 183 r = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 184 return []byte("ActiveState=inactive\n"), nil 185 }) 186 s.AddCleanup(r) 187 188 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 189 s.brands = assertstest.NewSigningAccounts(s.storeSigning) 190 s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 191 "validation": "verified", 192 }) 193 s.AddCleanup(sysdb.InjectTrusted(s.storeSigning.Trusted)) 194 195 s.devAcct = assertstest.NewAccount(s.storeSigning, "devdevdev", map[string]interface{}{ 196 "account-id": "devdevdev", 197 }, "") 198 err = s.storeSigning.Add(s.devAcct) 199 c.Assert(err, IsNil) 200 201 s.serveIDtoName = make(map[string]string) 202 s.serveSnapPath = make(map[string]string) 203 s.serveRevision = make(map[string]string) 204 s.serveOldPaths = make(map[string][]string) 205 s.serveOldRevs = make(map[string][]string) 206 s.hijackServeSnap = nil 207 208 s.checkDeviceAndAuthContext = nil 209 s.expectedSerial = "" 210 s.expectedStore = "" 211 s.sessionMacaroon = "" 212 213 s.AddCleanup(ifacestate.MockSecurityBackends(nil)) 214 215 o, err := overlord.New(nil) 216 c.Assert(err, IsNil) 217 st := o.State() 218 st.Lock() 219 st.Set("seeded", true) 220 st.Unlock() 221 err = o.StartUp() 222 c.Assert(err, IsNil) 223 o.InterfaceManager().DisableUDevMonitor() 224 s.o = o 225 226 st.Lock() 227 defer st.Unlock() 228 // registered 229 err = assertstate.Add(st, sysdb.GenericClassicModel()) 230 c.Assert(err, IsNil) 231 devicestatetest.SetDevice(st, &auth.DeviceState{ 232 Brand: "generic", 233 Model: "generic-classic", 234 Serial: "serialserial", 235 }) 236 237 // add "core" snap declaration 238 headers := map[string]interface{}{ 239 "series": "16", 240 "snap-name": "core", 241 "publisher-id": "can0nical", 242 "timestamp": time.Now().Format(time.RFC3339), 243 } 244 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 245 err = assertstate.Add(st, s.storeSigning.StoreAccountKey("")) 246 c.Assert(err, IsNil) 247 a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 248 c.Assert(err, IsNil) 249 err = assertstate.Add(st, a) 250 c.Assert(err, IsNil) 251 s.serveRevision["core"] = "1" 252 s.serveIDtoName[fakeSnapID("core")] = "core" 253 err = s.storeSigning.Add(a) 254 c.Assert(err, IsNil) 255 256 // add "snap1" snap declaration 257 headers = map[string]interface{}{ 258 "series": "16", 259 "snap-name": "snap1", 260 "publisher-id": "can0nical", 261 "timestamp": time.Now().Format(time.RFC3339), 262 } 263 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 264 a2, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 265 c.Assert(err, IsNil) 266 c.Assert(assertstate.Add(st, a2), IsNil) 267 c.Assert(s.storeSigning.Add(a2), IsNil) 268 269 // add "snap2" snap declaration 270 headers = map[string]interface{}{ 271 "series": "16", 272 "snap-name": "snap2", 273 "publisher-id": "can0nical", 274 "timestamp": time.Now().Format(time.RFC3339), 275 } 276 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 277 a3, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 278 c.Assert(err, IsNil) 279 c.Assert(assertstate.Add(st, a3), IsNil) 280 c.Assert(s.storeSigning.Add(a3), IsNil) 281 282 // add "some-snap" snap declaration 283 headers = map[string]interface{}{ 284 "series": "16", 285 "snap-name": "some-snap", 286 "publisher-id": "can0nical", 287 "timestamp": time.Now().Format(time.RFC3339), 288 } 289 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 290 a4, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 291 c.Assert(err, IsNil) 292 c.Assert(assertstate.Add(st, a4), IsNil) 293 c.Assert(s.storeSigning.Add(a4), IsNil) 294 295 // add "other-snap" snap declaration 296 headers = map[string]interface{}{ 297 "series": "16", 298 "snap-name": "other-snap", 299 "publisher-id": "can0nical", 300 "timestamp": time.Now().Format(time.RFC3339), 301 } 302 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 303 a5, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 304 c.Assert(err, IsNil) 305 c.Assert(assertstate.Add(st, a5), IsNil) 306 c.Assert(s.storeSigning.Add(a5), IsNil) 307 308 // add pc-kernel snap declaration 309 headers = map[string]interface{}{ 310 "series": "16", 311 "snap-name": "pc-kernel", 312 "publisher-id": "can0nical", 313 "timestamp": time.Now().Format(time.RFC3339), 314 } 315 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 316 a6, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 317 c.Assert(err, IsNil) 318 c.Assert(assertstate.Add(st, a6), IsNil) 319 c.Assert(s.storeSigning.Add(a6), IsNil) 320 321 // add pc snap declaration 322 headers = map[string]interface{}{ 323 "series": "16", 324 "snap-name": "pc", 325 "publisher-id": "can0nical", 326 "timestamp": time.Now().Format(time.RFC3339), 327 } 328 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 329 a7, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 330 c.Assert(err, IsNil) 331 c.Assert(assertstate.Add(st, a7), IsNil) 332 c.Assert(s.storeSigning.Add(a7), IsNil) 333 334 // add pi snap declaration 335 headers = map[string]interface{}{ 336 "series": "16", 337 "snap-name": "pi", 338 "publisher-id": "can0nical", 339 "timestamp": time.Now().Format(time.RFC3339), 340 } 341 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 342 a8, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 343 c.Assert(err, IsNil) 344 c.Assert(assertstate.Add(st, a8), IsNil) 345 c.Assert(s.storeSigning.Add(a8), IsNil) 346 347 // add pi-kernel snap declaration 348 headers = map[string]interface{}{ 349 "series": "16", 350 "snap-name": "pi-kernel", 351 "publisher-id": "can0nical", 352 "timestamp": time.Now().Format(time.RFC3339), 353 } 354 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 355 a9, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 356 c.Assert(err, IsNil) 357 c.Assert(assertstate.Add(st, a9), IsNil) 358 c.Assert(s.storeSigning.Add(a9), IsNil) 359 360 // add core itself 361 snapstate.Set(st, "core", &snapstate.SnapState{ 362 Active: true, 363 Sequence: []*snap.SideInfo{ 364 {RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}, 365 }, 366 Current: snap.R(1), 367 SnapType: "os", 368 Flags: snapstate.Flags{ 369 Required: true, 370 }, 371 }) 372 373 // don't actually try to talk to the store on snapstate.Ensure 374 // needs doing after the call to devicestate.Manager (which happens in overlord.New) 375 snapstate.CanAutoRefresh = nil 376 377 st.Set("refresh-privacy-key", "privacy-key") 378 379 // For triggering errors 380 erroringHandler := func(task *state.Task, _ *tomb.Tomb) error { 381 return errors.New("error out") 382 } 383 s.o.TaskRunner().AddHandler("error-trigger", erroringHandler, nil) 384 385 // setup cloud-init as restricted so that tests by default don't run the 386 // full EnsureCloudInitRestricted logic in the devicestate mgr 387 snapdCloudInitRestrictedFile := filepath.Join(dirs.GlobalRootDir, "etc/cloud/cloud.cfg.d/zzzz_snapd.cfg") 388 err = os.MkdirAll(filepath.Dir(snapdCloudInitRestrictedFile), 0755) 389 c.Assert(err, IsNil) 390 err = ioutil.WriteFile(snapdCloudInitRestrictedFile, nil, 0644) 391 c.Assert(err, IsNil) 392 } 393 394 type mgrsSuite struct { 395 baseMgrsSuite 396 } 397 398 func makeTestSnapWithFiles(c *C, snapYamlContent string, files [][]string) string { 399 info, err := snap.InfoFromSnapYaml([]byte(snapYamlContent)) 400 c.Assert(err, IsNil) 401 402 for _, app := range info.Apps { 403 // files is a list of (filename, content) 404 files = append(files, []string{app.Command, ""}) 405 } 406 407 return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, files) 408 } 409 410 func makeTestSnap(c *C, snapYamlContent string) string { 411 return makeTestSnapWithFiles(c, snapYamlContent, nil) 412 } 413 414 func (s *mgrsSuite) TestHappyLocalInstall(c *C) { 415 snapYamlContent := `name: foo 416 apps: 417 bar: 418 command: bin/bar 419 ` 420 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0") 421 422 st := s.o.State() 423 st.Lock() 424 defer st.Unlock() 425 426 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true}) 427 c.Assert(err, IsNil) 428 chg := st.NewChange("install-snap", "...") 429 chg.AddAll(ts) 430 431 st.Unlock() 432 err = s.o.Settle(settleTimeout) 433 st.Lock() 434 c.Assert(err, IsNil) 435 436 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 437 438 snap, err := snapstate.CurrentInfo(st, "foo") 439 c.Assert(err, IsNil) 440 441 // ensure that the binary wrapper file got generated with the right 442 // name 443 binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar") 444 c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true) 445 446 // data dirs 447 c.Assert(osutil.IsDirectory(snap.DataDir()), Equals, true) 448 c.Assert(osutil.IsDirectory(snap.CommonDataDir()), Equals, true) 449 450 // snap file and its mounting 451 452 // after install the snap file is in the right dir 453 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, true) 454 455 // ensure the right unit is created 456 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1")) 457 c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/x1", dirs.StripRootDir(dirs.SnapMountDir))) 458 c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_x1.snap") 459 } 460 461 func (s *mgrsSuite) TestLocalInstallUndo(c *C) { 462 snapYamlContent := `name: foo 463 apps: 464 bar: 465 command: bin/bar 466 hooks: 467 install: 468 configure: 469 ` 470 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0") 471 472 installHook := false 473 defer hookstate.MockRunHook(func(ctx *hookstate.Context, _ *tomb.Tomb) ([]byte, error) { 474 switch ctx.HookName() { 475 case "install": 476 installHook = true 477 _, _, err := ctlcmd.Run(ctx, []string{"set", "installed=true"}, 0) 478 c.Assert(err, IsNil) 479 return nil, nil 480 case "configure": 481 return nil, errors.New("configure failed") 482 } 483 return nil, nil 484 })() 485 486 st := s.o.State() 487 st.Lock() 488 defer st.Unlock() 489 490 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true}) 491 c.Assert(err, IsNil) 492 chg := st.NewChange("install-snap", "...") 493 chg.AddAll(ts) 494 495 st.Unlock() 496 err = s.o.Settle(settleTimeout) 497 st.Lock() 498 c.Assert(err, IsNil) 499 500 c.Assert(chg.Status(), Equals, state.ErrorStatus, Commentf("install-snap unexpectedly succeeded")) 501 502 // check undo statutes 503 for _, t := range chg.Tasks() { 504 which := t.Kind() 505 expectedStatus := state.UndoneStatus 506 switch t.Kind() { 507 case "prerequisites": 508 expectedStatus = state.DoneStatus 509 case "run-hook": 510 var hs hookstate.HookSetup 511 err := t.Get("hook-setup", &hs) 512 c.Assert(err, IsNil) 513 switch hs.Hook { 514 case "install": 515 expectedStatus = state.UndoneStatus 516 case "configure": 517 expectedStatus = state.ErrorStatus 518 case "check-health": 519 expectedStatus = state.HoldStatus 520 } 521 which += fmt.Sprintf("[%s]", hs.Hook) 522 } 523 c.Assert(t.Status(), Equals, expectedStatus, Commentf("%s", which)) 524 } 525 526 // install hooks was called 527 c.Check(installHook, Equals, true) 528 529 // nothing in snaps 530 all, err := snapstate.All(st) 531 c.Assert(err, IsNil) 532 c.Check(all, HasLen, 1) 533 _, ok := all["core"] 534 c.Check(ok, Equals, true) 535 536 // nothing in config 537 var config map[string]*json.RawMessage 538 err = st.Get("config", &config) 539 c.Assert(err, IsNil) 540 c.Check(config, HasLen, 1) 541 _, ok = config["core"] 542 c.Check(ok, Equals, true) 543 544 snapdirs, err := filepath.Glob(filepath.Join(dirs.SnapMountDir, "*")) 545 c.Assert(err, IsNil) 546 // just README and bin 547 c.Check(snapdirs, HasLen, 2) 548 for _, d := range snapdirs { 549 c.Check(filepath.Base(d), Not(Equals), "foo") 550 } 551 } 552 553 func (s *mgrsSuite) TestHappyRemove(c *C) { 554 oldEstimateSnapshotSize := snapstate.EstimateSnapshotSize 555 snapstate.EstimateSnapshotSize = func(st *state.State, instanceName string, users []string) (uint64, error) { 556 return 0, nil 557 } 558 defer func() { 559 snapstate.EstimateSnapshotSize = oldEstimateSnapshotSize 560 }() 561 562 st := s.o.State() 563 st.Lock() 564 defer st.Unlock() 565 566 snapYamlContent := `name: foo 567 apps: 568 bar: 569 command: bin/bar 570 ` 571 snapInfo := s.installLocalTestSnap(c, snapYamlContent+"version: 1.0") 572 573 // set config 574 tr := config.NewTransaction(st) 575 c.Assert(tr.Set("foo", "key", "value"), IsNil) 576 tr.Commit() 577 578 ts, err := snapstate.Remove(st, "foo", snap.R(0), nil) 579 c.Assert(err, IsNil) 580 chg := st.NewChange("remove-snap", "...") 581 chg.AddAll(ts) 582 583 st.Unlock() 584 err = s.o.Settle(settleTimeout) 585 st.Lock() 586 c.Assert(err, IsNil) 587 588 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err())) 589 590 // ensure that the binary wrapper file got removed 591 binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar") 592 c.Assert(osutil.FileExists(binaryWrapper), Equals, false) 593 594 // data dirs 595 c.Assert(osutil.FileExists(snapInfo.DataDir()), Equals, false) 596 c.Assert(osutil.FileExists(snapInfo.CommonDataDir()), Equals, false) 597 598 // snap file and its mount 599 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, false) 600 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1")) 601 c.Assert(osutil.FileExists(mup), Equals, false) 602 603 // automatic snapshot was created 604 c.Assert(s.automaticSnapshots, DeepEquals, []automaticSnapshotCall{{"foo", map[string]interface{}{"key": "value"}, nil}}) 605 } 606 607 func fakeSnapID(name string) string { 608 const suffix = "idididididididididididididididid" 609 return name + suffix[len(name)+1:] 610 } 611 612 const ( 613 snapV2 = `{ 614 "architectures": [ 615 "all" 616 ], 617 "download": { 618 "url": "@URL@", 619 "size": 123 620 }, 621 "epoch": @EPOCH@, 622 "type": "@TYPE@", 623 "name": "@NAME@", 624 "revision": @REVISION@, 625 "snap-id": "@SNAPID@", 626 "summary": "Foo", 627 "description": "this is a description", 628 "version": "@VERSION@", 629 "publisher": { 630 "id": "devdevdev", 631 "name": "bar" 632 }, 633 "media": [ 634 {"type": "icon", "url": "@ICON@"} 635 ] 636 }` 637 ) 638 639 var fooSnapID = fakeSnapID("foo") 640 641 func (s *baseMgrsSuite) prereqSnapAssertions(c *C, extraHeaders ...map[string]interface{}) *asserts.SnapDeclaration { 642 if len(extraHeaders) == 0 { 643 extraHeaders = []map[string]interface{}{{}} 644 } 645 var snapDecl *asserts.SnapDeclaration 646 for _, extraHeaders := range extraHeaders { 647 headers := map[string]interface{}{ 648 "series": "16", 649 "snap-name": "foo", 650 "publisher-id": "devdevdev", 651 "timestamp": time.Now().Format(time.RFC3339), 652 } 653 for h, v := range extraHeaders { 654 headers[h] = v 655 } 656 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 657 a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 658 c.Assert(err, IsNil) 659 err = s.storeSigning.Add(a) 660 c.Assert(err, IsNil) 661 snapDecl = a.(*asserts.SnapDeclaration) 662 } 663 return snapDecl 664 } 665 666 func (s *baseMgrsSuite) makeStoreTestSnapWithFiles(c *C, snapYaml string, revno string, files [][]string) (path, digest string) { 667 info, err := snap.InfoFromSnapYaml([]byte(snapYaml)) 668 c.Assert(err, IsNil) 669 670 snapPath := makeTestSnapWithFiles(c, snapYaml, files) 671 672 snapDigest, size, err := asserts.SnapFileSHA3_384(snapPath) 673 c.Assert(err, IsNil) 674 675 headers := map[string]interface{}{ 676 "snap-id": fakeSnapID(info.SnapName()), 677 "snap-sha3-384": snapDigest, 678 "snap-size": fmt.Sprintf("%d", size), 679 "snap-revision": revno, 680 "developer-id": "devdevdev", 681 "timestamp": time.Now().Format(time.RFC3339), 682 } 683 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 684 c.Assert(err, IsNil) 685 err = s.storeSigning.Add(snapRev) 686 c.Assert(err, IsNil) 687 688 return snapPath, snapDigest 689 } 690 691 func (s *baseMgrsSuite) makeStoreTestSnap(c *C, snapYaml string, revno string) (path, digest string) { 692 return s.makeStoreTestSnapWithFiles(c, snapYaml, revno, nil) 693 } 694 695 func (s *baseMgrsSuite) pathFor(name, revno string) string { 696 if revno == s.serveRevision[name] { 697 return s.serveSnapPath[name] 698 } 699 for i, r := range s.serveOldRevs[name] { 700 if r == revno { 701 return s.serveOldPaths[name][i] 702 } 703 } 704 return "/not/found" 705 } 706 707 func (s *baseMgrsSuite) newestThatCanRead(name string, epoch snap.Epoch) (info *snap.Info, rev string) { 708 if s.serveSnapPath[name] == "" { 709 return nil, "" 710 } 711 idx := len(s.serveOldPaths[name]) 712 rev = s.serveRevision[name] 713 path := s.serveSnapPath[name] 714 for { 715 snapf, err := snapfile.Open(path) 716 if err != nil { 717 panic(err) 718 } 719 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 720 if err != nil { 721 panic(err) 722 } 723 if info.Epoch.CanRead(epoch) { 724 return info, rev 725 } 726 idx-- 727 if idx < 0 { 728 return nil, "" 729 } 730 path = s.serveOldPaths[name][idx] 731 rev = s.serveOldRevs[name][idx] 732 } 733 } 734 735 func (s *baseMgrsSuite) mockStore(c *C) *httptest.Server { 736 var baseURL *url.URL 737 fillHit := func(hitTemplate, revno string, info *snap.Info) string { 738 epochBuf, err := json.Marshal(info.Epoch) 739 if err != nil { 740 panic(err) 741 } 742 name := info.SnapName() 743 744 hit := strings.Replace(hitTemplate, "@URL@", baseURL.String()+"/api/v1/snaps/download/"+name+"/"+revno, -1) 745 hit = strings.Replace(hit, "@NAME@", name, -1) 746 hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1) 747 hit = strings.Replace(hit, "@ICON@", baseURL.String()+"/icon", -1) 748 hit = strings.Replace(hit, "@VERSION@", info.Version, -1) 749 hit = strings.Replace(hit, "@REVISION@", revno, -1) 750 hit = strings.Replace(hit, `@TYPE@`, string(info.Type()), -1) 751 hit = strings.Replace(hit, `@EPOCH@`, string(epochBuf), -1) 752 return hit 753 } 754 755 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 756 // all URLS are /api/v1/snaps/... or /v2/snaps/ or /v2/assertions/... so 757 // check the url is sane and discard the common prefix 758 // to simplify indexing into the comps slice. 759 comps := strings.Split(r.URL.Path, "/") 760 if len(comps) < 2 { 761 panic("unexpected url path: " + r.URL.Path) 762 } 763 if comps[1] == "api" { //v1 764 if len(comps) <= 4 { 765 panic("unexpected url path: " + r.URL.Path) 766 } 767 comps = comps[4:] 768 if comps[0] == "auth" { 769 comps[0] = "auth:" + comps[1] 770 } 771 } else { // v2 772 if len(comps) <= 3 { 773 panic("unexpected url path: " + r.URL.Path) 774 } 775 if comps[2] == "assertions" { 776 // preserve "assertions" component 777 comps = comps[2:] 778 } else { 779 // drop common "snap" component 780 comps = comps[3:] 781 } 782 comps[0] = "v2:" + comps[0] 783 } 784 785 switch comps[0] { 786 case "auth:nonces": 787 w.Write([]byte(`{"nonce": "NONCE"}`)) 788 return 789 case "auth:sessions": 790 // quick sanity check 791 reqBody, err := ioutil.ReadAll(r.Body) 792 c.Check(err, IsNil) 793 c.Check(bytes.Contains(reqBody, []byte("nonce: NONCE")), Equals, true) 794 c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("serial: %s", s.expectedSerial))), Equals, true) 795 c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("store: %s", s.expectedStore))), Equals, true) 796 797 c.Check(s.sessionMacaroon, Not(Equals), "") 798 w.WriteHeader(200) 799 w.Write([]byte(fmt.Sprintf(`{"macaroon": "%s"}`, s.sessionMacaroon))) 800 return 801 case "v2:assertions": 802 ref := &asserts.Ref{ 803 Type: asserts.Type(comps[1]), 804 PrimaryKey: comps[2:], 805 } 806 a, err := ref.Resolve(s.storeSigning.Find) 807 if asserts.IsNotFound(err) { 808 w.Header().Set("Content-Type", "application/problem+json") 809 w.WriteHeader(404) 810 w.Write([]byte(`{"error-list":[{"code":"not-found","message":"..."}]}`)) 811 return 812 } 813 if err != nil { 814 panic(err) 815 } 816 w.Header().Set("Content-Type", asserts.MediaType) 817 w.WriteHeader(200) 818 w.Write(asserts.Encode(a)) 819 return 820 case "download": 821 if s.sessionMacaroon != "" { 822 // FIXME: download is still using the old headers! 823 c.Check(r.Header.Get("X-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon)) 824 } 825 if s.failNextDownload == comps[1] { 826 s.failNextDownload = "" 827 w.WriteHeader(418) 828 return 829 } 830 if s.hijackServeSnap != nil { 831 s.hijackServeSnap(w) 832 return 833 } 834 snapR, err := os.Open(s.pathFor(comps[1], comps[2])) 835 if err != nil { 836 panic(err) 837 } 838 io.Copy(w, snapR) 839 case "v2:refresh": 840 if s.sessionMacaroon != "" { 841 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon)) 842 } 843 dec := json.NewDecoder(r.Body) 844 var input struct { 845 Actions []struct { 846 Action string `json:"action"` 847 SnapID string `json:"snap-id"` 848 Name string `json:"name"` 849 InstanceKey string `json:"instance-key"` 850 Epoch snap.Epoch `json:"epoch"` 851 // assertions 852 Key string `json:"key"` 853 Assertions []struct { 854 Type string `json:"type"` 855 PrimaryKey []string `json:"primary-key"` 856 IfNewerThan *int `json:"if-newer-than"` 857 } 858 } `json:"actions"` 859 Context []struct { 860 SnapID string `json:"snap-id"` 861 Epoch snap.Epoch `json:"epoch"` 862 } `json:"context"` 863 } 864 if err := dec.Decode(&input); err != nil { 865 panic(err) 866 } 867 id2epoch := make(map[string]snap.Epoch, len(input.Context)) 868 for _, s := range input.Context { 869 id2epoch[s.SnapID] = s.Epoch 870 } 871 type resultJSON struct { 872 Result string `json:"result"` 873 SnapID string `json:"snap-id"` 874 Name string `json:"name"` 875 Snap json.RawMessage `json:"snap"` 876 InstanceKey string `json:"instance-key"` 877 // For assertions 878 Key string `json:"key"` 879 AssertionURLs []string `json:"assertion-stream-urls"` 880 } 881 var results []resultJSON 882 for _, a := range input.Actions { 883 if a.Action == "fetch-assertions" { 884 urls := []string{} 885 for _, ar := range a.Assertions { 886 ref := &asserts.Ref{ 887 Type: asserts.Type(ar.Type), 888 PrimaryKey: ar.PrimaryKey, 889 } 890 _, err := ref.Resolve(s.storeSigning.Find) 891 if err != nil { 892 panic("missing assertions not supported") 893 } 894 urls = append(urls, fmt.Sprintf("%s/v2/assertions/%s", baseURL.String(), ref.Unique())) 895 896 } 897 results = append(results, resultJSON{ 898 Result: "fetch-assertions", 899 Key: a.Key, 900 AssertionURLs: urls, 901 }) 902 continue 903 } 904 name := s.serveIDtoName[a.SnapID] 905 epoch := id2epoch[a.SnapID] 906 if a.Action == "install" { 907 name = a.Name 908 epoch = a.Epoch 909 } 910 911 info, revno := s.newestThatCanRead(name, epoch) 912 if info == nil { 913 // no match 914 continue 915 } 916 results = append(results, resultJSON{ 917 Result: a.Action, 918 SnapID: a.SnapID, 919 InstanceKey: a.InstanceKey, 920 Name: name, 921 Snap: json.RawMessage(fillHit(snapV2, revno, info)), 922 }) 923 } 924 w.WriteHeader(200) 925 output, err := json.Marshal(map[string]interface{}{ 926 "results": results, 927 }) 928 if err != nil { 929 panic(err) 930 } 931 w.Write(output) 932 933 default: 934 panic("unexpected url path: " + r.URL.Path) 935 } 936 })) 937 c.Assert(mockServer, NotNil) 938 939 baseURL, _ = url.Parse(mockServer.URL) 940 storeCfg := store.Config{ 941 StoreBaseURL: baseURL, 942 } 943 944 mStore := store.New(&storeCfg, nil) 945 st := s.o.State() 946 st.Lock() 947 snapstate.ReplaceStore(s.o.State(), mStore) 948 st.Unlock() 949 950 // this will be used by remodeling cases 951 storeNew := func(cfg *store.Config, dac store.DeviceAndAuthContext) *store.Store { 952 cfg.StoreBaseURL = baseURL 953 if s.checkDeviceAndAuthContext != nil { 954 s.checkDeviceAndAuthContext(dac) 955 } 956 return store.New(cfg, dac) 957 } 958 959 s.AddCleanup(overlord.MockStoreNew(storeNew)) 960 961 return mockServer 962 } 963 964 // serveSnap starts serving the snap at snapPath, moving the current 965 // one onto the list of previous ones if already set. 966 func (s *baseMgrsSuite) serveSnap(snapPath, revno string) { 967 snapf, err := snapfile.Open(snapPath) 968 if err != nil { 969 panic(err) 970 } 971 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 972 if err != nil { 973 panic(err) 974 } 975 name := info.SnapName() 976 s.serveIDtoName[fakeSnapID(name)] = name 977 978 if oldPath := s.serveSnapPath[name]; oldPath != "" { 979 oldRev := s.serveRevision[name] 980 if oldRev == "" { 981 panic("old path set but not old revision") 982 } 983 s.serveOldPaths[name] = append(s.serveOldPaths[name], oldPath) 984 s.serveOldRevs[name] = append(s.serveOldRevs[name], oldRev) 985 } 986 s.serveSnapPath[name] = snapPath 987 s.serveRevision[name] = revno 988 } 989 990 func (s *mgrsSuite) TestHappyRemoteInstallAndUpgradeSvc(c *C) { 991 // test install through store and update, plus some mechanics 992 // of update 993 // TODO: ok to split if it gets too messy to maintain 994 995 s.prereqSnapAssertions(c) 996 997 snapYamlContent := `name: foo 998 version: @VERSION@ 999 apps: 1000 bar: 1001 command: bin/bar 1002 svc: 1003 command: svc 1004 daemon: forking 1005 ` 1006 1007 ver := "1.0" 1008 revno := "42" 1009 snapPath, digest := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1010 s.serveSnap(snapPath, revno) 1011 1012 mockServer := s.mockStore(c) 1013 defer mockServer.Close() 1014 1015 st := s.o.State() 1016 st.Lock() 1017 defer st.Unlock() 1018 1019 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1020 c.Assert(err, IsNil) 1021 chg := st.NewChange("install-snap", "...") 1022 chg.AddAll(ts) 1023 1024 st.Unlock() 1025 err = s.o.Settle(settleTimeout) 1026 st.Lock() 1027 c.Assert(err, IsNil) 1028 1029 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1030 1031 info, err := snapstate.CurrentInfo(st, "foo") 1032 c.Assert(err, IsNil) 1033 1034 c.Check(info.Revision, Equals, snap.R(42)) 1035 c.Check(info.SnapID, Equals, fooSnapID) 1036 c.Check(info.Version, Equals, "1.0") 1037 c.Check(info.Summary(), Equals, "Foo") 1038 c.Check(info.Description(), Equals, "this is a description") 1039 c.Assert(osutil.FileExists(info.MountFile()), Equals, true) 1040 1041 pubAcct, err := assertstate.Publisher(st, info.SnapID) 1042 c.Assert(err, IsNil) 1043 c.Check(pubAcct.AccountID(), Equals, "devdevdev") 1044 c.Check(pubAcct.Username(), Equals, "devdevdev") 1045 1046 snapRev42, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{ 1047 "snap-sha3-384": digest, 1048 }) 1049 c.Assert(err, IsNil) 1050 c.Check(snapRev42.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID) 1051 c.Check(snapRev42.(*asserts.SnapRevision).SnapRevision(), Equals, 42) 1052 1053 // check service was setup properly 1054 svcFile := filepath.Join(dirs.SnapServicesDir, "snap.foo.svc.service") 1055 c.Assert(osutil.FileExists(svcFile), Equals, true) 1056 stat, err := os.Stat(svcFile) 1057 c.Assert(err, IsNil) 1058 // should _not_ be executable 1059 c.Assert(stat.Mode().String(), Equals, "-rw-r--r--") 1060 1061 // Refresh 1062 1063 ver = "2.0" 1064 revno = "50" 1065 snapPath, digest = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1066 s.serveSnap(snapPath, revno) 1067 1068 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 1069 c.Assert(err, IsNil) 1070 chg = st.NewChange("upgrade-snap", "...") 1071 chg.AddAll(ts) 1072 1073 st.Unlock() 1074 err = s.o.Settle(settleTimeout) 1075 st.Lock() 1076 c.Assert(err, IsNil) 1077 1078 c.Assert(chg.Err(), IsNil) 1079 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1080 1081 info, err = snapstate.CurrentInfo(st, "foo") 1082 c.Assert(err, IsNil) 1083 1084 c.Check(info.Revision, Equals, snap.R(50)) 1085 c.Check(info.SnapID, Equals, fooSnapID) 1086 c.Check(info.Version, Equals, "2.0") 1087 1088 snapRev50, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{ 1089 "snap-sha3-384": digest, 1090 }) 1091 c.Assert(err, IsNil) 1092 c.Check(snapRev50.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID) 1093 c.Check(snapRev50.(*asserts.SnapRevision).SnapRevision(), Equals, 50) 1094 1095 // check updated wrapper 1096 symlinkTarget, err := os.Readlink(info.Apps["bar"].WrapperPath()) 1097 c.Assert(err, IsNil) 1098 c.Assert(symlinkTarget, Equals, "/usr/bin/snap") 1099 1100 // check updated service file 1101 c.Assert(svcFile, testutil.FileContains, "/var/snap/foo/"+revno) 1102 } 1103 1104 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithEpochBump(c *C) { 1105 // test install through store and update, where there's an epoch bump in the upgrade 1106 // this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc 1107 1108 s.prereqSnapAssertions(c) 1109 1110 snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0}", "1") 1111 s.serveSnap(snapPath, "1") 1112 1113 mockServer := s.mockStore(c) 1114 defer mockServer.Close() 1115 1116 st := s.o.State() 1117 st.Lock() 1118 defer st.Unlock() 1119 1120 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1121 c.Assert(err, IsNil) 1122 chg := st.NewChange("install-snap", "...") 1123 chg.AddAll(ts) 1124 1125 st.Unlock() 1126 err = s.o.Settle(settleTimeout) 1127 st.Lock() 1128 c.Assert(err, IsNil) 1129 1130 // confirm it worked 1131 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1132 1133 // sanity checks 1134 info, err := snapstate.CurrentInfo(st, "foo") 1135 c.Assert(err, IsNil) 1136 c.Assert(info.Revision, Equals, snap.R(1)) 1137 c.Assert(info.SnapID, Equals, fooSnapID) 1138 c.Assert(info.Epoch.String(), Equals, "0") 1139 1140 // now add some more snaps 1141 for i, epoch := range []string{"1*", "2*", "3*"} { 1142 revno := fmt.Sprint(i + 2) 1143 snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0, epoch: "+epoch+"}", revno) 1144 s.serveSnap(snapPath, revno) 1145 } 1146 1147 // refresh 1148 1149 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 1150 c.Assert(err, IsNil) 1151 chg = st.NewChange("upgrade-snap", "...") 1152 chg.AddAll(ts) 1153 1154 st.Unlock() 1155 err = s.o.Settle(settleTimeout) 1156 st.Lock() 1157 c.Assert(err, IsNil) 1158 1159 c.Assert(chg.Err(), IsNil) 1160 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1161 1162 info, err = snapstate.CurrentInfo(st, "foo") 1163 c.Assert(err, IsNil) 1164 1165 c.Check(info.Revision, Equals, snap.R(4)) 1166 c.Check(info.SnapID, Equals, fooSnapID) 1167 c.Check(info.Epoch.String(), Equals, "3*") 1168 } 1169 1170 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithPostHocEpochBump(c *C) { 1171 // test install through store and update, where there is an epoch 1172 // bump in the upgrade that comes in after the initial update is 1173 // computed. 1174 1175 // this is mostly checking the same as TestHappyRemoteInstallAndUpdateWithEpochBump 1176 // but serves as a sanity check for the Without case that follows 1177 // (these two together serve as a test for the refresh filtering) 1178 s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, true) 1179 } 1180 1181 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithoutEpochBump(c *C) { 1182 // test install through store and update, where there _isn't_ an epoch bump in the upgrade 1183 // note that there _are_ refreshes available after the refresh, 1184 // but they're not an epoch bump so they're ignored 1185 s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, false) 1186 } 1187 1188 func (s *mgrsSuite) testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c *C, doBump bool) { 1189 s.prereqSnapAssertions(c) 1190 1191 snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 1}", "1") 1192 s.serveSnap(snapPath, "1") 1193 1194 mockServer := s.mockStore(c) 1195 defer mockServer.Close() 1196 1197 st := s.o.State() 1198 st.Lock() 1199 defer st.Unlock() 1200 1201 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1202 c.Assert(err, IsNil) 1203 chg := st.NewChange("install-snap", "...") 1204 chg.AddAll(ts) 1205 1206 st.Unlock() 1207 err = s.o.Settle(settleTimeout) 1208 st.Lock() 1209 c.Assert(err, IsNil) 1210 1211 // confirm it worked 1212 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1213 1214 // sanity checks 1215 info, err := snapstate.CurrentInfo(st, "foo") 1216 c.Assert(err, IsNil) 1217 c.Assert(info.Revision, Equals, snap.R(1)) 1218 c.Assert(info.SnapID, Equals, fooSnapID) 1219 c.Assert(info.Epoch.String(), Equals, "0") 1220 1221 // add a new revision 1222 snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 2}", "2") 1223 s.serveSnap(snapPath, "2") 1224 1225 // refresh 1226 1227 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 1228 c.Assert(err, IsNil) 1229 chg = st.NewChange("upgrade-snap", "...") 1230 chg.AddAll(ts) 1231 1232 // add another new revision, after the update was computed (maybe with an epoch bump) 1233 if doBump { 1234 snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3, epoch: 1*}", "3") 1235 } else { 1236 snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3}", "3") 1237 } 1238 s.serveSnap(snapPath, "3") 1239 1240 st.Unlock() 1241 err = s.o.Settle(settleTimeout) 1242 st.Lock() 1243 c.Assert(err, IsNil) 1244 1245 c.Assert(chg.Err(), IsNil) 1246 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1247 1248 info, err = snapstate.CurrentInfo(st, "foo") 1249 c.Assert(err, IsNil) 1250 1251 if doBump { 1252 // if the epoch bumped, then we should've re-refreshed 1253 c.Check(info.Revision, Equals, snap.R(3)) 1254 c.Check(info.SnapID, Equals, fooSnapID) 1255 c.Check(info.Epoch.String(), Equals, "1*") 1256 } else { 1257 // if the epoch did not bump, then we should _not_ have re-refreshed 1258 c.Check(info.Revision, Equals, snap.R(2)) 1259 c.Check(info.SnapID, Equals, fooSnapID) 1260 c.Check(info.Epoch.String(), Equals, "0") 1261 } 1262 } 1263 1264 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBump(c *C) { 1265 // test install through store and update many, where there's an epoch bump in the upgrade 1266 // this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc 1267 1268 snapNames := []string{"aaaa", "bbbb", "cccc"} 1269 for _, name := range snapNames { 1270 s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name}) 1271 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1") 1272 s.serveSnap(snapPath, "1") 1273 } 1274 1275 mockServer := s.mockStore(c) 1276 defer mockServer.Close() 1277 1278 st := s.o.State() 1279 st.Lock() 1280 defer st.Unlock() 1281 1282 affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0) 1283 c.Assert(err, IsNil) 1284 sort.Strings(affected) 1285 c.Check(affected, DeepEquals, snapNames) 1286 chg := st.NewChange("install-snaps", "...") 1287 for _, taskset := range tasksets { 1288 chg.AddAll(taskset) 1289 } 1290 1291 st.Unlock() 1292 err = s.o.Settle(settleTimeout) 1293 st.Lock() 1294 c.Assert(err, IsNil) 1295 1296 // confirm it worked 1297 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1298 1299 // sanity checks 1300 for _, name := range snapNames { 1301 info, err := snapstate.CurrentInfo(st, name) 1302 c.Assert(err, IsNil) 1303 c.Assert(info.Revision, Equals, snap.R(1)) 1304 c.Assert(info.SnapID, Equals, fakeSnapID(name)) 1305 c.Assert(info.Epoch.String(), Equals, "0") 1306 } 1307 1308 // now add some more snap revisions with increasing epochs 1309 for _, name := range snapNames { 1310 for i, epoch := range []string{"1*", "2*", "3*"} { 1311 revno := fmt.Sprint(i + 2) 1312 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno) 1313 s.serveSnap(snapPath, revno) 1314 } 1315 } 1316 1317 // refresh 1318 1319 affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{}) 1320 c.Assert(err, IsNil) 1321 sort.Strings(affected) 1322 c.Check(affected, DeepEquals, snapNames) 1323 chg = st.NewChange("upgrade-snaps", "...") 1324 for _, taskset := range tasksets { 1325 chg.AddAll(taskset) 1326 } 1327 1328 st.Unlock() 1329 err = s.o.Settle(settleTimeout) 1330 st.Lock() 1331 c.Assert(err, IsNil) 1332 1333 c.Assert(chg.Err(), IsNil) 1334 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1335 1336 for _, name := range snapNames { 1337 info, err := snapstate.CurrentInfo(st, name) 1338 c.Assert(err, IsNil) 1339 1340 c.Check(info.Revision, Equals, snap.R(4)) 1341 c.Check(info.SnapID, Equals, fakeSnapID(name)) 1342 c.Check(info.Epoch.String(), Equals, "3*") 1343 } 1344 } 1345 1346 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBumpAndOneFailing(c *C) { 1347 // test install through store and update, where there's an epoch bump in the upgrade and one of them fails 1348 1349 snapNames := []string{"aaaa", "bbbb", "cccc"} 1350 for _, name := range snapNames { 1351 s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name}) 1352 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1") 1353 s.serveSnap(snapPath, "1") 1354 } 1355 1356 mockServer := s.mockStore(c) 1357 defer mockServer.Close() 1358 1359 st := s.o.State() 1360 st.Lock() 1361 defer st.Unlock() 1362 1363 affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0) 1364 c.Assert(err, IsNil) 1365 sort.Strings(affected) 1366 c.Check(affected, DeepEquals, snapNames) 1367 chg := st.NewChange("install-snaps", "...") 1368 for _, taskset := range tasksets { 1369 chg.AddAll(taskset) 1370 } 1371 1372 st.Unlock() 1373 err = s.o.Settle(settleTimeout) 1374 st.Lock() 1375 c.Assert(err, IsNil) 1376 1377 // confirm it worked 1378 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1379 1380 // sanity checks 1381 for _, name := range snapNames { 1382 info, err := snapstate.CurrentInfo(st, name) 1383 c.Assert(err, IsNil) 1384 c.Assert(info.Revision, Equals, snap.R(1)) 1385 c.Assert(info.SnapID, Equals, fakeSnapID(name)) 1386 c.Assert(info.Epoch.String(), Equals, "0") 1387 } 1388 1389 // now add some more snap revisions with increasing epochs 1390 for _, name := range snapNames { 1391 for i, epoch := range []string{"1*", "2*", "3*"} { 1392 revno := fmt.Sprint(i + 2) 1393 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno) 1394 s.serveSnap(snapPath, revno) 1395 } 1396 } 1397 1398 // refresh 1399 affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{}) 1400 c.Assert(err, IsNil) 1401 sort.Strings(affected) 1402 c.Check(affected, DeepEquals, snapNames) 1403 chg = st.NewChange("upgrade-snaps", "...") 1404 for _, taskset := range tasksets { 1405 chg.AddAll(taskset) 1406 } 1407 1408 st.Unlock() 1409 // the download for the refresh above will be performed below, during 'settle'. 1410 // fail the refresh of cccc by failing its download 1411 s.failNextDownload = "cccc" 1412 err = s.o.Settle(settleTimeout) 1413 st.Lock() 1414 c.Assert(err, IsNil) 1415 1416 c.Assert(chg.Err(), NotNil) 1417 c.Assert(chg.Status(), Equals, state.ErrorStatus) 1418 1419 for _, name := range snapNames { 1420 comment := Commentf("%q", name) 1421 info, err := snapstate.CurrentInfo(st, name) 1422 c.Assert(err, IsNil, comment) 1423 1424 if name == "cccc" { 1425 // the failed one: still on rev 1 (epoch 0) 1426 c.Assert(info.Revision, Equals, snap.R(1)) 1427 c.Assert(info.SnapID, Equals, fakeSnapID(name)) 1428 c.Assert(info.Epoch.String(), Equals, "0") 1429 } else { 1430 // the non-failed ones: refreshed to rev 4 (epoch 3*) 1431 c.Check(info.Revision, Equals, snap.R(4), comment) 1432 c.Check(info.SnapID, Equals, fakeSnapID(name), comment) 1433 c.Check(info.Epoch.String(), Equals, "3*", comment) 1434 } 1435 } 1436 } 1437 1438 func (s *mgrsSuite) TestHappyLocalInstallWithStoreMetadata(c *C) { 1439 snapDecl := s.prereqSnapAssertions(c) 1440 1441 snapYamlContent := `name: foo 1442 apps: 1443 bar: 1444 command: bin/bar 1445 ` 1446 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1447 1448 si := &snap.SideInfo{ 1449 RealName: "foo", 1450 SnapID: fooSnapID, 1451 Revision: snap.R(55), 1452 } 1453 1454 st := s.o.State() 1455 st.Lock() 1456 defer st.Unlock() 1457 1458 // have the snap-declaration in the system db 1459 err := assertstate.Add(st, s.devAcct) 1460 c.Assert(err, IsNil) 1461 err = assertstate.Add(st, snapDecl) 1462 c.Assert(err, IsNil) 1463 1464 ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true}) 1465 c.Assert(err, IsNil) 1466 chg := st.NewChange("install-snap", "...") 1467 chg.AddAll(ts) 1468 1469 st.Unlock() 1470 err = s.o.Settle(settleTimeout) 1471 st.Lock() 1472 c.Assert(err, IsNil) 1473 1474 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1475 1476 info, err := snapstate.CurrentInfo(st, "foo") 1477 c.Assert(err, IsNil) 1478 c.Check(info.Revision, Equals, snap.R(55)) 1479 c.Check(info.SnapID, Equals, fooSnapID) 1480 c.Check(info.Version, Equals, "1.5") 1481 1482 // ensure that the binary wrapper file got generated with the right 1483 // name 1484 binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar") 1485 c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true) 1486 1487 // data dirs 1488 c.Assert(osutil.IsDirectory(info.DataDir()), Equals, true) 1489 c.Assert(osutil.IsDirectory(info.CommonDataDir()), Equals, true) 1490 1491 // snap file and its mounting 1492 1493 // after install the snap file is in the right dir 1494 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_55.snap")), Equals, true) 1495 1496 // ensure the right unit is created 1497 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/55")) 1498 c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/55", dirs.StripRootDir(dirs.SnapMountDir))) 1499 c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_55.snap") 1500 } 1501 1502 func (s *mgrsSuite) TestParallelInstanceLocalInstallSnapNameMismatch(c *C) { 1503 snapDecl := s.prereqSnapAssertions(c) 1504 1505 snapYamlContent := `name: foo 1506 apps: 1507 bar: 1508 command: bin/bar 1509 ` 1510 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1511 1512 si := &snap.SideInfo{ 1513 RealName: "foo", 1514 SnapID: fooSnapID, 1515 Revision: snap.R(55), 1516 } 1517 1518 st := s.o.State() 1519 st.Lock() 1520 defer st.Unlock() 1521 1522 // have the snap-declaration in the system db 1523 err := assertstate.Add(st, s.devAcct) 1524 c.Assert(err, IsNil) 1525 err = assertstate.Add(st, snapDecl) 1526 c.Assert(err, IsNil) 1527 1528 _, _, err = snapstate.InstallPath(st, si, snapPath, "bar_instance", "", snapstate.Flags{DevMode: true}) 1529 c.Assert(err, ErrorMatches, `cannot install snap "bar_instance", the name does not match the metadata "foo"`) 1530 } 1531 1532 func (s *mgrsSuite) TestParallelInstanceLocalInstallInvalidInstanceName(c *C) { 1533 snapDecl := s.prereqSnapAssertions(c) 1534 1535 snapYamlContent := `name: foo 1536 apps: 1537 bar: 1538 command: bin/bar 1539 ` 1540 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1541 1542 si := &snap.SideInfo{ 1543 RealName: "foo", 1544 SnapID: fooSnapID, 1545 Revision: snap.R(55), 1546 } 1547 1548 st := s.o.State() 1549 st.Lock() 1550 defer st.Unlock() 1551 1552 // have the snap-declaration in the system db 1553 err := assertstate.Add(st, s.devAcct) 1554 c.Assert(err, IsNil) 1555 err = assertstate.Add(st, snapDecl) 1556 c.Assert(err, IsNil) 1557 1558 _, _, err = snapstate.InstallPath(st, si, snapPath, "bar_invalid_instance_name", "", snapstate.Flags{DevMode: true}) 1559 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "invalid_instance_name"`) 1560 } 1561 1562 func (s *mgrsSuite) TestCheckInterfaces(c *C) { 1563 snapDecl := s.prereqSnapAssertions(c) 1564 1565 snapYamlContent := `name: foo 1566 apps: 1567 bar: 1568 command: bin/bar 1569 slots: 1570 network: 1571 ` 1572 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1573 1574 si := &snap.SideInfo{ 1575 RealName: "foo", 1576 SnapID: fooSnapID, 1577 Revision: snap.R(55), 1578 } 1579 1580 st := s.o.State() 1581 st.Lock() 1582 defer st.Unlock() 1583 1584 // have the snap-declaration in the system db 1585 err := assertstate.Add(st, s.devAcct) 1586 c.Assert(err, IsNil) 1587 err = assertstate.Add(st, snapDecl) 1588 c.Assert(err, IsNil) 1589 1590 // mock SanitizePlugsSlots so that unknown interfaces are not rejected 1591 restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 1592 defer restoreSanitize() 1593 1594 ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true}) 1595 c.Assert(err, IsNil) 1596 chg := st.NewChange("install-snap", "...") 1597 chg.AddAll(ts) 1598 1599 st.Unlock() 1600 err = s.o.Settle(settleTimeout) 1601 st.Lock() 1602 c.Assert(err, IsNil) 1603 1604 c.Assert(chg.Err(), ErrorMatches, `(?s).*installation not allowed by "network" slot rule of interface "network".*`) 1605 c.Check(chg.Status(), Equals, state.ErrorStatus) 1606 } 1607 1608 func (s *mgrsSuite) TestHappyRefreshControl(c *C) { 1609 // test install through store and update, plus some mechanics 1610 // of update 1611 // TODO: ok to split if it gets too messy to maintain 1612 1613 s.prereqSnapAssertions(c) 1614 1615 snapYamlContent := `name: foo 1616 version: @VERSION@ 1617 ` 1618 1619 ver := "1.0" 1620 revno := "42" 1621 snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1622 s.serveSnap(snapPath, revno) 1623 1624 mockServer := s.mockStore(c) 1625 defer mockServer.Close() 1626 1627 st := s.o.State() 1628 st.Lock() 1629 defer st.Unlock() 1630 1631 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1632 c.Assert(err, IsNil) 1633 chg := st.NewChange("install-snap", "...") 1634 chg.AddAll(ts) 1635 1636 st.Unlock() 1637 err = s.o.Settle(settleTimeout) 1638 st.Lock() 1639 c.Assert(err, IsNil) 1640 1641 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1642 1643 info, err := snapstate.CurrentInfo(st, "foo") 1644 c.Assert(err, IsNil) 1645 1646 c.Check(info.Revision, Equals, snap.R(42)) 1647 1648 // Refresh 1649 1650 // Setup refresh control 1651 1652 headers := map[string]interface{}{ 1653 "series": "16", 1654 "snap-id": "bar-id", 1655 "snap-name": "bar", 1656 "publisher-id": "devdevdev", 1657 "refresh-control": []interface{}{fooSnapID}, 1658 "timestamp": time.Now().Format(time.RFC3339), 1659 } 1660 snapDeclBar, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 1661 c.Assert(err, IsNil) 1662 err = s.storeSigning.Add(snapDeclBar) 1663 c.Assert(err, IsNil) 1664 err = assertstate.Add(st, snapDeclBar) 1665 c.Assert(err, IsNil) 1666 1667 snapstate.Set(st, "bar", &snapstate.SnapState{ 1668 Active: true, 1669 Sequence: []*snap.SideInfo{ 1670 {RealName: "bar", SnapID: "bar-id", Revision: snap.R(1)}, 1671 }, 1672 Current: snap.R(1), 1673 SnapType: "app", 1674 }) 1675 1676 develSigning := assertstest.NewSigningDB("devdevdev", develPrivKey) 1677 1678 develAccKey := assertstest.NewAccountKey(s.storeSigning, s.devAcct, nil, develPrivKey.PublicKey(), "") 1679 err = s.storeSigning.Add(develAccKey) 1680 c.Assert(err, IsNil) 1681 1682 ver = "2.0" 1683 revno = "50" 1684 snapPath, _ = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1685 s.serveSnap(snapPath, revno) 1686 1687 updated, tss, err := snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil) 1688 c.Check(updated, IsNil) 1689 c.Check(tss, IsNil) 1690 // no validation we, get an error 1691 c.Check(err, ErrorMatches, `cannot refresh "foo" to revision 50: no validation by "bar"`) 1692 1693 // setup validation 1694 headers = map[string]interface{}{ 1695 "series": "16", 1696 "snap-id": "bar-id", 1697 "approved-snap-id": fooSnapID, 1698 "approved-snap-revision": "50", 1699 "timestamp": time.Now().Format(time.RFC3339), 1700 } 1701 barValidation, err := develSigning.Sign(asserts.ValidationType, headers, nil, "") 1702 c.Assert(err, IsNil) 1703 err = s.storeSigning.Add(barValidation) 1704 c.Assert(err, IsNil) 1705 1706 // ... and try again 1707 updated, tss, err = snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil) 1708 c.Assert(err, IsNil) 1709 c.Assert(updated, DeepEquals, []string{"foo"}) 1710 c.Assert(tss, HasLen, 2) 1711 verifyLastTasksetIsRerefresh(c, tss) 1712 chg = st.NewChange("upgrade-snaps", "...") 1713 chg.AddAll(tss[0]) 1714 1715 st.Unlock() 1716 err = s.o.Settle(settleTimeout) 1717 st.Lock() 1718 c.Assert(err, IsNil) 1719 1720 c.Assert(chg.Err(), IsNil) 1721 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1722 1723 info, err = snapstate.CurrentInfo(st, "foo") 1724 c.Assert(err, IsNil) 1725 1726 c.Check(info.Revision, Equals, snap.R(50)) 1727 } 1728 1729 // core & kernel 1730 1731 var modelDefaults = map[string]interface{}{ 1732 "architecture": "amd64", 1733 "store": "my-brand-store-id", 1734 "gadget": "pc", 1735 "kernel": "pc-kernel", 1736 } 1737 1738 func findKind(chg *state.Change, kind string) *state.Task { 1739 for _, t := range chg.Tasks() { 1740 if t.Kind() == kind { 1741 return t 1742 } 1743 } 1744 return nil 1745 } 1746 1747 func (s *mgrsSuite) TestInstallCoreSnapUpdatesBootloaderEnvAndSplitsAcrossRestart(c *C) { 1748 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1749 bootloader.Force(bloader) 1750 defer bootloader.Force(nil) 1751 bloader.SetBootBase("core_99.snap") 1752 1753 restore := release.MockOnClassic(false) 1754 defer restore() 1755 1756 model := s.brands.Model("my-brand", "my-model", modelDefaults) 1757 1758 const packageOS = ` 1759 name: core 1760 version: 16.04-1 1761 type: os 1762 ` 1763 snapPath := makeTestSnap(c, packageOS) 1764 1765 st := s.o.State() 1766 st.Lock() 1767 defer st.Unlock() 1768 1769 // setup model assertion 1770 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 1771 devicestatetest.SetDevice(st, &auth.DeviceState{ 1772 Brand: "my-brand", 1773 Model: "my-model", 1774 Serial: "serialserialserial", 1775 }) 1776 err := assertstate.Add(st, model) 1777 c.Assert(err, IsNil) 1778 1779 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "core"}, snapPath, "", "", snapstate.Flags{}) 1780 c.Assert(err, IsNil) 1781 chg := st.NewChange("install-snap", "...") 1782 chg.AddAll(ts) 1783 1784 st.Unlock() 1785 err = s.o.Settle(settleTimeout) 1786 st.Lock() 1787 c.Assert(err, IsNil) 1788 1789 // final steps will are post poned until we are in the restarted snapd 1790 ok, rst := st.Restarting() 1791 c.Assert(ok, Equals, true) 1792 c.Assert(rst, Equals, state.RestartSystem) 1793 1794 t := findKind(chg, "auto-connect") 1795 c.Assert(t, NotNil) 1796 c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1797 1798 // this is already set 1799 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 1800 "snap_core": "core_99.snap", 1801 "snap_try_core": "core_x1.snap", 1802 "snap_try_kernel": "", 1803 "snap_mode": boot.TryStatus, 1804 }) 1805 1806 // simulate successful restart happened 1807 state.MockRestarting(st, state.RestartUnset) 1808 bloader.BootVars["snap_mode"] = boot.DefaultStatus 1809 bloader.SetBootBase("core_x1.snap") 1810 1811 st.Unlock() 1812 err = s.o.Settle(settleTimeout) 1813 st.Lock() 1814 c.Assert(err, IsNil) 1815 1816 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1817 } 1818 1819 type rebootEnv interface { 1820 SetTryingDuringReboot(which []snap.Type) error 1821 SetRollbackAcrossReboot(which []snap.Type) error 1822 } 1823 1824 func (s *baseMgrsSuite) mockSuccessfulReboot(c *C, be rebootEnv, which []snap.Type) { 1825 st := s.o.State() 1826 restarting, restartType := st.Restarting() 1827 c.Assert(restarting, Equals, true, Commentf("mockSuccessfulReboot called when there was no pending restart")) 1828 c.Assert(restartType, Equals, state.RestartSystem, Commentf("mockSuccessfulReboot called but restartType is not SystemRestart but %v", restartType)) 1829 state.MockRestarting(st, state.RestartUnset) 1830 err := be.SetTryingDuringReboot(which) 1831 c.Assert(err, IsNil) 1832 s.o.DeviceManager().ResetBootOk() 1833 st.Unlock() 1834 defer st.Lock() 1835 err = s.o.DeviceManager().Ensure() 1836 c.Assert(err, IsNil) 1837 } 1838 1839 func (s *baseMgrsSuite) mockRollbackAcrossReboot(c *C, be rebootEnv, which []snap.Type) { 1840 st := s.o.State() 1841 restarting, restartType := st.Restarting() 1842 c.Assert(restarting, Equals, true, Commentf("mockRollbackAcrossReboot called when there was no pending restart")) 1843 c.Assert(restartType, Equals, state.RestartSystem, Commentf("mockRollbackAcrossReboot called but restartType is not SystemRestart but %v", restartType)) 1844 state.MockRestarting(st, state.RestartUnset) 1845 err := be.SetRollbackAcrossReboot(which) 1846 c.Assert(err, IsNil) 1847 s.o.DeviceManager().ResetBootOk() 1848 st.Unlock() 1849 s.o.Settle(settleTimeout) 1850 st.Lock() 1851 } 1852 1853 func (s *mgrsSuite) TestInstallKernelSnapUpdatesBootloaderEnv(c *C) { 1854 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1855 bootloader.Force(bloader) 1856 defer bootloader.Force(nil) 1857 1858 restore := release.MockOnClassic(false) 1859 defer restore() 1860 1861 model := s.brands.Model("my-brand", "my-model", modelDefaults) 1862 1863 const packageKernel = ` 1864 name: pc-kernel 1865 version: 4.0-1 1866 type: kernel` 1867 1868 files := [][]string{ 1869 {"kernel.img", "I'm a kernel"}, 1870 {"initrd.img", "...and I'm an initrd"}, 1871 {"meta/kernel.yaml", "version: 4.2"}, 1872 } 1873 snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 1874 1875 st := s.o.State() 1876 st.Lock() 1877 defer st.Unlock() 1878 1879 // pretend we have core18/pc-kernel 1880 bloader.BootVars = map[string]string{ 1881 "snap_core": "core18_2.snap", 1882 "snap_kernel": "pc-kernel_123.snap", 1883 "snap_mode": boot.DefaultStatus, 1884 } 1885 si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)} 1886 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 1887 SnapType: "kernel", 1888 Active: true, 1889 Sequence: []*snap.SideInfo{si1}, 1890 Current: si1.Revision, 1891 }) 1892 snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{ 1893 {"meta/kernel.yaml", ""}, 1894 }) 1895 si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)} 1896 snapstate.Set(st, "core18", &snapstate.SnapState{ 1897 SnapType: "base", 1898 Active: true, 1899 Sequence: []*snap.SideInfo{si2}, 1900 Current: si2.Revision, 1901 }) 1902 1903 // setup model assertion 1904 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 1905 devicestatetest.SetDevice(st, &auth.DeviceState{ 1906 Brand: "my-brand", 1907 Model: "my-model", 1908 Serial: "serialserialserial", 1909 }) 1910 err := assertstate.Add(st, model) 1911 c.Assert(err, IsNil) 1912 1913 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{}) 1914 c.Assert(err, IsNil) 1915 chg := st.NewChange("install-snap", "...") 1916 chg.AddAll(ts) 1917 1918 // run, this will trigger a wait for the restart 1919 st.Unlock() 1920 err = s.o.Settle(settleTimeout) 1921 st.Lock() 1922 c.Assert(err, IsNil) 1923 // we are in restarting state and the change is not done yet 1924 restarting, _ := st.Restarting() 1925 c.Check(restarting, Equals, true) 1926 c.Check(chg.Status(), Equals, state.DoingStatus) 1927 1928 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 1929 "snap_core": "core18_2.snap", 1930 "snap_try_core": "", 1931 "snap_kernel": "pc-kernel_123.snap", 1932 "snap_try_kernel": "pc-kernel_x1.snap", 1933 "snap_mode": boot.TryStatus, 1934 }) 1935 // pretend we restarted 1936 s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel}) 1937 1938 st.Unlock() 1939 err = s.o.Settle(settleTimeout) 1940 st.Lock() 1941 c.Assert(err, IsNil) 1942 1943 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1944 } 1945 1946 func (s *mgrsSuite) TestInstallKernelSnapUndoUpdatesBootloaderEnv(c *C) { 1947 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 1948 bootloader.Force(bloader) 1949 defer bootloader.Force(nil) 1950 1951 restore := release.MockOnClassic(false) 1952 defer restore() 1953 1954 model := s.brands.Model("my-brand", "my-model", modelDefaults) 1955 1956 const packageKernel = ` 1957 name: pc-kernel 1958 version: 4.0-1 1959 type: kernel` 1960 1961 files := [][]string{ 1962 {"kernel.img", "I'm a kernel"}, 1963 {"initrd.img", "...and I'm an initrd"}, 1964 {"meta/kernel.yaml", "version: 4.2"}, 1965 } 1966 snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 1967 1968 st := s.o.State() 1969 st.Lock() 1970 defer st.Unlock() 1971 1972 // pretend we have core18/pc-kernel 1973 bloader.BootVars = map[string]string{ 1974 "snap_core": "core18_2.snap", 1975 "snap_kernel": "pc-kernel_123.snap", 1976 "snap_mode": boot.DefaultStatus, 1977 } 1978 si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)} 1979 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 1980 SnapType: "kernel", 1981 Active: true, 1982 Sequence: []*snap.SideInfo{si1}, 1983 Current: si1.Revision, 1984 }) 1985 snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{ 1986 {"meta/kernel.yaml", ""}, 1987 }) 1988 si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)} 1989 snapstate.Set(st, "core18", &snapstate.SnapState{ 1990 SnapType: "base", 1991 Active: true, 1992 Sequence: []*snap.SideInfo{si2}, 1993 Current: si2.Revision, 1994 }) 1995 1996 // setup model assertion 1997 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 1998 devicestatetest.SetDevice(st, &auth.DeviceState{ 1999 Brand: "my-brand", 2000 Model: "my-model", 2001 Serial: "serialserialserial", 2002 }) 2003 err := assertstate.Add(st, model) 2004 c.Assert(err, IsNil) 2005 2006 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{}) 2007 c.Assert(err, IsNil) 2008 2009 terr := st.NewTask("error-trigger", "provoking total undo") 2010 terr.WaitFor(ts.Tasks()[len(ts.Tasks())-1]) 2011 ts.AddTask(terr) 2012 chg := st.NewChange("install-snap", "...") 2013 chg.AddAll(ts) 2014 2015 // run, this will trigger a wait for the restart 2016 st.Unlock() 2017 err = s.o.Settle(settleTimeout) 2018 st.Lock() 2019 c.Assert(err, IsNil) 2020 2021 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 2022 "snap_core": "core18_2.snap", 2023 "snap_kernel": "pc-kernel_123.snap", 2024 "snap_try_kernel": "pc-kernel_x1.snap", 2025 "snap_mode": boot.TryStatus, 2026 "snap_try_core": "", 2027 }) 2028 2029 // we are in restarting state and the change is not done yet 2030 restarting, _ := st.Restarting() 2031 c.Check(restarting, Equals, true) 2032 c.Check(chg.Status(), Equals, state.DoingStatus) 2033 // pretend we restarted 2034 s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel}) 2035 2036 st.Unlock() 2037 err = s.o.Settle(settleTimeout) 2038 st.Lock() 2039 c.Assert(err, IsNil) 2040 2041 c.Assert(chg.Status(), Equals, state.ErrorStatus) 2042 2043 // and we undo the bootvars and trigger a reboot 2044 c.Check(bloader.BootVars, DeepEquals, map[string]string{ 2045 "snap_core": "core18_2.snap", 2046 "snap_try_core": "", 2047 "snap_try_kernel": "pc-kernel_123.snap", 2048 "snap_kernel": "pc-kernel_x1.snap", 2049 "snap_mode": boot.TryStatus, 2050 }) 2051 restarting, _ = st.Restarting() 2052 c.Check(restarting, Equals, true) 2053 } 2054 2055 func (s *mgrsSuite) TestInstallKernelSnap20UpdatesBootloaderEnv(c *C) { 2056 bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir())) 2057 bootloader.Force(bloader) 2058 defer bootloader.Force(nil) 2059 2060 // we have revision 1 installed 2061 kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap") 2062 c.Assert(err, IsNil) 2063 restore := bloader.SetEnabledKernel(kernel) 2064 defer restore() 2065 2066 restore = release.MockOnClassic(false) 2067 defer restore() 2068 2069 uc20ModelDefaults := map[string]interface{}{ 2070 "architecture": "amd64", 2071 "base": "core20", 2072 "store": "my-brand-store-id", 2073 "snaps": []interface{}{ 2074 map[string]interface{}{ 2075 "name": "pc-kernel", 2076 "id": snaptest.AssertedSnapID("pc-kernel"), 2077 "type": "kernel", 2078 "default-channel": "20", 2079 }, 2080 map[string]interface{}{ 2081 "name": "pc", 2082 "id": snaptest.AssertedSnapID("pc"), 2083 "type": "gadget", 2084 "default-channel": "20", 2085 }}, 2086 } 2087 2088 model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults) 2089 2090 const packageKernel = ` 2091 name: pc-kernel 2092 version: 4.0-1 2093 type: kernel` 2094 2095 files := [][]string{ 2096 {"kernel.efi", "I'm a kernel.efi"}, 2097 {"meta/kernel.yaml", "version: 4.2"}, 2098 } 2099 kernelSnapSideInfo := &snap.SideInfo{RealName: "pc-kernel"} 2100 kernelSnapPath, kernelSnapInfo := snaptest.MakeTestSnapInfoWithFiles(c, packageKernel, files, kernelSnapSideInfo) 2101 2102 // mock the modeenv file 2103 m := boot.Modeenv{ 2104 Mode: "run", 2105 RecoverySystem: "20191127", 2106 Base: "core20_1.snap", 2107 } 2108 err = m.WriteTo("") 2109 c.Assert(err, IsNil) 2110 2111 st := s.o.State() 2112 st.Lock() 2113 defer st.Unlock() 2114 2115 si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)} 2116 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 2117 SnapType: "kernel", 2118 Active: true, 2119 Sequence: []*snap.SideInfo{si1}, 2120 Current: si1.Revision, 2121 }) 2122 snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{ 2123 {"meta/kernel.yaml", ""}, 2124 }) 2125 si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)} 2126 snapstate.Set(st, "core20", &snapstate.SnapState{ 2127 SnapType: "base", 2128 Active: true, 2129 Sequence: []*snap.SideInfo{si2}, 2130 Current: si2.Revision, 2131 }) 2132 2133 // setup model assertion 2134 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 2135 devicestatetest.SetDevice(st, &auth.DeviceState{ 2136 Brand: "my-brand", 2137 Model: "my-model", 2138 Serial: "serialserialserial", 2139 }) 2140 err = assertstate.Add(st, model) 2141 c.Assert(err, IsNil) 2142 2143 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, kernelSnapPath, "", "", snapstate.Flags{}) 2144 c.Assert(err, IsNil) 2145 chg := st.NewChange("install-snap", "...") 2146 chg.AddAll(ts) 2147 2148 // run, this will trigger a wait for the restart 2149 st.Unlock() 2150 err = s.o.Settle(settleTimeout) 2151 st.Lock() 2152 c.Assert(err, IsNil) 2153 2154 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 2155 "kernel_status": boot.TryStatus, 2156 }) 2157 2158 // we are in restarting state and the change is not done yet 2159 restarting, _ := st.Restarting() 2160 c.Check(restarting, Equals, true) 2161 c.Check(chg.Status(), Equals, state.DoingStatus) 2162 2163 // the kernelSnapInfo we mocked earlier will not have a revision set for the 2164 // SideInfo, but since the previous revision was "1", the next revision will 2165 // be x1 since it's unasserted, so we can set the Revision on the SideInfo 2166 // here to make comparison easier 2167 kernelSnapInfo.SideInfo.Revision = snap.R(-1) 2168 2169 // the current kernel in the bootloader is still the same 2170 currentKernel, err := bloader.Kernel() 2171 c.Assert(err, IsNil) 2172 firstKernel := snap.Info{SideInfo: *si1} 2173 c.Assert(currentKernel.Filename(), Equals, firstKernel.Filename()) 2174 2175 // the current try kernel in the bootloader is our new kernel 2176 currentTryKernel, err := bloader.TryKernel() 2177 c.Assert(err, IsNil) 2178 c.Assert(currentTryKernel.Filename(), Equals, kernelSnapInfo.Filename()) 2179 2180 // check that we extracted the kernel snap assets 2181 extractedKernels := bloader.ExtractKernelAssetsCalls 2182 c.Assert(extractedKernels, HasLen, 1) 2183 c.Assert(extractedKernels[0].Filename(), Equals, kernelSnapInfo.Filename()) 2184 2185 // pretend we restarted 2186 s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel}) 2187 2188 st.Unlock() 2189 err = s.o.Settle(settleTimeout) 2190 st.Lock() 2191 c.Assert(err, IsNil) 2192 2193 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2194 2195 // also check that we are active on the second revision 2196 var snapst snapstate.SnapState 2197 err = snapstate.Get(st, "pc-kernel", &snapst) 2198 c.Assert(err, IsNil) 2199 c.Check(snapst.Sequence, HasLen, 2) 2200 c.Check(snapst.Sequence, DeepEquals, []*snap.SideInfo{si1, &kernelSnapInfo.SideInfo}) 2201 c.Check(snapst.Active, Equals, true) 2202 c.Check(snapst.Current, DeepEquals, snap.R(-1)) 2203 2204 // since we need to do a reboot to go back to the old kernel, we should now 2205 // have kernel on the bootloader as the new one, and no try kernel on the 2206 // bootloader 2207 finalCurrentKernel, err := bloader.Kernel() 2208 c.Assert(err, IsNil) 2209 c.Assert(finalCurrentKernel.Filename(), Equals, kernelSnapInfo.Filename()) 2210 2211 _, err = bloader.TryKernel() 2212 c.Assert(err, Equals, bootloader.ErrNoTryKernelRef) 2213 2214 // finally check that GetCurrentBoot gives us the new kernel 2215 dev, err := devicestate.DeviceCtx(st, nil, nil) 2216 c.Assert(err, IsNil) 2217 sn, err := boot.GetCurrentBoot(snap.TypeKernel, dev) 2218 c.Assert(err, IsNil) 2219 c.Assert(sn.Filename(), Equals, kernelSnapInfo.Filename()) 2220 } 2221 2222 func (s *mgrsSuite) TestInstallKernelSnap20UndoUpdatesBootloaderEnv(c *C) { 2223 bloader := boottest.MockUC20RunBootenv(bootloadertest.Mock("mock", c.MkDir())) 2224 bootloader.Force(bloader) 2225 defer bootloader.Force(nil) 2226 2227 // we have revision 1 installed 2228 kernel, err := snap.ParsePlaceInfoFromSnapFileName("pc-kernel_1.snap") 2229 c.Assert(err, IsNil) 2230 restore := bloader.SetEnabledKernel(kernel) 2231 defer restore() 2232 2233 restore = release.MockOnClassic(false) 2234 defer restore() 2235 2236 uc20ModelDefaults := map[string]interface{}{ 2237 "architecture": "amd64", 2238 "base": "core20", 2239 "store": "my-brand-store-id", 2240 "snaps": []interface{}{ 2241 map[string]interface{}{ 2242 "name": "pc-kernel", 2243 "id": snaptest.AssertedSnapID("pc-kernel"), 2244 "type": "kernel", 2245 "default-channel": "20", 2246 }, 2247 map[string]interface{}{ 2248 "name": "pc", 2249 "id": snaptest.AssertedSnapID("pc"), 2250 "type": "gadget", 2251 "default-channel": "20", 2252 }}, 2253 } 2254 2255 model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults) 2256 2257 const packageKernel = ` 2258 name: pc-kernel 2259 version: 4.0-1 2260 type: kernel` 2261 2262 files := [][]string{ 2263 {"kernel.efi", "I'm a kernel.efi"}, 2264 {"meta/kernel.yaml", "version: 4.2"}, 2265 } 2266 kernelSnapSideInfo := &snap.SideInfo{RealName: "pc-kernel"} 2267 kernelSnapPath, kernelSnapInfo := snaptest.MakeTestSnapInfoWithFiles(c, packageKernel, files, kernelSnapSideInfo) 2268 2269 // mock the modeenv file 2270 m := boot.Modeenv{ 2271 Mode: "run", 2272 RecoverySystem: "20191127", 2273 Base: "core20_1.snap", 2274 } 2275 err = m.WriteTo("") 2276 c.Assert(err, IsNil) 2277 2278 st := s.o.State() 2279 st.Lock() 2280 defer st.Unlock() 2281 2282 si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)} 2283 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 2284 SnapType: "kernel", 2285 Active: true, 2286 Sequence: []*snap.SideInfo{si1}, 2287 Current: si1.Revision, 2288 }) 2289 snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{ 2290 {"meta/kernel.yaml", ""}, 2291 }) 2292 si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)} 2293 snapstate.Set(st, "core20", &snapstate.SnapState{ 2294 SnapType: "base", 2295 Active: true, 2296 Sequence: []*snap.SideInfo{si2}, 2297 Current: si2.Revision, 2298 }) 2299 2300 // setup model assertion 2301 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 2302 devicestatetest.SetDevice(st, &auth.DeviceState{ 2303 Brand: "my-brand", 2304 Model: "my-model", 2305 Serial: "serialserialserial", 2306 }) 2307 err = assertstate.Add(st, model) 2308 c.Assert(err, IsNil) 2309 2310 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, kernelSnapPath, "", "", snapstate.Flags{}) 2311 c.Assert(err, IsNil) 2312 2313 terr := st.NewTask("error-trigger", "provoking total undo") 2314 terr.WaitFor(ts.Tasks()[len(ts.Tasks())-1]) 2315 ts.AddTask(terr) 2316 chg := st.NewChange("install-snap", "...") 2317 chg.AddAll(ts) 2318 2319 // run, this will trigger a wait for the restart 2320 st.Unlock() 2321 err = s.o.Settle(settleTimeout) 2322 st.Lock() 2323 c.Assert(err, IsNil) 2324 2325 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 2326 "kernel_status": boot.TryStatus, 2327 }) 2328 2329 // the kernelSnapInfo we mocked earlier will not have a revision set for the 2330 // SideInfo, but since the previous revision was "1", the next revision will 2331 // be x1 since it's unasserted, so we can set the Revision on the SideInfo 2332 // here to make comparison easier 2333 kernelSnapInfo.SideInfo.Revision = snap.R(-1) 2334 2335 // check that we extracted the kernel snap assets 2336 extractedKernels := bloader.ExtractKernelAssetsCalls 2337 c.Assert(extractedKernels, HasLen, 1) 2338 c.Assert(extractedKernels[0].Filename(), Equals, kernelSnapInfo.Filename()) 2339 2340 // the current kernel in the bootloader is still the same 2341 currentKernel, err := bloader.Kernel() 2342 c.Assert(err, IsNil) 2343 firstKernel := snap.Info{SideInfo: *si1} 2344 c.Assert(currentKernel.Filename(), Equals, firstKernel.Filename()) 2345 2346 // the current try kernel in the bootloader is our new kernel 2347 currentTryKernel, err := bloader.TryKernel() 2348 c.Assert(err, IsNil) 2349 c.Assert(currentTryKernel.Filename(), Equals, kernelSnapInfo.Filename()) 2350 2351 // we are in restarting state and the change is not done yet 2352 restarting, _ := st.Restarting() 2353 c.Check(restarting, Equals, true) 2354 c.Check(chg.Status(), Equals, state.DoingStatus) 2355 // pretend we restarted 2356 s.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeKernel}) 2357 2358 st.Unlock() 2359 err = s.o.Settle(settleTimeout) 2360 st.Lock() 2361 c.Assert(err, IsNil) 2362 2363 c.Assert(chg.Status(), Equals, state.ErrorStatus) 2364 2365 // we should have triggered a reboot to undo the boot changes 2366 restarting, _ = st.Restarting() 2367 c.Check(restarting, Equals, true) 2368 2369 // we need to reboot with a "new" try kernel, so kernel_status was set again 2370 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 2371 "kernel_status": boot.TryStatus, 2372 }) 2373 2374 // we should not have extracted any more kernel assets than before, since 2375 // the fallback kernel was already extracted 2376 extractedKernels = bloader.ExtractKernelAssetsCalls 2377 c.Assert(extractedKernels, HasLen, 1) // same as above check 2378 2379 // also check that we are active on the first revision again 2380 var snapst snapstate.SnapState 2381 err = snapstate.Get(st, "pc-kernel", &snapst) 2382 c.Assert(err, IsNil) 2383 c.Check(snapst.Sequence, HasLen, 1) 2384 c.Check(snapst.Sequence, DeepEquals, []*snap.SideInfo{si1}) 2385 c.Check(snapst.Active, Equals, true) 2386 c.Check(snapst.Current, DeepEquals, snap.R(1)) 2387 2388 // since we need to do a reboot to go back to the old kernel, we should now 2389 // have kernel on the bootloader as the new one, and the try kernel on the 2390 // booloader as the old one 2391 finalCurrentKernel, err := bloader.Kernel() 2392 c.Assert(err, IsNil) 2393 c.Assert(finalCurrentKernel.Filename(), Equals, kernelSnapInfo.Filename()) 2394 2395 finalTryKernel, err := bloader.TryKernel() 2396 c.Assert(err, IsNil) 2397 c.Assert(finalTryKernel.Filename(), Equals, firstKernel.Filename()) 2398 2399 // TODO:UC20: this test should probably simulate another reboot and confirm 2400 // that at the end of everything we have GetCurrentBoot() return the old 2401 // kernel we reverted back to again 2402 } 2403 2404 func (s *mgrsSuite) installLocalTestSnap(c *C, snapYamlContent string) *snap.Info { 2405 st := s.o.State() 2406 2407 snapPath := makeTestSnap(c, snapYamlContent) 2408 snapf, err := snapfile.Open(snapPath) 2409 c.Assert(err, IsNil) 2410 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 2411 c.Assert(err, IsNil) 2412 2413 // store current state 2414 snapName := info.InstanceName() 2415 var snapst snapstate.SnapState 2416 snapstate.Get(st, snapName, &snapst) 2417 2418 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName}, snapPath, "", "", snapstate.Flags{DevMode: true}) 2419 c.Assert(err, IsNil) 2420 chg := st.NewChange("install-snap", "...") 2421 chg.AddAll(ts) 2422 2423 st.Unlock() 2424 err = s.o.Settle(settleTimeout) 2425 st.Lock() 2426 c.Assert(err, IsNil) 2427 2428 c.Assert(chg.Err(), IsNil) 2429 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2430 2431 return info 2432 } 2433 2434 func (s *mgrsSuite) removeSnap(c *C, name string) { 2435 st := s.o.State() 2436 2437 ts, err := snapstate.Remove(st, name, snap.R(0), &snapstate.RemoveFlags{Purge: true}) 2438 c.Assert(err, IsNil) 2439 chg := st.NewChange("remove-snap", "...") 2440 chg.AddAll(ts) 2441 2442 st.Unlock() 2443 err = s.o.Settle(settleTimeout) 2444 st.Lock() 2445 c.Assert(err, IsNil) 2446 2447 c.Assert(chg.Err(), IsNil) 2448 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err())) 2449 } 2450 2451 func (s *mgrsSuite) TestHappyRevert(c *C) { 2452 st := s.o.State() 2453 st.Lock() 2454 defer st.Unlock() 2455 2456 x1Yaml := `name: foo 2457 version: 1.0 2458 apps: 2459 x1: 2460 command: bin/bar 2461 ` 2462 x1binary := filepath.Join(dirs.SnapBinariesDir, "foo.x1") 2463 2464 x2Yaml := `name: foo 2465 version: 2.0 2466 apps: 2467 x2: 2468 command: bin/bar 2469 ` 2470 x2binary := filepath.Join(dirs.SnapBinariesDir, "foo.x2") 2471 2472 s.installLocalTestSnap(c, x1Yaml) 2473 s.installLocalTestSnap(c, x2Yaml) 2474 2475 // ensure we are on x2 2476 _, err := os.Lstat(x2binary) 2477 c.Assert(err, IsNil) 2478 _, err = os.Lstat(x1binary) 2479 c.Assert(err, ErrorMatches, ".*no such file.*") 2480 2481 // now do the revert 2482 ts, err := snapstate.Revert(st, "foo", snapstate.Flags{}) 2483 c.Assert(err, IsNil) 2484 chg := st.NewChange("revert-snap", "...") 2485 chg.AddAll(ts) 2486 2487 st.Unlock() 2488 err = s.o.Settle(settleTimeout) 2489 st.Lock() 2490 c.Assert(err, IsNil) 2491 2492 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("revert-snap change failed with: %v", chg.Err())) 2493 2494 // ensure that we use x1 now 2495 _, err = os.Lstat(x1binary) 2496 c.Assert(err, IsNil) 2497 _, err = os.Lstat(x2binary) 2498 c.Assert(err, ErrorMatches, ".*no such file.*") 2499 2500 // ensure that x1,x2 is still there, revert just moves the "current" 2501 // pointer 2502 for _, fn := range []string{"foo_x2.snap", "foo_x1.snap"} { 2503 p := filepath.Join(dirs.SnapBlobDir, fn) 2504 c.Assert(osutil.FileExists(p), Equals, true) 2505 } 2506 } 2507 2508 func (s *mgrsSuite) TestHappyAlias(c *C) { 2509 st := s.o.State() 2510 st.Lock() 2511 defer st.Unlock() 2512 2513 fooYaml := `name: foo 2514 version: 1.0 2515 apps: 2516 foo: 2517 command: bin/foo 2518 ` 2519 s.installLocalTestSnap(c, fooYaml) 2520 2521 ts, err := snapstate.Alias(st, "foo", "foo", "foo_") 2522 c.Assert(err, IsNil) 2523 chg := st.NewChange("alias", "...") 2524 chg.AddAll(ts) 2525 2526 st.Unlock() 2527 err = s.o.Settle(settleTimeout) 2528 st.Lock() 2529 c.Assert(err, IsNil) 2530 2531 c.Assert(chg.Err(), IsNil) 2532 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err())) 2533 2534 foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_") 2535 dest, err := os.Readlink(foo_Alias) 2536 c.Assert(err, IsNil) 2537 2538 c.Check(dest, Equals, "foo") 2539 2540 var snapst snapstate.SnapState 2541 err = snapstate.Get(st, "foo", &snapst) 2542 c.Assert(err, IsNil) 2543 c.Check(snapst.AutoAliasesDisabled, Equals, false) 2544 c.Check(snapst.AliasesPending, Equals, false) 2545 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2546 "foo_": {Manual: "foo"}, 2547 }) 2548 2549 s.removeSnap(c, "foo") 2550 2551 c.Check(osutil.IsSymlink(foo_Alias), Equals, false) 2552 } 2553 2554 func (s *mgrsSuite) TestHappyUnalias(c *C) { 2555 st := s.o.State() 2556 st.Lock() 2557 defer st.Unlock() 2558 2559 fooYaml := `name: foo 2560 version: 1.0 2561 apps: 2562 foo: 2563 command: bin/foo 2564 ` 2565 s.installLocalTestSnap(c, fooYaml) 2566 2567 ts, err := snapstate.Alias(st, "foo", "foo", "foo_") 2568 c.Assert(err, IsNil) 2569 chg := st.NewChange("alias", "...") 2570 chg.AddAll(ts) 2571 2572 st.Unlock() 2573 err = s.o.Settle(settleTimeout) 2574 st.Lock() 2575 c.Assert(err, IsNil) 2576 2577 c.Assert(chg.Err(), IsNil) 2578 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err())) 2579 2580 foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_") 2581 dest, err := os.Readlink(foo_Alias) 2582 c.Assert(err, IsNil) 2583 2584 c.Check(dest, Equals, "foo") 2585 2586 ts, snapName, err := snapstate.RemoveManualAlias(st, "foo_") 2587 c.Assert(err, IsNil) 2588 c.Check(snapName, Equals, "foo") 2589 chg = st.NewChange("unalias", "...") 2590 chg.AddAll(ts) 2591 2592 st.Unlock() 2593 err = s.o.Settle(settleTimeout) 2594 st.Lock() 2595 c.Assert(err, IsNil) 2596 2597 c.Assert(chg.Err(), IsNil) 2598 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("unalias change failed with: %v", chg.Err())) 2599 2600 c.Check(osutil.IsSymlink(foo_Alias), Equals, false) 2601 2602 var snapst snapstate.SnapState 2603 err = snapstate.Get(st, "foo", &snapst) 2604 c.Assert(err, IsNil) 2605 c.Check(snapst.AutoAliasesDisabled, Equals, false) 2606 c.Check(snapst.AliasesPending, Equals, false) 2607 c.Check(snapst.Aliases, HasLen, 0) 2608 } 2609 2610 func (s *mgrsSuite) TestHappyRemoteInstallAutoAliases(c *C) { 2611 s.prereqSnapAssertions(c, map[string]interface{}{ 2612 "snap-name": "foo", 2613 "aliases": []interface{}{ 2614 map[string]interface{}{"name": "app1", "target": "app1"}, 2615 map[string]interface{}{"name": "app2", "target": "app2"}, 2616 }, 2617 }) 2618 2619 snapYamlContent := `name: foo 2620 version: @VERSION@ 2621 apps: 2622 app1: 2623 command: bin/app1 2624 app2: 2625 command: bin/app2 2626 ` 2627 2628 ver := "1.0" 2629 revno := "42" 2630 snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 2631 s.serveSnap(snapPath, revno) 2632 2633 mockServer := s.mockStore(c) 2634 defer mockServer.Close() 2635 2636 st := s.o.State() 2637 st.Lock() 2638 defer st.Unlock() 2639 2640 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 2641 c.Assert(err, IsNil) 2642 chg := st.NewChange("install-snap", "...") 2643 chg.AddAll(ts) 2644 2645 st.Unlock() 2646 err = s.o.Settle(settleTimeout) 2647 st.Lock() 2648 c.Assert(err, IsNil) 2649 2650 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2651 2652 var snapst snapstate.SnapState 2653 err = snapstate.Get(st, "foo", &snapst) 2654 c.Assert(err, IsNil) 2655 c.Check(snapst.AutoAliasesDisabled, Equals, false) 2656 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2657 "app1": {Auto: "app1"}, 2658 "app2": {Auto: "app2"}, 2659 }) 2660 2661 // check disk 2662 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 2663 dest, err := os.Readlink(app1Alias) 2664 c.Assert(err, IsNil) 2665 c.Check(dest, Equals, "foo.app1") 2666 2667 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 2668 dest, err = os.Readlink(app2Alias) 2669 c.Assert(err, IsNil) 2670 c.Check(dest, Equals, "foo.app2") 2671 } 2672 2673 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliases(c *C) { 2674 s.prereqSnapAssertions(c, map[string]interface{}{ 2675 "snap-name": "foo", 2676 "aliases": []interface{}{ 2677 map[string]interface{}{"name": "app1", "target": "app1"}, 2678 }, 2679 }) 2680 2681 fooYaml := `name: foo 2682 version: @VERSION@ 2683 apps: 2684 app1: 2685 command: bin/app1 2686 app2: 2687 command: bin/app2 2688 ` 2689 2690 fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10") 2691 s.serveSnap(fooPath, "10") 2692 2693 mockServer := s.mockStore(c) 2694 defer mockServer.Close() 2695 2696 st := s.o.State() 2697 st.Lock() 2698 defer st.Unlock() 2699 2700 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 2701 c.Assert(err, IsNil) 2702 chg := st.NewChange("install-snap", "...") 2703 chg.AddAll(ts) 2704 2705 st.Unlock() 2706 err = s.o.Settle(settleTimeout) 2707 st.Lock() 2708 c.Assert(err, IsNil) 2709 2710 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2711 2712 info, err := snapstate.CurrentInfo(st, "foo") 2713 c.Assert(err, IsNil) 2714 c.Check(info.Revision, Equals, snap.R(10)) 2715 c.Check(info.Version, Equals, "1.0") 2716 2717 var snapst snapstate.SnapState 2718 err = snapstate.Get(st, "foo", &snapst) 2719 c.Assert(err, IsNil) 2720 c.Check(snapst.AutoAliasesDisabled, Equals, false) 2721 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2722 "app1": {Auto: "app1"}, 2723 }) 2724 2725 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 2726 dest, err := os.Readlink(app1Alias) 2727 c.Assert(err, IsNil) 2728 c.Check(dest, Equals, "foo.app1") 2729 2730 s.prereqSnapAssertions(c, map[string]interface{}{ 2731 "snap-name": "foo", 2732 "aliases": []interface{}{ 2733 map[string]interface{}{"name": "app2", "target": "app2"}, 2734 }, 2735 "revision": "1", 2736 }) 2737 2738 // new foo version/revision 2739 fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15") 2740 s.serveSnap(fooPath, "15") 2741 2742 // refresh all 2743 updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil) 2744 c.Assert(err, IsNil) 2745 c.Assert(updated, DeepEquals, []string{"foo"}) 2746 c.Assert(tss, HasLen, 2) 2747 verifyLastTasksetIsRerefresh(c, tss) 2748 chg = st.NewChange("upgrade-snaps", "...") 2749 chg.AddAll(tss[0]) 2750 2751 st.Unlock() 2752 err = s.o.Settle(settleTimeout) 2753 st.Lock() 2754 c.Assert(err, IsNil) 2755 2756 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 2757 2758 info, err = snapstate.CurrentInfo(st, "foo") 2759 c.Assert(err, IsNil) 2760 c.Check(info.Revision, Equals, snap.R(15)) 2761 c.Check(info.Version, Equals, "1.5") 2762 2763 var snapst2 snapstate.SnapState 2764 err = snapstate.Get(st, "foo", &snapst2) 2765 c.Assert(err, IsNil) 2766 c.Check(snapst2.AutoAliasesDisabled, Equals, false) 2767 c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2768 "app2": {Auto: "app2"}, 2769 }) 2770 2771 c.Check(osutil.IsSymlink(app1Alias), Equals, false) 2772 2773 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 2774 dest, err = os.Readlink(app2Alias) 2775 c.Assert(err, IsNil) 2776 c.Check(dest, Equals, "foo.app2") 2777 } 2778 2779 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliasesUnaliased(c *C) { 2780 s.prereqSnapAssertions(c, map[string]interface{}{ 2781 "snap-name": "foo", 2782 "aliases": []interface{}{ 2783 map[string]interface{}{"name": "app1", "target": "app1"}, 2784 }, 2785 }) 2786 2787 fooYaml := `name: foo 2788 version: @VERSION@ 2789 apps: 2790 app1: 2791 command: bin/app1 2792 app2: 2793 command: bin/app2 2794 ` 2795 2796 fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10") 2797 s.serveSnap(fooPath, "10") 2798 2799 mockServer := s.mockStore(c) 2800 defer mockServer.Close() 2801 2802 st := s.o.State() 2803 st.Lock() 2804 defer st.Unlock() 2805 2806 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{Unaliased: true}) 2807 c.Assert(err, IsNil) 2808 chg := st.NewChange("install-snap", "...") 2809 chg.AddAll(ts) 2810 2811 st.Unlock() 2812 err = s.o.Settle(settleTimeout) 2813 st.Lock() 2814 c.Assert(err, IsNil) 2815 2816 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2817 2818 info, err := snapstate.CurrentInfo(st, "foo") 2819 c.Assert(err, IsNil) 2820 c.Check(info.Revision, Equals, snap.R(10)) 2821 c.Check(info.Version, Equals, "1.0") 2822 2823 var snapst snapstate.SnapState 2824 err = snapstate.Get(st, "foo", &snapst) 2825 c.Assert(err, IsNil) 2826 c.Check(snapst.AutoAliasesDisabled, Equals, true) 2827 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2828 "app1": {Auto: "app1"}, 2829 }) 2830 2831 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 2832 c.Check(osutil.IsSymlink(app1Alias), Equals, false) 2833 2834 s.prereqSnapAssertions(c, map[string]interface{}{ 2835 "snap-name": "foo", 2836 "aliases": []interface{}{ 2837 map[string]interface{}{"name": "app2", "target": "app2"}, 2838 }, 2839 "revision": "1", 2840 }) 2841 2842 // new foo version/revision 2843 fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15") 2844 s.serveSnap(fooPath, "15") 2845 2846 // refresh foo 2847 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 2848 c.Assert(err, IsNil) 2849 chg = st.NewChange("upgrade-snap", "...") 2850 chg.AddAll(ts) 2851 2852 st.Unlock() 2853 err = s.o.Settle(settleTimeout) 2854 st.Lock() 2855 c.Assert(err, IsNil) 2856 2857 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 2858 2859 info, err = snapstate.CurrentInfo(st, "foo") 2860 c.Assert(err, IsNil) 2861 c.Check(info.Revision, Equals, snap.R(15)) 2862 c.Check(info.Version, Equals, "1.5") 2863 2864 var snapst2 snapstate.SnapState 2865 err = snapstate.Get(st, "foo", &snapst2) 2866 c.Assert(err, IsNil) 2867 c.Check(snapst2.AutoAliasesDisabled, Equals, true) 2868 c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2869 "app2": {Auto: "app2"}, 2870 }) 2871 2872 c.Check(osutil.IsSymlink(app1Alias), Equals, false) 2873 2874 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 2875 c.Check(osutil.IsSymlink(app2Alias), Equals, false) 2876 } 2877 2878 func (s *mgrsSuite) TestHappyOrthogonalRefreshAutoAliases(c *C) { 2879 s.prereqSnapAssertions(c, map[string]interface{}{ 2880 "snap-name": "foo", 2881 "aliases": []interface{}{ 2882 map[string]interface{}{"name": "app1", "target": "app1"}, 2883 }, 2884 }, map[string]interface{}{ 2885 "snap-name": "bar", 2886 }) 2887 2888 fooYaml := `name: foo 2889 version: @VERSION@ 2890 apps: 2891 app1: 2892 command: bin/app1 2893 app2: 2894 command: bin/app2 2895 ` 2896 2897 barYaml := `name: bar 2898 version: @VERSION@ 2899 apps: 2900 app1: 2901 command: bin/app1 2902 app3: 2903 command: bin/app3 2904 ` 2905 2906 fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10") 2907 s.serveSnap(fooPath, "10") 2908 2909 barPath, _ := s.makeStoreTestSnap(c, strings.Replace(barYaml, "@VERSION@", "2.0", -1), "20") 2910 s.serveSnap(barPath, "20") 2911 2912 mockServer := s.mockStore(c) 2913 defer mockServer.Close() 2914 2915 st := s.o.State() 2916 st.Lock() 2917 defer st.Unlock() 2918 2919 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 2920 c.Assert(err, IsNil) 2921 chg := st.NewChange("install-snap", "...") 2922 chg.AddAll(ts) 2923 2924 st.Unlock() 2925 err = s.o.Settle(settleTimeout) 2926 st.Lock() 2927 c.Assert(err, IsNil) 2928 2929 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2930 2931 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2932 2933 ts, err = snapstate.Install(context.TODO(), st, "bar", nil, 0, snapstate.Flags{}) 2934 c.Assert(err, IsNil) 2935 chg = st.NewChange("install-snap", "...") 2936 chg.AddAll(ts) 2937 2938 st.Unlock() 2939 err = s.o.Settle(settleTimeout) 2940 st.Lock() 2941 c.Assert(err, IsNil) 2942 2943 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2944 2945 info, err := snapstate.CurrentInfo(st, "foo") 2946 c.Assert(err, IsNil) 2947 c.Check(info.Revision, Equals, snap.R(10)) 2948 c.Check(info.Version, Equals, "1.0") 2949 2950 info, err = snapstate.CurrentInfo(st, "bar") 2951 c.Assert(err, IsNil) 2952 c.Check(info.Revision, Equals, snap.R(20)) 2953 c.Check(info.Version, Equals, "2.0") 2954 2955 var snapst snapstate.SnapState 2956 err = snapstate.Get(st, "foo", &snapst) 2957 c.Assert(err, IsNil) 2958 c.Check(snapst.AutoAliasesDisabled, Equals, false) 2959 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2960 "app1": {Auto: "app1"}, 2961 }) 2962 2963 // foo gets a new version/revision and a change of automatic aliases 2964 // bar gets only the latter 2965 // app1 is transferred from foo to bar 2966 // UpdateMany after a snap-declaration refresh handles all of this 2967 s.prereqSnapAssertions(c, map[string]interface{}{ 2968 "snap-name": "foo", 2969 "aliases": []interface{}{ 2970 map[string]interface{}{"name": "app2", "target": "app2"}, 2971 }, 2972 "revision": "1", 2973 }, map[string]interface{}{ 2974 "snap-name": "bar", 2975 "aliases": []interface{}{ 2976 map[string]interface{}{"name": "app1", "target": "app1"}, 2977 map[string]interface{}{"name": "app3", "target": "app3"}, 2978 }, 2979 "revision": "1", 2980 }) 2981 2982 // new foo version/revision 2983 fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15") 2984 s.serveSnap(fooPath, "15") 2985 2986 // refresh all 2987 err = assertstate.RefreshSnapDeclarations(st, 0) 2988 c.Assert(err, IsNil) 2989 2990 updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil) 2991 c.Assert(err, IsNil) 2992 sort.Strings(updated) 2993 c.Assert(updated, DeepEquals, []string{"bar", "foo"}) 2994 c.Assert(tss, HasLen, 4) 2995 verifyLastTasksetIsRerefresh(c, tss) 2996 chg = st.NewChange("upgrade-snaps", "...") 2997 chg.AddAll(tss[0]) 2998 chg.AddAll(tss[1]) 2999 chg.AddAll(tss[2]) 3000 3001 st.Unlock() 3002 err = s.o.Settle(settleTimeout) 3003 st.Lock() 3004 c.Assert(err, IsNil) 3005 3006 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 3007 3008 info, err = snapstate.CurrentInfo(st, "foo") 3009 c.Assert(err, IsNil) 3010 c.Check(info.Revision, Equals, snap.R(15)) 3011 c.Check(info.Version, Equals, "1.5") 3012 3013 var snapst2 snapstate.SnapState 3014 err = snapstate.Get(st, "foo", &snapst2) 3015 c.Assert(err, IsNil) 3016 c.Check(snapst2.AutoAliasesDisabled, Equals, false) 3017 c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 3018 "app2": {Auto: "app2"}, 3019 }) 3020 var snapst3 snapstate.SnapState 3021 err = snapstate.Get(st, "bar", &snapst3) 3022 c.Assert(err, IsNil) 3023 c.Check(snapst3.AutoAliasesDisabled, Equals, false) 3024 c.Check(snapst3.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 3025 "app1": {Auto: "app1"}, 3026 "app3": {Auto: "app3"}, 3027 }) 3028 3029 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 3030 dest, err := os.Readlink(app2Alias) 3031 c.Assert(err, IsNil) 3032 c.Check(dest, Equals, "foo.app2") 3033 3034 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 3035 dest, err = os.Readlink(app1Alias) 3036 c.Assert(err, IsNil) 3037 c.Check(dest, Equals, "bar.app1") 3038 app3Alias := filepath.Join(dirs.SnapBinariesDir, "app3") 3039 dest, err = os.Readlink(app3Alias) 3040 c.Assert(err, IsNil) 3041 c.Check(dest, Equals, "bar.app3") 3042 } 3043 3044 func (s *mgrsSuite) TestHappyStopWhileDownloadingHeader(c *C) { 3045 s.prereqSnapAssertions(c) 3046 3047 snapYamlContent := `name: foo 3048 version: 1.0 3049 ` 3050 snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42") 3051 s.serveSnap(snapPath, "42") 3052 3053 stopped := make(chan struct{}) 3054 s.hijackServeSnap = func(_ http.ResponseWriter) { 3055 s.o.Stop() 3056 close(stopped) 3057 } 3058 3059 mockServer := s.mockStore(c) 3060 defer mockServer.Close() 3061 3062 st := s.o.State() 3063 st.Lock() 3064 defer st.Unlock() 3065 3066 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 3067 c.Assert(err, IsNil) 3068 chg := st.NewChange("install-snap", "...") 3069 chg.AddAll(ts) 3070 3071 st.Unlock() 3072 s.o.Loop() 3073 3074 <-stopped 3075 3076 st.Lock() 3077 c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 3078 } 3079 3080 func (s *mgrsSuite) TestHappyStopWhileDownloadingBody(c *C) { 3081 s.prereqSnapAssertions(c) 3082 3083 snapYamlContent := `name: foo 3084 version: 1.0 3085 ` 3086 snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42") 3087 s.serveSnap(snapPath, "42") 3088 3089 stopped := make(chan struct{}) 3090 s.hijackServeSnap = func(w http.ResponseWriter) { 3091 w.WriteHeader(200) 3092 // best effort to reach the body reading part in the client 3093 w.Write(make([]byte, 10000)) 3094 time.Sleep(100 * time.Millisecond) 3095 w.Write(make([]byte, 10000)) 3096 s.o.Stop() 3097 close(stopped) 3098 } 3099 3100 mockServer := s.mockStore(c) 3101 defer mockServer.Close() 3102 3103 st := s.o.State() 3104 st.Lock() 3105 defer st.Unlock() 3106 3107 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 3108 c.Assert(err, IsNil) 3109 chg := st.NewChange("install-snap", "...") 3110 chg.AddAll(ts) 3111 3112 st.Unlock() 3113 s.o.Loop() 3114 3115 <-stopped 3116 3117 st.Lock() 3118 c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 3119 } 3120 3121 type storeCtxSetupSuite struct { 3122 o *overlord.Overlord 3123 sc store.DeviceAndAuthContext 3124 3125 storeSigning *assertstest.StoreStack 3126 restoreTrusted func() 3127 3128 brands *assertstest.SigningAccounts 3129 3130 deviceKey asserts.PrivateKey 3131 3132 model *asserts.Model 3133 serial *asserts.Serial 3134 3135 restoreBackends func() 3136 } 3137 3138 func (s *storeCtxSetupSuite) SetUpTest(c *C) { 3139 tempdir := c.MkDir() 3140 dirs.SetRootDir(tempdir) 3141 err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) 3142 c.Assert(err, IsNil) 3143 3144 captureStoreCtx := func(_ *store.Config, dac store.DeviceAndAuthContext) *store.Store { 3145 s.sc = dac 3146 return store.New(nil, nil) 3147 } 3148 r := overlord.MockStoreNew(captureStoreCtx) 3149 defer r() 3150 3151 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 3152 s.restoreTrusted = sysdb.InjectTrusted(s.storeSigning.Trusted) 3153 3154 s.brands = assertstest.NewSigningAccounts(s.storeSigning) 3155 s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 3156 "verification": "verified", 3157 }) 3158 assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("my-brand")...) 3159 3160 s.model = s.brands.Model("my-brand", "my-model", modelDefaults) 3161 3162 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 3163 c.Assert(err, IsNil) 3164 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 3165 "authority-id": "my-brand", 3166 "brand-id": "my-brand", 3167 "model": "my-model", 3168 "serial": "7878", 3169 "device-key": string(encDevKey), 3170 "device-key-sha3-384": deviceKey.PublicKey().ID(), 3171 "timestamp": time.Now().Format(time.RFC3339), 3172 }, nil, "") 3173 c.Assert(err, IsNil) 3174 s.serial = serial.(*asserts.Serial) 3175 3176 s.restoreBackends = ifacestate.MockSecurityBackends(nil) 3177 3178 o, err := overlord.New(nil) 3179 c.Assert(err, IsNil) 3180 o.InterfaceManager().DisableUDevMonitor() 3181 s.o = o 3182 3183 st := o.State() 3184 st.Lock() 3185 defer st.Unlock() 3186 3187 assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey("")) 3188 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 3189 } 3190 3191 func (s *storeCtxSetupSuite) TearDownTest(c *C) { 3192 dirs.SetRootDir("") 3193 s.restoreBackends() 3194 s.restoreTrusted() 3195 } 3196 3197 func (s *storeCtxSetupSuite) TestStoreID(c *C) { 3198 st := s.o.State() 3199 st.Lock() 3200 defer st.Unlock() 3201 3202 st.Unlock() 3203 storeID, err := s.sc.StoreID("fallback") 3204 st.Lock() 3205 c.Assert(err, IsNil) 3206 c.Check(storeID, Equals, "fallback") 3207 3208 // setup model in system statey 3209 devicestatetest.SetDevice(st, &auth.DeviceState{ 3210 Brand: s.serial.BrandID(), 3211 Model: s.serial.Model(), 3212 Serial: s.serial.Serial(), 3213 }) 3214 err = assertstate.Add(st, s.model) 3215 c.Assert(err, IsNil) 3216 3217 st.Unlock() 3218 storeID, err = s.sc.StoreID("fallback") 3219 st.Lock() 3220 c.Assert(err, IsNil) 3221 c.Check(storeID, Equals, "my-brand-store-id") 3222 } 3223 3224 func (s *storeCtxSetupSuite) TestDeviceSessionRequestParams(c *C) { 3225 st := s.o.State() 3226 st.Lock() 3227 defer st.Unlock() 3228 3229 st.Unlock() 3230 _, err := s.sc.DeviceSessionRequestParams("NONCE") 3231 st.Lock() 3232 c.Check(err, Equals, store.ErrNoSerial) 3233 3234 // setup model, serial and key in system state 3235 err = assertstate.Add(st, s.model) 3236 c.Assert(err, IsNil) 3237 err = assertstate.Add(st, s.serial) 3238 c.Assert(err, IsNil) 3239 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 3240 c.Assert(err, IsNil) 3241 err = kpMgr.Put(deviceKey) 3242 c.Assert(err, IsNil) 3243 devicestatetest.SetDevice(st, &auth.DeviceState{ 3244 Brand: s.serial.BrandID(), 3245 Model: s.serial.Model(), 3246 Serial: s.serial.Serial(), 3247 KeyID: deviceKey.PublicKey().ID(), 3248 }) 3249 3250 st.Unlock() 3251 params, err := s.sc.DeviceSessionRequestParams("NONCE") 3252 st.Lock() 3253 c.Assert(err, IsNil) 3254 c.Check(strings.HasPrefix(params.EncodedRequest(), "type: device-session-request\n"), Equals, true) 3255 c.Check(params.EncodedSerial(), DeepEquals, string(asserts.Encode(s.serial))) 3256 c.Check(params.EncodedModel(), DeepEquals, string(asserts.Encode(s.model))) 3257 3258 } 3259 3260 func (s *storeCtxSetupSuite) TestProxyStoreParams(c *C) { 3261 st := s.o.State() 3262 st.Lock() 3263 defer st.Unlock() 3264 3265 defURL, err := url.Parse("http://store") 3266 c.Assert(err, IsNil) 3267 3268 st.Unlock() 3269 proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL) 3270 st.Lock() 3271 c.Assert(err, IsNil) 3272 c.Check(proxyStoreID, Equals, "") 3273 c.Check(proxyStoreURL, Equals, defURL) 3274 3275 // setup proxy store reference and assertion 3276 operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "") 3277 err = assertstate.Add(st, operatorAcct) 3278 c.Assert(err, IsNil) 3279 stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{ 3280 "store": "foo", 3281 "operator-id": operatorAcct.AccountID(), 3282 "url": "http://foo.internal", 3283 "timestamp": time.Now().Format(time.RFC3339), 3284 }, nil, "") 3285 c.Assert(err, IsNil) 3286 err = assertstate.Add(st, stoAs) 3287 c.Assert(err, IsNil) 3288 tr := config.NewTransaction(st) 3289 err = tr.Set("core", "proxy.store", "foo") 3290 c.Assert(err, IsNil) 3291 tr.Commit() 3292 3293 fooURL, err := url.Parse("http://foo.internal") 3294 c.Assert(err, IsNil) 3295 3296 st.Unlock() 3297 proxyStoreID, proxyStoreURL, err = s.sc.ProxyStoreParams(defURL) 3298 st.Lock() 3299 c.Assert(err, IsNil) 3300 c.Check(proxyStoreID, Equals, "foo") 3301 c.Check(proxyStoreURL, DeepEquals, fooURL) 3302 } 3303 3304 const snapYamlContent1 = `name: snap1 3305 plugs: 3306 shared-data-plug: 3307 interface: content 3308 target: import 3309 content: mylib 3310 apps: 3311 bar: 3312 command: bin/bar 3313 ` 3314 const snapYamlContent2 = `name: snap2 3315 slots: 3316 shared-data-slot: 3317 interface: content 3318 content: mylib 3319 read: 3320 - / 3321 apps: 3322 bar: 3323 command: bin/bar 3324 ` 3325 3326 func (s *mgrsSuite) testTwoInstalls(c *C, snapName1, snapYaml1, snapName2, snapYaml2 string) { 3327 snapPath1 := makeTestSnap(c, snapYaml1+"version: 1.0") 3328 snapPath2 := makeTestSnap(c, snapYaml2+"version: 1.0") 3329 3330 st := s.o.State() 3331 st.Lock() 3332 defer st.Unlock() 3333 3334 ts1, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName1, SnapID: fakeSnapID(snapName1), Revision: snap.R(3)}, snapPath1, "", "", snapstate.Flags{DevMode: true}) 3335 c.Assert(err, IsNil) 3336 chg := st.NewChange("install-snap", "...") 3337 chg.AddAll(ts1) 3338 3339 ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName2, SnapID: fakeSnapID(snapName2), Revision: snap.R(3)}, snapPath2, "", "", snapstate.Flags{DevMode: true}) 3340 c.Assert(err, IsNil) 3341 3342 ts2.WaitAll(ts1) 3343 chg.AddAll(ts2) 3344 3345 st.Unlock() 3346 err = s.o.Settle(settleTimeout) 3347 st.Lock() 3348 c.Assert(err, IsNil) 3349 3350 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 3351 3352 tasks := chg.Tasks() 3353 connectTask := tasks[len(tasks)-2] 3354 c.Assert(connectTask.Kind(), Equals, "connect") 3355 3356 setupProfilesTask := tasks[len(tasks)-1] 3357 c.Assert(setupProfilesTask.Kind(), Equals, "setup-profiles") 3358 3359 // verify connect task data 3360 var plugRef interfaces.PlugRef 3361 var slotRef interfaces.SlotRef 3362 c.Assert(connectTask.Get("plug", &plugRef), IsNil) 3363 c.Assert(connectTask.Get("slot", &slotRef), IsNil) 3364 c.Assert(plugRef.Snap, Equals, "snap1") 3365 c.Assert(plugRef.Name, Equals, "shared-data-plug") 3366 c.Assert(slotRef.Snap, Equals, "snap2") 3367 c.Assert(slotRef.Name, Equals, "shared-data-slot") 3368 3369 // verify that connection was made 3370 var conns map[string]interface{} 3371 c.Assert(st.Get("conns", &conns), IsNil) 3372 c.Assert(conns, HasLen, 1) 3373 3374 repo := s.o.InterfaceManager().Repository() 3375 cn, err := repo.Connected("snap1", "shared-data-plug") 3376 c.Assert(err, IsNil) 3377 c.Assert(cn, HasLen, 1) 3378 c.Assert(cn, DeepEquals, []*interfaces.ConnRef{{ 3379 PlugRef: interfaces.PlugRef{Snap: "snap1", Name: "shared-data-plug"}, 3380 SlotRef: interfaces.SlotRef{Snap: "snap2", Name: "shared-data-slot"}, 3381 }}) 3382 } 3383 3384 func (s *mgrsSuite) TestTwoInstallsWithAutoconnectPlugSnapFirst(c *C) { 3385 s.testTwoInstalls(c, "snap1", snapYamlContent1, "snap2", snapYamlContent2) 3386 } 3387 3388 func (s *mgrsSuite) TestTwoInstallsWithAutoconnectSlotSnapFirst(c *C) { 3389 s.testTwoInstalls(c, "snap2", snapYamlContent2, "snap1", snapYamlContent1) 3390 } 3391 3392 func (s *mgrsSuite) TestRemoveAndInstallWithAutoconnectHappy(c *C) { 3393 st := s.o.State() 3394 st.Lock() 3395 defer st.Unlock() 3396 3397 _ = s.installLocalTestSnap(c, snapYamlContent1+"version: 1.0") 3398 3399 ts, err := snapstate.Remove(st, "snap1", snap.R(0), &snapstate.RemoveFlags{Purge: true}) 3400 c.Assert(err, IsNil) 3401 chg := st.NewChange("remove-snap", "...") 3402 chg.AddAll(ts) 3403 3404 snapPath := makeTestSnap(c, snapYamlContent2+"version: 1.0") 3405 chg2 := st.NewChange("install-snap", "...") 3406 ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "snap2", SnapID: fakeSnapID("snap2"), Revision: snap.R(3)}, snapPath, "", "", snapstate.Flags{DevMode: true}) 3407 chg2.AddAll(ts2) 3408 c.Assert(err, IsNil) 3409 3410 st.Unlock() 3411 err = s.o.Settle(settleTimeout) 3412 st.Lock() 3413 c.Assert(err, IsNil) 3414 3415 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err())) 3416 c.Assert(chg2.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 3417 } 3418 3419 const otherSnapYaml = `name: other-snap 3420 version: 1.0 3421 apps: 3422 baz: 3423 command: bin/bar 3424 plugs: [media-hub] 3425 ` 3426 3427 func (s *mgrsSuite) TestUpdateManyWithAutoconnect(c *C) { 3428 const someSnapYaml = `name: some-snap 3429 version: 1.0 3430 apps: 3431 foo: 3432 command: bin/bar 3433 plugs: [network,home] 3434 slots: [media-hub] 3435 ` 3436 3437 const coreSnapYaml = `name: core 3438 type: os 3439 version: @VERSION@` 3440 3441 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 3442 s.serveSnap(snapPath, "40") 3443 3444 snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50") 3445 s.serveSnap(snapPath, "50") 3446 3447 corePath, _ := s.makeStoreTestSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "30", -1), "30") 3448 s.serveSnap(corePath, "30") 3449 3450 mockServer := s.mockStore(c) 3451 defer mockServer.Close() 3452 3453 st := s.o.State() 3454 st.Lock() 3455 defer st.Unlock() 3456 3457 st.Set("conns", map[string]interface{}{}) 3458 3459 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3460 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 3461 c.Assert(snapInfo.Plugs, HasLen, 2) 3462 3463 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 3464 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 3465 c.Assert(otherInfo.Plugs, HasLen, 1) 3466 3467 csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)} 3468 coreInfo := snaptest.MockSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "1", -1), csi) 3469 3470 // add implicit slots 3471 coreInfo.Slots["network"] = &snap.SlotInfo{ 3472 Name: "network", 3473 Snap: coreInfo, 3474 Interface: "network", 3475 } 3476 coreInfo.Slots["home"] = &snap.SlotInfo{ 3477 Name: "home", 3478 Snap: coreInfo, 3479 Interface: "home", 3480 } 3481 3482 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3483 Active: true, 3484 Sequence: []*snap.SideInfo{si}, 3485 Current: snap.R(1), 3486 SnapType: "app", 3487 }) 3488 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 3489 Active: true, 3490 Sequence: []*snap.SideInfo{oi}, 3491 Current: snap.R(1), 3492 SnapType: "app", 3493 }) 3494 3495 repo := s.o.InterfaceManager().Repository() 3496 3497 // add snaps to the repo to have plugs/slots 3498 c.Assert(repo.AddSnap(snapInfo), IsNil) 3499 c.Assert(repo.AddSnap(otherInfo), IsNil) 3500 c.Assert(repo.AddSnap(coreInfo), IsNil) 3501 3502 // refresh all 3503 err := assertstate.RefreshSnapDeclarations(st, 0) 3504 c.Assert(err, IsNil) 3505 3506 updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"core", "some-snap", "other-snap"}, 0, nil) 3507 c.Assert(err, IsNil) 3508 c.Check(updates, HasLen, 3) 3509 c.Assert(tts, HasLen, 4) 3510 verifyLastTasksetIsRerefresh(c, tts) 3511 3512 // to make TaskSnapSetup work 3513 chg := st.NewChange("refresh", "...") 3514 for _, ts := range tts[:len(tts)-1] { 3515 chg.AddAll(ts) 3516 } 3517 3518 // force hold state to hit ignore status of findSymmetricAutoconnect 3519 tts[2].Tasks()[0].SetStatus(state.HoldStatus) 3520 3521 st.Unlock() 3522 err = s.o.Settle(3 * time.Second) 3523 st.Lock() 3524 c.Assert(err, IsNil) 3525 3526 // simulate successful restart happened 3527 state.MockRestarting(st, state.RestartUnset) 3528 tts[2].Tasks()[0].SetStatus(state.DefaultStatus) 3529 st.Unlock() 3530 3531 err = s.o.Settle(settleTimeout) 3532 st.Lock() 3533 3534 c.Assert(err, IsNil) 3535 3536 c.Assert(chg.Status(), Equals, state.DoneStatus) 3537 3538 // check connections 3539 var conns map[string]interface{} 3540 st.Get("conns", &conns) 3541 c.Assert(conns, DeepEquals, map[string]interface{}{ 3542 "some-snap:home core:home": map[string]interface{}{"interface": "home", "auto": true}, 3543 "some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true}, 3544 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true}, 3545 }) 3546 3547 connections, err := repo.Connections("some-snap") 3548 c.Assert(err, IsNil) 3549 c.Assert(connections, HasLen, 3) 3550 } 3551 3552 func (s *mgrsSuite) TestUpdateWithAutoconnectAndInactiveRevisions(c *C) { 3553 const someSnapYaml = `name: some-snap 3554 version: 1.0 3555 apps: 3556 foo: 3557 command: bin/bar 3558 plugs: [network] 3559 ` 3560 const coreSnapYaml = `name: core 3561 type: os 3562 version: 1` 3563 3564 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 3565 s.serveSnap(snapPath, "40") 3566 3567 mockServer := s.mockStore(c) 3568 defer mockServer.Close() 3569 3570 st := s.o.State() 3571 st.Lock() 3572 defer st.Unlock() 3573 3574 si1 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3575 snapInfo := snaptest.MockSnap(c, someSnapYaml, si1) 3576 c.Assert(snapInfo.Plugs, HasLen, 1) 3577 3578 csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)} 3579 coreInfo := snaptest.MockSnap(c, coreSnapYaml, csi) 3580 3581 // add implicit slots 3582 coreInfo.Slots["network"] = &snap.SlotInfo{ 3583 Name: "network", 3584 Snap: coreInfo, 3585 Interface: "network", 3586 } 3587 3588 // some-snap has inactive revisions 3589 si0 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(0)} 3590 si2 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(2)} 3591 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3592 Active: true, 3593 Sequence: []*snap.SideInfo{si0, si1, si2}, 3594 Current: snap.R(1), 3595 SnapType: "app", 3596 }) 3597 3598 repo := s.o.InterfaceManager().Repository() 3599 3600 // add snaps to the repo to have plugs/slots 3601 c.Assert(repo.AddSnap(snapInfo), IsNil) 3602 c.Assert(repo.AddSnap(coreInfo), IsNil) 3603 3604 // refresh all 3605 err := assertstate.RefreshSnapDeclarations(st, 0) 3606 c.Assert(err, IsNil) 3607 3608 updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"some-snap"}, 0, nil) 3609 c.Assert(err, IsNil) 3610 c.Check(updates, HasLen, 1) 3611 c.Assert(tts, HasLen, 2) 3612 verifyLastTasksetIsRerefresh(c, tts) 3613 3614 // to make TaskSnapSetup work 3615 chg := st.NewChange("refresh", "...") 3616 chg.AddAll(tts[0]) 3617 3618 st.Unlock() 3619 err = s.o.Settle(settleTimeout) 3620 st.Lock() 3621 3622 c.Assert(err, IsNil) 3623 c.Assert(chg.Status(), Equals, state.DoneStatus) 3624 3625 // check connections 3626 var conns map[string]interface{} 3627 st.Get("conns", &conns) 3628 c.Assert(conns, DeepEquals, map[string]interface{}{ 3629 "some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true}, 3630 }) 3631 } 3632 3633 const someSnapYaml = `name: some-snap 3634 version: 1.0 3635 apps: 3636 foo: 3637 command: bin/bar 3638 slots: [media-hub] 3639 ` 3640 3641 func (s *mgrsSuite) testUpdateWithAutoconnectRetry(c *C, updateSnapName, removeSnapName string) { 3642 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 3643 s.serveSnap(snapPath, "40") 3644 3645 snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50") 3646 s.serveSnap(snapPath, "50") 3647 3648 mockServer := s.mockStore(c) 3649 defer mockServer.Close() 3650 3651 st := s.o.State() 3652 st.Lock() 3653 defer st.Unlock() 3654 3655 st.Set("conns", map[string]interface{}{}) 3656 3657 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3658 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 3659 c.Assert(snapInfo.Slots, HasLen, 1) 3660 3661 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 3662 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 3663 c.Assert(otherInfo.Plugs, HasLen, 1) 3664 3665 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3666 Active: true, 3667 Sequence: []*snap.SideInfo{si}, 3668 Current: snap.R(1), 3669 SnapType: "app", 3670 }) 3671 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 3672 Active: true, 3673 Sequence: []*snap.SideInfo{oi}, 3674 Current: snap.R(1), 3675 SnapType: "app", 3676 }) 3677 3678 repo := s.o.InterfaceManager().Repository() 3679 3680 // add snaps to the repo to have plugs/slots 3681 c.Assert(repo.AddSnap(snapInfo), IsNil) 3682 c.Assert(repo.AddSnap(otherInfo), IsNil) 3683 3684 // refresh all 3685 err := assertstate.RefreshSnapDeclarations(st, 0) 3686 c.Assert(err, IsNil) 3687 3688 ts, err := snapstate.Update(st, updateSnapName, nil, 0, snapstate.Flags{}) 3689 c.Assert(err, IsNil) 3690 3691 // to make TaskSnapSetup work 3692 chg := st.NewChange("refresh", "...") 3693 chg.AddAll(ts) 3694 3695 // remove other-snap 3696 ts2, err := snapstate.Remove(st, removeSnapName, snap.R(0), &snapstate.RemoveFlags{Purge: true}) 3697 c.Assert(err, IsNil) 3698 chg2 := st.NewChange("remove-snap", "...") 3699 chg2.AddAll(ts2) 3700 3701 // force hold state on first removal task to hit Retry error 3702 ts2.Tasks()[0].SetStatus(state.HoldStatus) 3703 3704 // Settle is not converging here because of the task in Hold status, therefore 3705 // it always hits given timeout before we carry on with the test. We're 3706 // interested in hitting the retry condition on auto-connect task, so 3707 // instead of passing a generous timeout to Settle(), repeat Settle() a number 3708 // of times with an aggressive timeout and break as soon as we reach the desired 3709 // state of auto-connect task. 3710 var retryCheck bool 3711 var autoconnectLog string 3712 for i := 0; i < 50 && !retryCheck; i++ { 3713 st.Unlock() 3714 s.o.Settle(aggressiveSettleTimeout) 3715 st.Lock() 3716 3717 for _, t := range st.Tasks() { 3718 if t.Kind() == "auto-connect" && t.Status() == state.DoingStatus && strings.Contains(strings.Join(t.Log(), ""), "Waiting") { 3719 autoconnectLog = strings.Join(t.Log(), "") 3720 retryCheck = true 3721 break 3722 } 3723 } 3724 } 3725 3726 c.Check(retryCheck, Equals, true) 3727 c.Assert(autoconnectLog, Matches, `.*Waiting for conflicting change in progress: conflicting snap.*`) 3728 3729 // back to default state, that will unblock autoconnect 3730 ts2.Tasks()[0].SetStatus(state.DefaultStatus) 3731 st.Unlock() 3732 err = s.o.Settle(settleTimeout) 3733 st.Lock() 3734 c.Assert(err, IsNil) 3735 3736 c.Check(chg.Err(), IsNil) 3737 c.Assert(chg.Status(), Equals, state.DoneStatus) 3738 3739 // check connections 3740 var conns map[string]interface{} 3741 st.Get("conns", &conns) 3742 c.Assert(conns, HasLen, 0) 3743 } 3744 3745 func (s *mgrsSuite) TestUpdateWithAutoconnectRetrySlotSide(c *C) { 3746 s.testUpdateWithAutoconnectRetry(c, "some-snap", "other-snap") 3747 } 3748 3749 func (s *mgrsSuite) TestUpdateWithAutoconnectRetryPlugSide(c *C) { 3750 s.testUpdateWithAutoconnectRetry(c, "other-snap", "some-snap") 3751 } 3752 3753 func (s *mgrsSuite) TestDisconnectIgnoredOnSymmetricRemove(c *C) { 3754 const someSnapYaml = `name: some-snap 3755 version: 1.0 3756 apps: 3757 foo: 3758 command: bin/bar 3759 slots: [media-hub] 3760 hooks: 3761 disconnect-slot-media-hub: 3762 ` 3763 const otherSnapYaml = `name: other-snap 3764 version: 1.0 3765 apps: 3766 baz: 3767 command: bin/bar 3768 plugs: [media-hub] 3769 hooks: 3770 disconnect-plug-media-hub: 3771 ` 3772 st := s.o.State() 3773 st.Lock() 3774 defer st.Unlock() 3775 3776 st.Set("conns", map[string]interface{}{ 3777 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false}, 3778 }) 3779 3780 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3781 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 3782 c.Assert(snapInfo.Slots, HasLen, 1) 3783 3784 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 3785 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 3786 c.Assert(otherInfo.Plugs, HasLen, 1) 3787 3788 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3789 Active: true, 3790 Sequence: []*snap.SideInfo{si}, 3791 Current: snap.R(1), 3792 SnapType: "app", 3793 }) 3794 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 3795 Active: true, 3796 Sequence: []*snap.SideInfo{oi}, 3797 Current: snap.R(1), 3798 SnapType: "app", 3799 }) 3800 3801 repo := s.o.InterfaceManager().Repository() 3802 3803 // add snaps to the repo to have plugs/slots 3804 c.Assert(repo.AddSnap(snapInfo), IsNil) 3805 c.Assert(repo.AddSnap(otherInfo), IsNil) 3806 repo.Connect(&interfaces.ConnRef{ 3807 PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"}, 3808 SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"}, 3809 }, nil, nil, nil, nil, nil) 3810 3811 flags := &snapstate.RemoveFlags{Purge: true} 3812 ts, err := snapstate.Remove(st, "some-snap", snap.R(0), flags) 3813 c.Assert(err, IsNil) 3814 chg := st.NewChange("uninstall", "...") 3815 chg.AddAll(ts) 3816 3817 // remove other-snap 3818 ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), flags) 3819 c.Assert(err, IsNil) 3820 chg2 := st.NewChange("uninstall", "...") 3821 chg2.AddAll(ts2) 3822 3823 st.Unlock() 3824 err = s.o.Settle(settleTimeout) 3825 st.Lock() 3826 c.Assert(err, IsNil) 3827 3828 c.Assert(chg.Status(), Equals, state.DoneStatus) 3829 3830 // check connections 3831 var conns map[string]interface{} 3832 st.Get("conns", &conns) 3833 c.Assert(conns, HasLen, 0) 3834 3835 var disconnectInterfacesCount, slotHookCount, plugHookCount int 3836 for _, t := range st.Tasks() { 3837 if t.Kind() == "auto-disconnect" { 3838 disconnectInterfacesCount++ 3839 } 3840 if t.Kind() == "run-hook" { 3841 var hsup hookstate.HookSetup 3842 c.Assert(t.Get("hook-setup", &hsup), IsNil) 3843 if hsup.Hook == "disconnect-plug-media-hub" { 3844 plugHookCount++ 3845 } 3846 if hsup.Hook == "disconnect-slot-media-hub" { 3847 slotHookCount++ 3848 } 3849 } 3850 } 3851 c.Assert(plugHookCount, Equals, 1) 3852 c.Assert(slotHookCount, Equals, 1) 3853 c.Assert(disconnectInterfacesCount, Equals, 2) 3854 3855 var snst snapstate.SnapState 3856 err = snapstate.Get(st, "other-snap", &snst) 3857 c.Assert(err, Equals, state.ErrNoState) 3858 _, err = repo.Connected("other-snap", "media-hub") 3859 c.Assert(err, ErrorMatches, `snap "other-snap" has no plug or slot named "media-hub"`) 3860 } 3861 3862 func (s *mgrsSuite) TestDisconnectOnUninstallRemovesAutoconnection(c *C) { 3863 st := s.o.State() 3864 st.Lock() 3865 defer st.Unlock() 3866 3867 st.Set("conns", map[string]interface{}{ 3868 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true}, 3869 }) 3870 3871 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3872 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 3873 3874 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 3875 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 3876 3877 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3878 Active: true, 3879 Sequence: []*snap.SideInfo{si}, 3880 Current: snap.R(1), 3881 SnapType: "app", 3882 }) 3883 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 3884 Active: true, 3885 Sequence: []*snap.SideInfo{oi}, 3886 Current: snap.R(1), 3887 SnapType: "app", 3888 }) 3889 3890 repo := s.o.InterfaceManager().Repository() 3891 3892 // add snaps to the repo to have plugs/slots 3893 c.Assert(repo.AddSnap(snapInfo), IsNil) 3894 c.Assert(repo.AddSnap(otherInfo), IsNil) 3895 repo.Connect(&interfaces.ConnRef{ 3896 PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"}, 3897 SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"}, 3898 }, nil, nil, nil, nil, nil) 3899 3900 ts, err := snapstate.Remove(st, "some-snap", snap.R(0), &snapstate.RemoveFlags{Purge: true}) 3901 c.Assert(err, IsNil) 3902 chg := st.NewChange("uninstall", "...") 3903 chg.AddAll(ts) 3904 3905 st.Unlock() 3906 err = s.o.Settle(settleTimeout) 3907 st.Lock() 3908 c.Assert(err, IsNil) 3909 3910 c.Assert(chg.Status(), Equals, state.DoneStatus) 3911 3912 // check connections; auto-connection should be removed completely from conns on uninstall. 3913 var conns map[string]interface{} 3914 st.Get("conns", &conns) 3915 c.Assert(conns, HasLen, 0) 3916 } 3917 3918 // TODO: add a custom checker in testutils for this and similar 3919 func validateDownloadCheckTasks(c *C, tasks []*state.Task, name, revno, channel string) int { 3920 var i int 3921 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Ensure prerequisites for "%s" are available`, name)) 3922 i++ 3923 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Download snap "%s" (%s) from channel "%s"`, name, revno, channel)) 3924 i++ 3925 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Fetch and check assertions for snap "%s" (%s)`, name, revno)) 3926 i++ 3927 return i 3928 } 3929 3930 const ( 3931 noConfigure = 1 << iota 3932 isGadget 3933 isKernel 3934 ) 3935 3936 func validateInstallTasks(c *C, tasks []*state.Task, name, revno string, flags int) int { 3937 var i int 3938 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno)) 3939 i++ 3940 if flags&isGadget != 0 || flags&isKernel != 0 { 3941 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update assets from gadget "%s" (%s)`, name, revno)) 3942 i++ 3943 } 3944 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name)) 3945 i++ 3946 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno)) 3947 i++ 3948 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno)) 3949 i++ 3950 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name)) 3951 i++ 3952 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name)) 3953 i++ 3954 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name)) 3955 i++ 3956 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run install hook of "%s" snap if present`, name)) 3957 i++ 3958 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno)) 3959 i++ 3960 if flags&noConfigure == 0 { 3961 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name)) 3962 i++ 3963 } 3964 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name)) 3965 i++ 3966 return i 3967 } 3968 3969 func validateRefreshTasks(c *C, tasks []*state.Task, name, revno string, flags int) int { 3970 var i int 3971 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno)) 3972 i++ 3973 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run pre-refresh hook of "%s" snap if present`, name)) 3974 i++ 3975 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Stop snap "%s" services`, name)) 3976 i++ 3977 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Remove aliases for snap "%s"`, name)) 3978 i++ 3979 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make current revision for snap "%s" unavailable`, name)) 3980 i++ 3981 if flags&isGadget != 0 || flags&isKernel != 0 { 3982 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Update assets from gadget %q (%s)`, name, revno)) 3983 i++ 3984 3985 } 3986 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name)) 3987 i++ 3988 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno)) 3989 i++ 3990 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno)) 3991 i++ 3992 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name)) 3993 i++ 3994 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name)) 3995 i++ 3996 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name)) 3997 i++ 3998 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run post-refresh hook of "%s" snap if present`, name)) 3999 i++ 4000 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno)) 4001 i++ 4002 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Clean up "%s" (%s) install`, name, revno)) 4003 i++ 4004 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name)) 4005 i++ 4006 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name)) 4007 i++ 4008 return i 4009 } 4010 4011 // byReadyTime sorts a list of tasks by their "ready" time 4012 type byReadyTime []*state.Task 4013 4014 func (a byReadyTime) Len() int { return len(a) } 4015 func (a byReadyTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 4016 func (a byReadyTime) Less(i, j int) bool { return a[i].ReadyTime().Before(a[j].ReadyTime()) } 4017 4018 func (s *mgrsSuite) TestRemodelRequiredSnapsAdded(c *C) { 4019 for _, name := range []string{"foo", "bar", "baz"} { 4020 s.prereqSnapAssertions(c, map[string]interface{}{ 4021 "snap-name": name, 4022 }) 4023 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1") 4024 s.serveSnap(snapPath, "1") 4025 } 4026 4027 mockServer := s.mockStore(c) 4028 defer mockServer.Close() 4029 4030 st := s.o.State() 4031 st.Lock() 4032 defer st.Unlock() 4033 4034 // pretend we have an old required snap installed 4035 si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)} 4036 snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{ 4037 SnapType: "app", 4038 Active: true, 4039 Sequence: []*snap.SideInfo{si1}, 4040 Current: si1.Revision, 4041 Flags: snapstate.Flags{Required: true}, 4042 }) 4043 4044 // create/set custom model assertion 4045 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 4046 4047 model := s.brands.Model("my-brand", "my-model", modelDefaults) 4048 4049 // setup model assertion 4050 devicestatetest.SetDevice(st, &auth.DeviceState{ 4051 Brand: "my-brand", 4052 Model: "my-model", 4053 Serial: "serialserialserial", 4054 }) 4055 err := assertstate.Add(st, model) 4056 c.Assert(err, IsNil) 4057 4058 // create a new model 4059 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 4060 "required-snaps": []interface{}{"foo", "bar", "baz"}, 4061 "revision": "1", 4062 }) 4063 4064 chg, err := devicestate.Remodel(st, newModel) 4065 c.Assert(err, IsNil) 4066 4067 c.Check(devicestate.Remodeling(st), Equals, true) 4068 4069 st.Unlock() 4070 err = s.o.Settle(settleTimeout) 4071 st.Lock() 4072 c.Assert(err, IsNil) 4073 4074 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 4075 4076 c.Check(devicestate.Remodeling(st), Equals, false) 4077 4078 // the new required-snap "foo" is installed 4079 var snapst snapstate.SnapState 4080 err = snapstate.Get(st, "foo", &snapst) 4081 c.Assert(err, IsNil) 4082 info, err := snapst.CurrentInfo() 4083 c.Assert(err, IsNil) 4084 c.Check(info.Revision, Equals, snap.R(1)) 4085 c.Check(info.Version, Equals, "1.0") 4086 4087 // and marked required 4088 c.Check(snapst.Required, Equals, true) 4089 4090 // and core is still marked required 4091 err = snapstate.Get(st, "core", &snapst) 4092 c.Assert(err, IsNil) 4093 c.Check(snapst.Required, Equals, true) 4094 4095 // but old-required-snap-1 is no longer marked required 4096 err = snapstate.Get(st, "old-required-snap-1", &snapst) 4097 c.Assert(err, IsNil) 4098 c.Check(snapst.Required, Equals, false) 4099 4100 // ensure sorting is correct 4101 tasks := chg.Tasks() 4102 sort.Sort(byReadyTime(tasks)) 4103 4104 var i int 4105 // first all downloads/checks in sequential order 4106 for _, name := range []string{"foo", "bar", "baz"} { 4107 i += validateDownloadCheckTasks(c, tasks[i:], name, "1", "stable") 4108 } 4109 // then all installs in sequential order 4110 for _, name := range []string{"foo", "bar", "baz"} { 4111 i += validateInstallTasks(c, tasks[i:], name, "1", 0) 4112 } 4113 // ensure that we only have the tasks we checked (plus the one 4114 // extra "set-model" task) 4115 c.Assert(tasks, HasLen, i+1) 4116 } 4117 4118 func (s *mgrsSuite) TestRemodelRequiredSnapsAddedUndo(c *C) { 4119 for _, name := range []string{"foo", "bar", "baz"} { 4120 s.prereqSnapAssertions(c, map[string]interface{}{ 4121 "snap-name": name, 4122 }) 4123 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1") 4124 s.serveSnap(snapPath, "1") 4125 } 4126 4127 mockServer := s.mockStore(c) 4128 defer mockServer.Close() 4129 4130 st := s.o.State() 4131 st.Lock() 4132 defer st.Unlock() 4133 4134 // pretend we have an old required snap installed 4135 si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)} 4136 snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{ 4137 SnapType: "app", 4138 Active: true, 4139 Sequence: []*snap.SideInfo{si1}, 4140 Current: si1.Revision, 4141 Flags: snapstate.Flags{Required: true}, 4142 }) 4143 4144 // create/set custom model assertion 4145 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 4146 curModel := s.brands.Model("my-brand", "my-model", modelDefaults) 4147 4148 // setup model assertion 4149 devicestatetest.SetDevice(st, &auth.DeviceState{ 4150 Brand: "my-brand", 4151 Model: "my-model", 4152 Serial: "serialserialserial", 4153 }) 4154 err := assertstate.Add(st, curModel) 4155 c.Assert(err, IsNil) 4156 4157 // create a new model 4158 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 4159 "required-snaps": []interface{}{"foo", "bar", "baz"}, 4160 "revision": "1", 4161 }) 4162 4163 devicestate.InjectSetModelError(fmt.Errorf("boom")) 4164 defer devicestate.InjectSetModelError(nil) 4165 4166 chg, err := devicestate.Remodel(st, newModel) 4167 c.Assert(err, IsNil) 4168 4169 st.Unlock() 4170 err = s.o.Settle(settleTimeout) 4171 st.Lock() 4172 c.Assert(err, IsNil) 4173 4174 c.Assert(chg.Status(), Equals, state.ErrorStatus) 4175 4176 // None of the new snaps got installed 4177 var snapst snapstate.SnapState 4178 for _, snapName := range []string{"foo", "bar", "baz"} { 4179 err = snapstate.Get(st, snapName, &snapst) 4180 c.Assert(err, Equals, state.ErrNoState) 4181 } 4182 4183 // old-required-snap-1 is still marked required 4184 err = snapstate.Get(st, "old-required-snap-1", &snapst) 4185 c.Assert(err, IsNil) 4186 c.Check(snapst.Required, Equals, true) 4187 4188 // check tasks are in undo state 4189 for _, t := range chg.Tasks() { 4190 if t.Kind() == "link-snap" { 4191 c.Assert(t.Status(), Equals, state.UndoneStatus) 4192 } 4193 } 4194 4195 model, err := s.o.DeviceManager().Model() 4196 c.Assert(err, IsNil) 4197 c.Assert(model, DeepEquals, curModel) 4198 } 4199 4200 func (s *mgrsSuite) TestRemodelDifferentBase(c *C) { 4201 // make "core18" snap available in the store 4202 s.prereqSnapAssertions(c, map[string]interface{}{ 4203 "snap-name": "core18", 4204 }) 4205 snapYamlContent := `name: core18 4206 version: 18.04 4207 type: base` 4208 snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "18") 4209 s.serveSnap(snapPath, "18") 4210 4211 mockServer := s.mockStore(c) 4212 defer mockServer.Close() 4213 4214 st := s.o.State() 4215 st.Lock() 4216 defer st.Unlock() 4217 4218 // create/set custom model assertion 4219 model := s.brands.Model("can0nical", "my-model", modelDefaults) 4220 // setup model assertion 4221 devicestatetest.SetDevice(st, &auth.DeviceState{ 4222 Brand: "can0nical", 4223 Model: "my-model", 4224 Serial: "serialserialserial", 4225 }) 4226 err := assertstate.Add(st, model) 4227 c.Assert(err, IsNil) 4228 4229 // create a new model 4230 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4231 "base": "core18", 4232 "revision": "1", 4233 }) 4234 4235 chg, err := devicestate.Remodel(st, newModel) 4236 c.Assert(err, ErrorMatches, "cannot remodel from core to bases yet") 4237 c.Assert(chg, IsNil) 4238 } 4239 4240 func (ms *mgrsSuite) TestRemodelSwitchToDifferentBase(c *C) { 4241 bloader := bootloadertest.Mock("mock", c.MkDir()) 4242 bootloader.Force(bloader) 4243 defer bootloader.Force(nil) 4244 bloader.SetBootVars(map[string]string{ 4245 "snap_mode": boot.DefaultStatus, 4246 "snap_core": "core18_1.snap", 4247 "snap_kernel": "pc-kernel_1.snap", 4248 }) 4249 4250 restore := release.MockOnClassic(false) 4251 defer restore() 4252 4253 mockServer := ms.mockStore(c) 4254 defer mockServer.Close() 4255 4256 st := ms.o.State() 4257 st.Lock() 4258 defer st.Unlock() 4259 4260 si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)} 4261 snapstate.Set(st, "core18", &snapstate.SnapState{ 4262 Active: true, 4263 Sequence: []*snap.SideInfo{si}, 4264 Current: snap.R(1), 4265 SnapType: "base", 4266 }) 4267 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 4268 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 4269 snapstate.Set(st, "pc", &snapstate.SnapState{ 4270 Active: true, 4271 Sequence: []*snap.SideInfo{si2}, 4272 Current: snap.R(1), 4273 SnapType: "gadget", 4274 }) 4275 gadgetYaml := ` 4276 volumes: 4277 volume-id: 4278 bootloader: grub 4279 ` 4280 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 4281 {"meta/gadget.yaml", gadgetYaml}, 4282 }) 4283 4284 // add "core20" snap to fake store 4285 const core20Yaml = `name: core20 4286 type: base 4287 version: 20.04` 4288 ms.prereqSnapAssertions(c, map[string]interface{}{ 4289 "snap-name": "core20", 4290 "publisher-id": "can0nical", 4291 }) 4292 snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2") 4293 ms.serveSnap(snapPath, "2") 4294 4295 // add "foo" snap to fake store 4296 ms.prereqSnapAssertions(c, map[string]interface{}{ 4297 "snap-name": "foo", 4298 }) 4299 snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1") 4300 ms.serveSnap(snapPath, "1") 4301 4302 // create/set custom model assertion 4303 model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4304 "base": "core18", 4305 }) 4306 4307 // setup model assertion 4308 devicestatetest.SetDevice(st, &auth.DeviceState{ 4309 Brand: "can0nical", 4310 Model: "my-model", 4311 Serial: "serialserialserial", 4312 }) 4313 err := assertstate.Add(st, model) 4314 c.Assert(err, IsNil) 4315 4316 // create a new model 4317 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4318 "base": "core20", 4319 "revision": "1", 4320 "required-snaps": []interface{}{"foo"}, 4321 }) 4322 4323 chg, err := devicestate.Remodel(st, newModel) 4324 c.Assert(err, IsNil) 4325 4326 st.Unlock() 4327 err = ms.o.Settle(settleTimeout) 4328 st.Lock() 4329 c.Assert(err, IsNil) 4330 c.Assert(chg.Err(), IsNil) 4331 4332 // system waits for a restart because of the new base 4333 t := findKind(chg, "auto-connect") 4334 c.Assert(t, NotNil) 4335 c.Assert(t.Status(), Equals, state.DoingStatus) 4336 4337 // check that the boot vars got updated as expected 4338 bvars, err := bloader.GetBootVars("snap_mode", "snap_core", "snap_try_core", "snap_kernel", "snap_try_kernel") 4339 c.Assert(err, IsNil) 4340 c.Assert(bvars, DeepEquals, map[string]string{ 4341 "snap_mode": boot.TryStatus, 4342 "snap_core": "core18_1.snap", 4343 "snap_try_core": "core20_2.snap", 4344 "snap_kernel": "pc-kernel_1.snap", 4345 "snap_try_kernel": "", 4346 }) 4347 4348 // simulate successful restart happened and that the bootvars 4349 // got updated 4350 state.MockRestarting(st, state.RestartUnset) 4351 bloader.SetBootVars(map[string]string{ 4352 "snap_mode": boot.DefaultStatus, 4353 "snap_core": "core20_2.snap", 4354 "snap_kernel": "pc-kernel_1.snap", 4355 }) 4356 4357 // continue 4358 st.Unlock() 4359 err = ms.o.Settle(settleTimeout) 4360 st.Lock() 4361 c.Assert(err, IsNil) 4362 4363 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 4364 4365 // ensure tasks were run in the right order 4366 tasks := chg.Tasks() 4367 sort.Sort(byReadyTime(tasks)) 4368 4369 // first all downloads/checks in sequential order 4370 var i int 4371 i += validateDownloadCheckTasks(c, tasks[i:], "core20", "2", "stable") 4372 i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable") 4373 4374 // then all installs in sequential order 4375 i += validateInstallTasks(c, tasks[i:], "core20", "2", noConfigure) 4376 i += validateInstallTasks(c, tasks[i:], "foo", "1", 0) 4377 4378 // ensure that we only have the tasks we checked (plus the one 4379 // extra "set-model" task) 4380 c.Assert(tasks, HasLen, i+1) 4381 } 4382 4383 func (ms *mgrsSuite) TestRemodelSwitchToDifferentBaseUndo(c *C) { 4384 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 4385 bootloader.Force(bloader) 4386 defer bootloader.Force(nil) 4387 bloader.SetBootVars(map[string]string{ 4388 "snap_mode": boot.DefaultStatus, 4389 "snap_core": "core18_1.snap", 4390 "snap_kernel": "pc-kernel_1.snap", 4391 }) 4392 4393 restore := release.MockOnClassic(false) 4394 defer restore() 4395 4396 mockServer := ms.mockStore(c) 4397 defer mockServer.Close() 4398 4399 st := ms.o.State() 4400 st.Lock() 4401 defer st.Unlock() 4402 4403 si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)} 4404 snapstate.Set(st, "core18", &snapstate.SnapState{ 4405 Active: true, 4406 Sequence: []*snap.SideInfo{si}, 4407 Current: snap.R(1), 4408 SnapType: "base", 4409 }) 4410 snaptest.MockSnapWithFiles(c, "name: core18\ntype: base\nversion: 1.0", si, nil) 4411 4412 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 4413 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 4414 snapstate.Set(st, "pc", &snapstate.SnapState{ 4415 Active: true, 4416 Sequence: []*snap.SideInfo{si2}, 4417 Current: snap.R(1), 4418 SnapType: "gadget", 4419 }) 4420 gadgetYaml := ` 4421 volumes: 4422 volume-id: 4423 bootloader: grub 4424 ` 4425 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 4426 {"meta/gadget.yaml", gadgetYaml}, 4427 }) 4428 4429 // add "core20" snap to fake store 4430 const core20Yaml = `name: core20 4431 type: base 4432 version: 20.04` 4433 ms.prereqSnapAssertions(c, map[string]interface{}{ 4434 "snap-name": "core20", 4435 "publisher-id": "can0nical", 4436 }) 4437 snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2") 4438 ms.serveSnap(snapPath, "2") 4439 4440 // add "foo" snap to fake store 4441 ms.prereqSnapAssertions(c, map[string]interface{}{ 4442 "snap-name": "foo", 4443 }) 4444 snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1") 4445 ms.serveSnap(snapPath, "1") 4446 4447 // create/set custom model assertion 4448 model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4449 "base": "core18", 4450 }) 4451 4452 // setup model assertion 4453 devicestatetest.SetDevice(st, &auth.DeviceState{ 4454 Brand: "can0nical", 4455 Model: "my-model", 4456 Serial: "serialserialserial", 4457 }) 4458 err := assertstate.Add(st, model) 4459 c.Assert(err, IsNil) 4460 4461 // create a new model 4462 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4463 "base": "core20", 4464 "revision": "1", 4465 "required-snaps": []interface{}{"foo"}, 4466 }) 4467 4468 devicestate.InjectSetModelError(fmt.Errorf("boom")) 4469 defer devicestate.InjectSetModelError(nil) 4470 4471 chg, err := devicestate.Remodel(st, newModel) 4472 c.Assert(err, IsNil) 4473 4474 st.Unlock() 4475 err = ms.o.Settle(settleTimeout) 4476 st.Lock() 4477 c.Assert(err, IsNil) 4478 c.Assert(chg.Err(), IsNil) 4479 4480 // system waits for a restart because of the new base 4481 t := findKind(chg, "auto-connect") 4482 c.Assert(t, NotNil) 4483 c.Assert(t.Status(), Equals, state.DoingStatus) 4484 4485 // check that the boot vars got updated as expected 4486 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 4487 "snap_mode": boot.TryStatus, 4488 "snap_core": "core18_1.snap", 4489 "snap_try_core": "core20_2.snap", 4490 "snap_kernel": "pc-kernel_1.snap", 4491 "snap_try_kernel": "", 4492 }) 4493 // simulate successful restart happened 4494 ms.mockSuccessfulReboot(c, bloader, []snap.Type{snap.TypeBase}) 4495 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 4496 "snap_mode": boot.DefaultStatus, 4497 "snap_core": "core20_2.snap", 4498 "snap_try_core": "", 4499 "snap_kernel": "pc-kernel_1.snap", 4500 "snap_try_kernel": "", 4501 }) 4502 4503 // continue 4504 st.Unlock() 4505 err = ms.o.Settle(settleTimeout) 4506 st.Lock() 4507 c.Assert(err, IsNil) 4508 4509 c.Assert(chg.Status(), Equals, state.ErrorStatus) 4510 4511 // and we are in restarting state 4512 restarting, restartType := st.Restarting() 4513 c.Check(restarting, Equals, true) 4514 c.Check(restartType, Equals, state.RestartSystem) 4515 4516 // and the undo gave us our old kernel back 4517 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 4518 "snap_core": "core20_2.snap", 4519 "snap_try_core": "core18_1.snap", 4520 "snap_kernel": "pc-kernel_1.snap", 4521 "snap_try_kernel": "", 4522 "snap_mode": boot.TryStatus, 4523 }) 4524 } 4525 4526 func (ms *mgrsSuite) TestRemodelSwitchToDifferentBaseUndoOnRollback(c *C) { 4527 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 4528 bootloader.Force(bloader) 4529 defer bootloader.Force(nil) 4530 bloader.SetBootVars(map[string]string{ 4531 "snap_mode": boot.DefaultStatus, 4532 "snap_core": "core18_1.snap", 4533 "snap_kernel": "pc-kernel_1.snap", 4534 }) 4535 4536 restore := release.MockOnClassic(false) 4537 defer restore() 4538 4539 mockServer := ms.mockStore(c) 4540 defer mockServer.Close() 4541 4542 st := ms.o.State() 4543 st.Lock() 4544 defer st.Unlock() 4545 4546 si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)} 4547 snapstate.Set(st, "core18", &snapstate.SnapState{ 4548 Active: true, 4549 Sequence: []*snap.SideInfo{si}, 4550 Current: snap.R(1), 4551 SnapType: "base", 4552 }) 4553 snaptest.MockSnapWithFiles(c, "name: core18\ntype: base\nversion: 1.0", si, nil) 4554 4555 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 4556 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 4557 snapstate.Set(st, "pc", &snapstate.SnapState{ 4558 Active: true, 4559 Sequence: []*snap.SideInfo{si2}, 4560 Current: snap.R(1), 4561 SnapType: "gadget", 4562 }) 4563 gadgetYaml := ` 4564 volumes: 4565 volume-id: 4566 bootloader: grub 4567 ` 4568 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 4569 {"meta/gadget.yaml", gadgetYaml}, 4570 }) 4571 4572 // add "core20" snap to fake store 4573 const core20Yaml = `name: core20 4574 type: base 4575 version: 20.04` 4576 ms.prereqSnapAssertions(c, map[string]interface{}{ 4577 "snap-name": "core20", 4578 "publisher-id": "can0nical", 4579 }) 4580 snapPath, _ := ms.makeStoreTestSnap(c, core20Yaml, "2") 4581 ms.serveSnap(snapPath, "2") 4582 4583 // add "foo" snap to fake store 4584 ms.prereqSnapAssertions(c, map[string]interface{}{ 4585 "snap-name": "foo", 4586 }) 4587 snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1") 4588 ms.serveSnap(snapPath, "1") 4589 4590 // create/set custom model assertion 4591 model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4592 "base": "core18", 4593 }) 4594 4595 // setup model assertion 4596 devicestatetest.SetDevice(st, &auth.DeviceState{ 4597 Brand: "can0nical", 4598 Model: "my-model", 4599 Serial: "serialserialserial", 4600 }) 4601 err := assertstate.Add(st, model) 4602 c.Assert(err, IsNil) 4603 4604 // create a new model 4605 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4606 "base": "core20", 4607 "revision": "1", 4608 "required-snaps": []interface{}{"foo"}, 4609 }) 4610 4611 chg, err := devicestate.Remodel(st, newModel) 4612 c.Assert(err, IsNil) 4613 4614 st.Unlock() 4615 err = ms.o.Settle(settleTimeout) 4616 st.Lock() 4617 c.Assert(err, IsNil) 4618 c.Assert(chg.Err(), IsNil) 4619 4620 // system waits for a restart because of the new base 4621 t := findKind(chg, "auto-connect") 4622 c.Assert(t, NotNil) 4623 c.Assert(t.Status(), Equals, state.DoingStatus) 4624 4625 // check that the boot vars got updated as expected 4626 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 4627 "snap_mode": boot.TryStatus, 4628 "snap_core": "core18_1.snap", 4629 "snap_try_core": "core20_2.snap", 4630 "snap_kernel": "pc-kernel_1.snap", 4631 "snap_try_kernel": "", 4632 }) 4633 // simulate successful restart happened 4634 ms.mockRollbackAcrossReboot(c, bloader, []snap.Type{snap.TypeBase}) 4635 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 4636 "snap_mode": boot.DefaultStatus, 4637 "snap_core": "core18_1.snap", 4638 "snap_try_core": "", 4639 "snap_kernel": "pc-kernel_1.snap", 4640 "snap_try_kernel": "", 4641 }) 4642 4643 // continue 4644 st.Unlock() 4645 err = ms.o.Settle(settleTimeout) 4646 st.Lock() 4647 c.Assert(err, IsNil) 4648 4649 c.Assert(chg.Status(), Equals, state.ErrorStatus) 4650 4651 // and we are *not* in restarting state 4652 restarting, _ := st.Restarting() 4653 c.Check(restarting, Equals, false) 4654 // bootvars unchanged 4655 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 4656 "snap_mode": boot.DefaultStatus, 4657 "snap_core": "core18_1.snap", 4658 "snap_try_core": "", 4659 "snap_kernel": "pc-kernel_1.snap", 4660 "snap_try_kernel": "", 4661 }) 4662 } 4663 4664 type kernelSuite struct { 4665 baseMgrsSuite 4666 4667 bloader *boottest.Bootenv16 4668 } 4669 4670 var _ = Suite(&kernelSuite{}) 4671 4672 func (s *kernelSuite) SetUpTest(c *C) { 4673 s.baseMgrsSuite.SetUpTest(c) 4674 4675 s.bloader = boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 4676 s.bloader.SetBootKernel("pc-kernel_1.snap") 4677 s.bloader.SetBootBase("core_1.snap") 4678 bootloader.Force(s.bloader) 4679 s.AddCleanup(func() { bootloader.Force(nil) }) 4680 4681 restore := release.MockOnClassic(false) 4682 s.AddCleanup(restore) 4683 mockServer := s.mockStore(c) 4684 s.AddCleanup(mockServer.Close) 4685 4686 st := s.o.State() 4687 st.Lock() 4688 defer st.Unlock() 4689 4690 // create/set custom model assertion 4691 model := s.brands.Model("can0nical", "my-model", modelDefaults) 4692 devicestatetest.SetDevice(st, &auth.DeviceState{ 4693 Brand: "can0nical", 4694 Model: "my-model", 4695 Serial: "serialserialserial", 4696 }) 4697 err := assertstate.Add(st, model) 4698 c.Assert(err, IsNil) 4699 4700 // make a mock "pc-kernel" kernel 4701 si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)} 4702 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 4703 Active: true, 4704 Sequence: []*snap.SideInfo{si}, 4705 Current: snap.R(1), 4706 SnapType: "kernel", 4707 }) 4708 snaptest.MockSnapWithFiles(c, "name: pc-kernel\ntype: kernel\nversion: 1.0", si, nil) 4709 4710 // make a mock "pc" gadget 4711 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 4712 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 4713 snapstate.Set(st, "pc", &snapstate.SnapState{ 4714 Active: true, 4715 Sequence: []*snap.SideInfo{si2}, 4716 Current: snap.R(1), 4717 SnapType: "gadget", 4718 }) 4719 gadgetYaml := ` 4720 volumes: 4721 volume-id: 4722 bootloader: grub 4723 ` 4724 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 4725 {"meta/gadget.yaml", gadgetYaml}, 4726 }) 4727 4728 // add some store snaps 4729 const kernelYaml = `name: pc-kernel 4730 type: kernel 4731 version: 2.0` 4732 snapPath, _ := s.makeStoreTestSnap(c, kernelYaml, "2") 4733 s.serveSnap(snapPath, "2") 4734 4735 const brandKernelYaml = `name: brand-kernel 4736 type: kernel 4737 version: 1.0` 4738 s.prereqSnapAssertions(c, map[string]interface{}{ 4739 "snap-name": "brand-kernel", 4740 "publisher-id": "can0nical", 4741 }) 4742 snapPath, _ = s.makeStoreTestSnap(c, brandKernelYaml, "2") 4743 s.serveSnap(snapPath, "2") 4744 4745 s.prereqSnapAssertions(c, map[string]interface{}{ 4746 "snap-name": "foo", 4747 }) 4748 snapPath, _ = s.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1") 4749 s.serveSnap(snapPath, "1") 4750 } 4751 4752 func (s *kernelSuite) TestRemodelSwitchKernelTrack(c *C) { 4753 st := s.o.State() 4754 st.Lock() 4755 defer st.Unlock() 4756 4757 // create a new model 4758 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4759 "kernel": "pc-kernel=18", 4760 "revision": "1", 4761 "required-snaps": []interface{}{"foo"}, 4762 }) 4763 4764 chg, err := devicestate.Remodel(st, newModel) 4765 c.Assert(err, IsNil) 4766 4767 st.Unlock() 4768 err = s.o.Settle(settleTimeout) 4769 st.Lock() 4770 c.Assert(err, IsNil) 4771 4772 // system waits for a restart because of the new kernel 4773 t := findKind(chg, "auto-connect") 4774 c.Assert(t, NotNil) 4775 c.Assert(t.Status(), Equals, state.DoingStatus) 4776 4777 // simulate successful restart happened 4778 s.mockSuccessfulReboot(c, s.bloader, []snap.Type{snap.TypeKernel}) 4779 4780 // continue 4781 st.Unlock() 4782 err = s.o.Settle(settleTimeout) 4783 st.Lock() 4784 c.Assert(err, IsNil) 4785 4786 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 4787 4788 // ensure tasks were run in the right order 4789 tasks := chg.Tasks() 4790 sort.Sort(byReadyTime(tasks)) 4791 4792 // first all downloads/checks in sequential order 4793 var i int 4794 i += validateDownloadCheckTasks(c, tasks[i:], "pc-kernel", "2", "18") 4795 i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable") 4796 4797 // then all installs in sequential order 4798 i += validateRefreshTasks(c, tasks[i:], "pc-kernel", "2", isKernel) 4799 i += validateInstallTasks(c, tasks[i:], "foo", "1", 0) 4800 4801 // ensure that we only have the tasks we checked (plus the one 4802 // extra "set-model" task) 4803 c.Assert(tasks, HasLen, i+1) 4804 } 4805 4806 func (ms *kernelSuite) TestRemodelSwitchToDifferentKernel(c *C) { 4807 st := ms.o.State() 4808 st.Lock() 4809 defer st.Unlock() 4810 4811 // create a new model 4812 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4813 "kernel": "brand-kernel", 4814 "revision": "1", 4815 "required-snaps": []interface{}{"foo"}, 4816 }) 4817 4818 chg, err := devicestate.Remodel(st, newModel) 4819 c.Assert(err, IsNil) 4820 4821 st.Unlock() 4822 err = ms.o.Settle(settleTimeout) 4823 st.Lock() 4824 c.Assert(err, IsNil) 4825 c.Assert(chg.Err(), IsNil) 4826 4827 // system waits for a restart because of the new kernel 4828 t := findKind(chg, "auto-connect") 4829 c.Assert(t, NotNil) 4830 c.Assert(t.Status(), Equals, state.DoingStatus) 4831 4832 // check that the system tries to boot the new brand kernel 4833 c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{ 4834 "snap_core": "core_1.snap", 4835 "snap_kernel": "pc-kernel_1.snap", 4836 "snap_try_kernel": "brand-kernel_2.snap", 4837 "snap_mode": boot.TryStatus, 4838 "snap_try_core": "", 4839 }) 4840 // simulate successful system-restart bootenv updates (those 4841 // vars will be cleared by snapd on a restart) 4842 ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel}) 4843 // bootvars are as expected 4844 c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{ 4845 "snap_core": "core_1.snap", 4846 "snap_kernel": "brand-kernel_2.snap", 4847 "snap_try_core": "", 4848 "snap_try_kernel": "", 4849 "snap_mode": boot.DefaultStatus, 4850 }) 4851 4852 // continue 4853 st.Unlock() 4854 err = ms.o.Settle(settleTimeout) 4855 st.Lock() 4856 c.Assert(err, IsNil) 4857 4858 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 4859 4860 // bootvars are as expected (i.e. nothing has changed since this 4861 // test simulated that we booted successfully) 4862 c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{ 4863 "snap_core": "core_1.snap", 4864 "snap_kernel": "brand-kernel_2.snap", 4865 "snap_try_kernel": "", 4866 "snap_try_core": "", 4867 "snap_mode": boot.DefaultStatus, 4868 }) 4869 4870 // ensure tasks were run in the right order 4871 tasks := chg.Tasks() 4872 sort.Sort(byReadyTime(tasks)) 4873 4874 // first all downloads/checks in sequential order 4875 var i int 4876 i += validateDownloadCheckTasks(c, tasks[i:], "brand-kernel", "2", "stable") 4877 i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable") 4878 4879 // then all installs in sequential order 4880 i += validateInstallTasks(c, tasks[i:], "brand-kernel", "2", isKernel) 4881 i += validateInstallTasks(c, tasks[i:], "foo", "1", 0) 4882 4883 // ensure that we only have the tasks we checked (plus the one 4884 // extra "set-model" task) 4885 c.Assert(tasks, HasLen, i+1) 4886 4887 // ensure we did not try device registration 4888 for _, t := range st.Tasks() { 4889 if t.Kind() == "request-serial" { 4890 c.Fatalf("test should not create a request-serial task but did") 4891 } 4892 } 4893 } 4894 4895 func (ms *kernelSuite) TestRemodelSwitchToDifferentKernelUndo(c *C) { 4896 st := ms.o.State() 4897 st.Lock() 4898 defer st.Unlock() 4899 4900 // create a new model 4901 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4902 "kernel": "brand-kernel", 4903 "revision": "1", 4904 "required-snaps": []interface{}{"foo"}, 4905 }) 4906 4907 devicestate.InjectSetModelError(fmt.Errorf("boom")) 4908 defer devicestate.InjectSetModelError(nil) 4909 4910 chg, err := devicestate.Remodel(st, newModel) 4911 c.Assert(err, IsNil) 4912 4913 st.Unlock() 4914 err = ms.o.Settle(settleTimeout) 4915 st.Lock() 4916 c.Assert(err, IsNil) 4917 c.Assert(chg.Err(), IsNil) 4918 4919 // system waits for a restart because of the new kernel 4920 t := findKind(chg, "auto-connect") 4921 c.Assert(t, NotNil) 4922 c.Assert(t.Status(), Equals, state.DoingStatus) 4923 4924 // simulate successful restart happened 4925 ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel}) 4926 4927 // continue 4928 st.Unlock() 4929 err = ms.o.Settle(settleTimeout) 4930 st.Lock() 4931 c.Assert(err, IsNil) 4932 4933 // the change was not successful 4934 c.Assert(chg.Status(), Equals, state.ErrorStatus) 4935 4936 // and we are in restarting state 4937 restarting, restartType := st.Restarting() 4938 c.Check(restarting, Equals, true) 4939 c.Check(restartType, Equals, state.RestartSystem) 4940 4941 // and the undo gave us our old kernel back 4942 c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{ 4943 "snap_core": "core_1.snap", 4944 "snap_try_core": "", 4945 "snap_try_kernel": "pc-kernel_1.snap", 4946 "snap_kernel": "brand-kernel_2.snap", 4947 "snap_mode": boot.TryStatus, 4948 }) 4949 } 4950 4951 func (ms *kernelSuite) TestRemodelSwitchToDifferentKernelUndoOnRollback(c *C) { 4952 st := ms.o.State() 4953 st.Lock() 4954 defer st.Unlock() 4955 4956 // create a new model 4957 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 4958 "kernel": "brand-kernel", 4959 "revision": "1", 4960 "required-snaps": []interface{}{"foo"}, 4961 }) 4962 4963 devicestate.InjectSetModelError(fmt.Errorf("boom")) 4964 defer devicestate.InjectSetModelError(nil) 4965 4966 chg, err := devicestate.Remodel(st, newModel) 4967 c.Assert(err, IsNil) 4968 4969 st.Unlock() 4970 err = ms.o.Settle(settleTimeout) 4971 st.Lock() 4972 c.Assert(err, IsNil) 4973 c.Assert(chg.Err(), IsNil) 4974 4975 // system waits for a restart because of the new kernel 4976 t := findKind(chg, "auto-connect") 4977 c.Assert(t, NotNil) 4978 c.Assert(t.Status(), Equals, state.DoingStatus) 4979 4980 // simulate rollback of the kernel during reboot 4981 ms.mockRollbackAcrossReboot(c, ms.bloader, []snap.Type{snap.TypeKernel}) 4982 4983 // continue 4984 st.Unlock() 4985 err = ms.o.Settle(settleTimeout) 4986 st.Lock() 4987 c.Assert(err, IsNil) 4988 4989 // the change was not successful 4990 c.Assert(chg.Status(), Equals, state.ErrorStatus) 4991 4992 // and we are *not* in restarting state 4993 restarting, _ := st.Restarting() 4994 c.Check(restarting, Equals, false) 4995 4996 // and the undo gave us our old kernel back 4997 c.Assert(ms.bloader.BootVars, DeepEquals, map[string]string{ 4998 "snap_core": "core_1.snap", 4999 "snap_try_core": "", 5000 "snap_kernel": "pc-kernel_1.snap", 5001 "snap_try_kernel": "", 5002 "snap_mode": boot.DefaultStatus, 5003 }) 5004 } 5005 5006 func (s *mgrsSuite) TestRemodelStoreSwitch(c *C) { 5007 s.prereqSnapAssertions(c, map[string]interface{}{ 5008 "snap-name": "foo", 5009 }) 5010 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1") 5011 s.serveSnap(snapPath, "1") 5012 5013 // track the creation of new DeviceAndAutContext (for new Store) 5014 newDAC := false 5015 5016 mockServer := s.mockStore(c) 5017 defer mockServer.Close() 5018 5019 st := s.o.State() 5020 st.Lock() 5021 defer st.Unlock() 5022 5023 s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) { 5024 // the DeviceAndAuthContext assumes state is unlocked 5025 st.Unlock() 5026 defer st.Lock() 5027 c.Check(dac, NotNil) 5028 stoID, err := dac.StoreID("") 5029 c.Assert(err, IsNil) 5030 c.Check(stoID, Equals, "switched-store") 5031 newDAC = true 5032 } 5033 5034 // create/set custom model assertion 5035 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 5036 5037 model := s.brands.Model("my-brand", "my-model", modelDefaults) 5038 5039 // setup model assertion 5040 err := assertstate.Add(st, model) 5041 c.Assert(err, IsNil) 5042 5043 // have a serial as well 5044 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 5045 c.Assert(err, IsNil) 5046 err = kpMgr.Put(deviceKey) 5047 c.Assert(err, IsNil) 5048 5049 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 5050 c.Assert(err, IsNil) 5051 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 5052 "authority-id": "my-brand", 5053 "brand-id": "my-brand", 5054 "model": "my-model", 5055 "serial": "store-switch-serial", 5056 "device-key": string(encDevKey), 5057 "device-key-sha3-384": deviceKey.PublicKey().ID(), 5058 "timestamp": time.Now().Format(time.RFC3339), 5059 }, nil, "") 5060 c.Assert(err, IsNil) 5061 err = assertstate.Add(st, serial) 5062 c.Assert(err, IsNil) 5063 5064 devicestatetest.SetDevice(st, &auth.DeviceState{ 5065 Brand: "my-brand", 5066 Model: "my-model", 5067 KeyID: deviceKey.PublicKey().ID(), 5068 Serial: "store-switch-serial", 5069 }) 5070 5071 // create a new model 5072 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 5073 "store": "switched-store", 5074 "required-snaps": []interface{}{"foo"}, 5075 "revision": "1", 5076 }) 5077 5078 s.expectedSerial = "store-switch-serial" 5079 s.expectedStore = "switched-store" 5080 s.sessionMacaroon = "switched-store-session" 5081 5082 chg, err := devicestate.Remodel(st, newModel) 5083 c.Assert(err, IsNil) 5084 5085 st.Unlock() 5086 err = s.o.Settle(settleTimeout) 5087 st.Lock() 5088 c.Assert(err, IsNil) 5089 5090 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 5091 5092 // the new required-snap "foo" is installed 5093 var snapst snapstate.SnapState 5094 err = snapstate.Get(st, "foo", &snapst) 5095 c.Assert(err, IsNil) 5096 5097 // and marked required 5098 c.Check(snapst.Required, Equals, true) 5099 5100 // a new store was made 5101 c.Check(newDAC, Equals, true) 5102 5103 // we have a session with the new store 5104 device, err := devicestatetest.Device(st) 5105 c.Assert(err, IsNil) 5106 c.Check(device.Serial, Equals, "store-switch-serial") 5107 c.Check(device.SessionMacaroon, Equals, "switched-store-session") 5108 } 5109 5110 func (s *mgrsSuite) TestRemodelSwitchGadgetTrack(c *C) { 5111 bloader := bootloadertest.Mock("mock", c.MkDir()) 5112 bootloader.Force(bloader) 5113 defer bootloader.Force(nil) 5114 5115 restore := release.MockOnClassic(false) 5116 defer restore() 5117 5118 mockServer := s.mockStore(c) 5119 defer mockServer.Close() 5120 5121 st := s.o.State() 5122 st.Lock() 5123 defer st.Unlock() 5124 5125 si := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 5126 snapstate.Set(st, "pc", &snapstate.SnapState{ 5127 Active: true, 5128 Sequence: []*snap.SideInfo{si}, 5129 Current: snap.R(1), 5130 SnapType: "gadget", 5131 }) 5132 gadgetSnapYaml := "name: pc\nversion: 2.0\ntype: gadget" 5133 gadgetYaml := ` 5134 volumes: 5135 volume-id: 5136 bootloader: grub 5137 ` 5138 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si, [][]string{ 5139 {"meta/gadget.yaml", gadgetYaml}, 5140 }) 5141 snapPath, _ := s.makeStoreTestSnapWithFiles(c, gadgetSnapYaml, "2", [][]string{ 5142 {"meta/gadget.yaml", gadgetYaml}, 5143 }) 5144 s.serveSnap(snapPath, "2") 5145 5146 // create/set custom model assertion 5147 model := s.brands.Model("can0nical", "my-model", modelDefaults) 5148 5149 // setup model assertion 5150 devicestatetest.SetDevice(st, &auth.DeviceState{ 5151 Brand: "can0nical", 5152 Model: "my-model", 5153 Serial: "serialserialserial", 5154 }) 5155 err := assertstate.Add(st, model) 5156 c.Assert(err, IsNil) 5157 5158 // create a new model 5159 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 5160 "gadget": "pc=18", 5161 "revision": "1", 5162 }) 5163 5164 chg, err := devicestate.Remodel(st, newModel) 5165 c.Assert(err, IsNil) 5166 5167 st.Unlock() 5168 err = s.o.Settle(settleTimeout) 5169 st.Lock() 5170 c.Assert(err, IsNil) 5171 c.Assert(chg.Err(), IsNil) 5172 5173 // ensure tasks were run in the right order 5174 tasks := chg.Tasks() 5175 sort.Sort(byReadyTime(tasks)) 5176 5177 // first all downloads/checks in sequential order 5178 var i int 5179 i += validateDownloadCheckTasks(c, tasks[i:], "pc", "2", "18") 5180 5181 // then all installs in sequential order 5182 i += validateRefreshTasks(c, tasks[i:], "pc", "2", isGadget) 5183 5184 // ensure that we only have the tasks we checked (plus the one 5185 // extra "set-model" task) 5186 c.Assert(tasks, HasLen, i+1) 5187 } 5188 5189 type mockUpdater struct{} 5190 5191 func (m *mockUpdater) Backup() error { return nil } 5192 5193 func (m *mockUpdater) Rollback() error { return nil } 5194 5195 func (m *mockUpdater) Update() error { return nil } 5196 5197 func (s *mgrsSuite) TestRemodelSwitchToDifferentGadget(c *C) { 5198 bloader := bootloadertest.Mock("mock", c.MkDir()) 5199 bootloader.Force(bloader) 5200 defer bootloader.Force(nil) 5201 restore := release.MockOnClassic(false) 5202 defer restore() 5203 5204 mockServer := s.mockStore(c) 5205 defer mockServer.Close() 5206 5207 st := s.o.State() 5208 st.Lock() 5209 defer st.Unlock() 5210 5211 si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)} 5212 snapstate.Set(st, "core18", &snapstate.SnapState{ 5213 Active: true, 5214 Sequence: []*snap.SideInfo{si}, 5215 Current: snap.R(1), 5216 SnapType: "base", 5217 }) 5218 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 5219 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 5220 snapstate.Set(st, "pc", &snapstate.SnapState{ 5221 Active: true, 5222 Sequence: []*snap.SideInfo{si2}, 5223 Current: snap.R(1), 5224 SnapType: "gadget", 5225 }) 5226 gadgetYaml := ` 5227 volumes: 5228 volume-id: 5229 bootloader: grub 5230 structure: 5231 - name: foo 5232 type: bare 5233 size: 1M 5234 content: 5235 - image: foo.img 5236 ` 5237 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 5238 {"meta/gadget.yaml", gadgetYaml}, 5239 {"foo.img", "foo"}, 5240 }) 5241 5242 // add new gadget "other-pc" snap to fake store 5243 const otherPcYaml = `name: other-pc 5244 type: gadget 5245 version: 2` 5246 s.prereqSnapAssertions(c, map[string]interface{}{ 5247 "snap-name": "other-pc", 5248 "publisher-id": "can0nical", 5249 }) 5250 otherGadgetYaml := ` 5251 volumes: 5252 volume-id: 5253 bootloader: grub 5254 structure: 5255 - name: foo 5256 type: bare 5257 size: 1M 5258 content: 5259 - image: new-foo.img 5260 ` 5261 snapPath, _ := s.makeStoreTestSnapWithFiles(c, otherPcYaml, "2", [][]string{ 5262 // use a compatible gadget YAML 5263 {"meta/gadget.yaml", otherGadgetYaml}, 5264 {"new-foo.img", "new foo"}, 5265 }) 5266 s.serveSnap(snapPath, "2") 5267 5268 updaterForStructureCalls := 0 5269 restore = gadget.MockUpdaterForStructure(func(ps *gadget.LaidOutStructure, rootDir, rollbackDir string, observer gadget.ContentUpdateObserver) (gadget.Updater, error) { 5270 updaterForStructureCalls++ 5271 c.Assert(ps.Name, Equals, "foo") 5272 return &mockUpdater{}, nil 5273 }) 5274 defer restore() 5275 5276 // create/set custom model assertion 5277 model := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 5278 "gadget": "pc", 5279 }) 5280 5281 // setup model assertion 5282 devicestatetest.SetDevice(st, &auth.DeviceState{ 5283 Brand: "can0nical", 5284 Model: "my-model", 5285 Serial: "serialserialserial", 5286 }) 5287 err := assertstate.Add(st, model) 5288 c.Assert(err, IsNil) 5289 5290 // create a new model 5291 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 5292 "gadget": "other-pc=18", 5293 "revision": "1", 5294 }) 5295 5296 chg, err := devicestate.Remodel(st, newModel) 5297 c.Assert(err, IsNil) 5298 5299 st.Unlock() 5300 err = s.o.Settle(settleTimeout) 5301 st.Lock() 5302 c.Assert(err, IsNil) 5303 c.Assert(chg.Err(), IsNil) 5304 5305 // gadget updater was set up 5306 c.Check(updaterForStructureCalls, Equals, 1) 5307 5308 // gadget update requests a restart 5309 restarting, _ := st.Restarting() 5310 c.Check(restarting, Equals, true) 5311 5312 // simulate successful restart happened 5313 state.MockRestarting(st, state.RestartUnset) 5314 5315 st.Unlock() 5316 err = s.o.Settle(settleTimeout) 5317 st.Lock() 5318 c.Assert(err, IsNil) 5319 5320 // ensure tasks were run in the right order 5321 tasks := chg.Tasks() 5322 sort.Sort(byReadyTime(tasks)) 5323 5324 // first all downloads/checks 5325 var i int 5326 i += validateDownloadCheckTasks(c, tasks[i:], "other-pc", "2", "18") 5327 5328 // then all installs 5329 i += validateInstallTasks(c, tasks[i:], "other-pc", "2", isGadget) 5330 5331 // ensure that we only have the tasks we checked (plus the one 5332 // extra "set-model" task) 5333 c.Assert(tasks, HasLen, i+1) 5334 } 5335 5336 func (s *mgrsSuite) TestRemodelSwitchToIncompatibleGadget(c *C) { 5337 bloader := bootloadertest.Mock("mock", c.MkDir()) 5338 bootloader.Force(bloader) 5339 defer bootloader.Force(nil) 5340 restore := release.MockOnClassic(false) 5341 defer restore() 5342 5343 mockServer := s.mockStore(c) 5344 defer mockServer.Close() 5345 5346 st := s.o.State() 5347 st.Lock() 5348 defer st.Unlock() 5349 5350 si := &snap.SideInfo{RealName: "core18", SnapID: fakeSnapID("core18"), Revision: snap.R(1)} 5351 snapstate.Set(st, "core18", &snapstate.SnapState{ 5352 Active: true, 5353 Sequence: []*snap.SideInfo{si}, 5354 Current: snap.R(1), 5355 SnapType: "base", 5356 }) 5357 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 5358 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 5359 snapstate.Set(st, "pc", &snapstate.SnapState{ 5360 Active: true, 5361 Sequence: []*snap.SideInfo{si2}, 5362 Current: snap.R(1), 5363 SnapType: "gadget", 5364 }) 5365 gadgetYaml := ` 5366 volumes: 5367 volume-id: 5368 bootloader: grub 5369 structure: 5370 - name: foo 5371 type: 00000000-0000-0000-0000-0000deadcafe 5372 size: 10M 5373 ` 5374 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 5375 {"meta/gadget.yaml", gadgetYaml}, 5376 }) 5377 5378 // add new gadget "other-pc" snap to fake store 5379 const otherPcYaml = `name: other-pc 5380 type: gadget 5381 version: 2` 5382 // new gadget layout is incompatible, a structure that exited before has 5383 // a different size now 5384 otherGadgetYaml := ` 5385 volumes: 5386 volume-id: 5387 bootloader: grub 5388 structure: 5389 - name: foo 5390 type: 00000000-0000-0000-0000-0000deadcafe 5391 size: 20M 5392 ` 5393 s.prereqSnapAssertions(c, map[string]interface{}{ 5394 "snap-name": "other-pc", 5395 "publisher-id": "can0nical", 5396 }) 5397 snapPath, _ := s.makeStoreTestSnapWithFiles(c, otherPcYaml, "2", [][]string{ 5398 {"meta/gadget.yaml", otherGadgetYaml}, 5399 }) 5400 s.serveSnap(snapPath, "2") 5401 5402 // create/set custom model assertion 5403 model := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 5404 "gadget": "pc", 5405 }) 5406 5407 // setup model assertion 5408 devicestatetest.SetDevice(st, &auth.DeviceState{ 5409 Brand: "can0nical", 5410 Model: "my-model", 5411 Serial: "serialserialserial", 5412 }) 5413 err := assertstate.Add(st, model) 5414 c.Assert(err, IsNil) 5415 5416 // create a new model 5417 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 5418 "gadget": "other-pc=18", 5419 "revision": "1", 5420 }) 5421 5422 chg, err := devicestate.Remodel(st, newModel) 5423 c.Assert(err, IsNil) 5424 5425 st.Unlock() 5426 err = s.o.Settle(settleTimeout) 5427 st.Lock() 5428 c.Assert(err, IsNil) 5429 c.Assert(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*cannot remodel to an incompatible gadget: .*cannot change structure size.*`) 5430 } 5431 5432 func (s *mgrsSuite) TestHappyDeviceRegistrationWithPrepareDeviceHook(c *C) { 5433 // just to 404 locally eager account-key requests 5434 mockStoreServer := s.mockStore(c) 5435 defer mockStoreServer.Close() 5436 5437 model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 5438 "gadget": "gadget", 5439 }) 5440 5441 // reset as seeded but not registered 5442 // shortcut: have already device key 5443 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 5444 c.Assert(err, IsNil) 5445 err = kpMgr.Put(deviceKey) 5446 c.Assert(err, IsNil) 5447 5448 st := s.o.State() 5449 st.Lock() 5450 defer st.Unlock() 5451 5452 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 5453 devicestatetest.SetDevice(st, &auth.DeviceState{ 5454 Brand: "my-brand", 5455 Model: "my-model", 5456 KeyID: deviceKey.PublicKey().ID(), 5457 }) 5458 err = assertstate.Add(st, model) 5459 c.Assert(err, IsNil) 5460 5461 signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) { 5462 brandID := headers["brand-id"].(string) 5463 model := headers["model"].(string) 5464 c.Check(brandID, Equals, "my-brand") 5465 c.Check(model, Equals, "my-model") 5466 headers["authority-id"] = brandID 5467 a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "") 5468 return a, nil, err 5469 } 5470 5471 bhv := &devicestatetest.DeviceServiceBehavior{ 5472 ReqID: "REQID-1", 5473 RequestIDURLPath: "/svc/request-id", 5474 SerialURLPath: "/svc/serial", 5475 SignSerial: signSerial, 5476 } 5477 5478 mockServer := devicestatetest.MockDeviceService(c, bhv) 5479 defer mockServer.Close() 5480 5481 pDBhv := &devicestatetest.PrepareDeviceBehavior{ 5482 DeviceSvcURL: mockServer.URL + "/svc/", 5483 Headers: map[string]string{ 5484 "x-extra-header": "extra", 5485 }, 5486 RegBody: map[string]string{ 5487 "mac": "00:00:00:00:ff:00", 5488 }, 5489 ProposedSerial: "12000", 5490 } 5491 5492 r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), pDBhv) 5493 defer r() 5494 5495 // run the whole device registration process 5496 st.Unlock() 5497 err = s.o.Settle(settleTimeout) 5498 st.Lock() 5499 c.Assert(err, IsNil) 5500 5501 var becomeOperational *state.Change 5502 for _, chg := range st.Changes() { 5503 if chg.Kind() == "become-operational" { 5504 becomeOperational = chg 5505 break 5506 } 5507 } 5508 c.Assert(becomeOperational, NotNil) 5509 5510 c.Check(becomeOperational.Status().Ready(), Equals, true) 5511 c.Check(becomeOperational.Err(), IsNil) 5512 5513 device, err := devicestatetest.Device(st) 5514 c.Assert(err, IsNil) 5515 c.Check(device.Brand, Equals, "my-brand") 5516 c.Check(device.Model, Equals, "my-model") 5517 c.Check(device.Serial, Equals, "12000") 5518 5519 a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{ 5520 "brand-id": "my-brand", 5521 "model": "my-model", 5522 "serial": "12000", 5523 }) 5524 c.Assert(err, IsNil) 5525 serial := a.(*asserts.Serial) 5526 5527 var details map[string]interface{} 5528 err = yaml.Unmarshal(serial.Body(), &details) 5529 c.Assert(err, IsNil) 5530 5531 c.Check(details, DeepEquals, map[string]interface{}{ 5532 "mac": "00:00:00:00:ff:00", 5533 }) 5534 5535 c.Check(serial.DeviceKey().ID(), Equals, device.KeyID) 5536 } 5537 5538 func (s *mgrsSuite) TestRemodelReregistration(c *C) { 5539 s.prereqSnapAssertions(c, map[string]interface{}{ 5540 "snap-name": "foo", 5541 }) 5542 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1") 5543 s.serveSnap(snapPath, "1") 5544 5545 // track the creation of new DeviceAndAutContext (for new Store) 5546 newDAC := false 5547 5548 mockServer := s.mockStore(c) 5549 defer mockServer.Close() 5550 5551 st := s.o.State() 5552 st.Lock() 5553 defer st.Unlock() 5554 5555 s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) { 5556 // the DeviceAndAuthContext assumes state is unlocked 5557 st.Unlock() 5558 defer st.Lock() 5559 c.Check(dac, NotNil) 5560 stoID, err := dac.StoreID("") 5561 c.Assert(err, IsNil) 5562 c.Check(stoID, Equals, "my-brand-substore") 5563 newDAC = true 5564 } 5565 5566 model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 5567 "gadget": "gadget", 5568 }) 5569 5570 // setup initial device identity 5571 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 5572 c.Assert(err, IsNil) 5573 err = kpMgr.Put(deviceKey) 5574 c.Assert(err, IsNil) 5575 5576 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 5577 devicestatetest.SetDevice(st, &auth.DeviceState{ 5578 Brand: "my-brand", 5579 Model: "my-model", 5580 KeyID: deviceKey.PublicKey().ID(), 5581 Serial: "orig-serial", 5582 }) 5583 err = assertstate.Add(st, model) 5584 c.Assert(err, IsNil) 5585 5586 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 5587 c.Assert(err, IsNil) 5588 serialHeaders := map[string]interface{}{ 5589 "brand-id": "my-brand", 5590 "model": "my-model", 5591 "serial": "orig-serial", 5592 "device-key": string(encDevKey), 5593 "device-key-sha3-384": deviceKey.PublicKey().ID(), 5594 "timestamp": time.Now().Format(time.RFC3339), 5595 } 5596 serialA, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, serialHeaders, nil, "") 5597 c.Assert(err, IsNil) 5598 serial := serialA.(*asserts.Serial) 5599 err = assertstate.Add(st, serial) 5600 c.Assert(err, IsNil) 5601 5602 signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) { 5603 brandID := headers["brand-id"].(string) 5604 model := headers["model"].(string) 5605 c.Check(brandID, Equals, "my-brand") 5606 c.Check(model, Equals, "other-model") 5607 headers["authority-id"] = brandID 5608 a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "") 5609 return a, nil, err 5610 } 5611 5612 bhv := &devicestatetest.DeviceServiceBehavior{ 5613 ReqID: "REQID-1", 5614 RequestIDURLPath: "/svc/request-id", 5615 SerialURLPath: "/svc/serial", 5616 SignSerial: signSerial, 5617 } 5618 5619 mockDeviceService := devicestatetest.MockDeviceService(c, bhv) 5620 defer mockDeviceService.Close() 5621 5622 r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), nil) 5623 defer r() 5624 5625 // set registration config on gadget 5626 tr := config.NewTransaction(st) 5627 c.Assert(tr.Set("gadget", "device-service.url", mockDeviceService.URL+"/svc/"), IsNil) 5628 c.Assert(tr.Set("gadget", "registration.proposed-serial", "orig-serial"), IsNil) 5629 tr.Commit() 5630 5631 // run the remodel 5632 // create a new model 5633 newModel := s.brands.Model("my-brand", "other-model", modelDefaults, map[string]interface{}{ 5634 "store": "my-brand-substore", 5635 "gadget": "gadget", 5636 "required-snaps": []interface{}{"foo"}, 5637 }) 5638 5639 s.expectedSerial = "orig-serial" 5640 s.expectedStore = "my-brand-substore" 5641 s.sessionMacaroon = "other-store-session" 5642 5643 chg, err := devicestate.Remodel(st, newModel) 5644 c.Assert(err, IsNil) 5645 5646 st.Unlock() 5647 err = s.o.Settle(settleTimeout) 5648 st.Lock() 5649 c.Assert(err, IsNil) 5650 5651 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 5652 5653 device, err := devicestatetest.Device(st) 5654 c.Assert(err, IsNil) 5655 c.Check(device.Brand, Equals, "my-brand") 5656 c.Check(device.Model, Equals, "other-model") 5657 c.Check(device.Serial, Equals, "orig-serial") 5658 5659 a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{ 5660 "brand-id": "my-brand", 5661 "model": "other-model", 5662 "serial": "orig-serial", 5663 }) 5664 c.Assert(err, IsNil) 5665 serial = a.(*asserts.Serial) 5666 5667 c.Check(serial.Body(), HasLen, 0) 5668 c.Check(serial.DeviceKey().ID(), Equals, device.KeyID) 5669 5670 // the new required-snap "foo" is installed 5671 var snapst snapstate.SnapState 5672 err = snapstate.Get(st, "foo", &snapst) 5673 c.Assert(err, IsNil) 5674 5675 // and marked required 5676 c.Check(snapst.Required, Equals, true) 5677 5678 // a new store was made 5679 c.Check(newDAC, Equals, true) 5680 5681 // we have a session with the new store 5682 c.Check(device.SessionMacaroon, Equals, "other-store-session") 5683 } 5684 5685 func (s *mgrsSuite) TestCheckRefreshFailureWithConcurrentRemoveOfConnectedSnap(c *C) { 5686 hookMgr := s.o.HookManager() 5687 c.Assert(hookMgr, NotNil) 5688 5689 // force configure hook failure for some-snap. 5690 hookMgr.RegisterHijack("configure", "some-snap", func(ctx *hookstate.Context) error { 5691 return fmt.Errorf("failing configure hook") 5692 }) 5693 5694 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 5695 s.serveSnap(snapPath, "40") 5696 snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50") 5697 s.serveSnap(snapPath, "50") 5698 5699 mockServer := s.mockStore(c) 5700 defer mockServer.Close() 5701 5702 st := s.o.State() 5703 st.Lock() 5704 defer st.Unlock() 5705 5706 st.Set("conns", map[string]interface{}{ 5707 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false}, 5708 }) 5709 5710 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 5711 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 5712 5713 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 5714 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 5715 5716 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 5717 Active: true, 5718 Sequence: []*snap.SideInfo{si}, 5719 Current: snap.R(1), 5720 SnapType: "app", 5721 }) 5722 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 5723 Active: true, 5724 Sequence: []*snap.SideInfo{oi}, 5725 Current: snap.R(1), 5726 SnapType: "app", 5727 }) 5728 5729 // add snaps to the repo and connect them 5730 repo := s.o.InterfaceManager().Repository() 5731 c.Assert(repo.AddSnap(snapInfo), IsNil) 5732 c.Assert(repo.AddSnap(otherInfo), IsNil) 5733 _, err := repo.Connect(&interfaces.ConnRef{ 5734 PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"}, 5735 SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"}, 5736 }, nil, nil, nil, nil, nil) 5737 c.Assert(err, IsNil) 5738 5739 // refresh all 5740 c.Assert(assertstate.RefreshSnapDeclarations(st, 0), IsNil) 5741 5742 ts, err := snapstate.Update(st, "some-snap", nil, 0, snapstate.Flags{}) 5743 c.Assert(err, IsNil) 5744 chg := st.NewChange("refresh", "...") 5745 chg.AddAll(ts) 5746 5747 // remove other-snap 5748 ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), &snapstate.RemoveFlags{Purge: true}) 5749 c.Assert(err, IsNil) 5750 chg2 := st.NewChange("remove-snap", "...") 5751 chg2.AddAll(ts2) 5752 5753 st.Unlock() 5754 err = s.o.Settle(settleTimeout) 5755 st.Lock() 5756 5757 c.Check(err, IsNil) 5758 5759 // the refresh change has failed due to configure hook error 5760 c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*failing configure hook.*`) 5761 c.Check(chg.Status(), Equals, state.ErrorStatus) 5762 5763 // download-snap is one of the first tasks in the refresh change, check that it was undone 5764 var downloadSnapStatus state.Status 5765 for _, t := range chg.Tasks() { 5766 if t.Kind() == "download-snap" { 5767 downloadSnapStatus = t.Status() 5768 break 5769 } 5770 } 5771 c.Check(downloadSnapStatus, Equals, state.UndoneStatus) 5772 5773 // the remove change succeeded 5774 c.Check(chg2.Err(), IsNil) 5775 c.Check(chg2.Status(), Equals, state.DoneStatus) 5776 } 5777 5778 func (s *mgrsSuite) TestInstallKernelSnapRollbackUpdatesBootloaderEnv(c *C) { 5779 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 5780 bootloader.Force(bloader) 5781 defer bootloader.Force(nil) 5782 5783 restore := release.MockOnClassic(false) 5784 defer restore() 5785 5786 model := s.brands.Model("my-brand", "my-model", modelDefaults) 5787 5788 const packageKernel = ` 5789 name: pc-kernel 5790 version: 4.0-1 5791 type: kernel` 5792 5793 files := [][]string{ 5794 {"kernel.img", "I'm a kernel"}, 5795 {"initrd.img", "...and I'm an initrd"}, 5796 {"meta/kernel.yaml", "version: 4.2"}, 5797 } 5798 snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 5799 5800 st := s.o.State() 5801 st.Lock() 5802 defer st.Unlock() 5803 5804 // pretend we have core18/pc-kernel 5805 bloader.BootVars = map[string]string{ 5806 "snap_core": "core18_2.snap", 5807 "snap_kernel": "pc-kernel_123.snap", 5808 "snap_mode": boot.DefaultStatus, 5809 } 5810 si1 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(123)} 5811 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 5812 SnapType: "kernel", 5813 Active: true, 5814 Sequence: []*snap.SideInfo{si1}, 5815 Current: si1.Revision, 5816 }) 5817 snaptest.MockSnapWithFiles(c, packageKernel, si1, [][]string{ 5818 {"meta/kernel.yaml", ""}, 5819 }) 5820 si2 := &snap.SideInfo{RealName: "core18", Revision: snap.R(2)} 5821 snapstate.Set(st, "core18", &snapstate.SnapState{ 5822 SnapType: "base", 5823 Active: true, 5824 Sequence: []*snap.SideInfo{si2}, 5825 Current: si2.Revision, 5826 }) 5827 5828 // setup model assertion 5829 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 5830 devicestatetest.SetDevice(st, &auth.DeviceState{ 5831 Brand: "my-brand", 5832 Model: "my-model", 5833 Serial: "serialserialserial", 5834 }) 5835 err := assertstate.Add(st, model) 5836 c.Assert(err, IsNil) 5837 5838 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{}) 5839 c.Assert(err, IsNil) 5840 5841 chg := st.NewChange("install-snap", "...") 5842 chg.AddAll(ts) 5843 5844 // run, this will trigger a wait for the restart 5845 st.Unlock() 5846 err = s.o.Settle(settleTimeout) 5847 st.Lock() 5848 c.Assert(err, IsNil) 5849 5850 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 5851 "snap_core": "core18_2.snap", 5852 "snap_try_core": "", 5853 "snap_kernel": "pc-kernel_123.snap", 5854 "snap_try_kernel": "pc-kernel_x1.snap", 5855 "snap_mode": boot.TryStatus, 5856 }) 5857 5858 // we are in restarting state and the change is not done yet 5859 restarting, _ := st.Restarting() 5860 c.Check(restarting, Equals, true) 5861 c.Check(chg.Status(), Equals, state.DoingStatus) 5862 s.mockRollbackAcrossReboot(c, bloader, []snap.Type{snap.TypeKernel}) 5863 5864 // the kernel revision got rolled back 5865 var snapst snapstate.SnapState 5866 snapstate.Get(st, "pc-kernel", &snapst) 5867 info, err := snapst.CurrentInfo() 5868 c.Assert(err, IsNil) 5869 c.Assert(info.Revision, Equals, snap.R(123)) 5870 5871 st.Unlock() 5872 err = s.o.Settle(settleTimeout) 5873 st.Lock() 5874 c.Assert(err, IsNil) 5875 5876 c.Assert(chg.Status(), Equals, state.ErrorStatus) 5877 c.Assert(chg.Err(), ErrorMatches, `(?ms).*cannot finish pc-kernel installation, there was a rollback across reboot\)`) 5878 5879 // and the bootvars are reset 5880 c.Check(bloader.BootVars, DeepEquals, map[string]string{ 5881 "snap_core": "core18_2.snap", 5882 "snap_kernel": "pc-kernel_123.snap", 5883 "snap_mode": boot.DefaultStatus, 5884 "snap_try_core": "", 5885 "snap_try_kernel": "", 5886 }) 5887 } 5888 5889 func (s *mgrsSuite) testUC20RunUpdateManagedBootConfig(c *C, snapPath string, si *snap.SideInfo, bl bootloader.Bootloader, updated bool) { 5890 restore := release.MockOnClassic(false) 5891 defer restore() 5892 5893 // pretend we booted with the right kernel 5894 bl.SetBootVars(map[string]string{"snap_kernel": "pc-kernel_1.snap"}) 5895 5896 uc20ModelDefaults := map[string]interface{}{ 5897 "architecture": "amd64", 5898 "base": "core20", 5899 "store": "my-brand-store-id", 5900 "snaps": []interface{}{ 5901 map[string]interface{}{ 5902 "name": "pc-kernel", 5903 "id": snaptest.AssertedSnapID("pc-kernel"), 5904 "type": "kernel", 5905 "default-channel": "20", 5906 }, 5907 map[string]interface{}{ 5908 "name": "pc", 5909 "id": snaptest.AssertedSnapID("pc"), 5910 "type": "gadget", 5911 "default-channel": "20", 5912 }}, 5913 } 5914 5915 model := s.brands.Model("my-brand", "my-model", uc20ModelDefaults) 5916 5917 // mock the modeenv file 5918 m := boot.Modeenv{ 5919 Mode: "run", 5920 RecoverySystem: "20191127", 5921 Base: "core20_1.snap", 5922 CurrentKernelCommandLines: []string{ 5923 "snapd_recovery_mode=run console=ttyS0 console=tty1 panic=-1", 5924 }, 5925 } 5926 err := m.WriteTo("") 5927 c.Assert(err, IsNil) 5928 5929 st := s.o.State() 5930 st.Lock() 5931 // defer st.Unlock() 5932 st.Set("seeded", true) 5933 5934 si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)} 5935 snapstate.Set(st, "snapd", &snapstate.SnapState{ 5936 SnapType: "snapd", 5937 Active: true, 5938 Sequence: []*snap.SideInfo{si1}, 5939 Current: si1.Revision, 5940 }) 5941 snaptest.MockSnapWithFiles(c, "name: snapd\ntype: snapd\nversion: 123", si1, nil) 5942 5943 si2 := &snap.SideInfo{RealName: "core20", Revision: snap.R(1)} 5944 snapstate.Set(st, "core20", &snapstate.SnapState{ 5945 SnapType: "base", 5946 Active: true, 5947 Sequence: []*snap.SideInfo{si2}, 5948 Current: si2.Revision, 5949 }) 5950 si3 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)} 5951 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 5952 SnapType: "kernel", 5953 Active: true, 5954 Sequence: []*snap.SideInfo{si3}, 5955 Current: si3.Revision, 5956 }) 5957 5958 // setup model assertion 5959 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 5960 devicestatetest.SetDevice(st, &auth.DeviceState{ 5961 Brand: "my-brand", 5962 Model: "my-model", 5963 Serial: "serialserialserial", 5964 }) 5965 err = assertstate.Add(st, model) 5966 c.Assert(err, IsNil) 5967 5968 ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{}) 5969 c.Assert(err, IsNil) 5970 5971 chg := st.NewChange("install-snap", "...") 5972 chg.AddAll(ts) 5973 5974 // run, this will trigger wait for restart with snapd snap (or be done 5975 // with core) 5976 st.Unlock() 5977 err = s.o.Settle(settleTimeout) 5978 st.Lock() 5979 c.Assert(err, IsNil) 5980 5981 if si.RealName == "core" { 5982 // core on UC20 is done at this point 5983 c.Assert(chg.Status(), Equals, state.DoneStatus) 5984 c.Assert(chg.Err(), IsNil) 5985 } else { 5986 // boot config is updated after link-snap, so first comes the 5987 // daemon restart 5988 c.Check(chg.Status(), Equals, state.DoingStatus) 5989 restarting, kind := st.Restarting() 5990 c.Check(restarting, Equals, true) 5991 c.Assert(kind, Equals, state.RestartDaemon) 5992 5993 // simulate successful daemon restart happened 5994 state.MockRestarting(st, state.RestartUnset) 5995 5996 // let the change run its course 5997 st.Unlock() 5998 err = s.o.Settle(settleTimeout) 5999 st.Lock() 6000 c.Assert(err, IsNil) 6001 6002 c.Check(chg.Status(), Equals, state.DoneStatus) 6003 restarting, kind = st.Restarting() 6004 if updated { 6005 // boot config updated, thus a system restart was 6006 // requested 6007 c.Check(restarting, Equals, true) 6008 c.Assert(kind, Equals, state.RestartSystem) 6009 } else { 6010 c.Check(restarting, Equals, false) 6011 } 6012 } 6013 } 6014 6015 func (s *mgrsSuite) TestUC20SnapdUpdatesManagedBootConfig(c *C) { 6016 mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets() 6017 bootloader.Force(mabloader) 6018 defer bootloader.Force(nil) 6019 6020 mabloader.Updated = true 6021 6022 const snapdSnap = ` 6023 name: snapd 6024 version: 1.0 6025 type: snapd` 6026 snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil) 6027 si := &snap.SideInfo{RealName: "snapd"} 6028 6029 const updated = true 6030 s.testUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader, updated) 6031 6032 c.Check(mabloader.UpdateCalls, Equals, 1) 6033 } 6034 6035 func (s *mgrsSuite) TestUC20SnapdUpdateManagedBootNotNeededConfig(c *C) { 6036 mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets() 6037 bootloader.Force(mabloader) 6038 defer bootloader.Force(nil) 6039 6040 // nothing was updated, eg. boot config editions are the same 6041 mabloader.Updated = false 6042 6043 const snapdSnap = ` 6044 name: snapd 6045 version: 1.0 6046 type: snapd` 6047 snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil) 6048 si := &snap.SideInfo{RealName: "snapd"} 6049 6050 const updated = false 6051 s.testUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader, updated) 6052 6053 c.Check(mabloader.UpdateCalls, Equals, 1) 6054 } 6055 6056 func (s *mgrsSuite) TestUC20CoreDoesNotUpdateManagedBootConfig(c *C) { 6057 mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets() 6058 bootloader.Force(mabloader) 6059 defer bootloader.Force(nil) 6060 6061 const coreSnap = ` 6062 name: core 6063 version: 1.0 6064 type: base` 6065 snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnap, nil) 6066 si := &snap.SideInfo{RealName: "core"} 6067 6068 const updated = false 6069 s.testUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader, updated) 6070 c.Check(mabloader.UpdateCalls, Equals, 0) 6071 } 6072 6073 func (s *mgrsSuite) testNonUC20RunUpdateManagedBootConfig(c *C, snapPath string, si *snap.SideInfo, bl bootloader.Bootloader) { 6074 // non UC20 device model 6075 6076 restore := release.MockOnClassic(false) 6077 defer restore() 6078 6079 // pretend we booted with the right kernel & base 6080 bl.SetBootVars(map[string]string{ 6081 "snap_core": "core_1.snap", 6082 "snap_kernel": "pc-kernel_1.snap", 6083 }) 6084 6085 model := s.brands.Model("my-brand", "my-model", modelDefaults) 6086 6087 st := s.o.State() 6088 st.Lock() 6089 // defer st.Unlock() 6090 st.Set("seeded", true) 6091 6092 si1 := &snap.SideInfo{RealName: "snapd", Revision: snap.R(1)} 6093 snapstate.Set(st, "snapd", &snapstate.SnapState{ 6094 SnapType: "snapd", 6095 Active: true, 6096 Sequence: []*snap.SideInfo{si1}, 6097 Current: si1.Revision, 6098 }) 6099 si2 := &snap.SideInfo{RealName: "core", Revision: snap.R(1)} 6100 snapstate.Set(st, "core", &snapstate.SnapState{ 6101 SnapType: "base", 6102 Active: true, 6103 Sequence: []*snap.SideInfo{si2}, 6104 Current: si2.Revision, 6105 }) 6106 si3 := &snap.SideInfo{RealName: "pc-kernel", Revision: snap.R(1)} 6107 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 6108 SnapType: "kernel", 6109 Active: true, 6110 Sequence: []*snap.SideInfo{si3}, 6111 Current: si3.Revision, 6112 }) 6113 6114 // setup model assertion 6115 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 6116 devicestatetest.SetDevice(st, &auth.DeviceState{ 6117 Brand: "my-brand", 6118 Model: "my-model", 6119 Serial: "serialserialserial", 6120 }) 6121 err := assertstate.Add(st, model) 6122 c.Assert(err, IsNil) 6123 6124 ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{}) 6125 c.Assert(err, IsNil) 6126 6127 chg := st.NewChange("install-snap", "...") 6128 chg.AddAll(ts) 6129 6130 // run, this will trigger a wait for the restart 6131 st.Unlock() 6132 err = s.o.Settle(settleTimeout) 6133 st.Lock() 6134 c.Assert(err, IsNil) 6135 6136 c.Check(chg.Status(), Equals, state.DoingStatus) 6137 restarting, _ := st.Restarting() 6138 c.Check(restarting, Equals, true) 6139 6140 // simulate successful restart happened 6141 state.MockRestarting(st, state.RestartUnset) 6142 if si.RealName == "core" { 6143 // pretend we switched to a new core 6144 bl.SetBootVars(map[string]string{ 6145 "snap_core": "core_x1.snap", 6146 "snap_kernel": "pc-kernel_1.snap", 6147 }) 6148 } 6149 6150 st.Unlock() 6151 err = s.o.Settle(settleTimeout) 6152 st.Lock() 6153 c.Assert(err, IsNil) 6154 6155 c.Assert(chg.Status(), Equals, state.DoneStatus) 6156 c.Assert(chg.Err(), IsNil) 6157 } 6158 6159 func (s *mgrsSuite) TestNonUC20DoesNotUpdateManagedBootConfig(c *C) { 6160 mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets() 6161 bootloader.Force(mabloader) 6162 defer bootloader.Force(nil) 6163 6164 const coreSnap = ` 6165 name: core 6166 version: 1.0 6167 type: base` 6168 snapPath := snaptest.MakeTestSnapWithFiles(c, coreSnap, nil) 6169 si := &snap.SideInfo{RealName: "core"} 6170 6171 s.testNonUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader) 6172 c.Check(mabloader.UpdateCalls, Equals, 0) 6173 } 6174 6175 func (s *mgrsSuite) TestNonUC20SnapdNoUpdateNotManagedBootConfig(c *C) { 6176 mabloader := bootloadertest.Mock("mock", c.MkDir()).WithTrustedAssets() 6177 bootloader.Force(mabloader) 6178 defer bootloader.Force(nil) 6179 6180 const snapdSnap = ` 6181 name: snapd 6182 version: 1.0 6183 type: snapd` 6184 snapPath := snaptest.MakeTestSnapWithFiles(c, snapdSnap, nil) 6185 si := &snap.SideInfo{RealName: "snapd"} 6186 6187 s.testNonUC20RunUpdateManagedBootConfig(c, snapPath, si, mabloader) 6188 c.Check(mabloader.UpdateCalls, Equals, 0) 6189 } 6190 6191 type gadgetUpdatesSuite struct { 6192 baseMgrsSuite 6193 6194 bloader *boottest.Bootenv16 6195 } 6196 6197 var _ = Suite(&gadgetUpdatesSuite{}) 6198 6199 func (ms *gadgetUpdatesSuite) SetUpTest(c *C) { 6200 ms.baseMgrsSuite.SetUpTest(c) 6201 6202 bloader := boottest.MockUC16Bootenv(bootloadertest.Mock("mock", c.MkDir())) 6203 bootloader.Force(bloader) 6204 ms.AddCleanup(func() { bootloader.Force(nil) }) 6205 bloader.BootVars = map[string]string{ 6206 "snap_core": "core18_2.snap", 6207 "snap_kernel": "pc-kernel_1.snap", 6208 "snap_mode": boot.DefaultStatus, 6209 } 6210 ms.bloader = bloader 6211 6212 restore := release.MockOnClassic(false) 6213 ms.AddCleanup(restore) 6214 6215 mockServer := ms.mockStore(c) 6216 ms.AddCleanup(mockServer.Close) 6217 6218 st := ms.o.State() 6219 st.Lock() 6220 defer st.Unlock() 6221 6222 // setup model assertion 6223 model := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 6224 "gadget": "pi", 6225 "kernel": "pi-kernel", 6226 }) 6227 devicestatetest.SetDevice(st, &auth.DeviceState{ 6228 Brand: "can0nical", 6229 Model: "my-model", 6230 Serial: "serialserial", 6231 }) 6232 err := assertstate.Add(st, model) 6233 c.Assert(err, IsNil) 6234 } 6235 6236 // makeMockDev mocks /dev/disk/by-label/{structureName} and the mount 6237 // point /run/mnt/{structureName} under the test rootdir and for 6238 // osutil.LoadMountInfo for use by gadget code for test gadgets using 6239 // structureName. This is useful for e.g. end-to-end testing of gadget 6240 // assets installs/updates. 6241 func (ms *gadgetUpdatesSuite) makeMockedDev(c *C, structureName string) { 6242 // mock /dev/disk/by-label/{structureName} 6243 byLabelDir := filepath.Join(dirs.GlobalRootDir, "/dev/disk/by-label/") 6244 err := os.MkdirAll(byLabelDir, 0755) 6245 c.Assert(err, IsNil) 6246 // create fakedevice node 6247 err = ioutil.WriteFile(filepath.Join(dirs.GlobalRootDir, "/dev/fakedevice0p1"), nil, 0644) 6248 c.Assert(err, IsNil) 6249 // and point the mocked by-label entry to the fakedevice node 6250 err = os.Symlink(filepath.Join(dirs.GlobalRootDir, "/dev/fakedevice0p1"), filepath.Join(byLabelDir, structureName)) 6251 c.Assert(err, IsNil) 6252 6253 // mock /proc/self/mountinfo with the above generated paths 6254 ms.AddCleanup(osutil.MockMountInfo(fmt.Sprintf("26 27 8:3 / %[1]s/run/mnt/%[2]s rw,relatime shared:7 - vfat %[1]s/dev/fakedevice0p1 rw", dirs.GlobalRootDir, structureName))) 6255 6256 // and mock the mount point 6257 err = os.MkdirAll(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName), 0755) 6258 c.Assert(err, IsNil) 6259 6260 } 6261 6262 // tsWithoutReRefresh removes the re-refresh task from the given taskset. 6263 // 6264 // It assumes that re-refresh is the last task and will fail if that is 6265 // not the case. 6266 // 6267 // This is needed because settle() will not converge with the re-refresh 6268 // task because re-refresh will always be in doing state. 6269 // 6270 // TODO: have variant of Settle() that ends if ensure next time is 6271 // stable or in the future by a value larger than some threshold, and 6272 // then we would mock the rerefresh interval to something large and 6273 // distinct from practical wait time even on slow systems. Once that 6274 // is done this function can be removed. 6275 func tsWithoutReRefresh(c *C, ts *state.TaskSet) *state.TaskSet { 6276 refreshIdx := len(ts.Tasks()) - 1 6277 c.Assert(ts.Tasks()[refreshIdx].Kind(), Equals, "check-rerefresh") 6278 ts = state.NewTaskSet(ts.Tasks()[:refreshIdx-1]...) 6279 return ts 6280 } 6281 6282 func (ms *gadgetUpdatesSuite) TestRefreshGadgetUpdates(c *C) { 6283 structureName := "ubuntu-seed" 6284 gadgetYaml := fmt.Sprintf(` 6285 volumes: 6286 volume-id: 6287 schema: mbr 6288 bootloader: u-boot 6289 structure: 6290 - name: %s 6291 filesystem: vfat 6292 type: 0C 6293 size: 1200M 6294 content: 6295 - source: boot-assets/ 6296 target: / 6297 - source: foo.img 6298 target: /subdir/foo-renamed.img`, structureName) 6299 newGadgetYaml := gadgetYaml + ` 6300 update: 6301 edition: 2 6302 ` 6303 ms.makeMockedDev(c, structureName) 6304 6305 st := ms.o.State() 6306 st.Lock() 6307 defer st.Unlock() 6308 6309 // we have an installed gadget 6310 si := &snap.SideInfo{RealName: "pi", SnapID: fakeSnapID("pi"), Revision: snap.R(1)} 6311 gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget" 6312 snapstate.Set(st, "pi", &snapstate.SnapState{ 6313 Active: true, 6314 Sequence: []*snap.SideInfo{si}, 6315 Current: snap.R(1), 6316 SnapType: "gadget", 6317 }) 6318 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si, [][]string{ 6319 {"meta/gadget.yaml", gadgetYaml}, 6320 }) 6321 6322 // add new gadget snap to fake store 6323 ms.prereqSnapAssertions(c, map[string]interface{}{ 6324 "snap-name": "pi", 6325 "publisher-id": "can0nical", 6326 "revision": "2", 6327 }) 6328 snapPath, _ := ms.makeStoreTestSnapWithFiles(c, gadgetSnapYaml, "2", [][]string{ 6329 {"meta/gadget.yaml", newGadgetYaml}, 6330 {"boot-assets/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2"}, 6331 {"boot-assets/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2"}, 6332 {"boot-assets/overlays/uart0.dtbo", "uart0.dtbo rev2"}, 6333 {"foo.img", "foo rev2"}, 6334 }) 6335 ms.serveSnap(snapPath, "2") 6336 6337 ts, err := snapstate.Update(st, "pi", nil, 0, snapstate.Flags{}) 6338 c.Assert(err, IsNil) 6339 // remove the re-refresh as it will prevent settle from converging 6340 ts = tsWithoutReRefresh(c, ts) 6341 6342 chg := st.NewChange("upgrade-gadget", "...") 6343 chg.AddAll(ts) 6344 6345 st.Unlock() 6346 err = ms.o.Settle(settleTimeout) 6347 st.Lock() 6348 c.Assert(err, IsNil) 6349 6350 // pretend we restarted 6351 t := findKind(chg, "auto-connect") 6352 c.Assert(t, NotNil) 6353 c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 6354 // simulate successful restart happened 6355 state.MockRestarting(st, state.RestartUnset) 6356 6357 // settle again 6358 st.Unlock() 6359 err = ms.o.Settle(settleTimeout) 6360 st.Lock() 6361 c.Assert(err, IsNil) 6362 6363 c.Assert(chg.Err(), IsNil) 6364 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 6365 6366 // check that files/dirs got updated and subdirs are correct 6367 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "subdir/foo-renamed.img"), testutil.FileContains, "foo rev2") 6368 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2") 6369 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2") 6370 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2") 6371 } 6372 6373 func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefKernelRefresh(c *C) { 6374 kernelYaml := ` 6375 assets: 6376 pidtbs: 6377 update: true 6378 content: 6379 - dtbs/broadcom/ 6380 - dtbs/overlays/` 6381 6382 structureName := "ubuntu-seed" 6383 gadgetYaml := fmt.Sprintf(` 6384 volumes: 6385 volume-id: 6386 schema: mbr 6387 bootloader: u-boot 6388 structure: 6389 - name: %s 6390 filesystem: vfat 6391 type: 0C 6392 size: 1200M 6393 content: 6394 - source: boot-assets/ 6395 target: / 6396 - source: $kernel:pidtbs/dtbs/broadcom/ 6397 target: / 6398 - source: $kernel:pidtbs/dtbs/overlays/ 6399 target: /overlays`, structureName) 6400 ms.makeMockedDev(c, structureName) 6401 6402 st := ms.o.State() 6403 st.Lock() 6404 defer st.Unlock() 6405 6406 // we have an installed gadget with kernel refs 6407 si := &snap.SideInfo{RealName: "pi", SnapID: fakeSnapID("pi"), Revision: snap.R(1)} 6408 gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget" 6409 snapstate.Set(st, "pi", &snapstate.SnapState{ 6410 Active: true, 6411 Sequence: []*snap.SideInfo{si}, 6412 Current: snap.R(1), 6413 SnapType: "gadget", 6414 }) 6415 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si, [][]string{ 6416 {"meta/gadget.yaml", gadgetYaml}, 6417 {"boot-assets/start.elf", "start.elf rev1"}, 6418 }) 6419 // we have an installed kernel with kernel.yaml 6420 si2 := &snap.SideInfo{RealName: "pi-kernel", SnapID: fakeSnapID("pi-kernel"), Revision: snap.R(1)} 6421 kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel" 6422 snapstate.Set(st, "pi-kernel", &snapstate.SnapState{ 6423 Active: true, 6424 Sequence: []*snap.SideInfo{si2}, 6425 Current: snap.R(1), 6426 SnapType: "kernel", 6427 }) 6428 snaptest.MockSnapWithFiles(c, kernelSnapYaml, si2, [][]string{ 6429 {"meta/kernel.yaml", kernelYaml}, 6430 }) 6431 6432 // add new kernel snap to fake store 6433 ms.prereqSnapAssertions(c, map[string]interface{}{ 6434 "snap-name": "pi-kernel", 6435 "publisher-id": "can0nical", 6436 "revision": "2", 6437 }) 6438 snapPath, _ := ms.makeStoreTestSnapWithFiles(c, kernelSnapYaml, "2", [][]string{ 6439 {"meta/kernel.yaml", kernelYaml}, 6440 {"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2"}, 6441 {"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2"}, 6442 {"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2"}, 6443 }) 6444 ms.serveSnap(snapPath, "2") 6445 6446 ts, err := snapstate.Update(st, "pi-kernel", nil, 0, snapstate.Flags{}) 6447 c.Assert(err, IsNil) 6448 // remove the re-refresh as it will prevent settle from converging 6449 ts = tsWithoutReRefresh(c, ts) 6450 6451 chg := st.NewChange("upgrade-kernel", "...") 6452 chg.AddAll(ts) 6453 6454 st.Unlock() 6455 err = ms.o.Settle(settleTimeout) 6456 st.Lock() 6457 c.Assert(err, IsNil) 6458 c.Assert(chg.Err(), IsNil) 6459 6460 // pretend we restarted 6461 t := findKind(chg, "auto-connect") 6462 c.Assert(t, NotNil) 6463 c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 6464 // pretend we restarted 6465 ms.mockSuccessfulReboot(c, ms.bloader, []snap.Type{snap.TypeKernel}) 6466 6467 // settle again 6468 st.Unlock() 6469 err = ms.o.Settle(settleTimeout) 6470 st.Lock() 6471 c.Assert(err, IsNil) 6472 c.Assert(chg.Err(), IsNil) 6473 6474 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 6475 6476 // check that files/dirs got updated and subdirs are correct 6477 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2") 6478 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2") 6479 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2") 6480 // BUT the gadget content is ignored and not copied again 6481 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileAbsent) 6482 } 6483 6484 func (ms *gadgetUpdatesSuite) TestGadgetWithKernelRefGadgetRefresh(c *C) { 6485 kernelYaml := ` 6486 assets: 6487 pidtbs: 6488 update: true 6489 content: 6490 - dtbs/broadcom/ 6491 - dtbs/overlays/` 6492 6493 structureName := "ubuntu-seed" 6494 gadgetYaml := fmt.Sprintf(` 6495 volumes: 6496 volume-id: 6497 schema: mbr 6498 bootloader: u-boot 6499 structure: 6500 - name: %s 6501 filesystem: vfat 6502 type: 0C 6503 size: 1200M 6504 content: 6505 - source: boot-assets/ 6506 target: / 6507 - source: $kernel:pidtbs/dtbs/broadcom/ 6508 target: / 6509 - source: $kernel:pidtbs/dtbs/overlays/ 6510 target: /overlays`, structureName) 6511 newGadgetYaml := gadgetYaml + ` 6512 update: 6513 edition: 2 6514 ` 6515 ms.makeMockedDev(c, structureName) 6516 6517 st := ms.o.State() 6518 st.Lock() 6519 defer st.Unlock() 6520 6521 // we have an installed gadget with kernel refs 6522 si := &snap.SideInfo{RealName: "pi", SnapID: fakeSnapID("pi"), Revision: snap.R(1)} 6523 gadgetSnapYaml := "name: pi\nversion: 1.0\ntype: gadget" 6524 snapstate.Set(st, "pi", &snapstate.SnapState{ 6525 Active: true, 6526 Sequence: []*snap.SideInfo{si}, 6527 Current: snap.R(1), 6528 SnapType: "gadget", 6529 }) 6530 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si, [][]string{ 6531 {"meta/gadget.yaml", gadgetYaml}, 6532 }) 6533 // we have an installed kernel with kernel.yaml 6534 si2 := &snap.SideInfo{RealName: "pi-kernel", SnapID: fakeSnapID("pi-kernel"), Revision: snap.R(1)} 6535 kernelSnapYaml := "name: pi-kernel\nversion: 1.0\ntype: kernel" 6536 snapstate.Set(st, "pi-kernel", &snapstate.SnapState{ 6537 Active: true, 6538 Sequence: []*snap.SideInfo{si2}, 6539 Current: snap.R(1), 6540 SnapType: "kernel", 6541 }) 6542 snaptest.MockSnapWithFiles(c, kernelSnapYaml, si2, [][]string{ 6543 {"meta/kernel.yaml", kernelYaml}, 6544 {"dtbs/broadcom/bcm2710-rpi-2-b.dtb", "bcm2710-rpi-2-b.dtb rev2"}, 6545 {"dtbs/broadcom/bcm2710-rpi-3-b.dtb", "bcm2710-rpi-3-b.dtb rev2"}, 6546 {"dtbs/overlays/uart0.dtbo", "uart0.dtbo rev2"}, 6547 }) 6548 6549 // add new gadget snap to fake store that has an "update: true" 6550 // for the kernel ref structure 6551 ms.prereqSnapAssertions(c, map[string]interface{}{ 6552 "snap-name": "pi", 6553 "publisher-id": "can0nical", 6554 "revision": "2", 6555 }) 6556 snapPath, _ := ms.makeStoreTestSnapWithFiles(c, gadgetSnapYaml, "2", [][]string{ 6557 {"meta/gadget.yaml", newGadgetYaml}, 6558 {"boot-assets/start.elf", "start.elf rev2"}, 6559 }) 6560 ms.serveSnap(snapPath, "2") 6561 6562 ts, err := snapstate.Update(st, "pi", nil, 0, snapstate.Flags{}) 6563 c.Assert(err, IsNil) 6564 // remove the re-refresh as it will prevent settle from converging 6565 ts = tsWithoutReRefresh(c, ts) 6566 6567 chg := st.NewChange("upgrade-gadget", "...") 6568 chg.AddAll(ts) 6569 6570 st.Unlock() 6571 err = ms.o.Settle(settleTimeout) 6572 st.Lock() 6573 c.Assert(err, IsNil) 6574 c.Assert(chg.Err(), IsNil) 6575 6576 // pretend we restarted 6577 t := findKind(chg, "auto-connect") 6578 c.Assert(t, NotNil) 6579 c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 6580 // simulate successful restart happened after gadget update 6581 state.MockRestarting(st, state.RestartUnset) 6582 6583 // settle again 6584 st.Unlock() 6585 err = ms.o.Settle(settleTimeout) 6586 st.Lock() 6587 c.Assert(err, IsNil) 6588 c.Assert(chg.Err(), IsNil) 6589 6590 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 6591 6592 // check that files/dirs got updated and subdirs are correct 6593 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-2-b.dtb"), testutil.FileContains, "bcm2710-rpi-2-b.dtb rev2") 6594 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "bcm2710-rpi-3-b.dtb"), testutil.FileContains, "bcm2710-rpi-3-b.dtb rev2") 6595 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "overlays/uart0.dtbo"), testutil.FileContains, "uart0.dtbo rev2") 6596 c.Check(filepath.Join(dirs.GlobalRootDir, "/run/mnt/", structureName, "start.elf"), testutil.FileContains, "start.elf rev2") 6597 }