Beispiel #1
0
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
}
Beispiel #2
0
// 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
}
Beispiel #3
0
// 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
}
Beispiel #4
0
// 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
}
Beispiel #5
0
// 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
}
Beispiel #6
0
// 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
}