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