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