func checkChangeConflict(s *state.State, snapName string, snapst *SnapState) error { for _, task := range s.Tasks() { k := task.Kind() chg := task.Change() if (k == "link-snap" || k == "unlink-snap") && (chg == nil || !chg.Status().Ready()) { ss, err := TaskSnapSetup(task) if err != nil { return fmt.Errorf("internal error: cannot obtain snap setup from task: %s", task.Summary()) } if ss.Name() == snapName { return fmt.Errorf("snap %q has changes in progress", snapName) } } } if snapst != nil { // caller wants us to also make sure the SnapState in state // matches the one they provided. Necessary because we need to // unlock while talking to the store, during which a change can // sneak in (if it's before the taskset is created) (e.g. for // install, while getting the snap info; for refresh, when // getting what needs refreshing). var cursnapst SnapState if err := Get(s, snapName, &cursnapst); err != nil && err != state.ErrNoState { return err } // TODO: implement the rather-boring-but-more-performant SnapState.Equals if !reflect.DeepEqual(snapst, &cursnapst) { return fmt.Errorf("snap %q state changed during install preparations", snapName) } } return nil }
// patch3: // - migrates pending tasks and add {start,stop}-snap-services tasks func patch3(s *state.State) error { // migrate all pending tasks and insert "{start,stop}-snap-server" for _, t := range s.Tasks() { if t.Status().Ready() { continue } if t.Kind() == "link-snap" { startSnapServices := s.NewTask("start-snap-services", fmt.Sprintf(i18n.G("Start snap services"))) startSnapServices.Set("snap-setup-task", t.ID()) startSnapServices.WaitFor(t) chg := t.Change() chg.AddTask(startSnapServices) } if t.Kind() == "unlink-snap" || t.Kind() == "unlink-current-snap" { stopSnapServices := s.NewTask("stop-snap-services", fmt.Sprintf(i18n.G("Stop snap services"))) stopSnapServices.Set("snap-setup-task", t.ID()) t.WaitFor(stopSnapServices) chg := t.Change() chg.AddTask(stopSnapServices) } } return nil }
// patch2: // - migrates SnapSetup.Name to SnapSetup.SideInfo.RealName // - backfills SnapState.{Sequence,Candidate}.RealName if its missing func patch2(s *state.State) error { var stateMap map[string]*OldSnapState err := s.Get("snaps", &stateMap) if err == state.ErrNoState { return nil } if err != nil { return err } // migrate SnapSetup in all tasks: // - the new SnapSetup uses SideInfo, backfil from Candidate // - also move SnapSetup.{Name,Revision} into SnapSetup.SideInfo.{RealName,Revision} var oldSS OldSnapSetup var newSS snapstate.SnapSetup for _, t := range s.Tasks() { err := t.Get("snap-setup", &oldSS) if err == state.ErrNoState { continue } if err != nil && err != state.ErrNoState { return err } // some things stay the same newSS.Channel = oldSS.Channel newSS.Flags = oldSS.Flags newSS.SnapPath = oldSS.SnapPath newSS.DownloadInfo = oldSS.DownloadInfo newSS.SideInfo = oldSS.SideInfo // ... and some change if newSS.SideInfo == nil { newSS.SideInfo = &snap.SideInfo{} if snapst, ok := stateMap[oldSS.Name]; ok && snapst.Candidate != nil { newSS.SideInfo = snapst.Candidate } } if newSS.SideInfo.RealName == "" { newSS.SideInfo.RealName = oldSS.Name } if newSS.SideInfo.Revision.Unset() { newSS.SideInfo.Revision = oldSS.Revision } t.Set("snap-setup", &newSS) } // backfill snapstate.SnapState.{Sequence,Candidate} with RealName // (if that is missing, was missing for e.g. sideloaded snaps) for snapName, snapState := range stateMap { for _, si := range snapState.Sequence { setRealName(si, snapName) } } s.Set("snaps", stateMap) return nil }
// patch2: // - migrates SnapSetup.Name to SnapSetup.SideInfo.RealName // - backfills SnapState.{Sequence,Candidate}.RealName if its missing func patch2(s *state.State) error { var oldStateMap map[string]*patch1SnapState err := s.Get("snaps", &oldStateMap) if err == state.ErrNoState { return nil } if err != nil { return err } newStateMap := make(map[string]*patch2SnapState, len(oldStateMap)) for key, oldSnapState := range oldStateMap { newStateMap[key] = patch2SnapStateFromPatch1(oldSnapState, key) } // migrate SnapSetup in all tasks: // - the new SnapSetup uses SideInfo, backfil from Candidate // - also move SnapSetup.{Name,Revision} into SnapSetup.SideInfo.{RealName,Revision} var oldSS patch1SnapSetup for _, t := range s.Tasks() { var newSS patch2SnapSetup err := t.Get("snap-setup", &oldSS) if err == state.ErrNoState { continue } if err != nil && err != state.ErrNoState { return err } // some things stay the same newSS.Channel = oldSS.Channel newSS.Flags = patch2Flags(oldSS.Flags) newSS.SnapPath = oldSS.SnapPath // ... and some change newSS.SideInfo = &patch2SideInfo{} if snapst, ok := oldStateMap[oldSS.Name]; ok && snapst.Candidate != nil { newSS.SideInfo = patch2SideInfoFromPatch1(snapst.Candidate, oldSS.Name) } if newSS.SideInfo.RealName == "" { newSS.SideInfo.RealName = oldSS.Name } if newSS.SideInfo.Revision.Unset() { newSS.SideInfo.Revision = oldSS.Revision } t.Set("snap-setup", &newSS) } s.Set("snaps", newStateMap) return nil }
// patch6: // - move from a flags-are-ints world to a flags-are-struct-of-bools world func patch6(st *state.State) error { var oldStateMap map[string]*patch4SnapState err := st.Get("snaps", &oldStateMap) if err == state.ErrNoState { return nil } if err != nil { return err } newStateMap := make(map[string]*patch6SnapState, len(oldStateMap)) for key, old := range oldStateMap { newStateMap[key] = &patch6SnapState{ SnapType: old.SnapType, Sequence: old.Sequence, Active: old.Active, Current: old.Current, Channel: old.Channel, patch6Flags: patch6FlagsFromPatch4(old.Flags), } } for _, task := range st.Tasks() { var old patch4SnapSetup err := task.Get("snap-setup", &old) if err == state.ErrNoState { continue } if err != nil && err != state.ErrNoState { return err } task.Set("snap-setup", &patch6SnapSetup{ Channel: old.Channel, UserID: old.UserID, SnapPath: old.SnapPath, DownloadInfo: old.DownloadInfo, SideInfo: old.SideInfo, patch6Flags: patch6FlagsFromPatch4(old.Flags), }) } st.Set("snaps", newStateMap) return nil }
// patch4: // - add Revert flag to in-progress revert-snap changes // - move from had-candidate to old-candidate-index in link-snap tasks // - add cleanup task to in-progress changes that have a copy-snap-data task func patch4(s *state.State) error { p4 := patch4T{} for _, change := range s.Changes() { // change is full done, take it easy if change.Status().Ready() { continue } if change.Kind() != "revert-snap" { continue } for _, task := range change.Tasks() { if err := p4.addRevertFlag(task); err != nil { return err } } } for _, task := range s.Tasks() { // change is full done, take it easy if task.Change().Status().Ready() { continue } switch task.Kind() { case "link-snap": if err := p4.mangle(task); err != nil { return err } case "copy-snap-data": if err := p4.addCleanup(task); err != nil { return err } } } return nil }