github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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, []*snap.Info{snap1, 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, []*snap.Info{snap1, snap2, snap3, snap4}, 0) 236 c.Assert(err, IsNil) 237 c.Check(sz, Equals, uint64(snap1Size+snap2Size+snap3Size+snap4Size+someBaseSize+otherBaseSize)) 238 } 239 240 func (s *snapmgrTestSuite) TestInstallSizeWithContentProviders(c *C) { 241 st := s.state 242 st.Lock() 243 defer st.Unlock() 244 245 repo := interfaces.NewRepository() 246 ifacerepo.Replace(st, repo) 247 248 s.setupInstallSizeStore() 249 250 snap1 := snaptest.MockSnap(c, snapYamlWithContentPlug1, &snap.SideInfo{ 251 RealName: "some-snap", 252 Revision: snap.R(1), 253 }) 254 snap1.Size = snap1Size 255 256 snap2 := snaptest.MockSnap(c, snapYamlWithContentPlug2, &snap.SideInfo{ 257 RealName: "some-snap2", 258 Revision: snap.R(1), 259 }) 260 snap2.Size = snap2Size 261 262 s.mockCoreSnap(c) 263 264 // both snaps have same content providers and base 265 sz, err := snapstate.InstallSize(st, []*snap.Info{snap1, snap2}, 0) 266 c.Assert(err, IsNil) 267 c.Check(sz, Equals, uint64(snap1Size+snap2Size+someBaseSize+snapContentSlotSize)) 268 } 269 270 func (s *snapmgrTestSuite) TestInstallSizeWithNestedDependencies(c *C) { 271 st := s.state 272 st.Lock() 273 defer st.Unlock() 274 275 repo := interfaces.NewRepository() 276 ifacerepo.Replace(st, repo) 277 278 s.setupInstallSizeStore() 279 snap1 := snaptest.MockSnap(c, snapYamlWithContentPlug3, &snap.SideInfo{ 280 RealName: "some-snap", 281 Revision: snap.R(1), 282 }) 283 snap1.Size = snap1Size 284 285 s.mockCoreSnap(c) 286 287 sz, err := snapstate.InstallSize(st, []*snap.Info{snap1}, 0) 288 c.Assert(err, IsNil) 289 c.Check(sz, Equals, uint64(snap1Size+someBaseSize+snapOtherContentSlotSize+someOtherBaseSize)) 290 } 291 292 func (s *snapmgrTestSuite) TestInstallSizeWithOtherChangeAffectingSameSnaps(c *C) { 293 st := s.state 294 st.Lock() 295 defer st.Unlock() 296 297 var currentSnapsCalled int 298 restore := snapstate.MockCurrentSnaps(func(st *state.State) ([]*store.CurrentSnap, error) { 299 currentSnapsCalled++ 300 // call original implementation of currentSnaps 301 curr, err := snapstate.CurrentSnaps(st) 302 if currentSnapsCalled == 1 { 303 return curr, err 304 } 305 // simulate other change that installed some-snap3 and other-base while 306 // we release the lock inside InstallSize. 307 curr = append(curr, &store.CurrentSnap{InstanceName: "some-snap3"}) 308 curr = append(curr, &store.CurrentSnap{InstanceName: "other-base"}) 309 return curr, nil 310 }) 311 defer restore() 312 313 s.setupInstallSizeStore() 314 315 snap1 := snaptest.MockSnap(c, snapYamlWithBase1, &snap.SideInfo{ 316 RealName: "some-snap1", 317 Revision: snap.R(1), 318 }) 319 snap1.Size = snap1Size 320 snap3 := snaptest.MockSnap(c, snapYamlWithBase3, &snap.SideInfo{ 321 RealName: "some-snap3", 322 Revision: snap.R(2), 323 }) 324 snap3.Size = snap3Size 325 326 sz, err := snapstate.InstallSize(st, []*snap.Info{snap1, snap3}, 0) 327 c.Assert(err, IsNil) 328 // snap3 and its base installed by another change, not counted here 329 c.Check(sz, Equals, uint64(snap1Size+someBaseSize)) 330 331 // sanity 332 c.Check(currentSnapsCalled, Equals, 2) 333 } 334 335 func (s *snapmgrTestSuite) TestInstallSizeErrorNoDownloadInfo(c *C) { 336 st := s.state 337 st.Lock() 338 defer st.Unlock() 339 340 snap1 := &snap.Info{ 341 SideInfo: snap.SideInfo{ 342 RealName: "snap", 343 }} 344 345 _, err := snapstate.InstallSize(st, []*snap.Info{snap1}, 0) 346 c.Assert(err, ErrorMatches, `internal error: download info missing.*`) 347 }