func (p4 patch4T) mangle(task *state.Task) error { snapsup, snapst, err := p4.snapSetupAndState(task) if err != nil { return err } var hadCandidate bool if err := p4.getMaybe(task, "had-candidate", &hadCandidate); err != nil && err != state.ErrNoState { return err } if hadCandidate { change := task.Change() if change.Kind() != "revert-snap" { return fmt.Errorf("had-candidate true for task %s (%s) of non-revert change %s (%s)", task.ID(), task.Kind(), change.ID(), change.Kind()) } } task.Clear("had-candidate") task.Set("old-candidate-index", snapst.LastIndex(snapsup.SideInfo.Revision)) return nil }
// gget does the actual work of get and getMaybe func (patch4T) gget(task *state.Task, key string, passThroughMissing bool, value interface{}) error { err := task.Get(key, value) if err == nil || (passThroughMissing && err == state.ErrNoState) { return err } change := task.Change() return fmt.Errorf("cannot get %q from task %s (%s) of change %s (%s): %v", key, task.ID(), task.Kind(), change.ID(), change.Kind(), err) }
func doInstall(s *state.State, snapst *SnapState, ss *SnapSetup) (*state.TaskSet, error) { if err := checkChangeConflict(s, ss.Name(), snapst); err != nil { return nil, err } targetRevision := ss.Revision() revisionStr := "" if ss.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 ss.SnapPath != "" || revisionIsLocal { prepare = s.NewTask("prepare-snap", fmt.Sprintf(i18n.G("Prepare snap %q%s"), ss.SnapPath, revisionStr)) } else { fromStore = true prepare = s.NewTask("download-snap", fmt.Sprintf(i18n.G("Download snap %q%s from channel %q"), ss.Name(), revisionStr, ss.Channel)) } prepare.Set("snap-setup", ss) 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 := s.NewTask("validate-snap", fmt.Sprintf(i18n.G("Fetch and check assertions for snap %q%s"), ss.Name(), revisionStr)) addTask(checkAsserts) prev = checkAsserts } // mount if !revisionIsLocal { mount := s.NewTask("mount-snap", fmt.Sprintf(i18n.G("Mount snap %q%s"), ss.Name(), revisionStr)) addTask(mount) prev = mount } if snapst.Active { // unlink-current-snap (will stop services for copy-data) stop := s.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap %q services"), ss.Name())) addTask(stop) prev = stop unlink := s.NewTask("unlink-current-snap", fmt.Sprintf(i18n.G("Make current revision for snap %q unavailable"), ss.Name())) addTask(unlink) prev = unlink } // copy-data (needs stopped services by unlink) if !ss.Flags.Revert { copyData := s.NewTask("copy-snap-data", fmt.Sprintf(i18n.G("Copy snap %q data"), ss.Name())) addTask(copyData) prev = copyData } // security setupSecurity := s.NewTask("setup-profiles", fmt.Sprintf(i18n.G("Setup snap %q%s security profiles"), ss.Name(), revisionStr)) addTask(setupSecurity) prev = setupSecurity // finalize (wrappers+current symlink) linkSnap := s.NewTask("link-snap", fmt.Sprintf(i18n.G("Make snap %q%s available to the system"), ss.Name(), revisionStr)) addTask(linkSnap) prev = linkSnap // run new serices startSnapServices := s.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap %q%s services"), ss.Name(), revisionStr)) addTask(startSnapServices) prev = startSnapServices // Do not do that if we are reverting to a local revision if snapst.HasCurrent() && !ss.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(s, ss.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] ts := removeInactiveRevision(s, ss.Name(), si.Revision) ts.WaitFor(prev) tasks = append(tasks, ts.Tasks()...) prev = tasks[len(tasks)-1] } addTask(s.NewTask("cleanup", fmt.Sprintf("Clean up %q%s install", ss.Name(), revisionStr))) } var defaults map[string]interface{} if !snapst.HasCurrent() && ss.SideInfo != nil && ss.SideInfo.SnapID != "" { gadget, err := GadgetInfo(s) 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[ss.SideInfo.SnapID] } } installSet := state.NewTaskSet(tasks...) configSet := Configure(s, ss.Name(), defaults) configSet.WaitAll(installSet) installSet.AddAll(configSet) return installSet, nil }