func snapRevert(inst *snapInstruction, st *state.State) (string, []*state.TaskSet, error) { var ts *state.TaskSet flags, err := modeFlags(inst.DevMode, inst.JailMode) if err != nil { return "", nil, err } if inst.Revision.Unset() { ts, err = snapstate.Revert(st, inst.Snaps[0], flags) } else { ts, err = snapstate.RevertToRevision(st, inst.Snaps[0], inst.Revision, flags) } if err != nil { return "", nil, err } msg := fmt.Sprintf(i18n.G("Revert %q snap"), inst.Snaps[0]) return msg, []*state.TaskSet{ts}, 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() }