github.com/chipaca/snappy@v0.0.0-20210104084008-1f06296fe8ad/overlord/snapstate/catalogrefresh_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017-2018 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 snapstate_test
    21  
    22  import (
    23  	"context"
    24  	"io"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"time"
    29  
    30  	. "gopkg.in/check.v1"
    31  
    32  	"github.com/snapcore/snapd/advisor"
    33  	"github.com/snapcore/snapd/dirs"
    34  	"github.com/snapcore/snapd/osutil"
    35  	"github.com/snapcore/snapd/overlord/auth"
    36  	"github.com/snapcore/snapd/overlord/snapstate"
    37  	"github.com/snapcore/snapd/overlord/snapstate/snapstatetest"
    38  	"github.com/snapcore/snapd/overlord/state"
    39  	"github.com/snapcore/snapd/store"
    40  	"github.com/snapcore/snapd/store/storetest"
    41  	"github.com/snapcore/snapd/testutil"
    42  )
    43  
    44  type catalogStore struct {
    45  	storetest.Store
    46  
    47  	ops     []string
    48  	tooMany bool
    49  }
    50  
    51  func (r *catalogStore) WriteCatalogs(ctx context.Context, w io.Writer, a store.SnapAdder) error {
    52  	if ctx == nil || !auth.IsEnsureContext(ctx) {
    53  		panic("Ensure marked context required")
    54  	}
    55  	r.ops = append(r.ops, "write-catalog")
    56  	if r.tooMany {
    57  		return store.ErrTooManyRequests
    58  	}
    59  	w.Write([]byte("pkg1\npkg2"))
    60  	a.AddSnap("foo", "1.0", "foo summary", []string{"foo", "meh"})
    61  	a.AddSnap("bar", "2.0", "bar summray", []string{"bar", "meh"})
    62  	return nil
    63  }
    64  
    65  func (r *catalogStore) Sections(ctx context.Context, _ *auth.UserState) ([]string, error) {
    66  	if ctx == nil || !auth.IsEnsureContext(ctx) {
    67  		panic("Ensure marked context required")
    68  	}
    69  	r.ops = append(r.ops, "sections")
    70  	if r.tooMany {
    71  		return nil, store.ErrTooManyRequests
    72  	}
    73  	return []string{"section1", "section2"}, nil
    74  }
    75  
    76  type catalogRefreshTestSuite struct {
    77  	state *state.State
    78  
    79  	store  *catalogStore
    80  	tmpdir string
    81  
    82  	testutil.BaseTest
    83  }
    84  
    85  var _ = Suite(&catalogRefreshTestSuite{})
    86  
    87  func (s *catalogRefreshTestSuite) SetUpTest(c *C) {
    88  	s.tmpdir = c.MkDir()
    89  	dirs.SetRootDir(s.tmpdir)
    90  	s.state = state.New(nil)
    91  	s.store = &catalogStore{}
    92  	s.state.Lock()
    93  	defer s.state.Unlock()
    94  	snapstate.ReplaceStore(s.state, s.store)
    95  	// mark system as seeded
    96  	s.state.Set("seeded", true)
    97  
    98  	// setup a simple deviceCtx since we check that for install mode
    99  	s.AddCleanup(snapstatetest.MockDeviceModel(DefaultModel()))
   100  
   101  	snapstate.CanAutoRefresh = func(*state.State) (bool, error) { return true, nil }
   102  	s.AddCleanup(func() { snapstate.CanAutoRefresh = nil })
   103  }
   104  
   105  func (s *catalogRefreshTestSuite) TestCatalogRefresh(c *C) {
   106  	// start with no catalog
   107  	c.Check(dirs.SnapSectionsFile, testutil.FileAbsent)
   108  	c.Check(dirs.SnapNamesFile, testutil.FileAbsent)
   109  	c.Check(dirs.SnapCommandsDB, testutil.FileAbsent)
   110  
   111  	cr7 := snapstate.NewCatalogRefresh(s.state)
   112  	// next is initially zero
   113  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   114  	t0 := time.Now()
   115  
   116  	err := cr7.Ensure()
   117  	c.Check(err, IsNil)
   118  
   119  	// next now has a delta (next refresh is not before t0 + delta)
   120  	c.Check(snapstate.NextCatalogRefresh(cr7).Before(t0.Add(snapstate.CatalogRefreshDelayWithDelta)), Equals, false)
   121  
   122  	c.Check(s.store.ops, DeepEquals, []string{"sections", "write-catalog"})
   123  
   124  	c.Check(osutil.FileExists(dirs.SnapSectionsFile), Equals, true)
   125  	c.Check(dirs.SnapSectionsFile, testutil.FileEquals, "section1\nsection2")
   126  
   127  	c.Check(osutil.FileExists(dirs.SnapNamesFile), Equals, true)
   128  	c.Check(dirs.SnapNamesFile, testutil.FileEquals, "pkg1\npkg2")
   129  
   130  	c.Check(osutil.FileExists(dirs.SnapCommandsDB), Equals, true)
   131  	dump, err := advisor.DumpCommands()
   132  	c.Assert(err, IsNil)
   133  	c.Check(dump, DeepEquals, map[string]string{
   134  		"foo": `[{"snap":"foo","version":"1.0"}]`,
   135  		"bar": `[{"snap":"bar","version":"2.0"}]`,
   136  		"meh": `[{"snap":"foo","version":"1.0"},{"snap":"bar","version":"2.0"}]`,
   137  	})
   138  }
   139  
   140  func (s *catalogRefreshTestSuite) TestCatalogRefreshTooMany(c *C) {
   141  	s.store.tooMany = true
   142  
   143  	cr7 := snapstate.NewCatalogRefresh(s.state)
   144  	// next is initially zero
   145  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   146  	t0 := time.Now()
   147  
   148  	err := cr7.Ensure()
   149  	c.Check(err, IsNil) // !!
   150  
   151  	// next now has a delta (next refresh is not before t0 + delta)
   152  	c.Check(snapstate.NextCatalogRefresh(cr7).Before(t0.Add(snapstate.CatalogRefreshDelayWithDelta)), Equals, false)
   153  
   154  	// it tried one endpoint and bailed at the first 429
   155  	c.Check(s.store.ops, HasLen, 1)
   156  
   157  	// nothing got created
   158  	c.Check(osutil.FileExists(dirs.SnapSectionsFile), Equals, false)
   159  	c.Check(osutil.FileExists(dirs.SnapNamesFile), Equals, false)
   160  	c.Check(osutil.FileExists(dirs.SnapCommandsDB), Equals, false)
   161  }
   162  
   163  func (s *catalogRefreshTestSuite) TestCatalogRefreshNotNeeded(c *C) {
   164  	cr7 := snapstate.NewCatalogRefresh(s.state)
   165  	snapstate.MockCatalogRefreshNextRefresh(cr7, time.Now().Add(1*time.Hour))
   166  	err := cr7.Ensure()
   167  	c.Check(err, IsNil)
   168  	c.Check(s.store.ops, HasLen, 0)
   169  	c.Check(osutil.FileExists(dirs.SnapSectionsFile), Equals, false)
   170  	c.Check(osutil.FileExists(dirs.SnapNamesFile), Equals, false)
   171  }
   172  
   173  func (s *catalogRefreshTestSuite) TestCatalogRefreshNewEnough(c *C) {
   174  	// write a fake sections file just to have it
   175  	c.Assert(os.MkdirAll(filepath.Dir(dirs.SnapNamesFile), 0755), IsNil)
   176  	c.Assert(ioutil.WriteFile(dirs.SnapNamesFile, nil, 0644), IsNil)
   177  	// set the timestamp to something known
   178  	t0 := time.Now().Truncate(time.Hour)
   179  	c.Assert(os.Chtimes(dirs.SnapNamesFile, t0, t0), IsNil)
   180  
   181  	cr7 := snapstate.NewCatalogRefresh(s.state)
   182  	// next is initially zero
   183  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   184  	err := cr7.Ensure()
   185  	c.Assert(err, IsNil)
   186  	c.Check(s.store.ops, HasLen, 0)
   187  	next := snapstate.NextCatalogRefresh(cr7)
   188  	// next is no longer zero,
   189  	c.Check(next.IsZero(), Equals, false)
   190  	// but has a delta WRT the timestamp
   191  	c.Check(next.Equal(t0.Add(snapstate.CatalogRefreshDelayWithDelta)), Equals, true)
   192  }
   193  
   194  func (s *catalogRefreshTestSuite) TestCatalogRefreshTooNew(c *C) {
   195  	// write a fake sections file just to have it
   196  	c.Assert(os.MkdirAll(filepath.Dir(dirs.SnapNamesFile), 0755), IsNil)
   197  	c.Assert(ioutil.WriteFile(dirs.SnapNamesFile, nil, 0644), IsNil)
   198  	// but set the timestamp in the future
   199  	t := time.Now().Add(time.Hour)
   200  	c.Assert(os.Chtimes(dirs.SnapNamesFile, t, t), IsNil)
   201  
   202  	cr7 := snapstate.NewCatalogRefresh(s.state)
   203  	err := cr7.Ensure()
   204  	c.Check(err, IsNil)
   205  	c.Check(s.store.ops, DeepEquals, []string{"sections", "write-catalog"})
   206  }
   207  
   208  func (s *catalogRefreshTestSuite) TestCatalogRefreshUnSeeded(c *C) {
   209  	// mark system as unseeded (first boot)
   210  	s.state.Lock()
   211  	s.state.Set("seeded", nil)
   212  	s.state.Unlock()
   213  
   214  	cr7 := snapstate.NewCatalogRefresh(s.state)
   215  	// next is initially zero
   216  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   217  
   218  	err := cr7.Ensure()
   219  	c.Assert(err, IsNil)
   220  
   221  	// next should be still zero as we skipped refresh on unseeded system
   222  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   223  	// nothing got created
   224  	c.Check(osutil.FileExists(dirs.SnapSectionsFile), Equals, false)
   225  	c.Check(osutil.FileExists(dirs.SnapNamesFile), Equals, false)
   226  	c.Check(osutil.FileExists(dirs.SnapCommandsDB), Equals, false)
   227  }
   228  
   229  func (s *catalogRefreshTestSuite) TestCatalogRefreshUC20InstallMode(c *C) {
   230  	// mark system as being in install mode
   231  	trivialInstallDevice := &snapstatetest.TrivialDeviceContext{
   232  		DeviceModel: DefaultModel(),
   233  		SysMode:     "install",
   234  	}
   235  
   236  	r := snapstatetest.MockDeviceContext(trivialInstallDevice)
   237  	defer r()
   238  
   239  	cr7 := snapstate.NewCatalogRefresh(s.state)
   240  	// next is initially zero
   241  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   242  
   243  	err := cr7.Ensure()
   244  	c.Assert(err, IsNil)
   245  
   246  	// next should be still zero as we skipped refresh on unseeded system
   247  	c.Check(snapstate.NextCatalogRefresh(cr7).IsZero(), Equals, true)
   248  	// nothing got created
   249  	c.Check(osutil.FileExists(dirs.SnapSectionsFile), Equals, false)
   250  	c.Check(osutil.FileExists(dirs.SnapNamesFile), Equals, false)
   251  	c.Check(osutil.FileExists(dirs.SnapCommandsDB), Equals, false)
   252  }