func (s *kernelOSSuite) TestInUse(c *C) { for _, t := range []struct { bootVarKey string bootVarValue string snapName string snapRev snap.Revision inUse bool }{ // in use {"snap_kernel", "kernel_41.snap", "kernel", snap.R(41), true}, {"snap_try_kernel", "kernel_82.snap", "kernel", snap.R(82), true}, {"snap_core", "core_21.snap", "core", snap.R(21), true}, {"snap_try_core", "core_42.snap", "core", snap.R(42), true}, // not in use {"snap_core", "core_111.snap", "core", snap.R(21), false}, {"snap_try_core", "core_111.snap", "core", snap.R(21), false}, {"snap_kernel", "kernel_111.snap", "kernel", snap.R(1), false}, {"snap_try_kernel", "kernel_111.snap", "kernel", snap.R(1), false}, } { s.bootloader.BootVars[t.bootVarKey] = t.bootVarValue c.Assert(boot.InUse(t.snapName, t.snapRev), Equals, t.inUse, Commentf("unexpected result: %s %s %v", t.snapName, t.snapRev, t.inUse)) } }
// canRemove verifies that a snap can be removed. func canRemove(si *snap.Info, active bool) bool { // Gadget snaps should not be removed as they are a key // building block for Gadgets. Pruning non active ones // is acceptable. if si.Type == snap.TypeGadget && active { return false } // You never want to remove an active kernel or OS if (si.Type == snap.TypeKernel || si.Type == snap.TypeOS) && active { return false } // TODO: on classic likely let remove core even if active if it's only snap left. // never remove anything that is used for booting if boot.InUse(si.Name(), si.Revision) { return false } return true }
func doInstall(st *state.State, snapst *SnapState, snapsup *SnapSetup) (*state.TaskSet, error) { if snapsup.Flags.Classic && !release.OnClassic { return nil, fmt.Errorf("classic confinement is only supported on classic systems") } if !snapst.HasCurrent() { // install? // check that the snap command namespace doesn't conflict with an enabled alias if err := checkSnapAliasConflict(st, snapsup.Name()); err != nil { return nil, err } } if err := checkChangeConflict(st, snapsup.Name(), snapst); err != nil { return nil, err } targetRevision := snapsup.Revision() revisionStr := "" if snapsup.SideInfo != nil { revisionStr = fmt.Sprintf(" (%s)", targetRevision) } // check if we already have the revision locally (alters tasks) revisionIsLocal := snapst.LastIndex(targetRevision) >= 0 var prepare, prev *state.Task fromStore := false // if we have a local revision here we go back to that if snapsup.SnapPath != "" || revisionIsLocal { prepare = st.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q%s"), snapsup.SnapPath, revisionStr)) } else { fromStore = true prepare = st.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q%s from channel %q"), snapsup.Name(), revisionStr, snapsup.Channel)) } prepare.Set("snap-setup", snapsup) tasks := []*state.Task{prepare} addTask := func(t *state.Task) { t.Set("snap-setup-task", prepare.ID()) t.WaitFor(prev) tasks = append(tasks, t) } prev = prepare if fromStore { // fetch and check assertions checkAsserts := st.NewTask("validate-snap", fmt.Sprintf(i18n.G("Fetch and check assertions for snap %q%s"), snapsup.Name(), revisionStr)) addTask(checkAsserts) prev = checkAsserts } // mount if !revisionIsLocal { mount := st.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q%s"), snapsup.Name(), revisionStr)) addTask(mount) prev = mount } if snapst.Active { // unlink-current-snap (will stop services for copy-data) stop := st.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), snapsup.Name())) addTask(stop) prev = stop removeAliases := st.NewTask("remove-aliases", fmt.Sprintf(i18n.G("Remove aliases for snap %q"), snapsup.Name())) addTask(removeAliases) prev = removeAliases unlink := st.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), snapsup.Name())) addTask(unlink) prev = unlink } // copy-data (needs stopped services by unlink) if !snapsup.Flags.Revert { copyData := st.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), snapsup.Name())) addTask(copyData) prev = copyData } // security setupSecurity := st.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q%s security profiles"), snapsup.Name(), revisionStr)) addTask(setupSecurity) prev = setupSecurity // finalize (wrappers+current symlink) linkSnap := st.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q%s available to the system"), snapsup.Name(), revisionStr)) addTask(linkSnap) prev = linkSnap // setup aliases setupAliases := st.NewTask("setup-aliases", fmt.Sprintf(i18n.G("Setup snap %q aliases"), snapsup.Name())) addTask(setupAliases) prev = setupAliases // run new serices startSnapServices := st.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q%s services"), snapsup.Name(), revisionStr)) addTask(startSnapServices) prev = startSnapServices // Do not do that if we are reverting to a local revision if snapst.HasCurrent() && !snapsup.Flags.Revert { seq := snapst.Sequence currentIndex := snapst.LastIndex(snapst.Current) // discard everything after "current" (we may have reverted to // a previous versions earlier) for i := currentIndex + 1; i < len(seq); i++ { si := seq[i] if si.Revision == targetRevision { // but don't discard this one; its' the thing we're switching to! continue } ts := removeInactiveRevision(st, snapsup.Name(), si.Revision) ts.WaitFor(prev) tasks = append(tasks, ts.Tasks()...) prev = tasks[len(tasks)-1] } // make sure we're not scheduling the removal of the target // revision in the case where the target revision is already in // the sequence. for i := 0; i < currentIndex; i++ { si := seq[i] if si.Revision == targetRevision { // we do *not* want to removeInactiveRevision of this one copy(seq[i:], seq[i+1:]) seq = seq[:len(seq)-1] currentIndex-- } } // normal garbage collect for i := 0; i <= currentIndex-2; i++ { si := seq[i] if boot.InUse(snapsup.Name(), si.Revision) { continue } ts := removeInactiveRevision(st, snapsup.Name(), si.Revision) ts.WaitFor(prev) tasks = append(tasks, ts.Tasks()...) prev = tasks[len(tasks)-1] } addTask(st.NewTask("cleanup", fmt.Sprintf("Clean up %q%s install", snapsup.Name(), revisionStr))) } var defaults map[string]interface{} if !snapst.HasCurrent() && snapsup.SideInfo != nil && snapsup.SideInfo.SnapID != "" { gadget, err := GadgetInfo(st) if err != nil && err != state.ErrNoState { return nil, err } if err == nil { gadgetInfo, err := snap.ReadGadgetInfo(gadget) if err != nil { return nil, err } defaults = gadgetInfo.Defaults[snapsup.SideInfo.SnapID] } } installSet := state.NewTaskSet(tasks...) configSet := Configure(st, snapsup.Name(), defaults) configSet.WaitAll(installSet) installSet.AddAll(configSet) return installSet, nil }