func (ms *mgrsSuite) TestHappyRevert(c *C) { st := ms.o.State() st.Lock() defer st.Unlock() x1Yaml := `name: foo version: 1.0 apps: x1: command: bin/bar ` x1binary := filepath.Join(dirs.SnapBinariesDir, "foo.x1") x2Yaml := `name: foo version: 2.0 apps: x2: command: bin/bar ` x2binary := filepath.Join(dirs.SnapBinariesDir, "foo.x2") ms.installLocalTestSnap(c, x1Yaml) ms.installLocalTestSnap(c, x2Yaml) // ensure we are on x2 _, err := os.Lstat(x2binary) c.Assert(err, IsNil) _, err = os.Lstat(x1binary) c.Assert(err, ErrorMatches, ".*no such file.*") // now do the revert ts, err := snapstate.Revert(st, "foo", snapstate.Flags(0)) c.Assert(err, IsNil) chg := st.NewChange("revert-snap", "...") chg.AddAll(ts) st.Unlock() err = ms.o.Settle() st.Lock() c.Assert(err, IsNil) c.Assert(chg.Status(), Equals, state.DoneStatus, Commentf("revert-snap change failed with: %v", chg.Err())) // ensure that we use x1 now _, err = os.Lstat(x1binary) c.Assert(err, IsNil) _, err = os.Lstat(x2binary) c.Assert(err, ErrorMatches, ".*no such file.*") // ensure that x1,x2 is still there, revert just moves the "current" // pointer for _, fn := range []string{"foo_x2.snap", "foo_x1.snap"} { p := filepath.Join(dirs.SnapBlobDir, fn) c.Assert(osutil.FileExists(p), Equals, true) } }
func modeFlags(devMode, jailMode bool) (snapstate.Flags, error) { devModeOS := release.ReleaseInfo.ForceDevMode() flags := snapstate.Flags(0) if jailMode { if devModeOS { return 0, errNoJailMode } if devMode { return 0, errModeConflict } flags |= snapstate.JailMode } if devMode || devModeOS { flags |= snapstate.DevMode } return flags, nil }
// UpdateRevisions synchronizes the active kernel and OS snap versions with // the versions that actually booted. This is needed because a // system may install "os=v2" but that fails to boot. The bootloader // fallback logic will revert to "os=v1" but on the filesystem snappy // still has the "active" version set to "v2" which is // misleading. This code will check what kernel/os booted and set // those versions active. func UpdateRevisions(ovld *overlord.Overlord) error { const errorPrefix = "cannot update revisions after boot changes: " if release.OnClassic { return nil } bootloader, err := partition.FindBootloader() if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } bv := "snap_kernel" kernelSnap, err := bootloader.GetBootVar(bv) if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } bv = "snap_core" osSnap, err := bootloader.GetBootVar(bv) if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } st := ovld.State() st.Lock() installed, err := snapstate.All(st) if err != nil { return fmt.Errorf(errorPrefix+"%s", err) } var tsAll []*state.TaskSet for _, snapNameAndRevno := range []string{kernelSnap, osSnap} { name, rev, err := nameAndRevnoFromSnap(snapNameAndRevno) if err != nil { logger.Noticef("cannot parse %q: %s", snapNameAndRevno, err) continue } for snapName, snapState := range installed { if name == snapName { if rev != snapState.Current { ts, err := snapstate.RevertToRevision(st, name, rev, snapstate.Flags(0)) if err != nil { return err } tsAll = append(tsAll, ts) } } } } st.Unlock() if len(tsAll) == 0 { return nil } st.Lock() msg := fmt.Sprintf("Update kernel and core snap revisions") chg := st.NewChange("update-revisions", msg) for _, ts := range tsAll { chg.AddAll(ts) } st.Unlock() // do it and wait for ready ovld.Loop() timeoutTime := 10 * time.Second st.EnsureBefore(0) select { case <-chg.Ready(): case <-time.After(timeoutTime): return fmt.Errorf("change did not apply after %s", timeoutTime) } st.Lock() status := chg.Status() err = chg.Err() st.Unlock() if status != state.DoneStatus { ovld.Stop() return fmt.Errorf(errorPrefix+"%s", err) } return ovld.Stop() }
func populateStateFromSeed() error { if osutil.FileExists(dirs.SnapStateFile) { return fmt.Errorf("cannot create state: state %q already exists", dirs.SnapStateFile) } ovld, err := overlord.New() if err != nil { return err } st := ovld.State() // ack all initial assertions if err := importAssertionsFromSeed(st); err != nil { return err } seed, err := snap.ReadSeedYaml(filepath.Join(dirs.SnapSeedDir, "seed.yaml")) if err != nil { return err } tsAll := []*state.TaskSet{} for i, sn := range seed.Snaps { st.Lock() flags := snapstate.Flags(0) if sn.DevMode { flags |= snapstate.DevMode } path := filepath.Join(dirs.SnapSeedDir, "snaps", sn.File) var sideInfo snap.SideInfo if sn.Unasserted { sideInfo.RealName = sn.Name } else { si, err := snapasserts.DeriveSideInfo(path, assertstate.DB(st)) if err == asserts.ErrNotFound { st.Unlock() return fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path) } if err != nil { st.Unlock() return err } sideInfo = *si sideInfo.Private = sn.Private } ts, err := snapstate.InstallPath(st, &sideInfo, path, sn.Channel, flags) if i > 0 { ts.WaitAll(tsAll[i-1]) } st.Unlock() if err != nil { return err } tsAll = append(tsAll, ts) } if len(tsAll) == 0 { return nil } st.Lock() msg := fmt.Sprintf("First boot seeding") chg := st.NewChange("seed", msg) for _, ts := range tsAll { chg.AddAll(ts) } st.Unlock() // do it and wait for ready ovld.Loop() st.EnsureBefore(0) <-chg.Ready() st.Lock() status := chg.Status() err = chg.Err() st.Unlock() if status != state.DoneStatus { ovld.Stop() return fmt.Errorf("cannot run seed change: %s", err) } return ovld.Stop() }