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 }