github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/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  func (s *TestingSeed20) MakeSeed(c *C, label, brandID, modelID string, modelHeaders map[string]interface{}, optSnaps []*seedwriter.OptionsSnap) *asserts.Model {
   238  	model := s.Brands.Model(brandID, modelID, modelHeaders)
   239  
   240  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   241  		Backstore: asserts.NewMemoryBackstore(),
   242  		Trusted:   s.StoreSigning.Trusted,
   243  	})
   244  	c.Assert(err, IsNil)
   245  
   246  	retrieve := func(ref *asserts.Ref) (asserts.Assertion, error) {
   247  		return ref.Resolve(s.StoreSigning.Find)
   248  	}
   249  	newFetcher := func(save func(asserts.Assertion) error) asserts.Fetcher {
   250  		save2 := func(a asserts.Assertion) error {
   251  			// for checking
   252  			err := db.Add(a)
   253  			if err != nil {
   254  				if _, ok := err.(*asserts.RevisionError); ok {
   255  					return nil
   256  				}
   257  				return err
   258  			}
   259  			return save(a)
   260  		}
   261  		return asserts.NewFetcher(db, retrieve, save2)
   262  	}
   263  	assertstest.AddMany(s.StoreSigning, s.Brands.AccountsAndKeys(brandID)...)
   264  
   265  	opts := seedwriter.Options{
   266  		SeedDir: s.SeedDir,
   267  		Label:   label,
   268  	}
   269  	w, err := seedwriter.New(model, &opts)
   270  	c.Assert(err, IsNil)
   271  
   272  	err = w.SetOptionsSnaps(optSnaps)
   273  	c.Assert(err, IsNil)
   274  
   275  	rf, err := w.Start(db, newFetcher)
   276  	c.Assert(err, IsNil)
   277  
   278  	localSnaps, err := w.LocalSnaps()
   279  	c.Assert(err, IsNil)
   280  
   281  	for _, sn := range localSnaps {
   282  		si, aRefs, err := seedwriter.DeriveSideInfo(sn.Path, rf, db)
   283  		if !asserts.IsNotFound(err) {
   284  			c.Assert(err, IsNil)
   285  		}
   286  		f, err := snapfile.Open(sn.Path)
   287  		c.Assert(err, IsNil)
   288  		info, err := snap.ReadInfoFromSnapFile(f, si)
   289  		c.Assert(err, IsNil)
   290  		w.SetInfo(sn, info)
   291  		sn.ARefs = aRefs
   292  	}
   293  
   294  	err = w.InfoDerived()
   295  	c.Assert(err, IsNil)
   296  
   297  	for {
   298  		snaps, err := w.SnapsToDownload()
   299  		c.Assert(err, IsNil)
   300  
   301  		for _, sn := range snaps {
   302  			name := sn.SnapName()
   303  
   304  			info := s.AssertedSnapInfo(name)
   305  			c.Assert(info, NotNil, Commentf("no snap info for %q", name))
   306  			err := w.SetInfo(sn, info)
   307  			c.Assert(err, IsNil)
   308  
   309  			prev := len(rf.Refs())
   310  			err = rf.Save(s.snapRevs[name])
   311  			c.Assert(err, IsNil)
   312  			sn.ARefs = rf.Refs()[prev:]
   313  
   314  			if _, err := os.Stat(sn.Path); err == nil {
   315  				// snap is already present
   316  				continue
   317  			}
   318  
   319  			err = os.Rename(s.AssertedSnap(name), sn.Path)
   320  			c.Assert(err, IsNil)
   321  		}
   322  
   323  		complete, err := w.Downloaded()
   324  		c.Assert(err, IsNil)
   325  		if complete {
   326  			break
   327  		}
   328  	}
   329  
   330  	copySnap := func(name, src, dst string) error {
   331  		return osutil.CopyFile(src, dst, 0)
   332  	}
   333  
   334  	err = w.SeedSnaps(copySnap)
   335  	c.Assert(err, IsNil)
   336  
   337  	err = w.WriteMeta()
   338  	c.Assert(err, IsNil)
   339  
   340  	return model
   341  }
   342  
   343  func ValidateSeed(c *C, root, label string, usesSnapd bool, trusted []asserts.Assertion) seed.Seed {
   344  	tm := &timings.Timings{}
   345  	db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{
   346  		Backstore: asserts.NewMemoryBackstore(),
   347  		Trusted:   trusted,
   348  	})
   349  	c.Assert(err, IsNil)
   350  	commitTo := func(b *asserts.Batch) error {
   351  		return b.CommitTo(db, nil)
   352  	}
   353  
   354  	sd, err := seed.Open(root, label)
   355  	c.Assert(err, IsNil)
   356  
   357  	err = sd.LoadAssertions(db, commitTo)
   358  	c.Assert(err, IsNil)
   359  
   360  	err = sd.LoadMeta(tm)
   361  	c.Assert(err, IsNil)
   362  
   363  	// core18/core20 use the snapd snap, old core does not
   364  	c.Check(sd.UsesSnapdSnap(), Equals, usesSnapd)
   365  	if usesSnapd {
   366  		// core*, kernel, gadget, snapd
   367  		c.Check(sd.EssentialSnaps(), HasLen, 4)
   368  	} else {
   369  		// core, kernel, gadget
   370  		c.Check(sd.EssentialSnaps(), HasLen, 3)
   371  	}
   372  	return sd
   373  }