github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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 "fmt" 29 "io" 30 "io/ioutil" 31 "net/http" 32 "net/http/httptest" 33 "net/url" 34 "os" 35 "path/filepath" 36 "sort" 37 "strings" 38 "time" 39 40 . "gopkg.in/check.v1" 41 "gopkg.in/yaml.v2" 42 43 "github.com/snapcore/snapd/asserts" 44 "github.com/snapcore/snapd/asserts/assertstest" 45 "github.com/snapcore/snapd/asserts/sysdb" 46 "github.com/snapcore/snapd/bootloader" 47 "github.com/snapcore/snapd/bootloader/bootloadertest" 48 "github.com/snapcore/snapd/client" 49 "github.com/snapcore/snapd/dirs" 50 "github.com/snapcore/snapd/interfaces" 51 "github.com/snapcore/snapd/osutil" 52 "github.com/snapcore/snapd/overlord" 53 "github.com/snapcore/snapd/overlord/assertstate" 54 "github.com/snapcore/snapd/overlord/assertstate/assertstatetest" 55 "github.com/snapcore/snapd/overlord/auth" 56 "github.com/snapcore/snapd/overlord/configstate/config" 57 "github.com/snapcore/snapd/overlord/devicestate" 58 "github.com/snapcore/snapd/overlord/devicestate/devicestatetest" 59 "github.com/snapcore/snapd/overlord/hookstate" 60 "github.com/snapcore/snapd/overlord/ifacestate" 61 "github.com/snapcore/snapd/overlord/snapshotstate" 62 snapshotbackend "github.com/snapcore/snapd/overlord/snapshotstate/backend" 63 "github.com/snapcore/snapd/overlord/snapstate" 64 "github.com/snapcore/snapd/overlord/state" 65 "github.com/snapcore/snapd/release" 66 "github.com/snapcore/snapd/snap" 67 "github.com/snapcore/snapd/snap/snaptest" 68 "github.com/snapcore/snapd/store" 69 "github.com/snapcore/snapd/systemd" 70 "github.com/snapcore/snapd/testutil" 71 ) 72 73 type automaticSnapshotCall struct { 74 InstanceName string 75 SnapConfig map[string]interface{} 76 Usernames []string 77 Flags *snapshotbackend.Flags 78 } 79 80 type mgrsSuite struct { 81 testutil.BaseTest 82 83 tempdir string 84 85 storeSigning *assertstest.StoreStack 86 brands *assertstest.SigningAccounts 87 88 devAcct *asserts.Account 89 90 serveIDtoName map[string]string 91 serveSnapPath map[string]string 92 serveRevision map[string]string 93 serveOldPaths map[string][]string 94 serveOldRevs map[string][]string 95 96 hijackServeSnap func(http.ResponseWriter) 97 98 checkDeviceAndAuthContext func(store.DeviceAndAuthContext) 99 expectedSerial string 100 expectedStore string 101 sessionMacaroon string 102 103 o *overlord.Overlord 104 105 failNextDownload string 106 107 automaticSnapshots []automaticSnapshotCall 108 } 109 110 var ( 111 _ = Suite(&mgrsSuite{}) 112 _ = Suite(&storeCtxSetupSuite{}) 113 ) 114 115 var ( 116 brandPrivKey, _ = assertstest.GenerateKey(752) 117 118 develPrivKey, _ = assertstest.GenerateKey(752) 119 120 deviceKey, _ = assertstest.GenerateKey(752) 121 ) 122 123 const ( 124 aggressiveSettleTimeout = 50 * time.Millisecond 125 connectRetryTimeout = 70 * time.Millisecond 126 ) 127 128 func verifyLastTasksetIsRerefresh(c *C, tts []*state.TaskSet) { 129 ts := tts[len(tts)-1] 130 c.Assert(ts.Tasks(), HasLen, 1) 131 c.Check(ts.Tasks()[0].Kind(), Equals, "check-rerefresh") 132 } 133 134 func (s *mgrsSuite) SetUpTest(c *C) { 135 s.BaseTest.SetUpTest(c) 136 137 s.tempdir = c.MkDir() 138 dirs.SetRootDir(s.tempdir) 139 s.AddCleanup(func() { dirs.SetRootDir("") }) 140 141 err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) 142 c.Assert(err, IsNil) 143 144 // needed by hooks 145 s.AddCleanup(testutil.MockCommand(c, "snap", "").Restore) 146 147 oldSetupInstallHook := snapstate.SetupInstallHook 148 oldSetupRemoveHook := snapstate.SetupRemoveHook 149 snapstate.SetupRemoveHook = hookstate.SetupRemoveHook 150 snapstate.SetupInstallHook = hookstate.SetupInstallHook 151 s.AddCleanup(func() { 152 snapstate.SetupRemoveHook = oldSetupRemoveHook 153 snapstate.SetupInstallHook = oldSetupInstallHook 154 }) 155 156 s.automaticSnapshots = nil 157 r := snapshotstate.MockBackendSave(func(_ context.Context, id uint64, si *snap.Info, cfg map[string]interface{}, usernames []string, flags *snapshotbackend.Flags) (*client.Snapshot, error) { 158 s.automaticSnapshots = append(s.automaticSnapshots, automaticSnapshotCall{InstanceName: si.InstanceName(), SnapConfig: cfg, Usernames: usernames, Flags: flags}) 159 return nil, nil 160 }) 161 s.AddCleanup(r) 162 163 s.AddCleanup(ifacestate.MockConnectRetryTimeout(connectRetryTimeout)) 164 165 os.Setenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS", "1") 166 s.AddCleanup(func() { os.Unsetenv("SNAPPY_SQUASHFS_UNPACK_FOR_TESTS") }) 167 168 // create a fake systemd environment 169 os.MkdirAll(filepath.Join(dirs.SnapServicesDir, "multi-user.target.wants"), 0755) 170 171 r = systemd.MockSystemctl(func(cmd ...string) ([]byte, error) { 172 return []byte("ActiveState=inactive\n"), nil 173 }) 174 s.AddCleanup(r) 175 176 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 177 s.brands = assertstest.NewSigningAccounts(s.storeSigning) 178 s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 179 "validation": "verified", 180 }) 181 s.AddCleanup(sysdb.InjectTrusted(s.storeSigning.Trusted)) 182 183 s.devAcct = assertstest.NewAccount(s.storeSigning, "devdevdev", map[string]interface{}{ 184 "account-id": "devdevdev", 185 }, "") 186 err = s.storeSigning.Add(s.devAcct) 187 c.Assert(err, IsNil) 188 189 s.serveIDtoName = make(map[string]string) 190 s.serveSnapPath = make(map[string]string) 191 s.serveRevision = make(map[string]string) 192 s.serveOldPaths = make(map[string][]string) 193 s.serveOldRevs = make(map[string][]string) 194 s.hijackServeSnap = nil 195 196 s.checkDeviceAndAuthContext = nil 197 s.expectedSerial = "" 198 s.expectedStore = "" 199 s.sessionMacaroon = "" 200 201 s.AddCleanup(ifacestate.MockSecurityBackends(nil)) 202 203 o, err := overlord.New(nil) 204 c.Assert(err, IsNil) 205 err = o.StartUp() 206 c.Assert(err, IsNil) 207 o.InterfaceManager().DisableUDevMonitor() 208 s.o = o 209 st := s.o.State() 210 st.Lock() 211 defer st.Unlock() 212 st.Set("seeded", true) 213 // registered 214 err = assertstate.Add(st, sysdb.GenericClassicModel()) 215 c.Assert(err, IsNil) 216 devicestatetest.SetDevice(st, &auth.DeviceState{ 217 Brand: "generic", 218 Model: "generic-classic", 219 Serial: "serialserial", 220 }) 221 222 // add "core" snap declaration 223 headers := map[string]interface{}{ 224 "series": "16", 225 "snap-name": "core", 226 "publisher-id": "can0nical", 227 "timestamp": time.Now().Format(time.RFC3339), 228 } 229 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 230 err = assertstate.Add(st, s.storeSigning.StoreAccountKey("")) 231 c.Assert(err, IsNil) 232 a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 233 c.Assert(err, IsNil) 234 err = assertstate.Add(st, a) 235 c.Assert(err, IsNil) 236 s.serveRevision["core"] = "1" 237 s.serveIDtoName[fakeSnapID("core")] = "core" 238 err = s.storeSigning.Add(a) 239 c.Assert(err, IsNil) 240 241 // add "snap1" snap declaration 242 headers = map[string]interface{}{ 243 "series": "16", 244 "snap-name": "snap1", 245 "publisher-id": "can0nical", 246 "timestamp": time.Now().Format(time.RFC3339), 247 } 248 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 249 a2, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 250 c.Assert(err, IsNil) 251 c.Assert(assertstate.Add(st, a2), IsNil) 252 c.Assert(s.storeSigning.Add(a2), IsNil) 253 254 // add "snap2" snap declaration 255 headers = map[string]interface{}{ 256 "series": "16", 257 "snap-name": "snap2", 258 "publisher-id": "can0nical", 259 "timestamp": time.Now().Format(time.RFC3339), 260 } 261 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 262 a3, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 263 c.Assert(err, IsNil) 264 c.Assert(assertstate.Add(st, a3), IsNil) 265 c.Assert(s.storeSigning.Add(a3), IsNil) 266 267 // add "some-snap" snap declaration 268 headers = map[string]interface{}{ 269 "series": "16", 270 "snap-name": "some-snap", 271 "publisher-id": "can0nical", 272 "timestamp": time.Now().Format(time.RFC3339), 273 } 274 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 275 a4, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 276 c.Assert(err, IsNil) 277 c.Assert(assertstate.Add(st, a4), IsNil) 278 c.Assert(s.storeSigning.Add(a4), IsNil) 279 280 // add "other-snap" snap declaration 281 headers = map[string]interface{}{ 282 "series": "16", 283 "snap-name": "other-snap", 284 "publisher-id": "can0nical", 285 "timestamp": time.Now().Format(time.RFC3339), 286 } 287 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 288 a5, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 289 c.Assert(err, IsNil) 290 c.Assert(assertstate.Add(st, a5), IsNil) 291 c.Assert(s.storeSigning.Add(a5), IsNil) 292 293 // add pc-kernel snap declaration 294 headers = map[string]interface{}{ 295 "series": "16", 296 "snap-name": "pc-kernel", 297 "publisher-id": "can0nical", 298 "timestamp": time.Now().Format(time.RFC3339), 299 } 300 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 301 a6, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 302 c.Assert(err, IsNil) 303 c.Assert(assertstate.Add(st, a6), IsNil) 304 c.Assert(s.storeSigning.Add(a6), IsNil) 305 306 // add core itself 307 snapstate.Set(st, "core", &snapstate.SnapState{ 308 Active: true, 309 Sequence: []*snap.SideInfo{ 310 {RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)}, 311 }, 312 Current: snap.R(1), 313 SnapType: "os", 314 Flags: snapstate.Flags{ 315 Required: true, 316 }, 317 }) 318 // don't actually try to talk to the store on snapstate.Ensure 319 // needs doing after the call to devicestate.Manager (which happens in overlord.New) 320 snapstate.CanAutoRefresh = nil 321 322 st.Set("refresh-privacy-key", "privacy-key") 323 } 324 325 var settleTimeout = 15 * time.Second 326 327 func makeTestSnap(c *C, snapYamlContent string) string { 328 info, err := snap.InfoFromSnapYaml([]byte(snapYamlContent)) 329 c.Assert(err, IsNil) 330 331 var files [][]string 332 for _, app := range info.Apps { 333 // files is a list of (filename, content) 334 files = append(files, []string{app.Command, ""}) 335 } 336 337 return snaptest.MakeTestSnapWithFiles(c, snapYamlContent, files) 338 } 339 340 func (s *mgrsSuite) TestHappyLocalInstall(c *C) { 341 snapYamlContent := `name: foo 342 apps: 343 bar: 344 command: bin/bar 345 ` 346 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.0") 347 348 st := s.o.State() 349 st.Lock() 350 defer st.Unlock() 351 352 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "foo"}, snapPath, "", "", snapstate.Flags{DevMode: true}) 353 c.Assert(err, IsNil) 354 chg := st.NewChange("install-snap", "...") 355 chg.AddAll(ts) 356 357 st.Unlock() 358 err = s.o.Settle(settleTimeout) 359 st.Lock() 360 c.Assert(err, IsNil) 361 362 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 363 364 snap, err := snapstate.CurrentInfo(st, "foo") 365 c.Assert(err, IsNil) 366 367 // ensure that the binary wrapper file got generated with the right 368 // name 369 binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar") 370 c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true) 371 372 // data dirs 373 c.Assert(osutil.IsDirectory(snap.DataDir()), Equals, true) 374 c.Assert(osutil.IsDirectory(snap.CommonDataDir()), Equals, true) 375 376 // snap file and its mounting 377 378 // after install the snap file is in the right dir 379 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, true) 380 381 // ensure the right unit is created 382 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1")) 383 c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/x1", dirs.StripRootDir(dirs.SnapMountDir))) 384 c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_x1.snap") 385 } 386 387 func (s *mgrsSuite) TestHappyRemove(c *C) { 388 st := s.o.State() 389 st.Lock() 390 defer st.Unlock() 391 392 snapYamlContent := `name: foo 393 apps: 394 bar: 395 command: bin/bar 396 ` 397 snapInfo := s.installLocalTestSnap(c, snapYamlContent+"version: 1.0") 398 399 // set config 400 tr := config.NewTransaction(st) 401 c.Assert(tr.Set("foo", "key", "value"), IsNil) 402 tr.Commit() 403 404 ts, err := snapstate.Remove(st, "foo", snap.R(0), nil) 405 c.Assert(err, IsNil) 406 chg := st.NewChange("remove-snap", "...") 407 chg.AddAll(ts) 408 409 st.Unlock() 410 err = s.o.Settle(settleTimeout) 411 st.Lock() 412 c.Assert(err, IsNil) 413 414 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err())) 415 416 // ensure that the binary wrapper file got removed 417 binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar") 418 c.Assert(osutil.FileExists(binaryWrapper), Equals, false) 419 420 // data dirs 421 c.Assert(osutil.FileExists(snapInfo.DataDir()), Equals, false) 422 c.Assert(osutil.FileExists(snapInfo.CommonDataDir()), Equals, false) 423 424 // snap file and its mount 425 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_x1.snap")), Equals, false) 426 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/x1")) 427 c.Assert(osutil.FileExists(mup), Equals, false) 428 429 // automatic snapshot was created 430 c.Assert(s.automaticSnapshots, DeepEquals, []automaticSnapshotCall{{"foo", map[string]interface{}{"key": "value"}, nil, &snapshotbackend.Flags{Auto: true}}}) 431 } 432 433 func fakeSnapID(name string) string { 434 const suffix = "idididididididididididididididid" 435 return name + suffix[len(name)+1:] 436 } 437 438 const ( 439 snapV2 = `{ 440 "architectures": [ 441 "all" 442 ], 443 "download": { 444 "url": "@URL@" 445 }, 446 "epoch": @EPOCH@, 447 "type": "@TYPE@", 448 "name": "@NAME@", 449 "revision": @REVISION@, 450 "snap-id": "@SNAPID@", 451 "summary": "Foo", 452 "description": "this is a description", 453 "version": "@VERSION@", 454 "publisher": { 455 "id": "devdevdev", 456 "name": "bar" 457 }, 458 "media": [ 459 {"type": "icon", "url": "@ICON@"} 460 ] 461 }` 462 ) 463 464 var fooSnapID = fakeSnapID("foo") 465 466 func (s *mgrsSuite) prereqSnapAssertions(c *C, extraHeaders ...map[string]interface{}) *asserts.SnapDeclaration { 467 if len(extraHeaders) == 0 { 468 extraHeaders = []map[string]interface{}{{}} 469 } 470 var snapDecl *asserts.SnapDeclaration 471 for _, extraHeaders := range extraHeaders { 472 headers := map[string]interface{}{ 473 "series": "16", 474 "snap-name": "foo", 475 "publisher-id": "devdevdev", 476 "timestamp": time.Now().Format(time.RFC3339), 477 } 478 for h, v := range extraHeaders { 479 headers[h] = v 480 } 481 headers["snap-id"] = fakeSnapID(headers["snap-name"].(string)) 482 a, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 483 c.Assert(err, IsNil) 484 err = s.storeSigning.Add(a) 485 c.Assert(err, IsNil) 486 snapDecl = a.(*asserts.SnapDeclaration) 487 } 488 return snapDecl 489 } 490 491 func (s *mgrsSuite) makeStoreTestSnap(c *C, snapYaml string, revno string) (path, digest string) { 492 info, err := snap.InfoFromSnapYaml([]byte(snapYaml)) 493 c.Assert(err, IsNil) 494 495 snapPath := makeTestSnap(c, snapYaml) 496 497 snapDigest, size, err := asserts.SnapFileSHA3_384(snapPath) 498 c.Assert(err, IsNil) 499 500 headers := map[string]interface{}{ 501 "snap-id": fakeSnapID(info.SnapName()), 502 "snap-sha3-384": snapDigest, 503 "snap-size": fmt.Sprintf("%d", size), 504 "snap-revision": revno, 505 "developer-id": "devdevdev", 506 "timestamp": time.Now().Format(time.RFC3339), 507 } 508 snapRev, err := s.storeSigning.Sign(asserts.SnapRevisionType, headers, nil, "") 509 c.Assert(err, IsNil) 510 err = s.storeSigning.Add(snapRev) 511 c.Assert(err, IsNil) 512 513 return snapPath, snapDigest 514 } 515 516 func (s *mgrsSuite) pathFor(name, revno string) string { 517 if revno == s.serveRevision[name] { 518 return s.serveSnapPath[name] 519 } 520 for i, r := range s.serveOldRevs[name] { 521 if r == revno { 522 return s.serveOldPaths[name][i] 523 } 524 } 525 return "/not/found" 526 } 527 528 func (s *mgrsSuite) newestThatCanRead(name string, epoch snap.Epoch) (info *snap.Info, rev string) { 529 if s.serveSnapPath[name] == "" { 530 return nil, "" 531 } 532 idx := len(s.serveOldPaths[name]) 533 rev = s.serveRevision[name] 534 path := s.serveSnapPath[name] 535 for { 536 snapf, err := snap.Open(path) 537 if err != nil { 538 panic(err) 539 } 540 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 541 if err != nil { 542 panic(err) 543 } 544 if info.Epoch.CanRead(epoch) { 545 return info, rev 546 } 547 idx-- 548 if idx < 0 { 549 return nil, "" 550 } 551 path = s.serveOldPaths[name][idx] 552 rev = s.serveOldRevs[name][idx] 553 } 554 } 555 556 func (s *mgrsSuite) mockStore(c *C) *httptest.Server { 557 var baseURL *url.URL 558 fillHit := func(hitTemplate, revno string, info *snap.Info) string { 559 epochBuf, err := json.Marshal(info.Epoch) 560 if err != nil { 561 panic(err) 562 } 563 name := info.SnapName() 564 565 hit := strings.Replace(hitTemplate, "@URL@", baseURL.String()+"/api/v1/snaps/download/"+name+"/"+revno, -1) 566 hit = strings.Replace(hit, "@NAME@", name, -1) 567 hit = strings.Replace(hit, "@SNAPID@", fakeSnapID(name), -1) 568 hit = strings.Replace(hit, "@ICON@", baseURL.String()+"/icon", -1) 569 hit = strings.Replace(hit, "@VERSION@", info.Version, -1) 570 hit = strings.Replace(hit, "@REVISION@", revno, -1) 571 hit = strings.Replace(hit, `@TYPE@`, string(info.GetType()), -1) 572 hit = strings.Replace(hit, `@EPOCH@`, string(epochBuf), -1) 573 return hit 574 } 575 576 mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 577 // all URLS are /api/v1/snaps/... or /v2/snaps/... so 578 // check the url is sane and discard the common prefix 579 // to simplify indexing into the comps slice. 580 comps := strings.Split(r.URL.Path, "/") 581 if len(comps) < 2 { 582 panic("unexpected url path: " + r.URL.Path) 583 } 584 if comps[1] == "api" { //v1 585 if len(comps) <= 4 { 586 panic("unexpected url path: " + r.URL.Path) 587 } 588 comps = comps[4:] 589 if comps[0] == "auth" { 590 comps[0] = "auth:" + comps[1] 591 } 592 } else { // v2 593 if len(comps) <= 3 { 594 panic("unexpected url path: " + r.URL.Path) 595 } 596 comps = comps[3:] 597 comps[0] = "v2:" + comps[0] 598 } 599 600 switch comps[0] { 601 case "auth:nonces": 602 w.Write([]byte(`{"nonce": "NONCE"}`)) 603 return 604 case "auth:sessions": 605 // quick sanity check 606 reqBody, err := ioutil.ReadAll(r.Body) 607 c.Check(err, IsNil) 608 c.Check(bytes.Contains(reqBody, []byte("nonce: NONCE")), Equals, true) 609 c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("serial: %s", s.expectedSerial))), Equals, true) 610 c.Check(bytes.Contains(reqBody, []byte(fmt.Sprintf("store: %s", s.expectedStore))), Equals, true) 611 612 c.Check(s.sessionMacaroon, Not(Equals), "") 613 w.WriteHeader(200) 614 w.Write([]byte(fmt.Sprintf(`{"macaroon": "%s"}`, s.sessionMacaroon))) 615 return 616 case "assertions": 617 ref := &asserts.Ref{ 618 Type: asserts.Type(comps[1]), 619 PrimaryKey: comps[2:], 620 } 621 a, err := ref.Resolve(s.storeSigning.Find) 622 if asserts.IsNotFound(err) { 623 w.Header().Set("Content-Type", "application/json") 624 w.WriteHeader(404) 625 w.Write([]byte(`{"status": 404}`)) 626 return 627 } 628 if err != nil { 629 panic(err) 630 } 631 w.Header().Set("Content-Type", asserts.MediaType) 632 w.WriteHeader(200) 633 w.Write(asserts.Encode(a)) 634 return 635 case "download": 636 if s.sessionMacaroon != "" { 637 // FIXME: download is still using the old headers! 638 c.Check(r.Header.Get("X-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon)) 639 } 640 if s.failNextDownload == comps[1] { 641 s.failNextDownload = "" 642 w.WriteHeader(418) 643 return 644 } 645 if s.hijackServeSnap != nil { 646 s.hijackServeSnap(w) 647 return 648 } 649 snapR, err := os.Open(s.pathFor(comps[1], comps[2])) 650 if err != nil { 651 panic(err) 652 } 653 io.Copy(w, snapR) 654 case "v2:refresh": 655 if s.sessionMacaroon != "" { 656 c.Check(r.Header.Get("Snap-Device-Authorization"), Equals, fmt.Sprintf(`Macaroon root="%s"`, s.sessionMacaroon)) 657 } 658 dec := json.NewDecoder(r.Body) 659 var input struct { 660 Actions []struct { 661 Action string `json:"action"` 662 SnapID string `json:"snap-id"` 663 Name string `json:"name"` 664 InstanceKey string `json:"instance-key"` 665 Epoch snap.Epoch `json:"epoch"` 666 } `json:"actions"` 667 Context []struct { 668 SnapID string `json:"snap-id"` 669 Epoch snap.Epoch `json:"epoch"` 670 } `json:"context"` 671 } 672 if err := dec.Decode(&input); err != nil { 673 panic(err) 674 } 675 id2epoch := make(map[string]snap.Epoch, len(input.Context)) 676 for _, s := range input.Context { 677 id2epoch[s.SnapID] = s.Epoch 678 } 679 type resultJSON struct { 680 Result string `json:"result"` 681 SnapID string `json:"snap-id"` 682 Name string `json:"name"` 683 Snap json.RawMessage `json:"snap"` 684 InstanceKey string `json:"instance-key"` 685 } 686 var results []resultJSON 687 for _, a := range input.Actions { 688 name := s.serveIDtoName[a.SnapID] 689 epoch := id2epoch[a.SnapID] 690 if a.Action == "install" { 691 name = a.Name 692 epoch = a.Epoch 693 } 694 695 info, revno := s.newestThatCanRead(name, epoch) 696 if info == nil { 697 // no match 698 continue 699 } 700 results = append(results, resultJSON{ 701 Result: a.Action, 702 SnapID: a.SnapID, 703 InstanceKey: a.InstanceKey, 704 Name: name, 705 Snap: json.RawMessage(fillHit(snapV2, revno, info)), 706 }) 707 } 708 w.WriteHeader(200) 709 output, err := json.Marshal(map[string]interface{}{ 710 "results": results, 711 }) 712 if err != nil { 713 panic(err) 714 } 715 w.Write(output) 716 717 default: 718 panic("unexpected url path: " + r.URL.Path) 719 } 720 })) 721 c.Assert(mockServer, NotNil) 722 723 baseURL, _ = url.Parse(mockServer.URL) 724 storeCfg := store.Config{ 725 StoreBaseURL: baseURL, 726 } 727 728 mStore := store.New(&storeCfg, nil) 729 st := s.o.State() 730 st.Lock() 731 snapstate.ReplaceStore(s.o.State(), mStore) 732 st.Unlock() 733 734 // this will be used by remodeling cases 735 storeNew := func(cfg *store.Config, dac store.DeviceAndAuthContext) *store.Store { 736 cfg.StoreBaseURL = baseURL 737 if s.checkDeviceAndAuthContext != nil { 738 s.checkDeviceAndAuthContext(dac) 739 } 740 return store.New(cfg, dac) 741 } 742 743 s.AddCleanup(overlord.MockStoreNew(storeNew)) 744 745 return mockServer 746 } 747 748 // serveSnap starts serving the snap at snapPath, moving the current 749 // one onto the list of previous ones if already set. 750 func (s *mgrsSuite) serveSnap(snapPath, revno string) { 751 snapf, err := snap.Open(snapPath) 752 if err != nil { 753 panic(err) 754 } 755 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 756 if err != nil { 757 panic(err) 758 } 759 name := info.SnapName() 760 s.serveIDtoName[fakeSnapID(name)] = name 761 762 if oldPath := s.serveSnapPath[name]; oldPath != "" { 763 oldRev := s.serveRevision[name] 764 if oldRev == "" { 765 panic("old path set but not old revision") 766 } 767 s.serveOldPaths[name] = append(s.serveOldPaths[name], oldPath) 768 s.serveOldRevs[name] = append(s.serveOldRevs[name], oldRev) 769 } 770 s.serveSnapPath[name] = snapPath 771 s.serveRevision[name] = revno 772 } 773 774 func (s *mgrsSuite) TestHappyRemoteInstallAndUpgradeSvc(c *C) { 775 // test install through store and update, plus some mechanics 776 // of update 777 // TODO: ok to split if it gets too messy to maintain 778 779 s.prereqSnapAssertions(c) 780 781 snapYamlContent := `name: foo 782 version: @VERSION@ 783 apps: 784 bar: 785 command: bin/bar 786 svc: 787 command: svc 788 daemon: forking 789 ` 790 791 ver := "1.0" 792 revno := "42" 793 snapPath, digest := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 794 s.serveSnap(snapPath, revno) 795 796 mockServer := s.mockStore(c) 797 defer mockServer.Close() 798 799 st := s.o.State() 800 st.Lock() 801 defer st.Unlock() 802 803 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 804 c.Assert(err, IsNil) 805 chg := st.NewChange("install-snap", "...") 806 chg.AddAll(ts) 807 808 st.Unlock() 809 err = s.o.Settle(settleTimeout) 810 st.Lock() 811 c.Assert(err, IsNil) 812 813 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 814 815 info, err := snapstate.CurrentInfo(st, "foo") 816 c.Assert(err, IsNil) 817 818 c.Check(info.Revision, Equals, snap.R(42)) 819 c.Check(info.SnapID, Equals, fooSnapID) 820 c.Check(info.Version, Equals, "1.0") 821 c.Check(info.Summary(), Equals, "Foo") 822 c.Check(info.Description(), Equals, "this is a description") 823 c.Assert(osutil.FileExists(info.MountFile()), Equals, true) 824 825 pubAcct, err := assertstate.Publisher(st, info.SnapID) 826 c.Assert(err, IsNil) 827 c.Check(pubAcct.AccountID(), Equals, "devdevdev") 828 c.Check(pubAcct.Username(), Equals, "devdevdev") 829 830 snapRev42, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{ 831 "snap-sha3-384": digest, 832 }) 833 c.Assert(err, IsNil) 834 c.Check(snapRev42.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID) 835 c.Check(snapRev42.(*asserts.SnapRevision).SnapRevision(), Equals, 42) 836 837 // check service was setup properly 838 svcFile := filepath.Join(dirs.SnapServicesDir, "snap.foo.svc.service") 839 c.Assert(osutil.FileExists(svcFile), Equals, true) 840 stat, err := os.Stat(svcFile) 841 c.Assert(err, IsNil) 842 // should _not_ be executable 843 c.Assert(stat.Mode().String(), Equals, "-rw-r--r--") 844 845 // Refresh 846 847 ver = "2.0" 848 revno = "50" 849 snapPath, digest = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 850 s.serveSnap(snapPath, revno) 851 852 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 853 c.Assert(err, IsNil) 854 chg = st.NewChange("upgrade-snap", "...") 855 chg.AddAll(ts) 856 857 st.Unlock() 858 err = s.o.Settle(settleTimeout) 859 st.Lock() 860 c.Assert(err, IsNil) 861 862 c.Assert(chg.Err(), IsNil) 863 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 864 865 info, err = snapstate.CurrentInfo(st, "foo") 866 c.Assert(err, IsNil) 867 868 c.Check(info.Revision, Equals, snap.R(50)) 869 c.Check(info.SnapID, Equals, fooSnapID) 870 c.Check(info.Version, Equals, "2.0") 871 872 snapRev50, err := assertstate.DB(st).Find(asserts.SnapRevisionType, map[string]string{ 873 "snap-sha3-384": digest, 874 }) 875 c.Assert(err, IsNil) 876 c.Check(snapRev50.(*asserts.SnapRevision).SnapID(), Equals, fooSnapID) 877 c.Check(snapRev50.(*asserts.SnapRevision).SnapRevision(), Equals, 50) 878 879 // check updated wrapper 880 symlinkTarget, err := os.Readlink(info.Apps["bar"].WrapperPath()) 881 c.Assert(err, IsNil) 882 c.Assert(symlinkTarget, Equals, "/usr/bin/snap") 883 884 // check updated service file 885 c.Assert(svcFile, testutil.FileContains, "/var/snap/foo/"+revno) 886 } 887 888 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithEpochBump(c *C) { 889 // test install through store and update, where there's an epoch bump in the upgrade 890 // this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc 891 892 s.prereqSnapAssertions(c) 893 894 snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0}", "1") 895 s.serveSnap(snapPath, "1") 896 897 mockServer := s.mockStore(c) 898 defer mockServer.Close() 899 900 st := s.o.State() 901 st.Lock() 902 defer st.Unlock() 903 904 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 905 c.Assert(err, IsNil) 906 chg := st.NewChange("install-snap", "...") 907 chg.AddAll(ts) 908 909 st.Unlock() 910 err = s.o.Settle(settleTimeout) 911 st.Lock() 912 c.Assert(err, IsNil) 913 914 // confirm it worked 915 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 916 917 // sanity checks 918 info, err := snapstate.CurrentInfo(st, "foo") 919 c.Assert(err, IsNil) 920 c.Assert(info.Revision, Equals, snap.R(1)) 921 c.Assert(info.SnapID, Equals, fooSnapID) 922 c.Assert(info.Epoch.String(), Equals, "0") 923 924 // now add some more snaps 925 for i, epoch := range []string{"1*", "2*", "3*"} { 926 revno := fmt.Sprint(i + 2) 927 snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 0, epoch: "+epoch+"}", revno) 928 s.serveSnap(snapPath, revno) 929 } 930 931 // refresh 932 933 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 934 c.Assert(err, IsNil) 935 chg = st.NewChange("upgrade-snap", "...") 936 chg.AddAll(ts) 937 938 st.Unlock() 939 err = s.o.Settle(settleTimeout) 940 st.Lock() 941 c.Assert(err, IsNil) 942 943 c.Assert(chg.Err(), IsNil) 944 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 945 946 info, err = snapstate.CurrentInfo(st, "foo") 947 c.Assert(err, IsNil) 948 949 c.Check(info.Revision, Equals, snap.R(4)) 950 c.Check(info.SnapID, Equals, fooSnapID) 951 c.Check(info.Epoch.String(), Equals, "3*") 952 } 953 954 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithPostHocEpochBump(c *C) { 955 // test install through store and update, where there is an epoch 956 // bump in the upgrade that comes in after the initial update is 957 // computed. 958 959 // this is mostly checking the same as TestHappyRemoteInstallAndUpdateWithEpochBump 960 // but serves as a sanity check for the Without case that follows 961 // (these two together serve as a test for the refresh filtering) 962 s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, true) 963 } 964 965 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateWithoutEpochBump(c *C) { 966 // test install through store and update, where there _isn't_ an epoch bump in the upgrade 967 // note that there _are_ refreshes available after the refresh, 968 // but they're not an epoch bump so they're ignored 969 s.testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c, false) 970 } 971 972 func (s *mgrsSuite) testHappyRemoteInstallAndUpdateWithMaybeEpochBump(c *C, doBump bool) { 973 s.prereqSnapAssertions(c) 974 975 snapPath, _ := s.makeStoreTestSnap(c, "{name: foo, version: 1}", "1") 976 s.serveSnap(snapPath, "1") 977 978 mockServer := s.mockStore(c) 979 defer mockServer.Close() 980 981 st := s.o.State() 982 st.Lock() 983 defer st.Unlock() 984 985 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 986 c.Assert(err, IsNil) 987 chg := st.NewChange("install-snap", "...") 988 chg.AddAll(ts) 989 990 st.Unlock() 991 err = s.o.Settle(settleTimeout) 992 st.Lock() 993 c.Assert(err, IsNil) 994 995 // confirm it worked 996 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 997 998 // sanity checks 999 info, err := snapstate.CurrentInfo(st, "foo") 1000 c.Assert(err, IsNil) 1001 c.Assert(info.Revision, Equals, snap.R(1)) 1002 c.Assert(info.SnapID, Equals, fooSnapID) 1003 c.Assert(info.Epoch.String(), Equals, "0") 1004 1005 // add a new revision 1006 snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 2}", "2") 1007 s.serveSnap(snapPath, "2") 1008 1009 // refresh 1010 1011 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 1012 c.Assert(err, IsNil) 1013 chg = st.NewChange("upgrade-snap", "...") 1014 chg.AddAll(ts) 1015 1016 // add another new revision, after the update was computed (maybe with an epoch bump) 1017 if doBump { 1018 snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3, epoch: 1*}", "3") 1019 } else { 1020 snapPath, _ = s.makeStoreTestSnap(c, "{name: foo, version: 3}", "3") 1021 } 1022 s.serveSnap(snapPath, "3") 1023 1024 st.Unlock() 1025 err = s.o.Settle(settleTimeout) 1026 st.Lock() 1027 c.Assert(err, IsNil) 1028 1029 c.Assert(chg.Err(), IsNil) 1030 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1031 1032 info, err = snapstate.CurrentInfo(st, "foo") 1033 c.Assert(err, IsNil) 1034 1035 if doBump { 1036 // if the epoch bumped, then we should've re-refreshed 1037 c.Check(info.Revision, Equals, snap.R(3)) 1038 c.Check(info.SnapID, Equals, fooSnapID) 1039 c.Check(info.Epoch.String(), Equals, "1*") 1040 } else { 1041 // if the epoch did not bump, then we should _not_ have re-refreshed 1042 c.Check(info.Revision, Equals, snap.R(2)) 1043 c.Check(info.SnapID, Equals, fooSnapID) 1044 c.Check(info.Epoch.String(), Equals, "0") 1045 } 1046 } 1047 1048 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBump(c *C) { 1049 // test install through store and update many, where there's an epoch bump in the upgrade 1050 // this does less checks on the details of install/update than TestHappyRemoteInstallAndUpgradeSvc 1051 1052 snapNames := []string{"aaaa", "bbbb", "cccc"} 1053 for _, name := range snapNames { 1054 s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name}) 1055 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1") 1056 s.serveSnap(snapPath, "1") 1057 } 1058 1059 mockServer := s.mockStore(c) 1060 defer mockServer.Close() 1061 1062 st := s.o.State() 1063 st.Lock() 1064 defer st.Unlock() 1065 1066 affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0) 1067 c.Assert(err, IsNil) 1068 sort.Strings(affected) 1069 c.Check(affected, DeepEquals, snapNames) 1070 chg := st.NewChange("install-snaps", "...") 1071 for _, taskset := range tasksets { 1072 chg.AddAll(taskset) 1073 } 1074 1075 st.Unlock() 1076 err = s.o.Settle(settleTimeout) 1077 st.Lock() 1078 c.Assert(err, IsNil) 1079 1080 // confirm it worked 1081 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1082 1083 // sanity checks 1084 for _, name := range snapNames { 1085 info, err := snapstate.CurrentInfo(st, name) 1086 c.Assert(err, IsNil) 1087 c.Assert(info.Revision, Equals, snap.R(1)) 1088 c.Assert(info.SnapID, Equals, fakeSnapID(name)) 1089 c.Assert(info.Epoch.String(), Equals, "0") 1090 } 1091 1092 // now add some more snap revisions with increasing epochs 1093 for _, name := range snapNames { 1094 for i, epoch := range []string{"1*", "2*", "3*"} { 1095 revno := fmt.Sprint(i + 2) 1096 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno) 1097 s.serveSnap(snapPath, revno) 1098 } 1099 } 1100 1101 // refresh 1102 1103 affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{}) 1104 c.Assert(err, IsNil) 1105 sort.Strings(affected) 1106 c.Check(affected, DeepEquals, snapNames) 1107 chg = st.NewChange("upgrade-snaps", "...") 1108 for _, taskset := range tasksets { 1109 chg.AddAll(taskset) 1110 } 1111 1112 st.Unlock() 1113 err = s.o.Settle(settleTimeout) 1114 st.Lock() 1115 c.Assert(err, IsNil) 1116 1117 c.Assert(chg.Err(), IsNil) 1118 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1119 1120 for _, name := range snapNames { 1121 info, err := snapstate.CurrentInfo(st, name) 1122 c.Assert(err, IsNil) 1123 1124 c.Check(info.Revision, Equals, snap.R(4)) 1125 c.Check(info.SnapID, Equals, fakeSnapID(name)) 1126 c.Check(info.Epoch.String(), Equals, "3*") 1127 } 1128 } 1129 1130 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateManyWithEpochBumpAndOneFailing(c *C) { 1131 // test install through store and update, where there's an epoch bump in the upgrade and one of them fails 1132 1133 snapNames := []string{"aaaa", "bbbb", "cccc"} 1134 for _, name := range snapNames { 1135 s.prereqSnapAssertions(c, map[string]interface{}{"snap-name": name}) 1136 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0}", name), "1") 1137 s.serveSnap(snapPath, "1") 1138 } 1139 1140 mockServer := s.mockStore(c) 1141 defer mockServer.Close() 1142 1143 st := s.o.State() 1144 st.Lock() 1145 defer st.Unlock() 1146 1147 affected, tasksets, err := snapstate.InstallMany(st, snapNames, 0) 1148 c.Assert(err, IsNil) 1149 sort.Strings(affected) 1150 c.Check(affected, DeepEquals, snapNames) 1151 chg := st.NewChange("install-snaps", "...") 1152 for _, taskset := range tasksets { 1153 chg.AddAll(taskset) 1154 } 1155 1156 st.Unlock() 1157 err = s.o.Settle(settleTimeout) 1158 st.Lock() 1159 c.Assert(err, IsNil) 1160 1161 // confirm it worked 1162 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1163 1164 // sanity checks 1165 for _, name := range snapNames { 1166 info, err := snapstate.CurrentInfo(st, name) 1167 c.Assert(err, IsNil) 1168 c.Assert(info.Revision, Equals, snap.R(1)) 1169 c.Assert(info.SnapID, Equals, fakeSnapID(name)) 1170 c.Assert(info.Epoch.String(), Equals, "0") 1171 } 1172 1173 // now add some more snap revisions with increasing epochs 1174 for _, name := range snapNames { 1175 for i, epoch := range []string{"1*", "2*", "3*"} { 1176 revno := fmt.Sprint(i + 2) 1177 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 0, epoch: %s}", name, epoch), revno) 1178 s.serveSnap(snapPath, revno) 1179 } 1180 } 1181 1182 // refresh 1183 affected, tasksets, err = snapstate.UpdateMany(context.TODO(), st, nil, 0, &snapstate.Flags{}) 1184 c.Assert(err, IsNil) 1185 sort.Strings(affected) 1186 c.Check(affected, DeepEquals, snapNames) 1187 chg = st.NewChange("upgrade-snaps", "...") 1188 for _, taskset := range tasksets { 1189 chg.AddAll(taskset) 1190 } 1191 1192 st.Unlock() 1193 // the download for the refresh above will be performed below, during 'settle'. 1194 // fail the refresh of cccc by failing its download 1195 s.failNextDownload = "cccc" 1196 err = s.o.Settle(settleTimeout) 1197 st.Lock() 1198 c.Assert(err, IsNil) 1199 1200 c.Assert(chg.Err(), NotNil) 1201 c.Assert(chg.Status(), Equals, state.ErrorStatus) 1202 1203 for _, name := range snapNames { 1204 comment := Commentf("%q", name) 1205 info, err := snapstate.CurrentInfo(st, name) 1206 c.Assert(err, IsNil, comment) 1207 1208 if name == "cccc" { 1209 // the failed one: still on rev 1 (epoch 0) 1210 c.Assert(info.Revision, Equals, snap.R(1)) 1211 c.Assert(info.SnapID, Equals, fakeSnapID(name)) 1212 c.Assert(info.Epoch.String(), Equals, "0") 1213 } else { 1214 // the non-failed ones: refreshed to rev 4 (epoch 3*) 1215 c.Check(info.Revision, Equals, snap.R(4), comment) 1216 c.Check(info.SnapID, Equals, fakeSnapID(name), comment) 1217 c.Check(info.Epoch.String(), Equals, "3*", comment) 1218 } 1219 } 1220 } 1221 1222 func (s *mgrsSuite) TestHappyLocalInstallWithStoreMetadata(c *C) { 1223 snapDecl := s.prereqSnapAssertions(c) 1224 1225 snapYamlContent := `name: foo 1226 apps: 1227 bar: 1228 command: bin/bar 1229 ` 1230 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1231 1232 si := &snap.SideInfo{ 1233 RealName: "foo", 1234 SnapID: fooSnapID, 1235 Revision: snap.R(55), 1236 } 1237 1238 st := s.o.State() 1239 st.Lock() 1240 defer st.Unlock() 1241 1242 // have the snap-declaration in the system db 1243 err := assertstate.Add(st, s.devAcct) 1244 c.Assert(err, IsNil) 1245 err = assertstate.Add(st, snapDecl) 1246 c.Assert(err, IsNil) 1247 1248 ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true}) 1249 c.Assert(err, IsNil) 1250 chg := st.NewChange("install-snap", "...") 1251 chg.AddAll(ts) 1252 1253 st.Unlock() 1254 err = s.o.Settle(settleTimeout) 1255 st.Lock() 1256 c.Assert(err, IsNil) 1257 1258 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1259 1260 info, err := snapstate.CurrentInfo(st, "foo") 1261 c.Assert(err, IsNil) 1262 c.Check(info.Revision, Equals, snap.R(55)) 1263 c.Check(info.SnapID, Equals, fooSnapID) 1264 c.Check(info.Version, Equals, "1.5") 1265 1266 // ensure that the binary wrapper file got generated with the right 1267 // name 1268 binaryWrapper := filepath.Join(dirs.SnapBinariesDir, "foo.bar") 1269 c.Assert(osutil.IsSymlink(binaryWrapper), Equals, true) 1270 1271 // data dirs 1272 c.Assert(osutil.IsDirectory(info.DataDir()), Equals, true) 1273 c.Assert(osutil.IsDirectory(info.CommonDataDir()), Equals, true) 1274 1275 // snap file and its mounting 1276 1277 // after install the snap file is in the right dir 1278 c.Assert(osutil.FileExists(filepath.Join(dirs.SnapBlobDir, "foo_55.snap")), Equals, true) 1279 1280 // ensure the right unit is created 1281 mup := systemd.MountUnitPath(filepath.Join(dirs.StripRootDir(dirs.SnapMountDir), "foo/55")) 1282 c.Assert(mup, testutil.FileMatches, fmt.Sprintf("(?ms).*^Where=%s/foo/55", dirs.StripRootDir(dirs.SnapMountDir))) 1283 c.Assert(mup, testutil.FileMatches, "(?ms).*^What=/var/lib/snapd/snaps/foo_55.snap") 1284 } 1285 1286 func (s *mgrsSuite) TestParallelInstanceLocalInstallSnapNameMismatch(c *C) { 1287 snapDecl := s.prereqSnapAssertions(c) 1288 1289 snapYamlContent := `name: foo 1290 apps: 1291 bar: 1292 command: bin/bar 1293 ` 1294 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1295 1296 si := &snap.SideInfo{ 1297 RealName: "foo", 1298 SnapID: fooSnapID, 1299 Revision: snap.R(55), 1300 } 1301 1302 st := s.o.State() 1303 st.Lock() 1304 defer st.Unlock() 1305 1306 // have the snap-declaration in the system db 1307 err := assertstate.Add(st, s.devAcct) 1308 c.Assert(err, IsNil) 1309 err = assertstate.Add(st, snapDecl) 1310 c.Assert(err, IsNil) 1311 1312 _, _, err = snapstate.InstallPath(st, si, snapPath, "bar_instance", "", snapstate.Flags{DevMode: true}) 1313 c.Assert(err, ErrorMatches, `cannot install snap "bar_instance", the name does not match the metadata "foo"`) 1314 } 1315 1316 func (s *mgrsSuite) TestParallelInstanceLocalInstallInvalidInstanceName(c *C) { 1317 snapDecl := s.prereqSnapAssertions(c) 1318 1319 snapYamlContent := `name: foo 1320 apps: 1321 bar: 1322 command: bin/bar 1323 ` 1324 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1325 1326 si := &snap.SideInfo{ 1327 RealName: "foo", 1328 SnapID: fooSnapID, 1329 Revision: snap.R(55), 1330 } 1331 1332 st := s.o.State() 1333 st.Lock() 1334 defer st.Unlock() 1335 1336 // have the snap-declaration in the system db 1337 err := assertstate.Add(st, s.devAcct) 1338 c.Assert(err, IsNil) 1339 err = assertstate.Add(st, snapDecl) 1340 c.Assert(err, IsNil) 1341 1342 _, _, err = snapstate.InstallPath(st, si, snapPath, "bar_invalid_instance_name", "", snapstate.Flags{DevMode: true}) 1343 c.Assert(err, ErrorMatches, `invalid instance name: invalid instance key: "invalid_instance_name"`) 1344 } 1345 1346 func (s *mgrsSuite) TestCheckInterfaces(c *C) { 1347 snapDecl := s.prereqSnapAssertions(c) 1348 1349 snapYamlContent := `name: foo 1350 apps: 1351 bar: 1352 command: bin/bar 1353 slots: 1354 network: 1355 ` 1356 snapPath := makeTestSnap(c, snapYamlContent+"version: 1.5") 1357 1358 si := &snap.SideInfo{ 1359 RealName: "foo", 1360 SnapID: fooSnapID, 1361 Revision: snap.R(55), 1362 } 1363 1364 st := s.o.State() 1365 st.Lock() 1366 defer st.Unlock() 1367 1368 // have the snap-declaration in the system db 1369 err := assertstate.Add(st, s.devAcct) 1370 c.Assert(err, IsNil) 1371 err = assertstate.Add(st, snapDecl) 1372 c.Assert(err, IsNil) 1373 1374 // mock SanitizePlugsSlots so that unknown interfaces are not rejected 1375 restoreSanitize := snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {}) 1376 defer restoreSanitize() 1377 1378 ts, _, err := snapstate.InstallPath(st, si, snapPath, "", "", snapstate.Flags{DevMode: true}) 1379 c.Assert(err, IsNil) 1380 chg := st.NewChange("install-snap", "...") 1381 chg.AddAll(ts) 1382 1383 st.Unlock() 1384 err = s.o.Settle(settleTimeout) 1385 st.Lock() 1386 c.Assert(err, IsNil) 1387 1388 c.Assert(chg.Err(), ErrorMatches, `(?s).*installation not allowed by "network" slot rule of interface "network".*`) 1389 c.Check(chg.Status(), Equals, state.ErrorStatus) 1390 } 1391 1392 func (s *mgrsSuite) TestHappyRefreshControl(c *C) { 1393 // test install through store and update, plus some mechanics 1394 // of update 1395 // TODO: ok to split if it gets too messy to maintain 1396 1397 s.prereqSnapAssertions(c) 1398 1399 snapYamlContent := `name: foo 1400 version: @VERSION@ 1401 ` 1402 1403 ver := "1.0" 1404 revno := "42" 1405 snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1406 s.serveSnap(snapPath, revno) 1407 1408 mockServer := s.mockStore(c) 1409 defer mockServer.Close() 1410 1411 st := s.o.State() 1412 st.Lock() 1413 defer st.Unlock() 1414 1415 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1416 c.Assert(err, IsNil) 1417 chg := st.NewChange("install-snap", "...") 1418 chg.AddAll(ts) 1419 1420 st.Unlock() 1421 err = s.o.Settle(settleTimeout) 1422 st.Lock() 1423 c.Assert(err, IsNil) 1424 1425 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1426 1427 info, err := snapstate.CurrentInfo(st, "foo") 1428 c.Assert(err, IsNil) 1429 1430 c.Check(info.Revision, Equals, snap.R(42)) 1431 1432 // Refresh 1433 1434 // Setup refresh control 1435 1436 headers := map[string]interface{}{ 1437 "series": "16", 1438 "snap-id": "bar-id", 1439 "snap-name": "bar", 1440 "publisher-id": "devdevdev", 1441 "refresh-control": []interface{}{fooSnapID}, 1442 "timestamp": time.Now().Format(time.RFC3339), 1443 } 1444 snapDeclBar, err := s.storeSigning.Sign(asserts.SnapDeclarationType, headers, nil, "") 1445 c.Assert(err, IsNil) 1446 err = s.storeSigning.Add(snapDeclBar) 1447 c.Assert(err, IsNil) 1448 err = assertstate.Add(st, snapDeclBar) 1449 c.Assert(err, IsNil) 1450 1451 snapstate.Set(st, "bar", &snapstate.SnapState{ 1452 Active: true, 1453 Sequence: []*snap.SideInfo{ 1454 {RealName: "bar", SnapID: "bar-id", Revision: snap.R(1)}, 1455 }, 1456 Current: snap.R(1), 1457 SnapType: "app", 1458 }) 1459 1460 develSigning := assertstest.NewSigningDB("devdevdev", develPrivKey) 1461 1462 develAccKey := assertstest.NewAccountKey(s.storeSigning, s.devAcct, nil, develPrivKey.PublicKey(), "") 1463 err = s.storeSigning.Add(develAccKey) 1464 c.Assert(err, IsNil) 1465 1466 ver = "2.0" 1467 revno = "50" 1468 snapPath, _ = s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1469 s.serveSnap(snapPath, revno) 1470 1471 updated, tss, err := snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil) 1472 c.Check(updated, IsNil) 1473 c.Check(tss, IsNil) 1474 // no validation we, get an error 1475 c.Check(err, ErrorMatches, `cannot refresh "foo" to revision 50: no validation by "bar"`) 1476 1477 // setup validation 1478 headers = map[string]interface{}{ 1479 "series": "16", 1480 "snap-id": "bar-id", 1481 "approved-snap-id": fooSnapID, 1482 "approved-snap-revision": "50", 1483 "timestamp": time.Now().Format(time.RFC3339), 1484 } 1485 barValidation, err := develSigning.Sign(asserts.ValidationType, headers, nil, "") 1486 c.Assert(err, IsNil) 1487 err = s.storeSigning.Add(barValidation) 1488 c.Assert(err, IsNil) 1489 1490 // ... and try again 1491 updated, tss, err = snapstate.UpdateMany(context.TODO(), st, []string{"foo"}, 0, nil) 1492 c.Assert(err, IsNil) 1493 c.Assert(updated, DeepEquals, []string{"foo"}) 1494 c.Assert(tss, HasLen, 2) 1495 verifyLastTasksetIsRerefresh(c, tss) 1496 chg = st.NewChange("upgrade-snaps", "...") 1497 chg.AddAll(tss[0]) 1498 1499 st.Unlock() 1500 err = s.o.Settle(settleTimeout) 1501 st.Lock() 1502 c.Assert(err, IsNil) 1503 1504 c.Assert(chg.Err(), IsNil) 1505 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 1506 1507 info, err = snapstate.CurrentInfo(st, "foo") 1508 c.Assert(err, IsNil) 1509 1510 c.Check(info.Revision, Equals, snap.R(50)) 1511 } 1512 1513 // core & kernel 1514 1515 var modelDefaults = map[string]interface{}{ 1516 "architecture": "amd64", 1517 "store": "my-brand-store-id", 1518 "gadget": "pc", 1519 "kernel": "pc-kernel", 1520 } 1521 1522 func findKind(chg *state.Change, kind string) *state.Task { 1523 for _, t := range chg.Tasks() { 1524 if t.Kind() == kind { 1525 return t 1526 } 1527 } 1528 return nil 1529 } 1530 1531 func (s *mgrsSuite) TestInstallCoreSnapUpdatesBootloaderAndSplitsAcrossRestart(c *C) { 1532 bloader := bootloadertest.Mock("mock", c.MkDir()) 1533 bootloader.Force(bloader) 1534 defer bootloader.Force(nil) 1535 1536 restore := release.MockOnClassic(false) 1537 defer restore() 1538 1539 model := s.brands.Model("my-brand", "my-model", modelDefaults) 1540 1541 const packageOS = ` 1542 name: core 1543 version: 16.04-1 1544 type: os 1545 ` 1546 snapPath := makeTestSnap(c, packageOS) 1547 1548 st := s.o.State() 1549 st.Lock() 1550 defer st.Unlock() 1551 1552 // setup model assertion 1553 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 1554 devicestatetest.SetDevice(st, &auth.DeviceState{ 1555 Brand: "my-brand", 1556 Model: "my-model", 1557 Serial: "serialserialserial", 1558 }) 1559 err := assertstate.Add(st, model) 1560 c.Assert(err, IsNil) 1561 1562 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "core"}, snapPath, "", "", snapstate.Flags{}) 1563 c.Assert(err, IsNil) 1564 chg := st.NewChange("install-snap", "...") 1565 chg.AddAll(ts) 1566 1567 st.Unlock() 1568 err = s.o.Settle(settleTimeout) 1569 st.Lock() 1570 c.Assert(err, IsNil) 1571 1572 // final steps will are post poned until we are in the restarted snapd 1573 ok, rst := st.Restarting() 1574 c.Assert(ok, Equals, true) 1575 c.Assert(rst, Equals, state.RestartSystem) 1576 1577 t := findKind(chg, "auto-connect") 1578 c.Assert(t, NotNil) 1579 c.Assert(t.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1580 1581 // this is already set 1582 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 1583 "snap_try_core": "core_x1.snap", 1584 "snap_mode": "try", 1585 }) 1586 1587 // simulate successful restart happened 1588 state.MockRestarting(st, state.RestartUnset) 1589 bloader.BootVars["snap_mode"] = "" 1590 bloader.SetBootBase("core_x1.snap") 1591 1592 st.Unlock() 1593 err = s.o.Settle(settleTimeout) 1594 st.Lock() 1595 c.Assert(err, IsNil) 1596 1597 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1598 1599 } 1600 1601 func (s *mgrsSuite) TestInstallKernelSnapUpdatesBootloader(c *C) { 1602 bloader := bootloadertest.Mock("mock", c.MkDir()) 1603 bootloader.Force(bloader) 1604 defer bootloader.Force(nil) 1605 1606 restore := release.MockOnClassic(false) 1607 defer restore() 1608 1609 model := s.brands.Model("my-brand", "my-model", modelDefaults) 1610 1611 const packageKernel = ` 1612 name: pc-kernel 1613 version: 4.0-1 1614 type: kernel` 1615 1616 files := [][]string{ 1617 {"kernel.img", "I'm a kernel"}, 1618 {"initrd.img", "...and I'm an initrd"}, 1619 {"meta/kernel.yaml", "version: 4.2"}, 1620 } 1621 snapPath := snaptest.MakeTestSnapWithFiles(c, packageKernel, files) 1622 1623 st := s.o.State() 1624 st.Lock() 1625 defer st.Unlock() 1626 1627 // setup model assertion 1628 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 1629 devicestatetest.SetDevice(st, &auth.DeviceState{ 1630 Brand: "my-brand", 1631 Model: "my-model", 1632 Serial: "serialserialserial", 1633 }) 1634 err := assertstate.Add(st, model) 1635 c.Assert(err, IsNil) 1636 1637 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "pc-kernel"}, snapPath, "", "", snapstate.Flags{}) 1638 c.Assert(err, IsNil) 1639 chg := st.NewChange("install-snap", "...") 1640 chg.AddAll(ts) 1641 1642 // run, this will trigger a wait for the restart 1643 st.Unlock() 1644 err = s.o.Settle(settleTimeout) 1645 st.Lock() 1646 c.Assert(err, IsNil) 1647 1648 // we are in restarting state and the change is not done yet 1649 restarting, _ := st.Restarting() 1650 c.Check(restarting, Equals, true) 1651 c.Check(chg.Status(), Equals, state.DoingStatus) 1652 // pretend we restarted 1653 state.MockRestarting(st, state.RestartUnset) 1654 1655 st.Unlock() 1656 err = s.o.Settle(settleTimeout) 1657 st.Lock() 1658 c.Assert(err, IsNil) 1659 1660 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1661 1662 c.Assert(bloader.BootVars, DeepEquals, map[string]string{ 1663 "snap_try_kernel": "pc-kernel_x1.snap", 1664 "snap_mode": "try", 1665 }) 1666 } 1667 1668 func (s *mgrsSuite) installLocalTestSnap(c *C, snapYamlContent string) *snap.Info { 1669 st := s.o.State() 1670 1671 snapPath := makeTestSnap(c, snapYamlContent) 1672 snapf, err := snap.Open(snapPath) 1673 c.Assert(err, IsNil) 1674 info, err := snap.ReadInfoFromSnapFile(snapf, nil) 1675 c.Assert(err, IsNil) 1676 1677 // store current state 1678 snapName := info.InstanceName() 1679 var snapst snapstate.SnapState 1680 snapstate.Get(st, snapName, &snapst) 1681 1682 ts, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName}, snapPath, "", "", snapstate.Flags{DevMode: true}) 1683 c.Assert(err, IsNil) 1684 chg := st.NewChange("install-snap", "...") 1685 chg.AddAll(ts) 1686 1687 st.Unlock() 1688 err = s.o.Settle(settleTimeout) 1689 st.Lock() 1690 c.Assert(err, IsNil) 1691 1692 c.Assert(chg.Err(), IsNil) 1693 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1694 1695 return info 1696 } 1697 1698 func (s *mgrsSuite) removeSnap(c *C, name string) { 1699 st := s.o.State() 1700 1701 ts, err := snapstate.Remove(st, name, snap.R(0), nil) 1702 c.Assert(err, IsNil) 1703 chg := st.NewChange("remove-snap", "...") 1704 chg.AddAll(ts) 1705 1706 st.Unlock() 1707 err = s.o.Settle(settleTimeout) 1708 st.Lock() 1709 c.Assert(err, IsNil) 1710 1711 c.Assert(chg.Err(), IsNil) 1712 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err())) 1713 } 1714 1715 func (s *mgrsSuite) TestHappyRevert(c *C) { 1716 st := s.o.State() 1717 st.Lock() 1718 defer st.Unlock() 1719 1720 x1Yaml := `name: foo 1721 version: 1.0 1722 apps: 1723 x1: 1724 command: bin/bar 1725 ` 1726 x1binary := filepath.Join(dirs.SnapBinariesDir, "foo.x1") 1727 1728 x2Yaml := `name: foo 1729 version: 2.0 1730 apps: 1731 x2: 1732 command: bin/bar 1733 ` 1734 x2binary := filepath.Join(dirs.SnapBinariesDir, "foo.x2") 1735 1736 s.installLocalTestSnap(c, x1Yaml) 1737 s.installLocalTestSnap(c, x2Yaml) 1738 1739 // ensure we are on x2 1740 _, err := os.Lstat(x2binary) 1741 c.Assert(err, IsNil) 1742 _, err = os.Lstat(x1binary) 1743 c.Assert(err, ErrorMatches, ".*no such file.*") 1744 1745 // now do the revert 1746 ts, err := snapstate.Revert(st, "foo", snapstate.Flags{}) 1747 c.Assert(err, IsNil) 1748 chg := st.NewChange("revert-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 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("revert-snap change failed with: %v", chg.Err())) 1757 1758 // ensure that we use x1 now 1759 _, err = os.Lstat(x1binary) 1760 c.Assert(err, IsNil) 1761 _, err = os.Lstat(x2binary) 1762 c.Assert(err, ErrorMatches, ".*no such file.*") 1763 1764 // ensure that x1,x2 is still there, revert just moves the "current" 1765 // pointer 1766 for _, fn := range []string{"foo_x2.snap", "foo_x1.snap"} { 1767 p := filepath.Join(dirs.SnapBlobDir, fn) 1768 c.Assert(osutil.FileExists(p), Equals, true) 1769 } 1770 } 1771 1772 func (s *mgrsSuite) TestHappyAlias(c *C) { 1773 st := s.o.State() 1774 st.Lock() 1775 defer st.Unlock() 1776 1777 fooYaml := `name: foo 1778 version: 1.0 1779 apps: 1780 foo: 1781 command: bin/foo 1782 ` 1783 s.installLocalTestSnap(c, fooYaml) 1784 1785 ts, err := snapstate.Alias(st, "foo", "foo", "foo_") 1786 c.Assert(err, IsNil) 1787 chg := st.NewChange("alias", "...") 1788 chg.AddAll(ts) 1789 1790 st.Unlock() 1791 err = s.o.Settle(settleTimeout) 1792 st.Lock() 1793 c.Assert(err, IsNil) 1794 1795 c.Assert(chg.Err(), IsNil) 1796 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err())) 1797 1798 foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_") 1799 dest, err := os.Readlink(foo_Alias) 1800 c.Assert(err, IsNil) 1801 1802 c.Check(dest, Equals, "foo") 1803 1804 var snapst snapstate.SnapState 1805 err = snapstate.Get(st, "foo", &snapst) 1806 c.Assert(err, IsNil) 1807 c.Check(snapst.AutoAliasesDisabled, Equals, false) 1808 c.Check(snapst.AliasesPending, Equals, false) 1809 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 1810 "foo_": {Manual: "foo"}, 1811 }) 1812 1813 s.removeSnap(c, "foo") 1814 1815 c.Check(osutil.IsSymlink(foo_Alias), Equals, false) 1816 } 1817 1818 func (s *mgrsSuite) TestHappyUnalias(c *C) { 1819 st := s.o.State() 1820 st.Lock() 1821 defer st.Unlock() 1822 1823 fooYaml := `name: foo 1824 version: 1.0 1825 apps: 1826 foo: 1827 command: bin/foo 1828 ` 1829 s.installLocalTestSnap(c, fooYaml) 1830 1831 ts, err := snapstate.Alias(st, "foo", "foo", "foo_") 1832 c.Assert(err, IsNil) 1833 chg := st.NewChange("alias", "...") 1834 chg.AddAll(ts) 1835 1836 st.Unlock() 1837 err = s.o.Settle(settleTimeout) 1838 st.Lock() 1839 c.Assert(err, IsNil) 1840 1841 c.Assert(chg.Err(), IsNil) 1842 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("alias change failed with: %v", chg.Err())) 1843 1844 foo_Alias := filepath.Join(dirs.SnapBinariesDir, "foo_") 1845 dest, err := os.Readlink(foo_Alias) 1846 c.Assert(err, IsNil) 1847 1848 c.Check(dest, Equals, "foo") 1849 1850 ts, snapName, err := snapstate.RemoveManualAlias(st, "foo_") 1851 c.Assert(err, IsNil) 1852 c.Check(snapName, Equals, "foo") 1853 chg = st.NewChange("unalias", "...") 1854 chg.AddAll(ts) 1855 1856 st.Unlock() 1857 err = s.o.Settle(settleTimeout) 1858 st.Lock() 1859 c.Assert(err, IsNil) 1860 1861 c.Assert(chg.Err(), IsNil) 1862 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("unalias change failed with: %v", chg.Err())) 1863 1864 c.Check(osutil.IsSymlink(foo_Alias), Equals, false) 1865 1866 var snapst snapstate.SnapState 1867 err = snapstate.Get(st, "foo", &snapst) 1868 c.Assert(err, IsNil) 1869 c.Check(snapst.AutoAliasesDisabled, Equals, false) 1870 c.Check(snapst.AliasesPending, Equals, false) 1871 c.Check(snapst.Aliases, HasLen, 0) 1872 } 1873 1874 func (s *mgrsSuite) TestHappyRemoteInstallAutoAliases(c *C) { 1875 s.prereqSnapAssertions(c, map[string]interface{}{ 1876 "snap-name": "foo", 1877 "aliases": []interface{}{ 1878 map[string]interface{}{"name": "app1", "target": "app1"}, 1879 map[string]interface{}{"name": "app2", "target": "app2"}, 1880 }, 1881 }) 1882 1883 snapYamlContent := `name: foo 1884 version: @VERSION@ 1885 apps: 1886 app1: 1887 command: bin/app1 1888 app2: 1889 command: bin/app2 1890 ` 1891 1892 ver := "1.0" 1893 revno := "42" 1894 snapPath, _ := s.makeStoreTestSnap(c, strings.Replace(snapYamlContent, "@VERSION@", ver, -1), revno) 1895 s.serveSnap(snapPath, revno) 1896 1897 mockServer := s.mockStore(c) 1898 defer mockServer.Close() 1899 1900 st := s.o.State() 1901 st.Lock() 1902 defer st.Unlock() 1903 1904 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1905 c.Assert(err, IsNil) 1906 chg := st.NewChange("install-snap", "...") 1907 chg.AddAll(ts) 1908 1909 st.Unlock() 1910 err = s.o.Settle(settleTimeout) 1911 st.Lock() 1912 c.Assert(err, IsNil) 1913 1914 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1915 1916 var snapst snapstate.SnapState 1917 err = snapstate.Get(st, "foo", &snapst) 1918 c.Assert(err, IsNil) 1919 c.Check(snapst.AutoAliasesDisabled, Equals, false) 1920 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 1921 "app1": {Auto: "app1"}, 1922 "app2": {Auto: "app2"}, 1923 }) 1924 1925 // check disk 1926 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 1927 dest, err := os.Readlink(app1Alias) 1928 c.Assert(err, IsNil) 1929 c.Check(dest, Equals, "foo.app1") 1930 1931 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 1932 dest, err = os.Readlink(app2Alias) 1933 c.Assert(err, IsNil) 1934 c.Check(dest, Equals, "foo.app2") 1935 } 1936 1937 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliases(c *C) { 1938 s.prereqSnapAssertions(c, map[string]interface{}{ 1939 "snap-name": "foo", 1940 "aliases": []interface{}{ 1941 map[string]interface{}{"name": "app1", "target": "app1"}, 1942 }, 1943 }) 1944 1945 fooYaml := `name: foo 1946 version: @VERSION@ 1947 apps: 1948 app1: 1949 command: bin/app1 1950 app2: 1951 command: bin/app2 1952 ` 1953 1954 fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10") 1955 s.serveSnap(fooPath, "10") 1956 1957 mockServer := s.mockStore(c) 1958 defer mockServer.Close() 1959 1960 st := s.o.State() 1961 st.Lock() 1962 defer st.Unlock() 1963 1964 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 1965 c.Assert(err, IsNil) 1966 chg := st.NewChange("install-snap", "...") 1967 chg.AddAll(ts) 1968 1969 st.Unlock() 1970 err = s.o.Settle(settleTimeout) 1971 st.Lock() 1972 c.Assert(err, IsNil) 1973 1974 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 1975 1976 info, err := snapstate.CurrentInfo(st, "foo") 1977 c.Assert(err, IsNil) 1978 c.Check(info.Revision, Equals, snap.R(10)) 1979 c.Check(info.Version, Equals, "1.0") 1980 1981 var snapst snapstate.SnapState 1982 err = snapstate.Get(st, "foo", &snapst) 1983 c.Assert(err, IsNil) 1984 c.Check(snapst.AutoAliasesDisabled, Equals, false) 1985 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 1986 "app1": {Auto: "app1"}, 1987 }) 1988 1989 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 1990 dest, err := os.Readlink(app1Alias) 1991 c.Assert(err, IsNil) 1992 c.Check(dest, Equals, "foo.app1") 1993 1994 s.prereqSnapAssertions(c, map[string]interface{}{ 1995 "snap-name": "foo", 1996 "aliases": []interface{}{ 1997 map[string]interface{}{"name": "app2", "target": "app2"}, 1998 }, 1999 "revision": "1", 2000 }) 2001 2002 // new foo version/revision 2003 fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15") 2004 s.serveSnap(fooPath, "15") 2005 2006 // refresh all 2007 updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil) 2008 c.Assert(err, IsNil) 2009 c.Assert(updated, DeepEquals, []string{"foo"}) 2010 c.Assert(tss, HasLen, 2) 2011 verifyLastTasksetIsRerefresh(c, tss) 2012 chg = st.NewChange("upgrade-snaps", "...") 2013 chg.AddAll(tss[0]) 2014 2015 st.Unlock() 2016 err = s.o.Settle(settleTimeout) 2017 st.Lock() 2018 c.Assert(err, IsNil) 2019 2020 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 2021 2022 info, err = snapstate.CurrentInfo(st, "foo") 2023 c.Assert(err, IsNil) 2024 c.Check(info.Revision, Equals, snap.R(15)) 2025 c.Check(info.Version, Equals, "1.5") 2026 2027 var snapst2 snapstate.SnapState 2028 err = snapstate.Get(st, "foo", &snapst2) 2029 c.Assert(err, IsNil) 2030 c.Check(snapst2.AutoAliasesDisabled, Equals, false) 2031 c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2032 "app2": {Auto: "app2"}, 2033 }) 2034 2035 c.Check(osutil.IsSymlink(app1Alias), Equals, false) 2036 2037 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 2038 dest, err = os.Readlink(app2Alias) 2039 c.Assert(err, IsNil) 2040 c.Check(dest, Equals, "foo.app2") 2041 } 2042 2043 func (s *mgrsSuite) TestHappyRemoteInstallAndUpdateAutoAliasesUnaliased(c *C) { 2044 s.prereqSnapAssertions(c, map[string]interface{}{ 2045 "snap-name": "foo", 2046 "aliases": []interface{}{ 2047 map[string]interface{}{"name": "app1", "target": "app1"}, 2048 }, 2049 }) 2050 2051 fooYaml := `name: foo 2052 version: @VERSION@ 2053 apps: 2054 app1: 2055 command: bin/app1 2056 app2: 2057 command: bin/app2 2058 ` 2059 2060 fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10") 2061 s.serveSnap(fooPath, "10") 2062 2063 mockServer := s.mockStore(c) 2064 defer mockServer.Close() 2065 2066 st := s.o.State() 2067 st.Lock() 2068 defer st.Unlock() 2069 2070 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{Unaliased: true}) 2071 c.Assert(err, IsNil) 2072 chg := st.NewChange("install-snap", "...") 2073 chg.AddAll(ts) 2074 2075 st.Unlock() 2076 err = s.o.Settle(settleTimeout) 2077 st.Lock() 2078 c.Assert(err, IsNil) 2079 2080 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2081 2082 info, err := snapstate.CurrentInfo(st, "foo") 2083 c.Assert(err, IsNil) 2084 c.Check(info.Revision, Equals, snap.R(10)) 2085 c.Check(info.Version, Equals, "1.0") 2086 2087 var snapst snapstate.SnapState 2088 err = snapstate.Get(st, "foo", &snapst) 2089 c.Assert(err, IsNil) 2090 c.Check(snapst.AutoAliasesDisabled, Equals, true) 2091 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2092 "app1": {Auto: "app1"}, 2093 }) 2094 2095 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 2096 c.Check(osutil.IsSymlink(app1Alias), Equals, false) 2097 2098 s.prereqSnapAssertions(c, map[string]interface{}{ 2099 "snap-name": "foo", 2100 "aliases": []interface{}{ 2101 map[string]interface{}{"name": "app2", "target": "app2"}, 2102 }, 2103 "revision": "1", 2104 }) 2105 2106 // new foo version/revision 2107 fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15") 2108 s.serveSnap(fooPath, "15") 2109 2110 // refresh foo 2111 ts, err = snapstate.Update(st, "foo", nil, 0, snapstate.Flags{}) 2112 c.Assert(err, IsNil) 2113 chg = st.NewChange("upgrade-snap", "...") 2114 chg.AddAll(ts) 2115 2116 st.Unlock() 2117 err = s.o.Settle(settleTimeout) 2118 st.Lock() 2119 c.Assert(err, IsNil) 2120 2121 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 2122 2123 info, err = snapstate.CurrentInfo(st, "foo") 2124 c.Assert(err, IsNil) 2125 c.Check(info.Revision, Equals, snap.R(15)) 2126 c.Check(info.Version, Equals, "1.5") 2127 2128 var snapst2 snapstate.SnapState 2129 err = snapstate.Get(st, "foo", &snapst2) 2130 c.Assert(err, IsNil) 2131 c.Check(snapst2.AutoAliasesDisabled, Equals, true) 2132 c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2133 "app2": {Auto: "app2"}, 2134 }) 2135 2136 c.Check(osutil.IsSymlink(app1Alias), Equals, false) 2137 2138 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 2139 c.Check(osutil.IsSymlink(app2Alias), Equals, false) 2140 } 2141 2142 func (s *mgrsSuite) TestHappyOrthogonalRefreshAutoAliases(c *C) { 2143 s.prereqSnapAssertions(c, map[string]interface{}{ 2144 "snap-name": "foo", 2145 "aliases": []interface{}{ 2146 map[string]interface{}{"name": "app1", "target": "app1"}, 2147 }, 2148 }, map[string]interface{}{ 2149 "snap-name": "bar", 2150 }) 2151 2152 fooYaml := `name: foo 2153 version: @VERSION@ 2154 apps: 2155 app1: 2156 command: bin/app1 2157 app2: 2158 command: bin/app2 2159 ` 2160 2161 barYaml := `name: bar 2162 version: @VERSION@ 2163 apps: 2164 app1: 2165 command: bin/app1 2166 app3: 2167 command: bin/app3 2168 ` 2169 2170 fooPath, _ := s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.0", -1), "10") 2171 s.serveSnap(fooPath, "10") 2172 2173 barPath, _ := s.makeStoreTestSnap(c, strings.Replace(barYaml, "@VERSION@", "2.0", -1), "20") 2174 s.serveSnap(barPath, "20") 2175 2176 mockServer := s.mockStore(c) 2177 defer mockServer.Close() 2178 2179 st := s.o.State() 2180 st.Lock() 2181 defer st.Unlock() 2182 2183 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 2184 c.Assert(err, IsNil) 2185 chg := st.NewChange("install-snap", "...") 2186 chg.AddAll(ts) 2187 2188 st.Unlock() 2189 err = s.o.Settle(settleTimeout) 2190 st.Lock() 2191 c.Assert(err, IsNil) 2192 2193 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2194 2195 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2196 2197 ts, err = snapstate.Install(context.TODO(), st, "bar", nil, 0, snapstate.Flags{}) 2198 c.Assert(err, IsNil) 2199 chg = st.NewChange("install-snap", "...") 2200 chg.AddAll(ts) 2201 2202 st.Unlock() 2203 err = s.o.Settle(settleTimeout) 2204 st.Lock() 2205 c.Assert(err, IsNil) 2206 2207 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2208 2209 info, err := snapstate.CurrentInfo(st, "foo") 2210 c.Assert(err, IsNil) 2211 c.Check(info.Revision, Equals, snap.R(10)) 2212 c.Check(info.Version, Equals, "1.0") 2213 2214 info, err = snapstate.CurrentInfo(st, "bar") 2215 c.Assert(err, IsNil) 2216 c.Check(info.Revision, Equals, snap.R(20)) 2217 c.Check(info.Version, Equals, "2.0") 2218 2219 var snapst snapstate.SnapState 2220 err = snapstate.Get(st, "foo", &snapst) 2221 c.Assert(err, IsNil) 2222 c.Check(snapst.AutoAliasesDisabled, Equals, false) 2223 c.Check(snapst.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2224 "app1": {Auto: "app1"}, 2225 }) 2226 2227 // foo gets a new version/revision and a change of automatic aliases 2228 // bar gets only the latter 2229 // app1 is transferred from foo to bar 2230 // UpdateMany after a snap-declaration refresh handles all of this 2231 s.prereqSnapAssertions(c, map[string]interface{}{ 2232 "snap-name": "foo", 2233 "aliases": []interface{}{ 2234 map[string]interface{}{"name": "app2", "target": "app2"}, 2235 }, 2236 "revision": "1", 2237 }, map[string]interface{}{ 2238 "snap-name": "bar", 2239 "aliases": []interface{}{ 2240 map[string]interface{}{"name": "app1", "target": "app1"}, 2241 map[string]interface{}{"name": "app3", "target": "app3"}, 2242 }, 2243 "revision": "1", 2244 }) 2245 2246 // new foo version/revision 2247 fooPath, _ = s.makeStoreTestSnap(c, strings.Replace(fooYaml, "@VERSION@", "1.5", -1), "15") 2248 s.serveSnap(fooPath, "15") 2249 2250 // refresh all 2251 err = assertstate.RefreshSnapDeclarations(st, 0) 2252 c.Assert(err, IsNil) 2253 2254 updated, tss, err := snapstate.UpdateMany(context.TODO(), st, nil, 0, nil) 2255 c.Assert(err, IsNil) 2256 sort.Strings(updated) 2257 c.Assert(updated, DeepEquals, []string{"bar", "foo"}) 2258 c.Assert(tss, HasLen, 4) 2259 verifyLastTasksetIsRerefresh(c, tss) 2260 chg = st.NewChange("upgrade-snaps", "...") 2261 chg.AddAll(tss[0]) 2262 chg.AddAll(tss[1]) 2263 chg.AddAll(tss[2]) 2264 2265 st.Unlock() 2266 err = s.o.Settle(settleTimeout) 2267 st.Lock() 2268 c.Assert(err, IsNil) 2269 2270 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 2271 2272 info, err = snapstate.CurrentInfo(st, "foo") 2273 c.Assert(err, IsNil) 2274 c.Check(info.Revision, Equals, snap.R(15)) 2275 c.Check(info.Version, Equals, "1.5") 2276 2277 var snapst2 snapstate.SnapState 2278 err = snapstate.Get(st, "foo", &snapst2) 2279 c.Assert(err, IsNil) 2280 c.Check(snapst2.AutoAliasesDisabled, Equals, false) 2281 c.Check(snapst2.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2282 "app2": {Auto: "app2"}, 2283 }) 2284 var snapst3 snapstate.SnapState 2285 err = snapstate.Get(st, "bar", &snapst3) 2286 c.Assert(err, IsNil) 2287 c.Check(snapst3.AutoAliasesDisabled, Equals, false) 2288 c.Check(snapst3.Aliases, DeepEquals, map[string]*snapstate.AliasTarget{ 2289 "app1": {Auto: "app1"}, 2290 "app3": {Auto: "app3"}, 2291 }) 2292 2293 app2Alias := filepath.Join(dirs.SnapBinariesDir, "app2") 2294 dest, err := os.Readlink(app2Alias) 2295 c.Assert(err, IsNil) 2296 c.Check(dest, Equals, "foo.app2") 2297 2298 app1Alias := filepath.Join(dirs.SnapBinariesDir, "app1") 2299 dest, err = os.Readlink(app1Alias) 2300 c.Assert(err, IsNil) 2301 c.Check(dest, Equals, "bar.app1") 2302 app3Alias := filepath.Join(dirs.SnapBinariesDir, "app3") 2303 dest, err = os.Readlink(app3Alias) 2304 c.Assert(err, IsNil) 2305 c.Check(dest, Equals, "bar.app3") 2306 } 2307 2308 func (s *mgrsSuite) TestHappyStopWhileDownloadingHeader(c *C) { 2309 s.prereqSnapAssertions(c) 2310 2311 snapYamlContent := `name: foo 2312 version: 1.0 2313 ` 2314 snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42") 2315 s.serveSnap(snapPath, "42") 2316 2317 stopped := make(chan struct{}) 2318 s.hijackServeSnap = func(_ http.ResponseWriter) { 2319 s.o.Stop() 2320 close(stopped) 2321 } 2322 2323 mockServer := s.mockStore(c) 2324 defer mockServer.Close() 2325 2326 st := s.o.State() 2327 st.Lock() 2328 defer st.Unlock() 2329 2330 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 2331 c.Assert(err, IsNil) 2332 chg := st.NewChange("install-snap", "...") 2333 chg.AddAll(ts) 2334 2335 st.Unlock() 2336 s.o.Loop() 2337 2338 <-stopped 2339 2340 st.Lock() 2341 c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2342 } 2343 2344 func (s *mgrsSuite) TestHappyStopWhileDownloadingBody(c *C) { 2345 s.prereqSnapAssertions(c) 2346 2347 snapYamlContent := `name: foo 2348 version: 1.0 2349 ` 2350 snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "42") 2351 s.serveSnap(snapPath, "42") 2352 2353 stopped := make(chan struct{}) 2354 s.hijackServeSnap = func(w http.ResponseWriter) { 2355 w.WriteHeader(200) 2356 // best effort to reach the body reading part in the client 2357 w.Write(make([]byte, 10000)) 2358 time.Sleep(100 * time.Millisecond) 2359 w.Write(make([]byte, 10000)) 2360 s.o.Stop() 2361 close(stopped) 2362 } 2363 2364 mockServer := s.mockStore(c) 2365 defer mockServer.Close() 2366 2367 st := s.o.State() 2368 st.Lock() 2369 defer st.Unlock() 2370 2371 ts, err := snapstate.Install(context.TODO(), st, "foo", nil, 0, snapstate.Flags{}) 2372 c.Assert(err, IsNil) 2373 chg := st.NewChange("install-snap", "...") 2374 chg.AddAll(ts) 2375 2376 st.Unlock() 2377 s.o.Loop() 2378 2379 <-stopped 2380 2381 st.Lock() 2382 c.Assert(chg.Status(), Equals, state.DoingStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2383 } 2384 2385 type storeCtxSetupSuite struct { 2386 o *overlord.Overlord 2387 sc store.DeviceAndAuthContext 2388 2389 storeSigning *assertstest.StoreStack 2390 restoreTrusted func() 2391 2392 brands *assertstest.SigningAccounts 2393 2394 deviceKey asserts.PrivateKey 2395 2396 model *asserts.Model 2397 serial *asserts.Serial 2398 2399 restoreBackends func() 2400 } 2401 2402 func (s *storeCtxSetupSuite) SetUpTest(c *C) { 2403 tempdir := c.MkDir() 2404 dirs.SetRootDir(tempdir) 2405 err := os.MkdirAll(filepath.Dir(dirs.SnapStateFile), 0755) 2406 c.Assert(err, IsNil) 2407 2408 captureStoreCtx := func(_ *store.Config, dac store.DeviceAndAuthContext) *store.Store { 2409 s.sc = dac 2410 return store.New(nil, nil) 2411 } 2412 r := overlord.MockStoreNew(captureStoreCtx) 2413 defer r() 2414 2415 s.storeSigning = assertstest.NewStoreStack("can0nical", nil) 2416 s.restoreTrusted = sysdb.InjectTrusted(s.storeSigning.Trusted) 2417 2418 s.brands = assertstest.NewSigningAccounts(s.storeSigning) 2419 s.brands.Register("my-brand", brandPrivKey, map[string]interface{}{ 2420 "verification": "verified", 2421 }) 2422 assertstest.AddMany(s.storeSigning, s.brands.AccountsAndKeys("my-brand")...) 2423 2424 s.model = s.brands.Model("my-brand", "my-model", modelDefaults) 2425 2426 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 2427 c.Assert(err, IsNil) 2428 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 2429 "authority-id": "my-brand", 2430 "brand-id": "my-brand", 2431 "model": "my-model", 2432 "serial": "7878", 2433 "device-key": string(encDevKey), 2434 "device-key-sha3-384": deviceKey.PublicKey().ID(), 2435 "timestamp": time.Now().Format(time.RFC3339), 2436 }, nil, "") 2437 c.Assert(err, IsNil) 2438 s.serial = serial.(*asserts.Serial) 2439 2440 s.restoreBackends = ifacestate.MockSecurityBackends(nil) 2441 2442 o, err := overlord.New(nil) 2443 c.Assert(err, IsNil) 2444 o.InterfaceManager().DisableUDevMonitor() 2445 s.o = o 2446 2447 st := o.State() 2448 st.Lock() 2449 defer st.Unlock() 2450 2451 assertstatetest.AddMany(st, s.storeSigning.StoreAccountKey("")) 2452 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 2453 } 2454 2455 func (s *storeCtxSetupSuite) TearDownTest(c *C) { 2456 dirs.SetRootDir("") 2457 s.restoreBackends() 2458 s.restoreTrusted() 2459 } 2460 2461 func (s *storeCtxSetupSuite) TestStoreID(c *C) { 2462 st := s.o.State() 2463 st.Lock() 2464 defer st.Unlock() 2465 2466 st.Unlock() 2467 storeID, err := s.sc.StoreID("fallback") 2468 st.Lock() 2469 c.Assert(err, IsNil) 2470 c.Check(storeID, Equals, "fallback") 2471 2472 // setup model in system statey 2473 devicestatetest.SetDevice(st, &auth.DeviceState{ 2474 Brand: s.serial.BrandID(), 2475 Model: s.serial.Model(), 2476 Serial: s.serial.Serial(), 2477 }) 2478 err = assertstate.Add(st, s.model) 2479 c.Assert(err, IsNil) 2480 2481 st.Unlock() 2482 storeID, err = s.sc.StoreID("fallback") 2483 st.Lock() 2484 c.Assert(err, IsNil) 2485 c.Check(storeID, Equals, "my-brand-store-id") 2486 } 2487 2488 func (s *storeCtxSetupSuite) TestDeviceSessionRequestParams(c *C) { 2489 st := s.o.State() 2490 st.Lock() 2491 defer st.Unlock() 2492 2493 st.Unlock() 2494 _, err := s.sc.DeviceSessionRequestParams("NONCE") 2495 st.Lock() 2496 c.Check(err, Equals, store.ErrNoSerial) 2497 2498 // setup model, serial and key in system state 2499 err = assertstate.Add(st, s.model) 2500 c.Assert(err, IsNil) 2501 err = assertstate.Add(st, s.serial) 2502 c.Assert(err, IsNil) 2503 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 2504 c.Assert(err, IsNil) 2505 err = kpMgr.Put(deviceKey) 2506 c.Assert(err, IsNil) 2507 devicestatetest.SetDevice(st, &auth.DeviceState{ 2508 Brand: s.serial.BrandID(), 2509 Model: s.serial.Model(), 2510 Serial: s.serial.Serial(), 2511 KeyID: deviceKey.PublicKey().ID(), 2512 }) 2513 2514 st.Unlock() 2515 params, err := s.sc.DeviceSessionRequestParams("NONCE") 2516 st.Lock() 2517 c.Assert(err, IsNil) 2518 c.Check(strings.HasPrefix(params.EncodedRequest(), "type: device-session-request\n"), Equals, true) 2519 c.Check(params.EncodedSerial(), DeepEquals, string(asserts.Encode(s.serial))) 2520 c.Check(params.EncodedModel(), DeepEquals, string(asserts.Encode(s.model))) 2521 2522 } 2523 2524 func (s *storeCtxSetupSuite) TestProxyStoreParams(c *C) { 2525 st := s.o.State() 2526 st.Lock() 2527 defer st.Unlock() 2528 2529 defURL, err := url.Parse("http://store") 2530 c.Assert(err, IsNil) 2531 2532 st.Unlock() 2533 proxyStoreID, proxyStoreURL, err := s.sc.ProxyStoreParams(defURL) 2534 st.Lock() 2535 c.Assert(err, IsNil) 2536 c.Check(proxyStoreID, Equals, "") 2537 c.Check(proxyStoreURL, Equals, defURL) 2538 2539 // setup proxy store reference and assertion 2540 operatorAcct := assertstest.NewAccount(s.storeSigning, "foo-operator", nil, "") 2541 err = assertstate.Add(st, operatorAcct) 2542 c.Assert(err, IsNil) 2543 stoAs, err := s.storeSigning.Sign(asserts.StoreType, map[string]interface{}{ 2544 "store": "foo", 2545 "operator-id": operatorAcct.AccountID(), 2546 "url": "http://foo.internal", 2547 "timestamp": time.Now().Format(time.RFC3339), 2548 }, nil, "") 2549 c.Assert(err, IsNil) 2550 err = assertstate.Add(st, stoAs) 2551 c.Assert(err, IsNil) 2552 tr := config.NewTransaction(st) 2553 err = tr.Set("core", "proxy.store", "foo") 2554 c.Assert(err, IsNil) 2555 tr.Commit() 2556 2557 fooURL, err := url.Parse("http://foo.internal") 2558 c.Assert(err, IsNil) 2559 2560 st.Unlock() 2561 proxyStoreID, proxyStoreURL, err = s.sc.ProxyStoreParams(defURL) 2562 st.Lock() 2563 c.Assert(err, IsNil) 2564 c.Check(proxyStoreID, Equals, "foo") 2565 c.Check(proxyStoreURL, DeepEquals, fooURL) 2566 } 2567 2568 const snapYamlContent1 = `name: snap1 2569 plugs: 2570 shared-data-plug: 2571 interface: content 2572 target: import 2573 content: mylib 2574 apps: 2575 bar: 2576 command: bin/bar 2577 ` 2578 const snapYamlContent2 = `name: snap2 2579 slots: 2580 shared-data-slot: 2581 interface: content 2582 content: mylib 2583 read: 2584 - / 2585 apps: 2586 bar: 2587 command: bin/bar 2588 ` 2589 2590 func (s *mgrsSuite) testTwoInstalls(c *C, snapName1, snapYaml1, snapName2, snapYaml2 string) { 2591 snapPath1 := makeTestSnap(c, snapYaml1+"version: 1.0") 2592 snapPath2 := makeTestSnap(c, snapYaml2+"version: 1.0") 2593 2594 st := s.o.State() 2595 st.Lock() 2596 defer st.Unlock() 2597 2598 ts1, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName1, SnapID: fakeSnapID(snapName1), Revision: snap.R(3)}, snapPath1, "", "", snapstate.Flags{DevMode: true}) 2599 c.Assert(err, IsNil) 2600 chg := st.NewChange("install-snap", "...") 2601 chg.AddAll(ts1) 2602 2603 ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: snapName2, SnapID: fakeSnapID(snapName2), Revision: snap.R(3)}, snapPath2, "", "", snapstate.Flags{DevMode: true}) 2604 c.Assert(err, IsNil) 2605 2606 ts2.WaitAll(ts1) 2607 chg.AddAll(ts2) 2608 2609 st.Unlock() 2610 err = s.o.Settle(settleTimeout) 2611 st.Lock() 2612 c.Assert(err, IsNil) 2613 2614 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2615 2616 tasks := chg.Tasks() 2617 connectTask := tasks[len(tasks)-2] 2618 c.Assert(connectTask.Kind(), Equals, "connect") 2619 2620 setupProfilesTask := tasks[len(tasks)-1] 2621 c.Assert(setupProfilesTask.Kind(), Equals, "setup-profiles") 2622 2623 // verify connect task data 2624 var plugRef interfaces.PlugRef 2625 var slotRef interfaces.SlotRef 2626 c.Assert(connectTask.Get("plug", &plugRef), IsNil) 2627 c.Assert(connectTask.Get("slot", &slotRef), IsNil) 2628 c.Assert(plugRef.Snap, Equals, "snap1") 2629 c.Assert(plugRef.Name, Equals, "shared-data-plug") 2630 c.Assert(slotRef.Snap, Equals, "snap2") 2631 c.Assert(slotRef.Name, Equals, "shared-data-slot") 2632 2633 // verify that connection was made 2634 var conns map[string]interface{} 2635 c.Assert(st.Get("conns", &conns), IsNil) 2636 c.Assert(conns, HasLen, 1) 2637 2638 repo := s.o.InterfaceManager().Repository() 2639 cn, err := repo.Connected("snap1", "shared-data-plug") 2640 c.Assert(err, IsNil) 2641 c.Assert(cn, HasLen, 1) 2642 c.Assert(cn, DeepEquals, []*interfaces.ConnRef{{ 2643 PlugRef: interfaces.PlugRef{Snap: "snap1", Name: "shared-data-plug"}, 2644 SlotRef: interfaces.SlotRef{Snap: "snap2", Name: "shared-data-slot"}, 2645 }}) 2646 } 2647 2648 func (s *mgrsSuite) TestTwoInstallsWithAutoconnectPlugSnapFirst(c *C) { 2649 s.testTwoInstalls(c, "snap1", snapYamlContent1, "snap2", snapYamlContent2) 2650 } 2651 2652 func (s *mgrsSuite) TestTwoInstallsWithAutoconnectSlotSnapFirst(c *C) { 2653 s.testTwoInstalls(c, "snap2", snapYamlContent2, "snap1", snapYamlContent1) 2654 } 2655 2656 func (s *mgrsSuite) TestRemoveAndInstallWithAutoconnectHappy(c *C) { 2657 st := s.o.State() 2658 st.Lock() 2659 defer st.Unlock() 2660 2661 _ = s.installLocalTestSnap(c, snapYamlContent1+"version: 1.0") 2662 2663 ts, err := snapstate.Remove(st, "snap1", snap.R(0), nil) 2664 c.Assert(err, IsNil) 2665 chg := st.NewChange("remove-snap", "...") 2666 chg.AddAll(ts) 2667 2668 snapPath := makeTestSnap(c, snapYamlContent2+"version: 1.0") 2669 chg2 := st.NewChange("install-snap", "...") 2670 ts2, _, err := snapstate.InstallPath(st, &snap.SideInfo{RealName: "snap2", SnapID: fakeSnapID("snap2"), Revision: snap.R(3)}, snapPath, "", "", snapstate.Flags{DevMode: true}) 2671 chg2.AddAll(ts2) 2672 c.Assert(err, IsNil) 2673 2674 st.Unlock() 2675 err = s.o.Settle(settleTimeout) 2676 st.Lock() 2677 c.Assert(err, IsNil) 2678 2679 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("remove-snap change failed with: %v", chg.Err())) 2680 c.Assert(chg2.Status(), Equals, state.DoneStatus, Commentf("install-snap change failed with: %v", chg.Err())) 2681 } 2682 2683 const otherSnapYaml = `name: other-snap 2684 version: 1.0 2685 apps: 2686 baz: 2687 command: bin/bar 2688 plugs: [media-hub] 2689 ` 2690 2691 func (s *mgrsSuite) TestUpdateManyWithAutoconnect(c *C) { 2692 const someSnapYaml = `name: some-snap 2693 version: 1.0 2694 apps: 2695 foo: 2696 command: bin/bar 2697 plugs: [network,home] 2698 slots: [media-hub] 2699 ` 2700 2701 const coreSnapYaml = `name: core 2702 type: os 2703 version: @VERSION@` 2704 2705 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 2706 s.serveSnap(snapPath, "40") 2707 2708 snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50") 2709 s.serveSnap(snapPath, "50") 2710 2711 corePath, _ := s.makeStoreTestSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "30", -1), "30") 2712 s.serveSnap(corePath, "30") 2713 2714 mockServer := s.mockStore(c) 2715 defer mockServer.Close() 2716 2717 st := s.o.State() 2718 st.Lock() 2719 defer st.Unlock() 2720 2721 st.Set("conns", map[string]interface{}{}) 2722 2723 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 2724 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 2725 c.Assert(snapInfo.Plugs, HasLen, 2) 2726 2727 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 2728 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 2729 c.Assert(otherInfo.Plugs, HasLen, 1) 2730 2731 csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)} 2732 coreInfo := snaptest.MockSnap(c, strings.Replace(coreSnapYaml, "@VERSION@", "1", -1), csi) 2733 2734 // add implicit slots 2735 coreInfo.Slots["network"] = &snap.SlotInfo{ 2736 Name: "network", 2737 Snap: coreInfo, 2738 Interface: "network", 2739 } 2740 coreInfo.Slots["home"] = &snap.SlotInfo{ 2741 Name: "home", 2742 Snap: coreInfo, 2743 Interface: "home", 2744 } 2745 2746 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 2747 Active: true, 2748 Sequence: []*snap.SideInfo{si}, 2749 Current: snap.R(1), 2750 SnapType: "app", 2751 }) 2752 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 2753 Active: true, 2754 Sequence: []*snap.SideInfo{oi}, 2755 Current: snap.R(1), 2756 SnapType: "app", 2757 }) 2758 2759 repo := s.o.InterfaceManager().Repository() 2760 2761 // add snaps to the repo to have plugs/slots 2762 c.Assert(repo.AddSnap(snapInfo), IsNil) 2763 c.Assert(repo.AddSnap(otherInfo), IsNil) 2764 c.Assert(repo.AddSnap(coreInfo), IsNil) 2765 2766 // refresh all 2767 err := assertstate.RefreshSnapDeclarations(st, 0) 2768 c.Assert(err, IsNil) 2769 2770 updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"core", "some-snap", "other-snap"}, 0, nil) 2771 c.Assert(err, IsNil) 2772 c.Check(updates, HasLen, 3) 2773 c.Assert(tts, HasLen, 4) 2774 verifyLastTasksetIsRerefresh(c, tts) 2775 2776 // to make TaskSnapSetup work 2777 chg := st.NewChange("refresh", "...") 2778 for _, ts := range tts[:len(tts)-1] { 2779 chg.AddAll(ts) 2780 } 2781 2782 // force hold state to hit ignore status of findSymmetricAutoconnect 2783 tts[2].Tasks()[0].SetStatus(state.HoldStatus) 2784 2785 st.Unlock() 2786 err = s.o.Settle(3 * time.Second) 2787 st.Lock() 2788 c.Assert(err, IsNil) 2789 2790 // simulate successful restart happened 2791 state.MockRestarting(st, state.RestartUnset) 2792 tts[2].Tasks()[0].SetStatus(state.DefaultStatus) 2793 st.Unlock() 2794 2795 err = s.o.Settle(settleTimeout) 2796 st.Lock() 2797 2798 c.Assert(err, IsNil) 2799 2800 c.Assert(chg.Status(), Equals, state.DoneStatus) 2801 2802 // check connections 2803 var conns map[string]interface{} 2804 st.Get("conns", &conns) 2805 c.Assert(conns, DeepEquals, map[string]interface{}{ 2806 "some-snap:home core:home": map[string]interface{}{"interface": "home", "auto": true}, 2807 "some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true}, 2808 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true}, 2809 }) 2810 2811 connections, err := repo.Connections("some-snap") 2812 c.Assert(err, IsNil) 2813 c.Assert(connections, HasLen, 3) 2814 } 2815 2816 func (s *mgrsSuite) TestUpdateWithAutoconnectAndInactiveRevisions(c *C) { 2817 const someSnapYaml = `name: some-snap 2818 version: 1.0 2819 apps: 2820 foo: 2821 command: bin/bar 2822 plugs: [network] 2823 ` 2824 const coreSnapYaml = `name: core 2825 type: os 2826 version: 1` 2827 2828 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 2829 s.serveSnap(snapPath, "40") 2830 2831 mockServer := s.mockStore(c) 2832 defer mockServer.Close() 2833 2834 st := s.o.State() 2835 st.Lock() 2836 defer st.Unlock() 2837 2838 si1 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 2839 snapInfo := snaptest.MockSnap(c, someSnapYaml, si1) 2840 c.Assert(snapInfo.Plugs, HasLen, 1) 2841 2842 csi := &snap.SideInfo{RealName: "core", SnapID: fakeSnapID("core"), Revision: snap.R(1)} 2843 coreInfo := snaptest.MockSnap(c, coreSnapYaml, csi) 2844 2845 // add implicit slots 2846 coreInfo.Slots["network"] = &snap.SlotInfo{ 2847 Name: "network", 2848 Snap: coreInfo, 2849 Interface: "network", 2850 } 2851 2852 // some-snap has inactive revisions 2853 si0 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(0)} 2854 si2 := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(2)} 2855 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 2856 Active: true, 2857 Sequence: []*snap.SideInfo{si0, si1, si2}, 2858 Current: snap.R(1), 2859 SnapType: "app", 2860 }) 2861 2862 repo := s.o.InterfaceManager().Repository() 2863 2864 // add snaps to the repo to have plugs/slots 2865 c.Assert(repo.AddSnap(snapInfo), IsNil) 2866 c.Assert(repo.AddSnap(coreInfo), IsNil) 2867 2868 // refresh all 2869 err := assertstate.RefreshSnapDeclarations(st, 0) 2870 c.Assert(err, IsNil) 2871 2872 updates, tts, err := snapstate.UpdateMany(context.TODO(), st, []string{"some-snap"}, 0, nil) 2873 c.Assert(err, IsNil) 2874 c.Check(updates, HasLen, 1) 2875 c.Assert(tts, HasLen, 2) 2876 verifyLastTasksetIsRerefresh(c, tts) 2877 2878 // to make TaskSnapSetup work 2879 chg := st.NewChange("refresh", "...") 2880 chg.AddAll(tts[0]) 2881 2882 st.Unlock() 2883 err = s.o.Settle(settleTimeout) 2884 st.Lock() 2885 2886 c.Assert(err, IsNil) 2887 c.Assert(chg.Status(), Equals, state.DoneStatus) 2888 2889 // check connections 2890 var conns map[string]interface{} 2891 st.Get("conns", &conns) 2892 c.Assert(conns, DeepEquals, map[string]interface{}{ 2893 "some-snap:network core:network": map[string]interface{}{"interface": "network", "auto": true}, 2894 }) 2895 } 2896 2897 const someSnapYaml = `name: some-snap 2898 version: 1.0 2899 apps: 2900 foo: 2901 command: bin/bar 2902 slots: [media-hub] 2903 ` 2904 2905 func (s *mgrsSuite) testUpdateWithAutoconnectRetry(c *C, updateSnapName, removeSnapName string) { 2906 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 2907 s.serveSnap(snapPath, "40") 2908 2909 snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50") 2910 s.serveSnap(snapPath, "50") 2911 2912 mockServer := s.mockStore(c) 2913 defer mockServer.Close() 2914 2915 st := s.o.State() 2916 st.Lock() 2917 defer st.Unlock() 2918 2919 st.Set("conns", map[string]interface{}{}) 2920 2921 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 2922 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 2923 c.Assert(snapInfo.Slots, HasLen, 1) 2924 2925 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 2926 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 2927 c.Assert(otherInfo.Plugs, HasLen, 1) 2928 2929 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 2930 Active: true, 2931 Sequence: []*snap.SideInfo{si}, 2932 Current: snap.R(1), 2933 SnapType: "app", 2934 }) 2935 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 2936 Active: true, 2937 Sequence: []*snap.SideInfo{oi}, 2938 Current: snap.R(1), 2939 SnapType: "app", 2940 }) 2941 2942 repo := s.o.InterfaceManager().Repository() 2943 2944 // add snaps to the repo to have plugs/slots 2945 c.Assert(repo.AddSnap(snapInfo), IsNil) 2946 c.Assert(repo.AddSnap(otherInfo), IsNil) 2947 2948 // refresh all 2949 err := assertstate.RefreshSnapDeclarations(st, 0) 2950 c.Assert(err, IsNil) 2951 2952 ts, err := snapstate.Update(st, updateSnapName, nil, 0, snapstate.Flags{}) 2953 c.Assert(err, IsNil) 2954 2955 // to make TaskSnapSetup work 2956 chg := st.NewChange("refresh", "...") 2957 chg.AddAll(ts) 2958 2959 // remove other-snap 2960 ts2, err := snapstate.Remove(st, removeSnapName, snap.R(0), nil) 2961 c.Assert(err, IsNil) 2962 chg2 := st.NewChange("remove-snap", "...") 2963 chg2.AddAll(ts2) 2964 2965 // force hold state on first removal task to hit Retry error 2966 ts2.Tasks()[0].SetStatus(state.HoldStatus) 2967 2968 // Settle is not converging here because of the task in Hold status, therefore 2969 // it always hits given timeout before we carry on with the test. We're 2970 // interested in hitting the retry condition on auto-connect task, so 2971 // instead of passing a generous timeout to Settle(), repeat Settle() a number 2972 // of times with an aggressive timeout and break as soon as we reach the desired 2973 // state of auto-connect task. 2974 var retryCheck bool 2975 var autoconnectLog string 2976 for i := 0; i < 50 && !retryCheck; i++ { 2977 st.Unlock() 2978 s.o.Settle(aggressiveSettleTimeout) 2979 st.Lock() 2980 2981 for _, t := range st.Tasks() { 2982 if t.Kind() == "auto-connect" && t.Status() == state.DoingStatus && strings.Contains(strings.Join(t.Log(), ""), "Waiting") { 2983 autoconnectLog = strings.Join(t.Log(), "") 2984 retryCheck = true 2985 break 2986 } 2987 } 2988 } 2989 2990 c.Check(retryCheck, Equals, true) 2991 c.Assert(autoconnectLog, Matches, `.*Waiting for conflicting change in progress: conflicting snap.*`) 2992 2993 // back to default state, that will unblock autoconnect 2994 ts2.Tasks()[0].SetStatus(state.DefaultStatus) 2995 st.Unlock() 2996 err = s.o.Settle(settleTimeout) 2997 st.Lock() 2998 c.Assert(err, IsNil) 2999 3000 c.Check(chg.Err(), IsNil) 3001 c.Assert(chg.Status(), Equals, state.DoneStatus) 3002 3003 // check connections 3004 var conns map[string]interface{} 3005 st.Get("conns", &conns) 3006 c.Assert(conns, HasLen, 0) 3007 } 3008 3009 func (s *mgrsSuite) TestUpdateWithAutoconnectRetrySlotSide(c *C) { 3010 s.testUpdateWithAutoconnectRetry(c, "some-snap", "other-snap") 3011 } 3012 3013 func (s *mgrsSuite) TestUpdateWithAutoconnectRetryPlugSide(c *C) { 3014 s.testUpdateWithAutoconnectRetry(c, "other-snap", "some-snap") 3015 } 3016 3017 func (s *mgrsSuite) TestDisconnectIgnoredOnSymmetricRemove(c *C) { 3018 const someSnapYaml = `name: some-snap 3019 version: 1.0 3020 apps: 3021 foo: 3022 command: bin/bar 3023 slots: [media-hub] 3024 hooks: 3025 disconnect-slot-media-hub: 3026 ` 3027 const otherSnapYaml = `name: other-snap 3028 version: 1.0 3029 apps: 3030 baz: 3031 command: bin/bar 3032 plugs: [media-hub] 3033 hooks: 3034 disconnect-plug-media-hub: 3035 ` 3036 st := s.o.State() 3037 st.Lock() 3038 defer st.Unlock() 3039 3040 st.Set("conns", map[string]interface{}{ 3041 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false}, 3042 }) 3043 3044 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3045 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 3046 c.Assert(snapInfo.Slots, HasLen, 1) 3047 3048 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 3049 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 3050 c.Assert(otherInfo.Plugs, HasLen, 1) 3051 3052 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3053 Active: true, 3054 Sequence: []*snap.SideInfo{si}, 3055 Current: snap.R(1), 3056 SnapType: "app", 3057 }) 3058 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 3059 Active: true, 3060 Sequence: []*snap.SideInfo{oi}, 3061 Current: snap.R(1), 3062 SnapType: "app", 3063 }) 3064 3065 repo := s.o.InterfaceManager().Repository() 3066 3067 // add snaps to the repo to have plugs/slots 3068 c.Assert(repo.AddSnap(snapInfo), IsNil) 3069 c.Assert(repo.AddSnap(otherInfo), IsNil) 3070 repo.Connect(&interfaces.ConnRef{ 3071 PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"}, 3072 SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"}, 3073 }, nil, nil, nil, nil, nil) 3074 3075 ts, err := snapstate.Remove(st, "some-snap", snap.R(0), nil) 3076 c.Assert(err, IsNil) 3077 chg := st.NewChange("uninstall", "...") 3078 chg.AddAll(ts) 3079 3080 // remove other-snap 3081 ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), nil) 3082 c.Assert(err, IsNil) 3083 chg2 := st.NewChange("uninstall", "...") 3084 chg2.AddAll(ts2) 3085 3086 st.Unlock() 3087 err = s.o.Settle(settleTimeout) 3088 st.Lock() 3089 c.Assert(err, IsNil) 3090 3091 c.Assert(chg.Status(), Equals, state.DoneStatus) 3092 3093 // check connections 3094 var conns map[string]interface{} 3095 st.Get("conns", &conns) 3096 c.Assert(conns, HasLen, 0) 3097 3098 var disconnectInterfacesCount, slotHookCount, plugHookCount int 3099 for _, t := range st.Tasks() { 3100 if t.Kind() == "auto-disconnect" { 3101 disconnectInterfacesCount++ 3102 } 3103 if t.Kind() == "run-hook" { 3104 var hsup hookstate.HookSetup 3105 c.Assert(t.Get("hook-setup", &hsup), IsNil) 3106 if hsup.Hook == "disconnect-plug-media-hub" { 3107 plugHookCount++ 3108 } 3109 if hsup.Hook == "disconnect-slot-media-hub" { 3110 slotHookCount++ 3111 } 3112 } 3113 } 3114 c.Assert(plugHookCount, Equals, 1) 3115 c.Assert(slotHookCount, Equals, 1) 3116 c.Assert(disconnectInterfacesCount, Equals, 2) 3117 3118 var snst snapstate.SnapState 3119 err = snapstate.Get(st, "other-snap", &snst) 3120 c.Assert(err, Equals, state.ErrNoState) 3121 _, err = repo.Connected("other-snap", "media-hub") 3122 c.Assert(err, ErrorMatches, `snap "other-snap" has no plug or slot named "media-hub"`) 3123 } 3124 3125 func (s *mgrsSuite) TestDisconnectOnUninstallRemovesAutoconnection(c *C) { 3126 st := s.o.State() 3127 st.Lock() 3128 defer st.Unlock() 3129 3130 st.Set("conns", map[string]interface{}{ 3131 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": true}, 3132 }) 3133 3134 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 3135 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 3136 3137 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 3138 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 3139 3140 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 3141 Active: true, 3142 Sequence: []*snap.SideInfo{si}, 3143 Current: snap.R(1), 3144 SnapType: "app", 3145 }) 3146 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 3147 Active: true, 3148 Sequence: []*snap.SideInfo{oi}, 3149 Current: snap.R(1), 3150 SnapType: "app", 3151 }) 3152 3153 repo := s.o.InterfaceManager().Repository() 3154 3155 // add snaps to the repo to have plugs/slots 3156 c.Assert(repo.AddSnap(snapInfo), IsNil) 3157 c.Assert(repo.AddSnap(otherInfo), IsNil) 3158 repo.Connect(&interfaces.ConnRef{ 3159 PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"}, 3160 SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"}, 3161 }, nil, nil, nil, nil, nil) 3162 3163 ts, err := snapstate.Remove(st, "some-snap", snap.R(0), nil) 3164 c.Assert(err, IsNil) 3165 chg := st.NewChange("uninstall", "...") 3166 chg.AddAll(ts) 3167 3168 st.Unlock() 3169 err = s.o.Settle(settleTimeout) 3170 st.Lock() 3171 c.Assert(err, IsNil) 3172 3173 c.Assert(chg.Status(), Equals, state.DoneStatus) 3174 3175 // check connections; auto-connection should be removed completely from conns on uninstall. 3176 var conns map[string]interface{} 3177 st.Get("conns", &conns) 3178 c.Assert(conns, HasLen, 0) 3179 } 3180 3181 // TODO: add a custom checker in testutils for this and similar 3182 func validateDownloadCheckTasks(c *C, tasks []*state.Task, name, revno, channel string) int { 3183 var i int 3184 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Ensure prerequisites for "%s" are available`, name)) 3185 i++ 3186 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Download snap "%s" (%s) from channel "%s"`, name, revno, channel)) 3187 i++ 3188 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Fetch and check assertions for snap "%s" (%s)`, name, revno)) 3189 i++ 3190 return i 3191 } 3192 3193 func validateInstallTasks(c *C, tasks []*state.Task, name, revno string) int { 3194 var i int 3195 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno)) 3196 i++ 3197 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name)) 3198 i++ 3199 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno)) 3200 i++ 3201 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno)) 3202 i++ 3203 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name)) 3204 i++ 3205 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name)) 3206 i++ 3207 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name)) 3208 i++ 3209 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run install hook of "%s" snap if present`, name)) 3210 i++ 3211 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno)) 3212 i++ 3213 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name)) 3214 i++ 3215 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name)) 3216 i++ 3217 return i 3218 } 3219 3220 func validateRefreshTasks(c *C, tasks []*state.Task, name, revno string) int { 3221 var i int 3222 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Mount snap "%s" (%s)`, name, revno)) 3223 i++ 3224 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run pre-refresh hook of "%s" snap if present`, name)) 3225 i++ 3226 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Stop snap "%s" services`, name)) 3227 i++ 3228 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Remove aliases for snap "%s"`, name)) 3229 i++ 3230 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make current revision for snap "%s" unavailable`, name)) 3231 i++ 3232 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Copy snap "%s" data`, name)) 3233 i++ 3234 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" (%s) security profiles`, name, revno)) 3235 i++ 3236 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Make snap "%s" (%s) available to the system`, name, revno)) 3237 i++ 3238 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Automatically connect eligible plugs and slots of snap "%s"`, name)) 3239 i++ 3240 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Set automatic aliases for snap "%s"`, name)) 3241 i++ 3242 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Setup snap "%s" aliases`, name)) 3243 i++ 3244 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run post-refresh hook of "%s" snap if present`, name)) 3245 i++ 3246 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Start snap "%s" (%s) services`, name, revno)) 3247 i++ 3248 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Clean up "%s" (%s) install`, name, revno)) 3249 i++ 3250 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run configure hook of "%s" snap if present`, name)) 3251 i++ 3252 c.Assert(tasks[i].Summary(), Equals, fmt.Sprintf(`Run health check of "%s" snap`, name)) 3253 i++ 3254 return i 3255 } 3256 3257 // byReadyTime sorts a list of tasks by their "ready" time 3258 type byReadyTime []*state.Task 3259 3260 func (a byReadyTime) Len() int { return len(a) } 3261 func (a byReadyTime) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 3262 func (a byReadyTime) Less(i, j int) bool { return a[i].ReadyTime().Before(a[j].ReadyTime()) } 3263 3264 func (s *mgrsSuite) TestRemodelRequiredSnapsAdded(c *C) { 3265 for _, name := range []string{"foo", "bar", "baz"} { 3266 s.prereqSnapAssertions(c, map[string]interface{}{ 3267 "snap-name": name, 3268 }) 3269 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", name), "1") 3270 s.serveSnap(snapPath, "1") 3271 } 3272 3273 mockServer := s.mockStore(c) 3274 defer mockServer.Close() 3275 3276 st := s.o.State() 3277 st.Lock() 3278 defer st.Unlock() 3279 3280 // pretend we have an old required snap installed 3281 si1 := &snap.SideInfo{RealName: "old-required-snap-1", Revision: snap.R(1)} 3282 snapstate.Set(st, "old-required-snap-1", &snapstate.SnapState{ 3283 SnapType: "app", 3284 Active: true, 3285 Sequence: []*snap.SideInfo{si1}, 3286 Current: si1.Revision, 3287 Flags: snapstate.Flags{Required: true}, 3288 }) 3289 3290 // create/set custom model assertion 3291 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 3292 3293 model := s.brands.Model("my-brand", "my-model", modelDefaults) 3294 3295 // setup model assertion 3296 devicestatetest.SetDevice(st, &auth.DeviceState{ 3297 Brand: "my-brand", 3298 Model: "my-model", 3299 Serial: "serialserialserial", 3300 }) 3301 err := assertstate.Add(st, model) 3302 c.Assert(err, IsNil) 3303 3304 // create a new model 3305 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 3306 "required-snaps": []interface{}{"foo", "bar", "baz"}, 3307 "revision": "1", 3308 }) 3309 3310 chg, err := devicestate.Remodel(st, newModel) 3311 c.Assert(err, IsNil) 3312 3313 c.Check(devicestate.Remodeling(st), Equals, true) 3314 3315 st.Unlock() 3316 err = s.o.Settle(settleTimeout) 3317 st.Lock() 3318 c.Assert(err, IsNil) 3319 3320 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 3321 3322 c.Check(devicestate.Remodeling(st), Equals, false) 3323 3324 // the new required-snap "foo" is installed 3325 var snapst snapstate.SnapState 3326 err = snapstate.Get(st, "foo", &snapst) 3327 c.Assert(err, IsNil) 3328 info, err := snapst.CurrentInfo() 3329 c.Assert(err, IsNil) 3330 c.Check(info.Revision, Equals, snap.R(1)) 3331 c.Check(info.Version, Equals, "1.0") 3332 3333 // and marked required 3334 c.Check(snapst.Required, Equals, true) 3335 3336 // and core is still marked required 3337 err = snapstate.Get(st, "core", &snapst) 3338 c.Assert(err, IsNil) 3339 c.Check(snapst.Required, Equals, true) 3340 3341 // but old-required-snap-1 is no longer marked required 3342 err = snapstate.Get(st, "old-required-snap-1", &snapst) 3343 c.Assert(err, IsNil) 3344 c.Check(snapst.Required, Equals, false) 3345 3346 // ensure sorting is correct 3347 tasks := chg.Tasks() 3348 sort.Sort(byReadyTime(tasks)) 3349 3350 var i int 3351 // first all downloads/checks in sequential order 3352 for _, name := range []string{"foo", "bar", "baz"} { 3353 i += validateDownloadCheckTasks(c, tasks[i:], name, "1", "stable") 3354 } 3355 // then all installs in sequential order 3356 for _, name := range []string{"foo", "bar", "baz"} { 3357 i += validateInstallTasks(c, tasks[i:], name, "1") 3358 } 3359 // ensure that we only have the tasks we checked (plus the one 3360 // extra "set-model" task) 3361 c.Assert(tasks, HasLen, i+1) 3362 } 3363 3364 func (s *mgrsSuite) TestRemodelDifferentBase(c *C) { 3365 // make "core18" snap available in the store 3366 s.prereqSnapAssertions(c, map[string]interface{}{ 3367 "snap-name": "core18", 3368 }) 3369 snapYamlContent := `name: core18 3370 version: 18.04 3371 type: base` 3372 snapPath, _ := s.makeStoreTestSnap(c, snapYamlContent, "18") 3373 s.serveSnap(snapPath, "18") 3374 3375 mockServer := s.mockStore(c) 3376 defer mockServer.Close() 3377 3378 st := s.o.State() 3379 st.Lock() 3380 defer st.Unlock() 3381 3382 // create/set custom model assertion 3383 model := s.brands.Model("can0nical", "my-model", modelDefaults) 3384 // setup model assertion 3385 devicestatetest.SetDevice(st, &auth.DeviceState{ 3386 Brand: "can0nical", 3387 Model: "my-model", 3388 Serial: "serialserialserial", 3389 }) 3390 err := assertstate.Add(st, model) 3391 c.Assert(err, IsNil) 3392 3393 // create a new model 3394 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 3395 "base": "core18", 3396 "revision": "1", 3397 }) 3398 3399 chg, err := devicestate.Remodel(st, newModel) 3400 c.Assert(err, ErrorMatches, "cannot remodel to different bases yet") 3401 c.Assert(chg, IsNil) 3402 } 3403 3404 func (s *mgrsSuite) TestRemodelSwitchKernelTrack(c *C) { 3405 bloader := bootloadertest.Mock("mock", c.MkDir()) 3406 bloader.SetBootKernel("pc-kernel_1.snap") 3407 bloader.SetBootBase("core_1.snap") 3408 bootloader.Force(bloader) 3409 defer bootloader.Force(nil) 3410 3411 restore := release.MockOnClassic(false) 3412 defer restore() 3413 3414 mockServer := s.mockStore(c) 3415 defer mockServer.Close() 3416 3417 st := s.o.State() 3418 st.Lock() 3419 defer st.Unlock() 3420 3421 si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)} 3422 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 3423 Active: true, 3424 Sequence: []*snap.SideInfo{si}, 3425 Current: snap.R(1), 3426 SnapType: "kernel", 3427 }) 3428 3429 const kernelYaml = `name: pc-kernel 3430 type: kernel 3431 version: 2.0` 3432 snapPath, _ := s.makeStoreTestSnap(c, kernelYaml, "2") 3433 s.serveSnap(snapPath, "2") 3434 3435 s.prereqSnapAssertions(c, map[string]interface{}{ 3436 "snap-name": "foo", 3437 }) 3438 snapPath, _ = s.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1") 3439 s.serveSnap(snapPath, "1") 3440 3441 // create/set custom model assertion 3442 model := s.brands.Model("can0nical", "my-model", modelDefaults) 3443 // setup model assertion 3444 devicestatetest.SetDevice(st, &auth.DeviceState{ 3445 Brand: "can0nical", 3446 Model: "my-model", 3447 Serial: "serialserialserial", 3448 }) 3449 err := assertstate.Add(st, model) 3450 c.Assert(err, IsNil) 3451 3452 // create a new model 3453 newModel := s.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 3454 "kernel": "pc-kernel=18", 3455 "revision": "1", 3456 "required-snaps": []interface{}{"foo"}, 3457 }) 3458 3459 chg, err := devicestate.Remodel(st, newModel) 3460 c.Assert(err, IsNil) 3461 3462 st.Unlock() 3463 err = s.o.Settle(settleTimeout) 3464 st.Lock() 3465 c.Assert(err, IsNil) 3466 3467 // system waits for a restart because of the new kernel 3468 t := findKind(chg, "auto-connect") 3469 c.Assert(t, NotNil) 3470 c.Assert(t.Status(), Equals, state.DoingStatus) 3471 3472 // simulate successful restart happened 3473 state.MockRestarting(st, state.RestartUnset) 3474 3475 // continue 3476 st.Unlock() 3477 err = s.o.Settle(settleTimeout) 3478 st.Lock() 3479 c.Assert(err, IsNil) 3480 3481 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 3482 3483 // ensure tasks were run in the right order 3484 tasks := chg.Tasks() 3485 sort.Sort(byReadyTime(tasks)) 3486 3487 // first all downloads/checks in sequential order 3488 var i int 3489 i += validateDownloadCheckTasks(c, tasks[i:], "pc-kernel", "2", "18") 3490 i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable") 3491 3492 // then all installs in sequential order 3493 i += validateRefreshTasks(c, tasks[i:], "pc-kernel", "2") 3494 i += validateInstallTasks(c, tasks[i:], "foo", "1") 3495 3496 // ensure that we only have the tasks we checked (plus the one 3497 // extra "set-model" task) 3498 c.Assert(tasks, HasLen, i+1) 3499 } 3500 3501 func (ms *mgrsSuite) TestRemodelSwitchToDifferentKernel(c *C) { 3502 bloader := bootloadertest.Mock("mock", c.MkDir()) 3503 bootloader.Force(bloader) 3504 defer bootloader.Force(nil) 3505 3506 restore := release.MockOnClassic(false) 3507 defer restore() 3508 3509 mockServer := ms.mockStore(c) 3510 defer mockServer.Close() 3511 3512 st := ms.o.State() 3513 st.Lock() 3514 defer st.Unlock() 3515 3516 si := &snap.SideInfo{RealName: "pc-kernel", SnapID: fakeSnapID("pc-kernel"), Revision: snap.R(1)} 3517 snapstate.Set(st, "pc-kernel", &snapstate.SnapState{ 3518 Active: true, 3519 Sequence: []*snap.SideInfo{si}, 3520 Current: snap.R(1), 3521 SnapType: "kernel", 3522 }) 3523 bloader.SetBootVars(map[string]string{ 3524 "snap_mode": "", 3525 "snap_core": "core_1.snap", 3526 "snap_kernel": "pc-kernel_1.snap", 3527 }) 3528 si2 := &snap.SideInfo{RealName: "pc", SnapID: fakeSnapID("pc"), Revision: snap.R(1)} 3529 gadgetSnapYaml := "name: pc\nversion: 1.0\ntype: gadget" 3530 snapstate.Set(st, "pc", &snapstate.SnapState{ 3531 Active: true, 3532 Sequence: []*snap.SideInfo{si2}, 3533 Current: snap.R(1), 3534 SnapType: "gadget", 3535 }) 3536 gadgetYaml := ` 3537 volumes: 3538 volume-id: 3539 bootloader: grub 3540 ` 3541 snaptest.MockSnapWithFiles(c, gadgetSnapYaml, si2, [][]string{ 3542 {"meta/gadget.yaml", gadgetYaml}, 3543 }) 3544 3545 // add "brand-kernel" snap to fake store 3546 const brandKernelYaml = `name: brand-kernel 3547 type: kernel 3548 version: 1.0` 3549 ms.prereqSnapAssertions(c, map[string]interface{}{ 3550 "snap-name": "brand-kernel", 3551 "publisher-id": "can0nical", 3552 }) 3553 snapPath, _ := ms.makeStoreTestSnap(c, brandKernelYaml, "2") 3554 ms.serveSnap(snapPath, "2") 3555 3556 // add "foo" snap to fake store 3557 ms.prereqSnapAssertions(c, map[string]interface{}{ 3558 "snap-name": "foo", 3559 }) 3560 snapPath, _ = ms.makeStoreTestSnap(c, `{name: "foo", version: 1.0}`, "1") 3561 ms.serveSnap(snapPath, "1") 3562 3563 // create/set custom model assertion 3564 model := ms.brands.Model("can0nical", "my-model", modelDefaults) 3565 3566 // setup model assertion 3567 devicestatetest.SetDevice(st, &auth.DeviceState{ 3568 Brand: "can0nical", 3569 Model: "my-model", 3570 Serial: "serialserialserial", 3571 }) 3572 err := assertstate.Add(st, model) 3573 c.Assert(err, IsNil) 3574 3575 // create a new model 3576 newModel := ms.brands.Model("can0nical", "my-model", modelDefaults, map[string]interface{}{ 3577 "kernel": "brand-kernel", 3578 "revision": "1", 3579 "required-snaps": []interface{}{"foo"}, 3580 }) 3581 3582 chg, err := devicestate.Remodel(st, newModel) 3583 c.Assert(err, IsNil) 3584 3585 st.Unlock() 3586 // regular settleTimeout is not enough on arm buildds :/ 3587 err = ms.o.Settle(4 * settleTimeout) 3588 st.Lock() 3589 c.Assert(err, IsNil) 3590 c.Assert(chg.Err(), IsNil) 3591 3592 // system waits for a restart because of the new kernel 3593 t := findKind(chg, "auto-connect") 3594 c.Assert(t, NotNil) 3595 c.Assert(t.Status(), Equals, state.DoingStatus) 3596 3597 // simulate successful restart happened 3598 state.MockRestarting(st, state.RestartUnset) 3599 3600 // continue 3601 st.Unlock() 3602 // regular settleTimeout is not enough on arm buildds :/ 3603 err = ms.o.Settle(4 * settleTimeout) 3604 st.Lock() 3605 c.Assert(err, IsNil) 3606 3607 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 3608 3609 // ensure tasks were run in the right order 3610 tasks := chg.Tasks() 3611 sort.Sort(byReadyTime(tasks)) 3612 3613 // first all downloads/checks in sequential order 3614 var i int 3615 i += validateDownloadCheckTasks(c, tasks[i:], "brand-kernel", "2", "stable") 3616 i += validateDownloadCheckTasks(c, tasks[i:], "foo", "1", "stable") 3617 3618 // then all installs in sequential order 3619 i += validateInstallTasks(c, tasks[i:], "brand-kernel", "2") 3620 i += validateInstallTasks(c, tasks[i:], "foo", "1") 3621 3622 // ensure that we only have the tasks we checked (plus the one 3623 // extra "set-model" task) 3624 c.Assert(tasks, HasLen, i+1) 3625 3626 // ensure we did not try device registration 3627 for _, t := range st.Tasks() { 3628 if t.Kind() == "request-serial" { 3629 c.Fatalf("test should not create a request-serial task but did") 3630 } 3631 } 3632 } 3633 3634 func (s *mgrsSuite) TestRemodelStoreSwitch(c *C) { 3635 s.prereqSnapAssertions(c, map[string]interface{}{ 3636 "snap-name": "foo", 3637 }) 3638 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1") 3639 s.serveSnap(snapPath, "1") 3640 3641 // track the creation of new DeviceAndAutContext (for new Store) 3642 newDAC := false 3643 3644 mockServer := s.mockStore(c) 3645 defer mockServer.Close() 3646 3647 st := s.o.State() 3648 st.Lock() 3649 defer st.Unlock() 3650 3651 s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) { 3652 // the DeviceAndAuthContext assumes state is unlocked 3653 st.Unlock() 3654 defer st.Lock() 3655 c.Check(dac, NotNil) 3656 stoID, err := dac.StoreID("") 3657 c.Assert(err, IsNil) 3658 c.Check(stoID, Equals, "switched-store") 3659 newDAC = true 3660 } 3661 3662 // create/set custom model assertion 3663 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 3664 3665 model := s.brands.Model("my-brand", "my-model", modelDefaults) 3666 3667 // setup model assertion 3668 err := assertstate.Add(st, model) 3669 c.Assert(err, IsNil) 3670 3671 // have a serial as well 3672 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 3673 c.Assert(err, IsNil) 3674 err = kpMgr.Put(deviceKey) 3675 c.Assert(err, IsNil) 3676 3677 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 3678 c.Assert(err, IsNil) 3679 serial, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, map[string]interface{}{ 3680 "authority-id": "my-brand", 3681 "brand-id": "my-brand", 3682 "model": "my-model", 3683 "serial": "store-switch-serial", 3684 "device-key": string(encDevKey), 3685 "device-key-sha3-384": deviceKey.PublicKey().ID(), 3686 "timestamp": time.Now().Format(time.RFC3339), 3687 }, nil, "") 3688 c.Assert(err, IsNil) 3689 err = assertstate.Add(st, serial) 3690 c.Assert(err, IsNil) 3691 3692 devicestatetest.SetDevice(st, &auth.DeviceState{ 3693 Brand: "my-brand", 3694 Model: "my-model", 3695 KeyID: deviceKey.PublicKey().ID(), 3696 Serial: "store-switch-serial", 3697 }) 3698 3699 // create a new model 3700 newModel := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 3701 "store": "switched-store", 3702 "required-snaps": []interface{}{"foo"}, 3703 "revision": "1", 3704 }) 3705 3706 s.expectedSerial = "store-switch-serial" 3707 s.expectedStore = "switched-store" 3708 s.sessionMacaroon = "switched-store-session" 3709 3710 chg, err := devicestate.Remodel(st, newModel) 3711 c.Assert(err, IsNil) 3712 3713 st.Unlock() 3714 err = s.o.Settle(settleTimeout) 3715 st.Lock() 3716 c.Assert(err, IsNil) 3717 3718 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 3719 3720 // the new required-snap "foo" is installed 3721 var snapst snapstate.SnapState 3722 err = snapstate.Get(st, "foo", &snapst) 3723 c.Assert(err, IsNil) 3724 3725 // and marked required 3726 c.Check(snapst.Required, Equals, true) 3727 3728 // a new store was made 3729 c.Check(newDAC, Equals, true) 3730 3731 // we have a session with the new store 3732 device, err := devicestatetest.Device(st) 3733 c.Assert(err, IsNil) 3734 c.Check(device.Serial, Equals, "store-switch-serial") 3735 c.Check(device.SessionMacaroon, Equals, "switched-store-session") 3736 } 3737 3738 func (s *mgrsSuite) TestHappyDeviceRegistrationWithPrepareDeviceHook(c *C) { 3739 // just to 404 locally eager account-key requests 3740 mockStoreServer := s.mockStore(c) 3741 defer mockStoreServer.Close() 3742 3743 model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 3744 "gadget": "gadget", 3745 }) 3746 3747 // reset as seeded but not registered 3748 // shortcut: have already device key 3749 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 3750 c.Assert(err, IsNil) 3751 err = kpMgr.Put(deviceKey) 3752 c.Assert(err, IsNil) 3753 3754 st := s.o.State() 3755 st.Lock() 3756 defer st.Unlock() 3757 3758 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 3759 devicestatetest.SetDevice(st, &auth.DeviceState{ 3760 Brand: "my-brand", 3761 Model: "my-model", 3762 KeyID: deviceKey.PublicKey().ID(), 3763 }) 3764 err = assertstate.Add(st, model) 3765 c.Assert(err, IsNil) 3766 3767 signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) { 3768 brandID := headers["brand-id"].(string) 3769 model := headers["model"].(string) 3770 c.Check(brandID, Equals, "my-brand") 3771 c.Check(model, Equals, "my-model") 3772 headers["authority-id"] = brandID 3773 a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "") 3774 return a, nil, err 3775 } 3776 3777 bhv := &devicestatetest.DeviceServiceBehavior{ 3778 ReqID: "REQID-1", 3779 RequestIDURLPath: "/svc/request-id", 3780 SerialURLPath: "/svc/serial", 3781 SignSerial: signSerial, 3782 } 3783 3784 mockServer := devicestatetest.MockDeviceService(c, bhv) 3785 defer mockServer.Close() 3786 3787 pDBhv := &devicestatetest.PrepareDeviceBehavior{ 3788 DeviceSvcURL: mockServer.URL + "/svc/", 3789 Headers: map[string]string{ 3790 "x-extra-header": "extra", 3791 }, 3792 RegBody: map[string]string{ 3793 "mac": "00:00:00:00:ff:00", 3794 }, 3795 ProposedSerial: "12000", 3796 } 3797 3798 r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), pDBhv) 3799 defer r() 3800 3801 // run the whole device registration process 3802 st.Unlock() 3803 err = s.o.Settle(settleTimeout) 3804 st.Lock() 3805 c.Assert(err, IsNil) 3806 3807 var becomeOperational *state.Change 3808 for _, chg := range st.Changes() { 3809 if chg.Kind() == "become-operational" { 3810 becomeOperational = chg 3811 break 3812 } 3813 } 3814 c.Assert(becomeOperational, NotNil) 3815 3816 c.Check(becomeOperational.Status().Ready(), Equals, true) 3817 c.Check(becomeOperational.Err(), IsNil) 3818 3819 device, err := devicestatetest.Device(st) 3820 c.Assert(err, IsNil) 3821 c.Check(device.Brand, Equals, "my-brand") 3822 c.Check(device.Model, Equals, "my-model") 3823 c.Check(device.Serial, Equals, "12000") 3824 3825 a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{ 3826 "brand-id": "my-brand", 3827 "model": "my-model", 3828 "serial": "12000", 3829 }) 3830 c.Assert(err, IsNil) 3831 serial := a.(*asserts.Serial) 3832 3833 var details map[string]interface{} 3834 err = yaml.Unmarshal(serial.Body(), &details) 3835 c.Assert(err, IsNil) 3836 3837 c.Check(details, DeepEquals, map[string]interface{}{ 3838 "mac": "00:00:00:00:ff:00", 3839 }) 3840 3841 c.Check(serial.DeviceKey().ID(), Equals, device.KeyID) 3842 } 3843 3844 func (s *mgrsSuite) TestRemodelReregistration(c *C) { 3845 s.prereqSnapAssertions(c, map[string]interface{}{ 3846 "snap-name": "foo", 3847 }) 3848 snapPath, _ := s.makeStoreTestSnap(c, fmt.Sprintf("{name: %s, version: 1.0}", "foo"), "1") 3849 s.serveSnap(snapPath, "1") 3850 3851 // track the creation of new DeviceAndAutContext (for new Store) 3852 newDAC := false 3853 3854 mockServer := s.mockStore(c) 3855 defer mockServer.Close() 3856 3857 st := s.o.State() 3858 st.Lock() 3859 defer st.Unlock() 3860 3861 s.checkDeviceAndAuthContext = func(dac store.DeviceAndAuthContext) { 3862 // the DeviceAndAuthContext assumes state is unlocked 3863 st.Unlock() 3864 defer st.Lock() 3865 c.Check(dac, NotNil) 3866 stoID, err := dac.StoreID("") 3867 c.Assert(err, IsNil) 3868 c.Check(stoID, Equals, "my-brand-substore") 3869 newDAC = true 3870 } 3871 3872 model := s.brands.Model("my-brand", "my-model", modelDefaults, map[string]interface{}{ 3873 "gadget": "gadget", 3874 }) 3875 3876 // setup initial device identity 3877 kpMgr, err := asserts.OpenFSKeypairManager(dirs.SnapDeviceDir) 3878 c.Assert(err, IsNil) 3879 err = kpMgr.Put(deviceKey) 3880 c.Assert(err, IsNil) 3881 3882 assertstatetest.AddMany(st, s.brands.AccountsAndKeys("my-brand")...) 3883 devicestatetest.SetDevice(st, &auth.DeviceState{ 3884 Brand: "my-brand", 3885 Model: "my-model", 3886 KeyID: deviceKey.PublicKey().ID(), 3887 Serial: "orig-serial", 3888 }) 3889 err = assertstate.Add(st, model) 3890 c.Assert(err, IsNil) 3891 3892 encDevKey, err := asserts.EncodePublicKey(deviceKey.PublicKey()) 3893 c.Assert(err, IsNil) 3894 serialHeaders := map[string]interface{}{ 3895 "brand-id": "my-brand", 3896 "model": "my-model", 3897 "serial": "orig-serial", 3898 "device-key": string(encDevKey), 3899 "device-key-sha3-384": deviceKey.PublicKey().ID(), 3900 "timestamp": time.Now().Format(time.RFC3339), 3901 } 3902 serialA, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, serialHeaders, nil, "") 3903 c.Assert(err, IsNil) 3904 serial := serialA.(*asserts.Serial) 3905 err = assertstate.Add(st, serial) 3906 c.Assert(err, IsNil) 3907 3908 signSerial := func(c *C, bhv *devicestatetest.DeviceServiceBehavior, headers map[string]interface{}, body []byte) (serial asserts.Assertion, ancillary []asserts.Assertion, err error) { 3909 brandID := headers["brand-id"].(string) 3910 model := headers["model"].(string) 3911 c.Check(brandID, Equals, "my-brand") 3912 c.Check(model, Equals, "other-model") 3913 headers["authority-id"] = brandID 3914 a, err := s.brands.Signing("my-brand").Sign(asserts.SerialType, headers, body, "") 3915 return a, nil, err 3916 } 3917 3918 bhv := &devicestatetest.DeviceServiceBehavior{ 3919 ReqID: "REQID-1", 3920 RequestIDURLPath: "/svc/request-id", 3921 SerialURLPath: "/svc/serial", 3922 SignSerial: signSerial, 3923 } 3924 3925 mockDeviceService := devicestatetest.MockDeviceService(c, bhv) 3926 defer mockDeviceService.Close() 3927 3928 r := devicestatetest.MockGadget(c, st, "gadget", snap.R(2), nil) 3929 defer r() 3930 3931 // set registration config on gadget 3932 tr := config.NewTransaction(st) 3933 c.Assert(tr.Set("gadget", "device-service.url", mockDeviceService.URL+"/svc/"), IsNil) 3934 c.Assert(tr.Set("gadget", "registration.proposed-serial", "orig-serial"), IsNil) 3935 tr.Commit() 3936 3937 // run the remodel 3938 // create a new model 3939 newModel := s.brands.Model("my-brand", "other-model", modelDefaults, map[string]interface{}{ 3940 "store": "my-brand-substore", 3941 "gadget": "gadget", 3942 "required-snaps": []interface{}{"foo"}, 3943 }) 3944 3945 s.expectedSerial = "orig-serial" 3946 s.expectedStore = "my-brand-substore" 3947 s.sessionMacaroon = "other-store-session" 3948 3949 chg, err := devicestate.Remodel(st, newModel) 3950 c.Assert(err, IsNil) 3951 3952 st.Unlock() 3953 err = s.o.Settle(settleTimeout) 3954 st.Lock() 3955 c.Assert(err, IsNil) 3956 3957 c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("upgrade-snap change failed with: %v", chg.Err())) 3958 3959 device, err := devicestatetest.Device(st) 3960 c.Assert(err, IsNil) 3961 c.Check(device.Brand, Equals, "my-brand") 3962 c.Check(device.Model, Equals, "other-model") 3963 c.Check(device.Serial, Equals, "orig-serial") 3964 3965 a, err := assertstate.DB(st).Find(asserts.SerialType, map[string]string{ 3966 "brand-id": "my-brand", 3967 "model": "other-model", 3968 "serial": "orig-serial", 3969 }) 3970 c.Assert(err, IsNil) 3971 serial = a.(*asserts.Serial) 3972 3973 c.Check(serial.Body(), HasLen, 0) 3974 c.Check(serial.DeviceKey().ID(), Equals, device.KeyID) 3975 3976 // the new required-snap "foo" is installed 3977 var snapst snapstate.SnapState 3978 err = snapstate.Get(st, "foo", &snapst) 3979 c.Assert(err, IsNil) 3980 3981 // and marked required 3982 c.Check(snapst.Required, Equals, true) 3983 3984 // a new store was made 3985 c.Check(newDAC, Equals, true) 3986 3987 // we have a session with the new store 3988 c.Check(device.SessionMacaroon, Equals, "other-store-session") 3989 } 3990 3991 func (s *mgrsSuite) TestCheckRefreshFailureWithConcurrentRemoveOfConnectedSnap(c *C) { 3992 hookMgr := s.o.HookManager() 3993 c.Assert(hookMgr, NotNil) 3994 3995 // force configure hook failure for some-snap. 3996 hookMgr.RegisterHijack("configure", "some-snap", func(ctx *hookstate.Context) error { 3997 return fmt.Errorf("failing configure hook") 3998 }) 3999 4000 snapPath, _ := s.makeStoreTestSnap(c, someSnapYaml, "40") 4001 s.serveSnap(snapPath, "40") 4002 snapPath, _ = s.makeStoreTestSnap(c, otherSnapYaml, "50") 4003 s.serveSnap(snapPath, "50") 4004 4005 mockServer := s.mockStore(c) 4006 defer mockServer.Close() 4007 4008 st := s.o.State() 4009 st.Lock() 4010 defer st.Unlock() 4011 4012 st.Set("conns", map[string]interface{}{ 4013 "other-snap:media-hub some-snap:media-hub": map[string]interface{}{"interface": "media-hub", "auto": false}, 4014 }) 4015 4016 si := &snap.SideInfo{RealName: "some-snap", SnapID: fakeSnapID("some-snap"), Revision: snap.R(1)} 4017 snapInfo := snaptest.MockSnap(c, someSnapYaml, si) 4018 4019 oi := &snap.SideInfo{RealName: "other-snap", SnapID: fakeSnapID("other-snap"), Revision: snap.R(1)} 4020 otherInfo := snaptest.MockSnap(c, otherSnapYaml, oi) 4021 4022 snapstate.Set(st, "some-snap", &snapstate.SnapState{ 4023 Active: true, 4024 Sequence: []*snap.SideInfo{si}, 4025 Current: snap.R(1), 4026 SnapType: "app", 4027 }) 4028 snapstate.Set(st, "other-snap", &snapstate.SnapState{ 4029 Active: true, 4030 Sequence: []*snap.SideInfo{oi}, 4031 Current: snap.R(1), 4032 SnapType: "app", 4033 }) 4034 4035 // add snaps to the repo and connect them 4036 repo := s.o.InterfaceManager().Repository() 4037 c.Assert(repo.AddSnap(snapInfo), IsNil) 4038 c.Assert(repo.AddSnap(otherInfo), IsNil) 4039 _, err := repo.Connect(&interfaces.ConnRef{ 4040 PlugRef: interfaces.PlugRef{Snap: "other-snap", Name: "media-hub"}, 4041 SlotRef: interfaces.SlotRef{Snap: "some-snap", Name: "media-hub"}, 4042 }, nil, nil, nil, nil, nil) 4043 c.Assert(err, IsNil) 4044 4045 // refresh all 4046 c.Assert(assertstate.RefreshSnapDeclarations(st, 0), IsNil) 4047 4048 ts, err := snapstate.Update(st, "some-snap", nil, 0, snapstate.Flags{}) 4049 c.Assert(err, IsNil) 4050 chg := st.NewChange("refresh", "...") 4051 chg.AddAll(ts) 4052 4053 // remove other-snap 4054 ts2, err := snapstate.Remove(st, "other-snap", snap.R(0), nil) 4055 c.Assert(err, IsNil) 4056 chg2 := st.NewChange("remove-snap", "...") 4057 chg2.AddAll(ts2) 4058 4059 st.Unlock() 4060 err = s.o.Settle(settleTimeout) 4061 st.Lock() 4062 4063 c.Check(err, IsNil) 4064 4065 // the refresh change has failed due to configure hook error 4066 c.Check(chg.Err(), ErrorMatches, `cannot perform the following tasks:\n.*failing configure hook.*`) 4067 c.Check(chg.Status(), Equals, state.ErrorStatus) 4068 4069 // download-snap is one of the first tasks in the refresh change, check that it was undone 4070 var downloadSnapStatus state.Status 4071 for _, t := range chg.Tasks() { 4072 if t.Kind() == "download-snap" { 4073 downloadSnapStatus = t.Status() 4074 break 4075 } 4076 } 4077 c.Check(downloadSnapStatus, Equals, state.UndoneStatus) 4078 4079 // the remove change succeeded 4080 c.Check(chg2.Err(), IsNil) 4081 c.Check(chg2.Status(), Equals, state.DoneStatus) 4082 }