github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/overlord/snapstate/handlers_download_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 snapstate_test 21 22 import ( 23 "path/filepath" 24 25 . "gopkg.in/check.v1" 26 27 "github.com/snapcore/snapd/dirs" 28 "github.com/snapcore/snapd/overlord/configstate/config" 29 "github.com/snapcore/snapd/overlord/snapstate" 30 "github.com/snapcore/snapd/overlord/snapstate/snapstatetest" 31 "github.com/snapcore/snapd/overlord/state" 32 "github.com/snapcore/snapd/snap" 33 "github.com/snapcore/snapd/store" 34 ) 35 36 type downloadSnapSuite struct { 37 baseHandlerSuite 38 39 fakeStore *fakeStore 40 } 41 42 var _ = Suite(&downloadSnapSuite{}) 43 44 func (s *downloadSnapSuite) SetUpTest(c *C) { 45 s.setup(c, nil) 46 47 s.fakeStore = &fakeStore{ 48 state: s.state, 49 fakeBackend: s.fakeBackend, 50 } 51 s.state.Lock() 52 defer s.state.Unlock() 53 snapstate.ReplaceStore(s.state, s.fakeStore) 54 s.state.Set("refresh-privacy-key", "privacy-key") 55 56 s.AddCleanup(snapstatetest.UseFallbackDeviceModel()) 57 } 58 59 func (s *downloadSnapSuite) TestDoDownloadSnapCompatibility(c *C) { 60 s.state.Lock() 61 t := s.state.NewTask("download-snap", "test") 62 t.Set("snap-setup", &snapstate.SnapSetup{ 63 SideInfo: &snap.SideInfo{ 64 RealName: "foo", 65 }, 66 Channel: "some-channel", 67 // explicitly set to "nil", this ensures the compatibility 68 // code path in the task is hit and the store is queried 69 // in the task (instead of using the new 70 // SnapSetup.{SideInfo,DownloadInfo} that gets set in 71 // snapstate.{Install,Update} directly. 72 DownloadInfo: nil, 73 }) 74 s.state.NewChange("dummy", "...").AddTask(t) 75 76 s.state.Unlock() 77 78 s.se.Ensure() 79 s.se.Wait() 80 81 // the compat code called the store "Snap" endpoint 82 c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{ 83 { 84 op: "storesvc-snap-action", 85 }, 86 { 87 op: "storesvc-snap-action:action", 88 action: store.SnapAction{ 89 Action: "install", 90 InstanceName: "foo", 91 Channel: "some-channel", 92 }, 93 revno: snap.R(11), 94 }, 95 { 96 op: "storesvc-download", 97 name: "foo", 98 }, 99 }) 100 101 s.state.Lock() 102 defer s.state.Unlock() 103 104 var snapsup snapstate.SnapSetup 105 t.Get("snap-setup", &snapsup) 106 c.Check(snapsup.SideInfo, DeepEquals, &snap.SideInfo{ 107 RealName: "foo", 108 SnapID: "foo-id", 109 Revision: snap.R(11), 110 Channel: "some-channel", 111 }) 112 c.Check(t.Status(), Equals, state.DoneStatus) 113 } 114 115 func (s *downloadSnapSuite) TestDoDownloadSnapNormal(c *C) { 116 s.state.Lock() 117 118 si := &snap.SideInfo{ 119 RealName: "foo", 120 SnapID: "mySnapID", 121 Revision: snap.R(11), 122 Channel: "my-channel", 123 } 124 125 // download, ensure the store does not query 126 t := s.state.NewTask("download-snap", "test") 127 t.Set("snap-setup", &snapstate.SnapSetup{ 128 Channel: "some-channel", 129 SideInfo: si, 130 DownloadInfo: &snap.DownloadInfo{ 131 DownloadURL: "http://some-url.com/snap", 132 }, 133 }) 134 chg := s.state.NewChange("dummy", "...") 135 chg.AddTask(t) 136 137 s.state.Unlock() 138 139 s.se.Ensure() 140 s.se.Wait() 141 142 s.state.Lock() 143 defer s.state.Unlock() 144 145 c.Assert(chg.Err(), IsNil) 146 147 // only the download endpoint of the store was hit 148 c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{ 149 { 150 op: "storesvc-download", 151 name: "foo", 152 }, 153 }) 154 155 var snapsup snapstate.SnapSetup 156 t.Get("snap-setup", &snapsup) 157 c.Check(snapsup.SideInfo, DeepEquals, si) 158 c.Check(t.Status(), Equals, state.DoneStatus) 159 160 // check no IsAutoRefresh was passed in 161 c.Assert(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 162 { 163 name: "foo", 164 target: filepath.Join(dirs.SnapBlobDir, "foo_11.snap"), 165 opts: nil, 166 }, 167 }) 168 } 169 170 func (s *downloadSnapSuite) TestDoDownloadSnapWithDeviceContext(c *C) { 171 s.state.Lock() 172 173 // unset the global store, it will need to come via the device context 174 // CtxStore 175 snapstate.ReplaceStore(s.state, nil) 176 177 r := snapstatetest.MockDeviceContext(&snapstatetest.TrivialDeviceContext{ 178 CtxStore: s.fakeStore, 179 }) 180 defer r() 181 182 si := &snap.SideInfo{ 183 RealName: "foo", 184 SnapID: "mySnapID", 185 Revision: snap.R(11), 186 Channel: "my-channel", 187 } 188 189 // download, ensure the store does not query 190 t := s.state.NewTask("download-snap", "test") 191 t.Set("snap-setup", &snapstate.SnapSetup{ 192 Channel: "some-channel", 193 SideInfo: si, 194 DownloadInfo: &snap.DownloadInfo{ 195 DownloadURL: "http://some-url.com/snap", 196 }, 197 }) 198 s.state.NewChange("dummy", "...").AddTask(t) 199 200 s.state.Unlock() 201 202 s.se.Ensure() 203 s.se.Wait() 204 205 // only the download endpoint of the store was hit 206 c.Assert(s.fakeBackend.ops, DeepEquals, fakeOps{ 207 { 208 op: "storesvc-download", 209 name: "foo", 210 }, 211 }) 212 } 213 214 func (s *downloadSnapSuite) TestDoUndoDownloadSnap(c *C) { 215 s.state.Lock() 216 si := &snap.SideInfo{ 217 RealName: "foo", 218 Revision: snap.R(33), 219 } 220 t := s.state.NewTask("download-snap", "test") 221 t.Set("snap-setup", &snapstate.SnapSetup{ 222 SideInfo: si, 223 DownloadInfo: &snap.DownloadInfo{ 224 DownloadURL: "http://something.com/snap", 225 }, 226 }) 227 chg := s.state.NewChange("dummy", "...") 228 chg.AddTask(t) 229 230 terr := s.state.NewTask("error-trigger", "provoking total undo") 231 terr.WaitFor(t) 232 chg.AddTask(terr) 233 234 s.state.Unlock() 235 236 for i := 0; i < 3; i++ { 237 s.se.Ensure() 238 s.se.Wait() 239 } 240 241 s.state.Lock() 242 defer s.state.Unlock() 243 244 // task was undone 245 c.Check(t.Status(), Equals, state.UndoneStatus) 246 247 // and nothing is in the state for "foo" 248 var snapst snapstate.SnapState 249 err := snapstate.Get(s.state, "foo", &snapst) 250 c.Assert(err, Equals, state.ErrNoState) 251 252 } 253 254 func (s *downloadSnapSuite) TestDoDownloadRateLimitedIntegration(c *C) { 255 s.state.Lock() 256 257 // set auto-refresh rate-limit 258 tr := config.NewTransaction(s.state) 259 tr.Set("core", "refresh.rate-limit", "1234B") 260 tr.Commit() 261 262 // setup fake auto-refresh download 263 si := &snap.SideInfo{ 264 RealName: "foo", 265 SnapID: "foo-id", 266 Revision: snap.R(11), 267 } 268 t := s.state.NewTask("download-snap", "test") 269 t.Set("snap-setup", &snapstate.SnapSetup{ 270 SideInfo: si, 271 DownloadInfo: &snap.DownloadInfo{ 272 DownloadURL: "http://some-url.com/snap", 273 }, 274 Flags: snapstate.Flags{ 275 IsAutoRefresh: true, 276 }, 277 }) 278 s.state.NewChange("dummy", "...").AddTask(t) 279 280 s.state.Unlock() 281 282 s.se.Ensure() 283 s.se.Wait() 284 285 // ensure that rate limit was honored 286 c.Assert(s.fakeStore.downloads, DeepEquals, []fakeDownload{ 287 { 288 name: "foo", 289 target: filepath.Join(dirs.SnapBlobDir, "foo_11.snap"), 290 opts: &store.DownloadOptions{ 291 RateLimit: 1234, 292 IsAutoRefresh: true, 293 }, 294 }, 295 }) 296 297 }