func loadState(backend state.Backend) (*state.State, error) { if !osutil.FileExists(dirs.SnapStateFile) { // fail fast, mostly interesting for tests, this dir is setup // by the snapd package stateDir := filepath.Dir(dirs.SnapStateFile) if !osutil.IsDirectory(stateDir) { return nil, fmt.Errorf("fatal: directory %q must be present", stateDir) } s := state.New(backend) patch.Init(s) return s, nil } r, err := os.Open(dirs.SnapStateFile) if err != nil { return nil, fmt.Errorf("cannot read the state file: %s", err) } defer r.Close() s, err := state.ReadState(backend, r) if err != nil { return nil, err } // one-shot migrations err = patch.Apply(s) if err != nil { return nil, err } return s, nil }
func (s *patchSuite) TestNothingToDo(c *C) { restore := patch.Mock(2, nil) defer restore() st := state.New(nil) st.Lock() st.Set("patch-level", 2) st.Unlock() err := patch.Apply(st) c.Assert(err, IsNil) }
func (s *patchSuite) TestNoDowngrade(c *C) { restore := patch.Mock(2, nil) defer restore() st := state.New(nil) st.Lock() st.Set("patch-level", 3) st.Unlock() err := patch.Apply(st) c.Assert(err, ErrorMatches, `cannot downgrade: snapd is too old for the current system state \(patch level 3\)`) }
func (s *patch4Suite) TestPatch4OnRefreshes(c *C) { restorer := patch.MockLevel(4) defer restorer() r, err := os.Open(dirs.SnapStateFile) c.Assert(err, IsNil) defer r.Close() st, err := state.ReadState(nil, r) c.Assert(err, IsNil) func() { st.Lock() defer st.Unlock() task := st.Task("16") c.Assert(task, NotNil) // simulate that the task was running (but the change // is not fully done yet) task.SetStatus(state.DoneStatus) snapsup, err := patch.Patch4TaskSnapSetup(task) c.Assert(err, IsNil) c.Check(snapsup.Flags.Revert(), Equals, false) var had bool var idx int c.Check(task.Get("had-candidate", &had), IsNil) c.Check(had, Equals, false) c.Check(task.Get("old-candidate-index", &idx), Equals, state.ErrNoState) c.Check(len(task.Change().Tasks()), Equals, 7) }() // go from patch level 3 -> 4 err = patch.Apply(st) c.Assert(err, IsNil) st.Lock() defer st.Unlock() task := st.Task("16") c.Assert(task, NotNil) snapsup, err := patch.Patch4TaskSnapSetup(task) c.Assert(err, IsNil) c.Check(snapsup.Flags.Revert(), Equals, false) var had bool var idx int c.Check(task.Get("had-candidate", &had), Equals, state.ErrNoState) c.Check(task.Get("old-candidate-index", &idx), IsNil) c.Check(idx, Equals, 1) // we added cleanup c.Check(len(task.Change().Tasks()), Equals, 7+1) }
func (s *patch2Suite) TestPatch2(c *C) { restorer := patch.MockLevel(2) defer restorer() r, err := os.Open(dirs.SnapStateFile) c.Assert(err, IsNil) defer r.Close() st, err := state.ReadState(nil, r) c.Assert(err, IsNil) // go from patch level 1 -> 2 err = patch.Apply(st) c.Assert(err, IsNil) st.Lock() defer st.Unlock() // our mocks are correct c.Assert(st.Changes(), HasLen, 2) c.Assert(st.Tasks(), HasLen, 2) var snapsup snapstate.SnapSetup // transition of: // - SnapSetup.{Name,Revision} -> SnapSetup.SideInfo.{RealName,Revision} t := st.Task("1") err = t.Get("snap-setup", &snapsup) c.Assert(err, IsNil) c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ RealName: "foo", Revision: snap.R("x3"), }) // transition of: // - SnapState.Sequence is backfilled with "RealName" (if missing) var snapst snapstate.SnapState err = snapstate.Get(st, "foo", &snapst) c.Assert(err, IsNil) c.Check(snapst.Sequence[0].RealName, Equals, "foo") c.Check(snapst.Sequence[1].RealName, Equals, "foo") // transition of: // - Candidate for "bar" -> tasks SnapSetup.SideInfo t = st.Task("2") err = t.Get("snap-setup", &snapsup) c.Assert(err, IsNil) c.Assert(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ RealName: "bar", Revision: snap.R("x1"), }) // FIXME: bar is now empty and should no longer be there? err = snapstate.Get(st, "bar", &snapst) c.Assert(err, IsNil) }
// This test simulates a link-snap task that is scheduled but has not // run yet. It has no "had-candidate" data set yet. func (s *patch4Suite) TestPatch4OnRefreshesNoHadCandidateYet(c *C) { restorer := patch.MockLevel(4) defer restorer() r, err := os.Open(dirs.SnapStateFile) c.Assert(err, IsNil) defer r.Close() st, err := state.ReadState(nil, r) c.Assert(err, IsNil) func() { st.Lock() defer st.Unlock() task := st.Task("16") c.Assert(task, NotNil) // its ready to run but has not run yet task.Clear("had-candidate") task.SetStatus(state.DoStatus) ss, err := snapstate.TaskSnapSetup(task) c.Assert(err, IsNil) c.Check(ss.Flags.Revert(), Equals, false) var had bool var idx int c.Check(task.Get("had-candidate", &had), Equals, state.ErrNoState) c.Check(task.Get("old-candidate-index", &idx), Equals, state.ErrNoState) c.Check(len(task.Change().Tasks()), Equals, 7) }() // go from patch level 3 -> 4 err = patch.Apply(st) c.Assert(err, IsNil) st.Lock() defer st.Unlock() task := st.Task("16") c.Assert(task, NotNil) ss, err := snapstate.TaskSnapSetup(task) c.Assert(err, IsNil) c.Check(ss.Flags.Revert(), Equals, false) var had bool var idx int c.Check(task.Get("had-candidate", &had), Equals, state.ErrNoState) c.Check(task.Get("old-candidate-index", &idx), IsNil) c.Check(idx, Equals, 1) // we added cleanup c.Check(len(task.Change().Tasks()), Equals, 7+1) }
func (s *patchSuite) TestMissing(c *C) { restore := patch.Mock(3, map[int]func(*state.State) error{ 3: func(s *state.State) error { return nil }, }) defer restore() st := state.New(nil) st.Lock() st.Set("patch-level", 1) st.Unlock() err := patch.Apply(st) c.Assert(err, ErrorMatches, `cannot upgrade: snapd is too new for the current system state \(patch level 1\)`) }
func (s *patchSuite) TestError(c *C) { p12 := func(st *state.State) error { var n int st.Get("n", &n) st.Set("n", n+1) return nil } p23 := func(st *state.State) error { var n int st.Get("n", &n) st.Set("n", n*10) return fmt.Errorf("boom") } p34 := func(st *state.State) error { var n int st.Get("n", &n) st.Set("n", n*100) return nil } restore := patch.Mock(3, map[int]func(*state.State) error{ 2: p12, 3: p23, 4: p34, }) defer restore() st := state.New(nil) st.Lock() st.Set("patch-level", 1) st.Unlock() err := patch.Apply(st) c.Assert(err, ErrorMatches, `cannot patch system state from level 2 to 3: boom`) st.Lock() defer st.Unlock() var level int err = st.Get("patch-level", &level) c.Assert(err, IsNil) c.Check(level, Equals, 2) var n int err = st.Get("n", &n) c.Assert(err, IsNil) c.Check(n, Equals, 10) }
func (s *patch1Suite) TestPatch1(c *C) { restore := patch.MockReadInfo(s.readInfo) defer restore() r, err := os.Open(dirs.SnapStateFile) c.Assert(err, IsNil) defer r.Close() st, err := state.ReadState(nil, r) c.Assert(err, IsNil) // go from patch-level 0 to patch-level 1 restorer := patch.MockLevel(1) defer restorer() err = patch.Apply(st) c.Assert(err, IsNil) st.Lock() defer st.Unlock() expected := []struct { name string typ snap.Type cur snap.Revision }{ {"foo", snap.TypeApp, snap.R(22)}, {"core", snap.TypeOS, snap.R(111)}, {"borken", snap.TypeApp, snap.R(-2)}, {"wip", "", snap.R(0)}, } for _, exp := range expected { var snapst snapstate.SnapState err := snapstate.Get(st, exp.name, &snapst) c.Assert(err, IsNil) c.Check(snap.Type(snapst.SnapType), Equals, exp.typ) c.Check(snapst.Current, Equals, exp.cur) } // ensure we only moved forward to patch-level 1 var patchLevel int err = st.Get("patch-level", &patchLevel) c.Assert(err, IsNil) c.Assert(patchLevel, Equals, 1) }
func (s *patchSuite) TestApply(c *C) { p12 := func(st *state.State) error { var n int st.Get("n", &n) st.Set("n", n+1) return nil } p23 := func(st *state.State) error { var n int st.Get("n", &n) st.Set("n", n*10) return nil } restore := patch.Mock(3, map[int]func(*state.State) error{ 2: p12, 3: p23, }) defer restore() st := state.New(nil) st.Lock() st.Set("patch-level", 1) st.Unlock() err := patch.Apply(st) c.Assert(err, IsNil) st.Lock() defer st.Unlock() var level int err = st.Get("patch-level", &level) c.Assert(err, IsNil) c.Check(level, Equals, 3) var n int err = st.Get("n", &n) c.Assert(err, IsNil) c.Check(n, Equals, 10) }
func (s *patch6Suite) TestPatch6(c *C) { restorer := patch.MockLevel(6) defer restorer() r, err := os.Open(dirs.SnapStateFile) c.Assert(err, IsNil) defer r.Close() st, err := state.ReadState(nil, r) c.Assert(err, IsNil) func() { st.Lock() defer st.Unlock() stateMap, err := patch.Patch4StateMap(st) c.Assert(err, IsNil) c.Check(int(stateMap["a"].Flags), Equals, 1) c.Check(int(stateMap["b"].Flags), Equals, 2) c.Check(int(stateMap["c"].Flags), Equals, 4) }() c.Assert(patch.Apply(st), IsNil) st.Lock() defer st.Unlock() stateMap, err := patch.Patch6StateMap(st) c.Assert(err, IsNil) c.Check(stateMap["a"].DevMode, Equals, true) c.Check(stateMap["a"].TryMode, Equals, false) c.Check(stateMap["a"].JailMode, Equals, false) c.Check(stateMap["b"].DevMode, Equals, false) c.Check(stateMap["b"].TryMode, Equals, true) c.Check(stateMap["b"].JailMode, Equals, false) c.Check(stateMap["c"].DevMode, Equals, false) c.Check(stateMap["c"].TryMode, Equals, false) c.Check(stateMap["c"].JailMode, Equals, true) for _, task := range st.Tasks() { ss, err := patch.Patch6SnapSetup(task) if err == state.ErrNoState { continue } c.Assert(err, IsNil) var snaps []string c.Assert(task.Change().Get("snap-names", &snaps), IsNil) c.Assert(snaps, HasLen, 1) switch snaps[0] { case "a": c.Check(ss.DevMode, Equals, true, Commentf("a")) c.Check(ss.TryMode, Equals, false, Commentf("a")) c.Check(ss.JailMode, Equals, false, Commentf("a")) c.Check(ss.Revert, Equals, false, Commentf("a")) case "b": c.Check(ss.DevMode, Equals, false, Commentf("b")) c.Check(ss.TryMode, Equals, true, Commentf("b")) c.Check(ss.JailMode, Equals, false, Commentf("b")) c.Check(ss.Revert, Equals, false, Commentf("b")) case "c": c.Check(ss.DevMode, Equals, false, Commentf("c")) c.Check(ss.TryMode, Equals, false, Commentf("c")) c.Check(ss.JailMode, Equals, true, Commentf("c")) c.Check(ss.Revert, Equals, true, Commentf("c")) } } }