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  }