github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/patch/patch_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2016 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package patch_test
    21  
    22  import (
    23  	"fmt"
    24  	"sort"
    25  	"testing"
    26  
    27  	. "gopkg.in/check.v1"
    28  
    29  	"github.com/snapcore/snapd/overlord/patch"
    30  	"github.com/snapcore/snapd/overlord/state"
    31  	"github.com/snapcore/snapd/snap"
    32  	"github.com/snapcore/snapd/snapdtool"
    33  )
    34  
    35  func Test(t *testing.T) { TestingT(t) }
    36  
    37  type patchSuite struct {
    38  	restoreSanitize func()
    39  }
    40  
    41  var _ = Suite(&patchSuite{})
    42  
    43  func (s *patchSuite) SetUpTest(c *C) {
    44  	s.restoreSanitize = snap.MockSanitizePlugsSlots(func(snapInfo *snap.Info) {})
    45  }
    46  
    47  func (s *patchSuite) TearDownTest(c *C) {
    48  	s.restoreSanitize()
    49  }
    50  
    51  func (s *patchSuite) TestInit(c *C) {
    52  	restore := patch.Mock(2, 1, nil)
    53  	defer restore()
    54  
    55  	st := state.New(nil)
    56  	patch.Init(st)
    57  
    58  	st.Lock()
    59  	defer st.Unlock()
    60  	var patchLevel int
    61  	err := st.Get("patch-level", &patchLevel)
    62  	c.Assert(err, IsNil)
    63  	c.Check(patchLevel, Equals, 2)
    64  
    65  	var patchSublevel int
    66  	c.Assert(st.Get("patch-sublevel", &patchSublevel), IsNil)
    67  	c.Check(patchSublevel, Equals, 1)
    68  }
    69  
    70  func (s *patchSuite) TestNothingToDo(c *C) {
    71  	restore := patch.Mock(2, 1, nil)
    72  	defer restore()
    73  
    74  	st := state.New(nil)
    75  	st.Lock()
    76  	st.Set("patch-level", 2)
    77  	st.Unlock()
    78  	err := patch.Apply(st)
    79  	c.Assert(err, IsNil)
    80  }
    81  
    82  func (s *patchSuite) TestNoDowngrade(c *C) {
    83  	restore := patch.Mock(2, 0, nil)
    84  	defer restore()
    85  
    86  	st := state.New(nil)
    87  	st.Lock()
    88  	st.Set("patch-level", 3)
    89  	st.Unlock()
    90  	err := patch.Apply(st)
    91  	c.Assert(err, ErrorMatches, `cannot downgrade: snapd is too old for the current system state \(patch level 3\)`)
    92  }
    93  
    94  func (s *patchSuite) TestApply(c *C) {
    95  	p12 := func(st *state.State) error {
    96  		var n int
    97  		st.Get("n", &n)
    98  		st.Set("n", n+1)
    99  		return nil
   100  	}
   101  	p121 := func(st *state.State) error {
   102  		var o int
   103  		st.Get("o", &o)
   104  		st.Set("o", o+1)
   105  		return nil
   106  	}
   107  	p23 := func(st *state.State) error {
   108  		var n int
   109  		st.Get("n", &n)
   110  		st.Set("n", n*10)
   111  		return nil
   112  	}
   113  
   114  	// patch level 3, sublevel 1
   115  	restore := patch.Mock(3, 1, map[int][]patch.PatchFunc{
   116  		2: {p12, p121},
   117  		3: {p23},
   118  	})
   119  	defer restore()
   120  
   121  	st := state.New(nil)
   122  	st.Lock()
   123  	st.Set("patch-level", 1)
   124  	st.Unlock()
   125  	err := patch.Apply(st)
   126  	c.Assert(err, IsNil)
   127  
   128  	st.Lock()
   129  	defer st.Unlock()
   130  
   131  	var level int
   132  	err = st.Get("patch-level", &level)
   133  	c.Assert(err, IsNil)
   134  	c.Check(level, Equals, 3)
   135  
   136  	var sublevel int
   137  	c.Assert(st.Get("patch-sublevel", &sublevel), IsNil)
   138  	c.Check(sublevel, Equals, 0)
   139  
   140  	var n, o int
   141  	err = st.Get("n", &n)
   142  	c.Assert(err, IsNil)
   143  	c.Check(n, Equals, 10)
   144  
   145  	c.Assert(st.Get("o", &o), IsNil)
   146  	c.Assert(o, Equals, 1)
   147  }
   148  
   149  func (s *patchSuite) TestApplyLevel6(c *C) {
   150  	var sequence []int
   151  	p50 := generatePatchFunc(50, &sequence)
   152  	p60 := generatePatchFunc(60, &sequence)
   153  	p61 := generatePatchFunc(61, &sequence)
   154  
   155  	restore := patch.Mock(6, 1, map[int][]patch.PatchFunc{
   156  		5: {p50},
   157  		6: {p60, p61},
   158  	})
   159  	defer restore()
   160  
   161  	// simulate the special case where sublevel is introduced for system that's already on patch level 6.
   162  	// only p61 patch should be applied.
   163  	st := state.New(nil)
   164  	st.Lock()
   165  	st.Set("patch-level", 6)
   166  	st.Unlock()
   167  	c.Assert(patch.Apply(st), IsNil)
   168  
   169  	st.Lock()
   170  	defer st.Unlock()
   171  
   172  	var level, sublevel int
   173  	c.Assert(sequence, DeepEquals, []int{61})
   174  	c.Assert(st.Get("patch-level", &level), IsNil)
   175  	c.Assert(st.Get("patch-sublevel", &sublevel), IsNil)
   176  	c.Check(level, Equals, 6)
   177  	c.Check(sublevel, Equals, 1)
   178  }
   179  
   180  func (s *patchSuite) TestApplyFromSublevel(c *C) {
   181  	var sequence []int
   182  	p60 := generatePatchFunc(60, &sequence)
   183  	p61 := generatePatchFunc(61, &sequence)
   184  	p62 := generatePatchFunc(62, &sequence)
   185  	p70 := generatePatchFunc(70, &sequence)
   186  	p71 := generatePatchFunc(71, &sequence)
   187  
   188  	restore := patch.Mock(7, 1, map[int][]patch.PatchFunc{
   189  		6: {p60, p61, p62},
   190  		7: {p70, p71},
   191  	})
   192  	defer restore()
   193  
   194  	// we'll be patching from 6.0 -> 7.1
   195  	st := state.New(nil)
   196  	st.Lock()
   197  	st.Set("patch-level", 6)
   198  	st.Set("patch-sublevel", 0)
   199  	st.Unlock()
   200  	c.Assert(patch.Apply(st), IsNil)
   201  
   202  	st.Lock()
   203  
   204  	var level, sublevel int
   205  	c.Assert(st.Get("patch-level", &level), IsNil)
   206  	c.Assert(st.Get("patch-sublevel", &sublevel), IsNil)
   207  	c.Check(level, Equals, 7)
   208  	c.Check(sublevel, Equals, 1)
   209  	c.Assert(sequence, DeepEquals, []int{61, 62, 70, 71})
   210  
   211  	// now patching from 7.1 -> 7.2
   212  	sequence = []int{}
   213  	p72 := generatePatchFunc(72, &sequence)
   214  	patch.Mock(7, 2, map[int][]patch.PatchFunc{
   215  		6: {p60, p61, p62},
   216  		7: {p70, p71, p72},
   217  	})
   218  
   219  	st.Unlock()
   220  	c.Assert(patch.Apply(st), IsNil)
   221  	c.Assert(sequence, DeepEquals, []int{72})
   222  
   223  	st.Lock()
   224  	defer st.Unlock()
   225  
   226  	c.Assert(st.Get("patch-level", &level), IsNil)
   227  	c.Assert(st.Get("patch-sublevel", &sublevel), IsNil)
   228  	c.Check(level, Equals, 7)
   229  	c.Check(sublevel, Equals, 2)
   230  }
   231  
   232  func (s *patchSuite) TestMissing(c *C) {
   233  	restore := patch.Mock(3, 0, map[int][]patch.PatchFunc{
   234  		3: {func(s *state.State) error { return nil }},
   235  	})
   236  	defer restore()
   237  
   238  	st := state.New(nil)
   239  	st.Lock()
   240  	st.Set("patch-level", 1)
   241  	st.Unlock()
   242  	err := patch.Apply(st)
   243  	c.Assert(err, ErrorMatches, `cannot upgrade: snapd is too new for the current system state \(patch level 1\)`)
   244  }
   245  
   246  func (s *patchSuite) TestDowngradeSublevel(c *C) {
   247  	restore := patch.Mock(3, 1, map[int][]patch.PatchFunc{
   248  		3: {func(s *state.State) error { return nil }},
   249  	})
   250  	defer restore()
   251  
   252  	st := state.New(nil)
   253  	st.Lock()
   254  	st.Set("patch-level", 3)
   255  	st.Set("patch-sublevel", 6)
   256  	st.Unlock()
   257  
   258  	// we're at patch level 3, sublevel 6 according to state, but the implemented level is 3,1
   259  	c.Assert(patch.Apply(st), IsNil)
   260  
   261  	st.Lock()
   262  	defer st.Unlock()
   263  	var level, sublevel int
   264  	c.Assert(st.Get("patch-level", &level), IsNil)
   265  	c.Assert(st.Get("patch-sublevel", &sublevel), IsNil)
   266  	c.Check(level, Equals, 3)
   267  	c.Check(sublevel, Equals, 1)
   268  }
   269  
   270  func (s *patchSuite) TestError(c *C) {
   271  	p12 := func(st *state.State) error {
   272  		var n int
   273  		st.Get("n", &n)
   274  		st.Set("n", n+1)
   275  		return nil
   276  	}
   277  	p23 := func(st *state.State) error {
   278  		var n int
   279  		st.Get("n", &n)
   280  		st.Set("n", n*10)
   281  		return fmt.Errorf("boom")
   282  	}
   283  	p34 := func(st *state.State) error {
   284  		var n int
   285  		st.Get("n", &n)
   286  		st.Set("n", n*100)
   287  		return nil
   288  	}
   289  	restore := patch.Mock(3, 0, map[int][]patch.PatchFunc{
   290  		2: {p12},
   291  		3: {p23},
   292  		4: {p34},
   293  	})
   294  	defer restore()
   295  
   296  	st := state.New(nil)
   297  	st.Lock()
   298  	st.Set("patch-level", 1)
   299  	st.Unlock()
   300  	err := patch.Apply(st)
   301  	c.Assert(err, ErrorMatches, `cannot patch system state to level 3, sublevel 0: boom`)
   302  
   303  	st.Lock()
   304  	defer st.Unlock()
   305  
   306  	var level int
   307  	err = st.Get("patch-level", &level)
   308  	c.Assert(err, IsNil)
   309  	c.Check(level, Equals, 2)
   310  
   311  	var n int
   312  	err = st.Get("n", &n)
   313  	c.Assert(err, IsNil)
   314  	c.Check(n, Equals, 10)
   315  }
   316  
   317  func (s *patchSuite) testMaybeResetPatchLevel6(c *C, snapdVersion, lastVersion string, expectedPatches []int) {
   318  	var sequence []int
   319  
   320  	snapdtool.MockVersion(snapdVersion)
   321  
   322  	p60 := generatePatchFunc(60, &sequence)
   323  	p61 := generatePatchFunc(61, &sequence)
   324  	p62 := generatePatchFunc(62, &sequence)
   325  
   326  	restore := patch.Mock(6, 2, map[int][]patch.PatchFunc{
   327  		6: {p60, p61, p62},
   328  	})
   329  
   330  	defer restore()
   331  
   332  	st := state.New(nil)
   333  	st.Lock()
   334  
   335  	if lastVersion != "" {
   336  		st.Set("patch-sublevel-last-version", lastVersion)
   337  	}
   338  	st.Set("patch-level", 6)
   339  	st.Set("patch-sublevel", 2)
   340  
   341  	st.Unlock()
   342  
   343  	c.Assert(patch.Apply(st), IsNil)
   344  	c.Assert(sequence, DeepEquals, expectedPatches)
   345  
   346  	st.Lock()
   347  	defer st.Unlock()
   348  	var level, sublevel int
   349  	var ver string
   350  	var lastRefresh interface{}
   351  	c.Assert(st.Get("patch-level", &level), IsNil)
   352  	c.Assert(st.Get("patch-sublevel", &sublevel), IsNil)
   353  	c.Assert(st.Get("patch-sublevel-last-version", &ver), IsNil)
   354  	c.Assert(st.Get("patch-sublevel-reset", &lastRefresh), Equals, state.ErrNoState)
   355  	c.Check(ver, Equals, "snapd-version-1")
   356  	c.Check(level, Equals, 6)
   357  	c.Check(sublevel, Equals, 2)
   358  }
   359  
   360  func (s *patchSuite) TestSameSnapdVersionLvl60PatchesNotApplied(c *C) {
   361  	// sublevel patches not applied if snapd version is same
   362  	s.testMaybeResetPatchLevel6(c, "snapd-version-1", "snapd-version-1", nil)
   363  }
   364  
   365  func (s *patchSuite) TestDifferentSnapdVersionPatchLevel6NoLastVersion(c *C) {
   366  	s.testMaybeResetPatchLevel6(c, "snapd-version-1", "", []int{61, 62})
   367  }
   368  
   369  func (s *patchSuite) TestDifferentSnapdVersionPatchLevel6(c *C) {
   370  	s.testMaybeResetPatchLevel6(c, "snapd-version-1", "snapd-version-2", []int{61, 62})
   371  }
   372  
   373  func (s *patchSuite) TestSanity(c *C) {
   374  	patches := patch.PatchesForTest()
   375  	levels := make([]int, 0, len(patches))
   376  	for l := range patches {
   377  		levels = append(levels, l)
   378  	}
   379  	sort.Ints(levels)
   380  	// all steps present
   381  	for i, level := range levels {
   382  		c.Check(level, Equals, i+1)
   383  	}
   384  	// ends at implemented patch level
   385  	c.Check(levels[len(levels)-1], Equals, patch.Level)
   386  
   387  	// Sublevel matches the number of patches for last Level.
   388  	c.Check(len(patches[patch.Level])-1, Equals, patch.Sublevel)
   389  }
   390  
   391  func generatePatchFunc(testValue int, sequence *[]int) patch.PatchFunc {
   392  	return func(st *state.State) error {
   393  		*sequence = append(*sequence, testValue)
   394  		return nil
   395  	}
   396  }