gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/overlord/snapstate/storehelpers_test.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 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 "fmt" 25 26 . "gopkg.in/check.v1" 27 28 "github.com/snapcore/snapd/interfaces" 29 "github.com/snapcore/snapd/overlord/auth" 30 "github.com/snapcore/snapd/overlord/ifacestate/ifacerepo" 31 "github.com/snapcore/snapd/overlord/snapstate" 32 "github.com/snapcore/snapd/overlord/state" 33 "github.com/snapcore/snapd/snap" 34 "github.com/snapcore/snapd/snap/snaptest" 35 "github.com/snapcore/snapd/store" 36 ) 37 38 const snapYaml1 = ` 39 name: some-snap 40 version: 1.0 41 ` 42 const snapYaml2 = ` 43 name: some-snap 44 version: 1.0 45 base: none 46 ` 47 48 const snapYamlWithBase1 = ` 49 name: some-snap1 50 version: 1.0 51 base: some-base 52 ` 53 const snapYamlWithBase2 = ` 54 name: some-snap2 55 version: 1.0 56 base: some-base 57 ` 58 const snapYamlWithBase3 = ` 59 name: some-snap3 60 version: 2.0 61 base: other-base 62 ` 63 const snapYamlWithBase4 = ` 64 name: some-snap4 65 version: 1.0 66 base: yet-another-base 67 ` 68 const snapYamlWithContentPlug1 = ` 69 name: some-snap 70 version: 1.0 71 base: some-base 72 plugs: 73 some-plug: 74 interface: content 75 content: shared-content 76 default-provider: snap-content-slot 77 ` 78 79 const snapYamlWithContentPlug2 = ` 80 name: some-snap2 81 version: 1.0 82 base: some-base 83 plugs: 84 some-plug: 85 interface: content 86 content: shared-content 87 default-provider: snap-content-slot 88 ` 89 90 const snapYamlWithContentPlug3 = ` 91 name: some-snap 92 version: 1.0 93 base: some-base 94 plugs: 95 some-plug: 96 interface: content 97 content: shared-content 98 default-provider: snap-content-slot-other 99 ` 100 101 const ( 102 // use sizes that make it easier to spot unexpected dependencies in the 103 // total sum. 104 someBaseSize = 1 105 otherBaseSize = 100 106 snap1Size = 1000 107 snap2Size = 10000 108 snap3Size = 100000 109 snap4Size = 1000000 110 snapContentSlotSize = 10000000 111 snapOtherContentSlotSize = 100000000 112 someOtherBaseSize = 1000000000 113 ) 114 115 type installSizeTestStore struct { 116 *fakeStore 117 } 118 119 func (f installSizeTestStore) SnapAction(ctx context.Context, currentSnaps []*store.CurrentSnap, actions []*store.SnapAction, assertQuery store.AssertionQuery, user *auth.UserState, opts *store.RefreshOptions) ([]store.SnapActionResult, []store.AssertionResult, error) { 120 sizes := map[string]int64{ 121 "some-base": someBaseSize, 122 "other-base": otherBaseSize, 123 "snap-content-slot": snapContentSlotSize, 124 "snap-content-slot-other": snapOtherContentSlotSize, 125 "some-other-base": someOtherBaseSize, 126 } 127 for _, sa := range actions { 128 if sa.Action != "install" { 129 panic(fmt.Sprintf("unexpected action: %s", sa.Action)) 130 } 131 if sa.Channel != "stable" { 132 panic(fmt.Sprintf("unexpected channel: %s", sa.Channel)) 133 } 134 if _, ok := sizes[sa.InstanceName]; !ok { 135 panic(fmt.Sprintf("unexpected snap: %q", sa.InstanceName)) 136 } 137 } 138 sars, _, err := f.fakeStore.SnapAction(ctx, currentSnaps, actions, assertQuery, user, opts) 139 if err != nil { 140 return nil, nil, err 141 } 142 143 for _, sr := range sars { 144 if sz, ok := sizes[sr.Info.InstanceName()]; ok { 145 sr.Info.Size = sz 146 } else { 147 panic(fmt.Sprintf("unexpected snap: %q", sr.Info.InstanceName())) 148 } 149 if sr.Info.InstanceName() == "snap-content-slot-other" { 150 sr.Info.Base = "some-other-base" 151 } 152 } 153 return sars, nil, nil 154 } 155 156 func (s *snapmgrTestSuite) mockCoreSnap(c *C) { 157 snapstate.Set(s.state, "core", &snapstate.SnapState{ 158 Active: true, 159 Sequence: []*snap.SideInfo{ 160 {RealName: "core", SnapID: "core-id", Revision: snap.R(1)}, 161 }, 162 Current: snap.R(1), 163 SnapType: "os", 164 }) 165 // mock the yaml 166 makeInstalledMockCoreSnap(c) 167 } 168 169 func (s *snapmgrTestSuite) setupInstallSizeStore() { 170 fakestore := installSizeTestStore{fakeStore: s.fakeStore} 171 snapstate.ReplaceStore(s.state, fakestore) 172 } 173 174 func (s *snapmgrTestSuite) TestInstallSizeSimple(c *C) { 175 st := s.state 176 st.Lock() 177 defer st.Unlock() 178 179 s.setupInstallSizeStore() 180 s.mockCoreSnap(c) 181 182 snap1 := snaptest.MockSnap(c, snapYaml1, &snap.SideInfo{ 183 RealName: "some-snap1", 184 Revision: snap.R(1), 185 }) 186 snap1.Size = snap1Size 187 snap2 := snaptest.MockSnap(c, snapYaml2, &snap.SideInfo{ 188 RealName: "some-snap2", 189 Revision: snap.R(2), 190 }) 191 snap2.Size = snap2Size 192 193 sz, err := snapstate.InstallSize(st, []snapstate.MinimalInstallInfo{snapstate.InstallSnapInfo{Info: snap1}, snapstate.InstallSnapInfo{Info: snap2}}, 0) 194 c.Assert(err, IsNil) 195 c.Check(sz, Equals, uint64(snap1Size+snap2Size)) 196 } 197 198 func (s *snapmgrTestSuite) TestInstallSizeWithBases(c *C) { 199 st := s.state 200 st.Lock() 201 defer st.Unlock() 202 203 s.setupInstallSizeStore() 204 205 snap1 := snaptest.MockSnap(c, snapYamlWithBase1, &snap.SideInfo{ 206 RealName: "some-snap1", 207 Revision: snap.R(1), 208 }) 209 snap1.Size = snap1Size 210 snap2 := snaptest.MockSnap(c, snapYamlWithBase2, &snap.SideInfo{ 211 RealName: "some-snap2", 212 Revision: snap.R(2), 213 }) 214 snap2.Size = snap2Size 215 snap3 := snaptest.MockSnap(c, snapYamlWithBase3, &snap.SideInfo{ 216 RealName: "some-snap3", 217 Revision: snap.R(4), 218 }) 219 snap3.Size = snap3Size 220 snap4 := snaptest.MockSnap(c, snapYamlWithBase4, &snap.SideInfo{ 221 RealName: "some-snap4", 222 Revision: snap.R(1), 223 }) 224 snap4.Size = snap4Size 225 226 // base of some-snap4 is already installed 227 snapstate.Set(st, "yet-another-base", &snapstate.SnapState{ 228 Active: true, 229 Sequence: []*snap.SideInfo{ 230 {RealName: "yet-another-base", Revision: snap.R(1), SnapID: "yet-another-base-id"}, 231 }, 232 Current: snap.R(1), 233 }) 234 235 sz, err := snapstate.InstallSize(st, []snapstate.MinimalInstallInfo{ 236 snapstate.InstallSnapInfo{Info: snap1}, 237 snapstate.InstallSnapInfo{Info: snap2}, 238 snapstate.InstallSnapInfo{Info: snap3}, 239 snapstate.InstallSnapInfo{Info: snap4}}, 0) 240 c.Assert(err, IsNil) 241 c.Check(sz, Equals, uint64(snap1Size+snap2Size+snap3Size+snap4Size+someBaseSize+otherBaseSize)) 242 } 243 244 func (s *snapmgrTestSuite) TestInstallSizeWithContentProviders(c *C) { 245 st := s.state 246 st.Lock() 247 defer st.Unlock() 248 249 repo := interfaces.NewRepository() 250 ifacerepo.Replace(st, repo) 251 252 s.setupInstallSizeStore() 253 254 snap1 := snaptest.MockSnap(c, snapYamlWithContentPlug1, &snap.SideInfo{ 255 RealName: "some-snap", 256 Revision: snap.R(1), 257 }) 258 snap1.Size = snap1Size 259 260 snap2 := snaptest.MockSnap(c, snapYamlWithContentPlug2, &snap.SideInfo{ 261 RealName: "some-snap2", 262 Revision: snap.R(1), 263 }) 264 snap2.Size = snap2Size 265 266 s.mockCoreSnap(c) 267 268 // both snaps have same content providers and base 269 sz, err := snapstate.InstallSize(st, []snapstate.MinimalInstallInfo{ 270 snapstate.InstallSnapInfo{Info: snap1}, snapstate.InstallSnapInfo{Info: snap2}}, 0) 271 c.Assert(err, IsNil) 272 c.Check(sz, Equals, uint64(snap1Size+snap2Size+someBaseSize+snapContentSlotSize)) 273 } 274 275 func (s *snapmgrTestSuite) TestInstallSizeWithNestedDependencies(c *C) { 276 st := s.state 277 st.Lock() 278 defer st.Unlock() 279 280 repo := interfaces.NewRepository() 281 ifacerepo.Replace(st, repo) 282 283 s.setupInstallSizeStore() 284 snap1 := snaptest.MockSnap(c, snapYamlWithContentPlug3, &snap.SideInfo{ 285 RealName: "some-snap", 286 Revision: snap.R(1), 287 }) 288 snap1.Size = snap1Size 289 290 s.mockCoreSnap(c) 291 292 sz, err := snapstate.InstallSize(st, []snapstate.MinimalInstallInfo{snapstate.InstallSnapInfo{Info: snap1}}, 0) 293 c.Assert(err, IsNil) 294 c.Check(sz, Equals, uint64(snap1Size+someBaseSize+snapOtherContentSlotSize+someOtherBaseSize)) 295 } 296 297 func (s *snapmgrTestSuite) TestInstallSizeWithOtherChangeAffectingSameSnaps(c *C) { 298 st := s.state 299 st.Lock() 300 defer st.Unlock() 301 302 var currentSnapsCalled int 303 restore := snapstate.MockCurrentSnaps(func(st *state.State) ([]*store.CurrentSnap, error) { 304 currentSnapsCalled++ 305 // call original implementation of currentSnaps 306 curr, err := snapstate.CurrentSnaps(st) 307 if currentSnapsCalled == 1 { 308 return curr, err 309 } 310 // simulate other change that installed some-snap3 and other-base while 311 // we release the lock inside InstallSize. 312 curr = append(curr, &store.CurrentSnap{InstanceName: "some-snap3"}) 313 curr = append(curr, &store.CurrentSnap{InstanceName: "other-base"}) 314 return curr, nil 315 }) 316 defer restore() 317 318 s.setupInstallSizeStore() 319 320 snap1 := snaptest.MockSnap(c, snapYamlWithBase1, &snap.SideInfo{ 321 RealName: "some-snap1", 322 Revision: snap.R(1), 323 }) 324 snap1.Size = snap1Size 325 snap3 := snaptest.MockSnap(c, snapYamlWithBase3, &snap.SideInfo{ 326 RealName: "some-snap3", 327 Revision: snap.R(2), 328 }) 329 snap3.Size = snap3Size 330 331 sz, err := snapstate.InstallSize(st, []snapstate.MinimalInstallInfo{ 332 snapstate.InstallSnapInfo{Info: snap1}, snapstate.InstallSnapInfo{Info: snap3}}, 0) 333 c.Assert(err, IsNil) 334 // snap3 and its base installed by another change, not counted here 335 c.Check(sz, Equals, uint64(snap1Size+someBaseSize)) 336 337 // sanity 338 c.Check(currentSnapsCalled, Equals, 2) 339 } 340 341 func (s *snapmgrTestSuite) TestInstallSizeErrorNoDownloadInfo(c *C) { 342 st := s.state 343 st.Lock() 344 defer st.Unlock() 345 346 snap1 := &snap.Info{ 347 SideInfo: snap.SideInfo{ 348 RealName: "snap", 349 }} 350 351 _, err := snapstate.InstallSize(st, []snapstate.MinimalInstallInfo{snapstate.InstallSnapInfo{Info: snap1}}, 0) 352 c.Assert(err, ErrorMatches, `internal error: download info missing.*`) 353 }