// 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 }
func updateInfo(st *state.State, snapst *SnapState, channel string, userID int, flags Flags) (*snap.Info, error) { user, err := userFromUserID(st, userID) if err != nil { return nil, err } curInfo, err := snapst.CurrentInfo() if err != nil { return nil, err } if curInfo.SnapID == "" { // covers also trymode return nil, fmt.Errorf("cannot refresh local snap %q", curInfo.Name()) } refreshCand := &store.RefreshCandidate{ // the desired channel Channel: channel, DevMode: flags.DevModeAllowed(), Block: snapst.Block(), SnapID: curInfo.SnapID, Revision: curInfo.Revision, Epoch: curInfo.Epoch, } theStore := Store(st) st.Unlock() // calls to the store should be done without holding the state lock res, err := theStore.ListRefresh([]*store.RefreshCandidate{refreshCand}, user) st.Lock() if len(res) == 0 { return nil, fmt.Errorf("snap %q has no updates available", curInfo.Name()) } return res[0], nil }
func doFetch(s *state.State, userID int, fetching func(asserts.Fetcher) error) error { // TODO: once we have a bulk assertion retrieval endpoint this approach will change user, err := userFromUserID(s, userID) if err != nil { return err } sto := snapstate.Store(s) retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { // TODO: ignore errors if already in db? return sto.Assertion(ref.Type, ref.PrimaryKey, user) } f := newFetcher(s, retrieve) s.Unlock() err = fetching(f) s.Lock() if err != nil { return err } // TODO: trigger w. caller a global sanity check if a is revoked // (but try to save as much possible still), // or err is a check error return f.commit() }
// Apply applies any necessary patches to update the provided state to // conventions required by the current patch level of the system. func Apply(s *state.State) error { var stateLevel int s.Lock() err := s.Get("patch-level", &stateLevel) s.Unlock() if err != nil && err != state.ErrNoState { return err } if stateLevel == Level { // already at right level, nothing to do return nil } if stateLevel > Level { return fmt.Errorf("cannot downgrade: snapd is too old for the current system state (patch level %d)", stateLevel) } level := stateLevel for level < Level { logger.Noticef("Patching system state from level %d to %d", level, level+1) patch := patches[level+1] if patch == nil { return fmt.Errorf("cannot upgrade: snapd is too new for the current system state (patch level %d)", level) } err := applyOne(patch, s, level) if err != nil { logger.Noticef("Cannnot patch: %v", err) return fmt.Errorf("cannot patch system state from level %d to %d: %v", level, level+1, err) } level++ } return nil }
// checkSnap ensures that the snap can be installed. func checkSnap(st *state.State, snapFilePath string, si *snap.SideInfo, curInfo *snap.Info, flags Flags) error { // This assumes that the snap was already verified or --dangerous was used. s, _, err := openSnapFile(snapFilePath, si) if err != nil { return err } if s.NeedsDevMode() && !flags.DevModeAllowed() { return fmt.Errorf("snap %q requires devmode or confinement override", s.Name()) } // verify we have a valid architecture if !arch.IsSupportedArchitecture(s.Architectures) { return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", s.Name(), strings.Join(s.Architectures, ", "), arch.UbuntuArchitecture()) } // check assumes err = checkAssumes(s) if err != nil { return err } st.Lock() defer st.Unlock() for _, check := range checkSnapCallbacks { err := check(st, s, curInfo, flags) if err != nil { return err } } return nil }
// Init initializes an empty state to the current implemented patch level. func Init(s *state.State) { s.Lock() defer s.Unlock() if s.Get("patch-level", new(int)) != state.ErrNoState { panic("internal error: expected empty state, attempting to override patch-level without actual patching") } s.Set("patch-level", Level) }
func importAssertionsFromSeed(st *state.State) error { st.Lock() defer st.Unlock() assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions") dc, err := ioutil.ReadDir(assertSeedDir) if err != nil { return fmt.Errorf("cannot read assert seed dir: %s", err) } // FIXME: remove this check once asserts are mandatory if len(dc) == 0 { return nil } // collect var modelRef *asserts.Ref batch := assertstate.NewBatch() for _, fi := range dc { fn := filepath.Join(assertSeedDir, fi.Name()) refs, err := readAsserts(fn, batch) if err != nil { return fmt.Errorf("cannot read assertions: %s", err) } for _, ref := range refs { if ref.Type == asserts.ModelType { if modelRef != nil && modelRef.Unique() != ref.Unique() { return fmt.Errorf("cannot add more than one model assertion") } modelRef = ref } } } // verify we have one model assertion if modelRef == nil { return fmt.Errorf("need a model assertion") } if err := batch.Commit(st); err != nil { return err } a, err := modelRef.Resolve(assertstate.DB(st).Find) if err != nil { return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err) } modelAssertion := a.(*asserts.Model) // set device,model from the model assertion auth.SetDevice(st, &auth.DeviceState{ Brand: modelAssertion.BrandID(), Model: modelAssertion.Model(), }) return nil }
func snapInfo(st *state.State, name, channel string, revision snap.Revision, userID int, flags Flags) (*snap.Info, error) { user, err := userFromUserID(st, userID) if err != nil { return nil, err } theStore := Store(st) st.Unlock() // calls to the store should be done without holding the state lock snap, err := theStore.Snap(name, channel, flags.DevModeAllowed(), revision, user) st.Lock() return snap, err }
func applyOne(patch func(s *state.State) error, s *state.State, level int) error { s.Lock() defer s.Unlock() err := patch(s) if err != nil { return err } s.Set("patch-level", level+1) return nil }
// checkSnap ensures that the snap can be installed. func checkSnap(st *state.State, snapFilePath string, curInfo *snap.Info, flags Flags) error { // This assumes that the snap was already verified or --dangerous was used. s, _, err := openSnapFile(snapFilePath, nil) if err != nil { return err } if s.NeedsDevMode() && !flags.DevModeAllowed() { return fmt.Errorf("snap %q requires devmode or confinement override", s.Name()) } // verify we have a valid architecture if !arch.IsSupportedArchitecture(s.Architectures) { return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", s.Name(), strings.Join(s.Architectures, ", "), arch.UbuntuArchitecture()) } // check assumes err = checkAssumes(s) if err != nil { return err } if s.Type != snap.TypeGadget { return nil } // gadget specific checks if release.OnClassic { // for the time being return fmt.Errorf("cannot install a gadget snap on classic") } st.Lock() defer st.Unlock() currentGadget, err := GadgetInfo(st) // in firstboot we have no gadget yet - that is ok if err == state.ErrNoState && !firstboot.HasRun() { return nil } if err != nil { return fmt.Errorf("cannot find original gadget snap") } // TODO: actually compare snap ids, from current gadget and candidate if currentGadget.Name() != s.Name() { return fmt.Errorf("cannot replace gadget snap with a different one") } return nil }
// Manager returns a new assertion manager. func Manager(s *state.State) (*AssertManager, error) { runner := state.NewTaskRunner(s) runner.AddHandler("validate-snap", doValidateSnap, nil) db, err := sysdb.Open() if err != nil { return nil, err } s.Lock() ReplaceDB(s, db) s.Unlock() return &AssertManager{runner: runner}, 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 }
// localSnapInfo returns the information about the current snap for the given name plus the SnapState with the active flag and other snap revisions. func localSnapInfo(st *state.State, name string) (*snap.Info, *snapstate.SnapState, error) { st.Lock() defer st.Unlock() var snapst snapstate.SnapState err := snapstate.Get(st, name, &snapst) if err != nil && err != state.ErrNoState { return nil, nil, fmt.Errorf("cannot consult state: %v", err) } info, err := snapst.CurrentInfo() if err == snapstate.ErrNoCurrent { return nil, nil, errNoSnap } if err != nil { return nil, nil, fmt.Errorf("cannot read snap details: %v", err) } return info, &snapst, nil }
func newRunnerManager(s *state.State) *runnerManager { rm := &runnerManager{ runner: state.NewTaskRunner(s), } rm.runner.AddHandler("runMgr1", func(t *state.Task, _ *tomb.Tomb) error { s := t.State() s.Lock() defer s.Unlock() s.Set("runMgr1Mark", 1) return nil }, nil) rm.runner.AddHandler("runMgr2", func(t *state.Task, _ *tomb.Tomb) error { s := t.State() s.Lock() defer s.Unlock() s.Set("runMgr2Mark", 1) return nil }, nil) rm.runner.AddHandler("runMgrEnsureBefore", func(t *state.Task, _ *tomb.Tomb) error { s := t.State() s.Lock() defer s.Unlock() s.EnsureBefore(20 * time.Millisecond) return nil }, nil) return rm }
func (bs *bootedSuite) makeInstalledKernelOS(c *C, st *state.State) { st.Lock() defer st.Unlock() snaptest.MockSnap(c, "name: ubuntu-core\ntype: os\nversion: 1", osSI1) snaptest.MockSnap(c, "name: ubuntu-core\ntype: os\nversion: 2", osSI2) snapstate.Set(st, "ubuntu-core", &snapstate.SnapState{ SnapType: "os", Active: true, Sequence: []*snap.SideInfo{osSI1, osSI2}, Current: snap.R(2), }) snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 1", kernelSI1) snaptest.MockSnap(c, "name: canonical-pc-linux\ntype: os\nversion: 2", kernelSI2) snapstate.Set(st, "canonical-pc-linux", &snapstate.SnapState{ SnapType: "kernel", Active: true, Sequence: []*snap.SideInfo{kernelSI1, kernelSI2}, Current: snap.R(2), }) }
func refreshCandidates(st *state.State, names []string, user *auth.UserState) ([]*snap.Info, map[string]*SnapState, error) { snapStates, err := All(st) if err != nil { return nil, nil, err } sort.Strings(names) stateByID := make(map[string]*SnapState, len(snapStates)) candidatesInfo := make([]*store.RefreshCandidate, 0, len(snapStates)) for _, snapst := range snapStates { if len(names) == 0 && (snapst.TryMode || snapst.DevMode) { // no auto-refresh for trymode nor devmode continue } // FIXME: snaps that are not active are skipped for now // until we know what we want to do if !snapst.Active { continue } snapInfo, err := snapst.CurrentInfo() if err != nil { // log something maybe? continue } if snapInfo.SnapID == "" { // no refresh for sideloaded continue } if len(names) > 0 && !contains(names, snapInfo.Name()) { continue } stateByID[snapInfo.SnapID] = snapst // get confinement preference from the snapstate candidateInfo := &store.RefreshCandidate{ // the desired channel (not info.Channel!) Channel: snapst.Channel, DevMode: snapst.DevModeAllowed(), SnapID: snapInfo.SnapID, Revision: snapInfo.Revision, Epoch: snapInfo.Epoch, } if len(names) == 0 { candidateInfo.Block = snapst.Block() } candidatesInfo = append(candidatesInfo, candidateInfo) } theStore := Store(st) st.Unlock() updates, err := theStore.ListRefresh(candidatesInfo, user) st.Lock() if err != nil { return nil, nil, err } return updates, stateByID, nil }