// allLocalSnapInfos returns the information about the all current snaps and their SnapStates. func allLocalSnapInfos(st *state.State) ([]aboutSnap, error) { st.Lock() defer st.Unlock() snapStates, err := snapstate.All(st) if err != nil { return nil, err } about := make([]aboutSnap, 0, len(snapStates)) var firstErr error for _, snapState := range snapStates { info, err := snapState.CurrentInfo() if err != nil { // XXX: aggregate instead? if firstErr == nil { firstErr = err } continue } about = append(about, aboutSnap{info, snapState}) } return about, firstErr }
// patch5: // - regenerate generated .service files func patch5(st *state.State) error { log := log{} snapStates, err := snapstate.All(st) if err != nil { return err } for snapName, snapState := range snapStates { if !snapState.Active { continue } info, err := snapState.CurrentInfo() if err != nil { return err } if len(info.Apps) == 0 { logger.Debugf("patch 5: skipping for %q: no apps", snapName) continue } err = wrappers.StopSnapServices(info, log) if err != nil { return err } err = wrappers.AddSnapServices(info, log) if err != nil { return err } err = wrappers.StartSnapServices(info, log) if err != nil { return err } logger.Noticef("patch 5: %q updated", snapName) } return nil }
// allLocalSnapInfos returns the information about the all current snaps and their SnapStates. func allLocalSnapInfos(st *state.State, all bool) ([]aboutSnap, error) { st.Lock() defer st.Unlock() snapStates, err := snapstate.All(st) if err != nil { return nil, err } about := make([]aboutSnap, 0, len(snapStates)) var firstErr error for _, snapst := range snapStates { var infos []*snap.Info var info *snap.Info var err error if all { for _, seq := range snapst.Sequence { info, err = snap.ReadInfo(seq.RealName, seq) if err != nil { break } infos = append(infos, info) } } else { info, err = snapst.CurrentInfo() infos = append(infos, info) } if err != nil { // XXX: aggregate instead? if firstErr == nil { firstErr = err } continue } for _, info := range infos { about = append(about, aboutSnap{info, snapst}) } } return about, firstErr }
// RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites. func RefreshSnapDeclarations(s *state.State, userID int) error { snapStates, err := snapstate.All(s) if err != nil { return nil } fetching := func(f asserts.Fetcher) error { for _, snapState := range snapStates { info, err := snapState.CurrentInfo() if err != nil { return err } if info.SnapID == "" { continue } if err := snapasserts.FetchSnapDeclaration(f, info.SnapID); err != nil { return fmt.Errorf("cannot refresh snap-declaration for %q: %v", info.Name(), err) } } return nil } return doFetch(s, userID, fetching) }
// ValidateRefreshes validates the refresh candidate revisions represented by the snapInfos, looking for the needed refresh control validation assertions, it returns a validated subset in validated and a summary error if not all candidates validated. func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, userID int) (validated []*snap.Info, err error) { // maps gated snap-ids to gating snap-ids controlled := make(map[string][]string) // maps gating snap-ids to their snap names gatingNames := make(map[string]string) db := DB(s) snapStates, err := snapstate.All(s) if err != nil { return nil, err } for snapName, snapState := range snapStates { info, err := snapState.CurrentInfo() if err != nil { return nil, err } if info.SnapID == "" { continue } gatingID := info.SnapID a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ "series": release.Series, "snap-id": gatingID, }) if err != nil { return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q (id %q): err", snapName, gatingID) } decl := a.(*asserts.SnapDeclaration) control := decl.RefreshControl() if len(control) == 0 { continue } gatingNames[gatingID] = decl.SnapName() for _, gatedID := range control { controlled[gatedID] = append(controlled[gatedID], gatingID) } } var errs []error for _, candInfo := range snapInfos { gatedID := candInfo.SnapID gating := controlled[gatedID] if len(gating) == 0 { // easy case, no refresh control validated = append(validated, candInfo) continue } var validationRefs []*asserts.Ref fetching := func(f asserts.Fetcher) error { for _, gatingID := range gating { valref := &asserts.Ref{ Type: asserts.ValidationType, PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()}, } err := f.Fetch(valref) if notFound, ok := err.(*store.AssertionNotFoundError); ok && notFound.Ref.Type == asserts.ValidationType { return fmt.Errorf("no validation by %q", gatingNames[gatingID]) } if err != nil { return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err) } validationRefs = append(validationRefs, valref) } return nil } err := doFetch(s, userID, fetching) if err != nil { errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.Name(), candInfo.Revision, err)) continue } var revoked *asserts.Validation for _, valref := range validationRefs { a, err := valref.Resolve(db.Find) if err != nil { return nil, fmt.Errorf("internal error: cannot find just fetched %v: %v", valref, err) } if val := a.(*asserts.Validation); val.Revoked() { revoked = val break } } if revoked != nil { errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: validation by %q (id %q) revoked", candInfo.Name(), candInfo.Revision, gatingNames[revoked.SnapID()], revoked.SnapID())) continue } validated = append(validated, candInfo) } if errs != nil { return validated, &refreshControlError{errs} } return validated, 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() }