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