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 }