github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/seed/seed16.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-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 seed
    21  
    22  /* ATTN this should *not* use:
    23  
    24  * dirs package: it is passed an explicit directory to work on
    25  
    26  * release.OnClassic: it assumes classic based on the model classic
    27    option; consistency between system and model can/must be enforced
    28    elsewhere
    29  
    30  */
    31  
    32  import (
    33  	"fmt"
    34  	"path/filepath"
    35  
    36  	"github.com/snapcore/snapd/asserts"
    37  	"github.com/snapcore/snapd/asserts/snapasserts"
    38  	"github.com/snapcore/snapd/osutil"
    39  	"github.com/snapcore/snapd/seed/internal"
    40  	"github.com/snapcore/snapd/snap"
    41  	"github.com/snapcore/snapd/snap/channel"
    42  	"github.com/snapcore/snapd/snap/naming"
    43  	"github.com/snapcore/snapd/timings"
    44  )
    45  
    46  type seed16 struct {
    47  	seedDir string
    48  
    49  	db asserts.RODatabase
    50  
    51  	model *asserts.Model
    52  
    53  	yamlSnaps []*internal.Snap16
    54  
    55  	essCache map[string]*Snap
    56  
    57  	snaps             []*Snap
    58  	essentialSnapsNum int
    59  
    60  	usesSnapdSnap bool
    61  }
    62  
    63  func (s *seed16) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error {
    64  	if db == nil {
    65  		// a db was not provided, create an internal temporary one
    66  		var err error
    67  		db, commitTo, err = newMemAssertionsDB(nil)
    68  		if err != nil {
    69  			return err
    70  		}
    71  	}
    72  
    73  	assertSeedDir := filepath.Join(s.seedDir, "assertions")
    74  	// collect assertions and find model assertion
    75  	var modelRef *asserts.Ref
    76  	checkForModel := func(ref *asserts.Ref) error {
    77  		if ref.Type == asserts.ModelType {
    78  			if modelRef != nil && modelRef.Unique() != ref.Unique() {
    79  				return fmt.Errorf("cannot have multiple model assertions in seed")
    80  			}
    81  			modelRef = ref
    82  		}
    83  		return nil
    84  	}
    85  
    86  	batch, err := loadAssertions(assertSeedDir, checkForModel)
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	// verify we have one model assertion
    92  	if modelRef == nil {
    93  		return fmt.Errorf("seed must have a model assertion")
    94  	}
    95  
    96  	if err := commitTo(batch); err != nil {
    97  		return err
    98  	}
    99  
   100  	a, err := modelRef.Resolve(db.Find)
   101  	if err != nil {
   102  		return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err)
   103  	}
   104  
   105  	// remember db for later use
   106  	s.db = db
   107  	s.model = a.(*asserts.Model)
   108  
   109  	return nil
   110  }
   111  
   112  func (s *seed16) Model() *asserts.Model {
   113  	if s.model == nil {
   114  		panic("internal error: model assertion unset (LoadAssertions not called)")
   115  	}
   116  	return s.model
   117  }
   118  
   119  func (s *seed16) Brand() (*asserts.Account, error) {
   120  	return findBrand(s, s.db)
   121  }
   122  
   123  func (s *seed16) addSnap(sn *internal.Snap16, pinnedTrack string, cache map[string]*Snap, tm timings.Measurer) (*Snap, error) {
   124  	path := filepath.Join(s.seedDir, "snaps", sn.File)
   125  
   126  	seedSnap := cache[path]
   127  	if seedSnap == nil {
   128  		snapChannel := sn.Channel
   129  		if pinnedTrack != "" {
   130  			var err error
   131  			snapChannel, err = channel.ResolvePinned(pinnedTrack, snapChannel)
   132  			if err != nil {
   133  				// fallback to using the pinned track directly
   134  				snapChannel = pinnedTrack
   135  			}
   136  		}
   137  		seedSnap = &Snap{
   138  			Path:    path,
   139  			Channel: snapChannel,
   140  			Classic: sn.Classic,
   141  			DevMode: sn.DevMode,
   142  		}
   143  
   144  		var sideInfo snap.SideInfo
   145  		if sn.Unasserted {
   146  			sideInfo.RealName = sn.Name
   147  		} else {
   148  			var si *snap.SideInfo
   149  			var err error
   150  			timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", sn.Name), func(nested timings.Measurer) {
   151  				si, err = snapasserts.DeriveSideInfo(path, s.db)
   152  			})
   153  			if asserts.IsNotFound(err) {
   154  				return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path)
   155  			}
   156  			if err != nil {
   157  				return nil, err
   158  			}
   159  			sideInfo = *si
   160  			sideInfo.Private = sn.Private
   161  			// TODO: consider whether to use this if we have links?
   162  			sideInfo.EditedContact = sn.Contact
   163  		}
   164  
   165  		seedSnap.SideInfo = &sideInfo
   166  		if cache != nil {
   167  			cache[path] = seedSnap
   168  		}
   169  	}
   170  
   171  	s.snaps = append(s.snaps, seedSnap)
   172  
   173  	return seedSnap, nil
   174  }
   175  
   176  type essentialSnapMissingError struct {
   177  	SnapName string
   178  }
   179  
   180  func (e *essentialSnapMissingError) Error() string {
   181  	return fmt.Sprintf("essential snap %q required by the model is missing in the seed", e.SnapName)
   182  }
   183  
   184  func (s *seed16) loadYaml() error {
   185  	if s.yamlSnaps != nil {
   186  		return nil
   187  	}
   188  
   189  	seedYamlFile := filepath.Join(s.seedDir, "seed.yaml")
   190  	if !osutil.FileExists(seedYamlFile) {
   191  		return ErrNoMeta
   192  	}
   193  
   194  	seedYaml, err := internal.ReadSeedYaml(seedYamlFile)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	s.yamlSnaps = seedYaml.Snaps
   199  
   200  	return nil
   201  }
   202  
   203  func (s *seed16) resetSnaps() {
   204  	// setup essential snaps cache
   205  	if s.essCache == nil {
   206  		// 4 = snapd+base+kernel+gadget
   207  		s.essCache = make(map[string]*Snap, 4)
   208  	}
   209  
   210  	s.snaps = nil
   211  	s.essentialSnapsNum = 0
   212  }
   213  
   214  func (s *seed16) loadEssentialMeta(essentialTypes []snap.Type, required *naming.SnapSet, added map[string]bool, tm timings.Measurer) error {
   215  	model := s.Model()
   216  
   217  	seeding := make(map[string]*internal.Snap16, len(s.yamlSnaps))
   218  	for _, sn := range s.yamlSnaps {
   219  		seeding[sn.Name] = sn
   220  	}
   221  
   222  	classic := model.Classic()
   223  	_, usesSnapdSnap := seeding["snapd"]
   224  	usesSnapdSnap = usesSnapdSnap || required.Contains(naming.Snap("snapd"))
   225  	s.usesSnapdSnap = usesSnapdSnap
   226  
   227  	baseSnap := "core"
   228  	classicWithSnapd := false
   229  	if model.Base() != "" {
   230  		baseSnap = model.Base()
   231  	}
   232  	if classic && s.usesSnapdSnap {
   233  		classicWithSnapd = true
   234  		// there is no system-wide base as such
   235  		// if there is a gadget we will install its base first though
   236  		baseSnap = ""
   237  	}
   238  
   239  	// add the essential snaps
   240  	addEssential := func(snapName string, pinnedTrack string, essType snap.Type) (*Snap, error) {
   241  		// be idempotent
   242  		if added[snapName] {
   243  			return nil, nil
   244  		}
   245  
   246  		// filter if required
   247  		if essentialTypes != nil {
   248  			skip := true
   249  			for _, t := range essentialTypes {
   250  				if t == essType {
   251  					skip = false
   252  					break
   253  				}
   254  			}
   255  			if skip {
   256  				return nil, nil
   257  			}
   258  		}
   259  
   260  		yamlSnap := seeding[snapName]
   261  		if yamlSnap == nil {
   262  			return nil, &essentialSnapMissingError{SnapName: snapName}
   263  		}
   264  
   265  		seedSnap, err := s.addSnap(yamlSnap, pinnedTrack, s.essCache, tm)
   266  		if err != nil {
   267  			return nil, err
   268  		}
   269  
   270  		if essType == snap.TypeBase && snapName == "core" {
   271  			essType = snap.TypeOS
   272  		}
   273  
   274  		seedSnap.EssentialType = essType
   275  		seedSnap.Essential = true
   276  		seedSnap.Required = true
   277  		added[snapName] = true
   278  
   279  		return seedSnap, nil
   280  	}
   281  
   282  	// if there are snaps to seed, core/base needs to be seeded too
   283  	if len(s.yamlSnaps) != 0 {
   284  		// ensure "snapd" snap is installed first
   285  		if model.Base() != "" || classicWithSnapd {
   286  			if _, err := addEssential("snapd", "", snap.TypeSnapd); err != nil {
   287  				return err
   288  			}
   289  		}
   290  		if !classicWithSnapd {
   291  			if _, err := addEssential(baseSnap, "", snap.TypeBase); err != nil {
   292  				return err
   293  			}
   294  		}
   295  	}
   296  
   297  	if kernelName := model.Kernel(); kernelName != "" {
   298  		if _, err := addEssential(kernelName, model.KernelTrack(), snap.TypeKernel); err != nil {
   299  			return err
   300  		}
   301  	}
   302  
   303  	if gadgetName := model.Gadget(); gadgetName != "" {
   304  		gadget, err := addEssential(gadgetName, model.GadgetTrack(), snap.TypeGadget)
   305  		if err != nil {
   306  			return err
   307  		}
   308  		// not skipped
   309  		if gadget != nil {
   310  
   311  			// always make sure the base of gadget is installed first
   312  			info, err := readInfo(gadget.Path, gadget.SideInfo)
   313  			if err != nil {
   314  				return err
   315  			}
   316  			gadgetBase := info.Base
   317  			if gadgetBase == "" {
   318  				gadgetBase = "core"
   319  			}
   320  			// Sanity check
   321  			// TODO: do we want to relax this? the new logic would allow
   322  			// but it might just be confusing for now
   323  			if baseSnap != "" && gadgetBase != baseSnap {
   324  				return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base())
   325  			}
   326  			if _, err = addEssential(gadgetBase, "", snap.TypeBase); err != nil {
   327  				return err
   328  			}
   329  		}
   330  	}
   331  
   332  	s.essentialSnapsNum = len(s.snaps)
   333  
   334  	return nil
   335  }
   336  
   337  func (s *seed16) LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error {
   338  	model := s.Model()
   339  
   340  	if err := s.loadYaml(); err != nil {
   341  		return err
   342  	}
   343  
   344  	required := naming.NewSnapSet(model.RequiredWithEssentialSnaps())
   345  	added := make(map[string]bool, 3)
   346  
   347  	s.resetSnaps()
   348  
   349  	return s.loadEssentialMeta(essentialTypes, required, added, tm)
   350  }
   351  
   352  func (s *seed16) LoadMeta(tm timings.Measurer) error {
   353  	model := s.Model()
   354  
   355  	if err := s.loadYaml(); err != nil {
   356  		return err
   357  	}
   358  
   359  	required := naming.NewSnapSet(model.RequiredWithEssentialSnaps())
   360  	added := make(map[string]bool, 3)
   361  
   362  	s.resetSnaps()
   363  
   364  	if err := s.loadEssentialMeta(nil, required, added, tm); err != nil {
   365  		return err
   366  	}
   367  
   368  	// the rest of the snaps
   369  	for _, sn := range s.yamlSnaps {
   370  		if added[sn.Name] {
   371  			continue
   372  		}
   373  		seedSnap, err := s.addSnap(sn, "", nil, tm)
   374  		if err != nil {
   375  			return err
   376  		}
   377  		if required.Contains(seedSnap) {
   378  			seedSnap.Required = true
   379  		}
   380  	}
   381  
   382  	return nil
   383  }
   384  
   385  func (s *seed16) UsesSnapdSnap() bool {
   386  	return s.usesSnapdSnap
   387  }
   388  
   389  func (s *seed16) EssentialSnaps() []*Snap {
   390  	return s.snaps[:s.essentialSnapsNum]
   391  }
   392  
   393  func (s *seed16) ModeSnaps(mode string) ([]*Snap, error) {
   394  	if mode != "run" {
   395  		return nil, fmt.Errorf("internal error: Core 16/18 have only run mode, got: %s", mode)
   396  	}
   397  	return s.snaps[s.essentialSnapsNum:], nil
   398  }
   399  
   400  func (s *seed16) NumSnaps() int {
   401  	return len(s.snaps)
   402  }
   403  
   404  func (s *seed16) Iter(f func(sn *Snap) error) error {
   405  	for _, sn := range s.snaps {
   406  		if err := f(sn); err != nil {
   407  			return err
   408  		}
   409  	}
   410  	return nil
   411  }