github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/seed/seedtest/seedtest.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2019 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 seedtest 21 22 import ( 23 "fmt" 24 "io" 25 "os" 26 "path/filepath" 27 "time" 28 29 . "gopkg.in/check.v1" 30 31 "github.com/snapcore/snapd/asserts" 32 "github.com/snapcore/snapd/asserts/assertstest" 33 "github.com/snapcore/snapd/osutil" 34 "github.com/snapcore/snapd/seed" 35 "github.com/snapcore/snapd/seed/seedwriter" 36 "github.com/snapcore/snapd/snap" 37 "github.com/snapcore/snapd/snap/naming" 38 "github.com/snapcore/snapd/snap/snapfile" 39 "github.com/snapcore/snapd/snap/snaptest" 40 "github.com/snapcore/snapd/timings" 41 ) 42 43 // SeedSnaps helps creating snaps for a seed. 44 type SeedSnaps struct { 45 StoreSigning *assertstest.StoreStack 46 Brands *assertstest.SigningAccounts 47 48 snaps map[string]string 49 infos map[string]*snap.Info 50 51 snapAssertNow time.Time 52 53 snapRevs map[string]*asserts.SnapRevision 54 } 55 56 // SetupAssertSigning initializes StoreSigning for storeBrandID and Brands. 57 func (ss *SeedSnaps) SetupAssertSigning(storeBrandID string) { 58 ss.StoreSigning = assertstest.NewStoreStack(storeBrandID, nil) 59 ss.Brands = assertstest.NewSigningAccounts(ss.StoreSigning) 60 } 61 62 func (ss *SeedSnaps) AssertedSnapID(snapName string) string { 63 snapID := naming.WellKnownSnapID(snapName) 64 if snapID != "" { 65 return snapID 66 } 67 return snaptest.AssertedSnapID(snapName) 68 } 69 70 func (ss *SeedSnaps) snapAssertionNow() time.Time { 71 if ss.snapAssertNow.IsZero() { 72 return time.Now() 73 } 74 return ss.snapAssertNow 75 } 76 77 func (ss *SeedSnaps) SetSnapAssertionNow(t time.Time) { 78 ss.snapAssertNow = t 79 } 80 81 func (ss *SeedSnaps) MakeAssertedSnap(c *C, snapYaml string, files [][]string, revision snap.Revision, developerID string, dbs ...*asserts.Database) (*asserts.SnapDeclaration, *asserts.SnapRevision) { 82 info, err := snap.InfoFromSnapYaml([]byte(snapYaml)) 83 c.Assert(err, IsNil) 84 snapName := info.SnapName() 85 86 snapFile := snaptest.MakeTestSnapWithFiles(c, snapYaml, files) 87 88 snapID := ss.AssertedSnapID(snapName) 89 declA, err := ss.StoreSigning.Sign(asserts.SnapDeclarationType, map[string]interface{}{ 90 "series": "16", 91 "snap-id": snapID, 92 "publisher-id": developerID, 93 "snap-name": snapName, 94 "timestamp": ss.snapAssertionNow().UTC().Format(time.RFC3339), 95 }, nil, "") 96 c.Assert(err, IsNil) 97 98 sha3_384, size, err := asserts.SnapFileSHA3_384(snapFile) 99 c.Assert(err, IsNil) 100 101 revA, err := ss.StoreSigning.Sign(asserts.SnapRevisionType, map[string]interface{}{ 102 "snap-sha3-384": sha3_384, 103 "snap-size": fmt.Sprintf("%d", size), 104 "snap-id": snapID, 105 "developer-id": developerID, 106 "snap-revision": revision.String(), 107 "timestamp": ss.snapAssertionNow().UTC().Format(time.RFC3339), 108 }, nil, "") 109 c.Assert(err, IsNil) 110 111 if !revision.Unset() { 112 info.SnapID = snapID 113 info.Revision = revision 114 } 115 116 for _, db := range dbs { 117 err := db.Add(declA) 118 c.Assert(err, IsNil) 119 err = db.Add(revA) 120 c.Assert(err, IsNil) 121 } 122 123 if ss.snaps == nil { 124 ss.snaps = make(map[string]string) 125 ss.infos = make(map[string]*snap.Info) 126 ss.snapRevs = make(map[string]*asserts.SnapRevision) 127 } 128 129 ss.snaps[snapName] = snapFile 130 info.SideInfo.RealName = snapName 131 ss.infos[snapName] = info 132 snapDecl := declA.(*asserts.SnapDeclaration) 133 snapRev := revA.(*asserts.SnapRevision) 134 ss.snapRevs[snapName] = snapRev 135 136 return snapDecl, snapRev 137 } 138 139 func (ss *SeedSnaps) AssertedSnap(snapName string) (snapFile string) { 140 return ss.snaps[snapName] 141 } 142 143 func (ss *SeedSnaps) AssertedSnapInfo(snapName string) *snap.Info { 144 return ss.infos[snapName] 145 } 146 147 func (ss *SeedSnaps) AssertedSnapRevision(snapName string) *asserts.SnapRevision { 148 return ss.snapRevs[snapName] 149 } 150 151 // TestingSeed16 helps setting up a populated Core 16/18 testing seed. 152 type TestingSeed16 struct { 153 SeedSnaps 154 155 SeedDir string 156 } 157 158 func (s *TestingSeed16) SnapsDir() string { 159 return filepath.Join(s.SeedDir, "snaps") 160 } 161 162 func (s *TestingSeed16) AssertsDir() string { 163 return filepath.Join(s.SeedDir, "assertions") 164 } 165 166 func (s *TestingSeed16) MakeAssertedSnap(c *C, snapYaml string, files [][]string, revision snap.Revision, developerID string) (snapFname string, snapDecl *asserts.SnapDeclaration, snapRev *asserts.SnapRevision) { 167 decl, rev := s.SeedSnaps.MakeAssertedSnap(c, snapYaml, files, revision, developerID) 168 169 snapFile := s.snaps[decl.SnapName()] 170 171 snapFname = filepath.Base(snapFile) 172 targetFile := filepath.Join(s.SnapsDir(), snapFname) 173 err := os.Rename(snapFile, targetFile) 174 c.Assert(err, IsNil) 175 176 return snapFname, decl, rev 177 } 178 179 func (s *TestingSeed16) MakeModelAssertionChain(brandID, model string, extras ...map[string]interface{}) []asserts.Assertion { 180 assertChain := []asserts.Assertion{} 181 modelA := s.Brands.Model(brandID, model, extras...) 182 183 assertChain = append(assertChain, s.Brands.Account(modelA.BrandID())) 184 assertChain = append(assertChain, s.Brands.AccountKey(modelA.BrandID())) 185 assertChain = append(assertChain, modelA) 186 187 storeAccountKey := s.StoreSigning.StoreAccountKey("") 188 assertChain = append(assertChain, storeAccountKey) 189 return assertChain 190 } 191 192 func (s *TestingSeed16) WriteAssertions(fn string, assertions ...asserts.Assertion) { 193 fn = filepath.Join(s.AssertsDir(), fn) 194 WriteAssertions(fn, assertions...) 195 } 196 197 func WriteAssertions(fn string, assertions ...asserts.Assertion) { 198 f, err := os.OpenFile(fn, os.O_CREATE|os.O_WRONLY, 0644) 199 if err != nil { 200 panic(err) 201 } 202 defer f.Close() 203 enc := asserts.NewEncoder(f) 204 for _, a := range assertions { 205 err := enc.Encode(a) 206 if err != nil { 207 panic(err) 208 } 209 } 210 } 211 212 func ReadAssertions(c *C, fn string) []asserts.Assertion { 213 f, err := os.Open(fn) 214 c.Assert(err, IsNil) 215 216 var as []asserts.Assertion 217 dec := asserts.NewDecoder(f) 218 for { 219 a, err := dec.Decode() 220 if err == io.EOF { 221 break 222 } 223 c.Assert(err, IsNil) 224 as = append(as, a) 225 } 226 227 return as 228 } 229 230 // TestingSeed20 helps setting up a populated Core 20 testing seed directory. 231 type TestingSeed20 struct { 232 SeedSnaps 233 234 SeedDir string 235 } 236 237 // MakeSeed creates the seed with given label and generates model assertions 238 func (s *TestingSeed20) MakeSeed(c *C, label, brandID, modelID string, modelHeaders map[string]interface{}, optSnaps []*seedwriter.OptionsSnap) *asserts.Model { 239 model := s.Brands.Model(brandID, modelID, modelHeaders) 240 241 assertstest.AddMany(s.StoreSigning, s.Brands.AccountsAndKeys(brandID)...) 242 243 s.MakeSeedWithModel(c, label, model, optSnaps) 244 return model 245 } 246 247 // MakeSeedWithModel creates the seed with given label for a given model 248 func (s *TestingSeed20) MakeSeedWithModel(c *C, label string, model *asserts.Model, optSnaps []*seedwriter.OptionsSnap) { 249 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 250 Backstore: asserts.NewMemoryBackstore(), 251 Trusted: s.StoreSigning.Trusted, 252 }) 253 c.Assert(err, IsNil) 254 255 retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) { 256 return ref.Resolve(s.StoreSigning.Find) 257 } 258 newFetcher := func(save func(asserts.Assertion) error) asserts.Fetcher { 259 save2 := func(a asserts.Assertion) error { 260 // for checking 261 err := db.Add(a) 262 if err != nil { 263 if _, ok := err.(*asserts.RevisionError); ok { 264 return nil 265 } 266 return err 267 } 268 return save(a) 269 } 270 return asserts.NewFetcher(db, retrieve, save2) 271 } 272 273 opts := seedwriter.Options{ 274 SeedDir: s.SeedDir, 275 Label: label, 276 } 277 w, err := seedwriter.New(model, &opts) 278 c.Assert(err, IsNil) 279 280 err = w.SetOptionsSnaps(optSnaps) 281 c.Assert(err, IsNil) 282 283 rf, err := w.Start(db, newFetcher) 284 c.Assert(err, IsNil) 285 286 localSnaps, err := w.LocalSnaps() 287 c.Assert(err, IsNil) 288 289 for _, sn := range localSnaps { 290 si, aRefs, err := seedwriter.DeriveSideInfo(sn.Path, rf, db) 291 if !asserts.IsNotFound(err) { 292 c.Assert(err, IsNil) 293 } 294 f, err := snapfile.Open(sn.Path) 295 c.Assert(err, IsNil) 296 info, err := snap.ReadInfoFromSnapFile(f, si) 297 c.Assert(err, IsNil) 298 w.SetInfo(sn, info) 299 sn.ARefs = aRefs 300 } 301 302 err = w.InfoDerived() 303 c.Assert(err, IsNil) 304 305 for { 306 snaps, err := w.SnapsToDownload() 307 c.Assert(err, IsNil) 308 309 for _, sn := range snaps { 310 name := sn.SnapName() 311 312 info := s.AssertedSnapInfo(name) 313 c.Assert(info, NotNil, Commentf("no snap info for %q", name)) 314 err := w.SetInfo(sn, info) 315 c.Assert(err, IsNil) 316 317 prev := len(rf.Refs()) 318 err = rf.Save(s.snapRevs[name]) 319 c.Assert(err, IsNil) 320 sn.ARefs = rf.Refs()[prev:] 321 322 if _, err := os.Stat(sn.Path); err == nil { 323 // snap is already present 324 continue 325 } 326 327 err = os.Rename(s.AssertedSnap(name), sn.Path) 328 c.Assert(err, IsNil) 329 } 330 331 complete, err := w.Downloaded() 332 c.Assert(err, IsNil) 333 if complete { 334 break 335 } 336 } 337 338 copySnap := func(name, src, dst string) error { 339 return osutil.CopyFile(src, dst, 0) 340 } 341 342 err = w.SeedSnaps(copySnap) 343 c.Assert(err, IsNil) 344 345 err = w.WriteMeta() 346 c.Assert(err, IsNil) 347 } 348 349 func ValidateSeed(c *C, root, label string, usesSnapd bool, trusted []asserts.Assertion) seed.Seed { 350 tm := &timings.Timings{} 351 db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ 352 Backstore: asserts.NewMemoryBackstore(), 353 Trusted: trusted, 354 }) 355 c.Assert(err, IsNil) 356 commitTo := func(b *asserts.Batch) error { 357 return b.CommitTo(db, nil) 358 } 359 360 sd, err := seed.Open(root, label) 361 c.Assert(err, IsNil) 362 363 err = sd.LoadAssertions(db, commitTo) 364 c.Assert(err, IsNil) 365 366 err = sd.LoadMeta(tm) 367 c.Assert(err, IsNil) 368 369 // core18/core20 use the snapd snap, old core does not 370 c.Check(sd.UsesSnapdSnap(), Equals, usesSnapd) 371 if usesSnapd { 372 // core*, kernel, gadget, snapd 373 c.Check(sd.EssentialSnaps(), HasLen, 4) 374 } else { 375 // core, kernel, gadget 376 c.Check(sd.EssentialSnaps(), HasLen, 3) 377 } 378 return sd 379 }