github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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  	snaps             []*Snap
    54  	essentialSnapsNum int
    55  
    56  	usesSnapdSnap bool
    57  }
    58  
    59  func (s *seed16) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error {
    60  	if db == nil {
    61  		// a db was not provided, create an internal temporary one
    62  		var err error
    63  		db, commitTo, err = newMemAssertionsDB()
    64  		if err != nil {
    65  			return err
    66  		}
    67  	}
    68  
    69  	assertSeedDir := filepath.Join(s.seedDir, "assertions")
    70  	// collect assertions and find model assertion
    71  	var modelRef *asserts.Ref
    72  	checkForModel := func(ref *asserts.Ref) error {
    73  		if ref.Type == asserts.ModelType {
    74  			if modelRef != nil && modelRef.Unique() != ref.Unique() {
    75  				return fmt.Errorf("cannot have multiple model assertions in seed")
    76  			}
    77  			modelRef = ref
    78  		}
    79  		return nil
    80  	}
    81  
    82  	batch, err := loadAssertions(assertSeedDir, checkForModel)
    83  	if err != nil {
    84  		return err
    85  	}
    86  
    87  	// verify we have one model assertion
    88  	if modelRef == nil {
    89  		return fmt.Errorf("seed must have a model assertion")
    90  	}
    91  
    92  	if err := commitTo(batch); err != nil {
    93  		return err
    94  	}
    95  
    96  	a, err := modelRef.Resolve(db.Find)
    97  	if err != nil {
    98  		return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err)
    99  	}
   100  
   101  	// remember db for later use
   102  	s.db = db
   103  	s.model = a.(*asserts.Model)
   104  
   105  	return nil
   106  }
   107  
   108  func (s *seed16) Model() *asserts.Model {
   109  	if s.model == nil {
   110  		panic("internal error: model assertion unset (LoadAssertions not called)")
   111  	}
   112  	return s.model
   113  }
   114  
   115  func (s *seed16) Brand() (*asserts.Account, error) {
   116  	return findBrand(s, s.db)
   117  }
   118  
   119  func (s *seed16) addSnap(sn *internal.Snap16, pinnedTrack string, tm timings.Measurer) (*Snap, error) {
   120  	path := filepath.Join(s.seedDir, "snaps", sn.File)
   121  	snapChannel := sn.Channel
   122  	if pinnedTrack != "" {
   123  		var err error
   124  		snapChannel, err = channel.ResolvePinned(pinnedTrack, snapChannel)
   125  		if err != nil {
   126  			// fallback to using the pinned track directly
   127  			snapChannel = pinnedTrack
   128  		}
   129  	}
   130  	seedSnap := &Snap{
   131  		Path:    path,
   132  		Channel: snapChannel,
   133  		Classic: sn.Classic,
   134  		DevMode: sn.DevMode,
   135  	}
   136  
   137  	var sideInfo snap.SideInfo
   138  	if sn.Unasserted {
   139  		sideInfo.RealName = sn.Name
   140  	} else {
   141  		var si *snap.SideInfo
   142  		var err error
   143  		timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", sn.Name), func(nested timings.Measurer) {
   144  			si, err = snapasserts.DeriveSideInfo(path, s.db)
   145  		})
   146  		if asserts.IsNotFound(err) {
   147  			return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path)
   148  		}
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  		sideInfo = *si
   153  		sideInfo.Private = sn.Private
   154  		sideInfo.Contact = sn.Contact
   155  	}
   156  
   157  	seedSnap.SideInfo = &sideInfo
   158  
   159  	s.snaps = append(s.snaps, seedSnap)
   160  
   161  	return seedSnap, nil
   162  }
   163  
   164  type essentialSnapMissingError struct {
   165  	SnapName string
   166  }
   167  
   168  func (e *essentialSnapMissingError) Error() string {
   169  	return fmt.Sprintf("essential snap %q required by the model is missing in the seed", e.SnapName)
   170  }
   171  
   172  func (s *seed16) LoadMeta(tm timings.Measurer) error {
   173  	model := s.Model()
   174  
   175  	seedYamlFile := filepath.Join(s.seedDir, "seed.yaml")
   176  	if !osutil.FileExists(seedYamlFile) {
   177  		return ErrNoMeta
   178  	}
   179  
   180  	seedYaml, err := internal.ReadSeedYaml(seedYamlFile)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	yamlSnaps := seedYaml.Snaps
   185  
   186  	required := naming.NewSnapSet(model.RequiredWithEssentialSnaps())
   187  	seeding := make(map[string]*internal.Snap16, len(yamlSnaps))
   188  	for _, sn := range yamlSnaps {
   189  		seeding[sn.Name] = sn
   190  	}
   191  	added := make(map[string]bool, 3)
   192  	classic := model.Classic()
   193  	_, usesSnapdSnap := seeding["snapd"]
   194  	usesSnapdSnap = usesSnapdSnap || required.Contains(naming.Snap("snapd"))
   195  	s.usesSnapdSnap = usesSnapdSnap
   196  
   197  	baseSnap := "core"
   198  	classicWithSnapd := false
   199  	if model.Base() != "" {
   200  		baseSnap = model.Base()
   201  	}
   202  	if classic && s.usesSnapdSnap {
   203  		classicWithSnapd = true
   204  		// there is no system-wide base as such
   205  		// if there is a gadget we will install its base first though
   206  		baseSnap = ""
   207  	}
   208  
   209  	// add the essential snaps
   210  	addEssential := func(snapName string, pinnedTrack string, essType snap.Type) (*Snap, error) {
   211  		// be idempotent
   212  		if added[snapName] {
   213  			return nil, nil
   214  		}
   215  		yamlSnap := seeding[snapName]
   216  		if yamlSnap == nil {
   217  			return nil, &essentialSnapMissingError{SnapName: snapName}
   218  		}
   219  
   220  		seedSnap, err := s.addSnap(yamlSnap, pinnedTrack, tm)
   221  		if err != nil {
   222  			return nil, err
   223  		}
   224  
   225  		if essType == snap.TypeBase && snapName == "core" {
   226  			essType = snap.TypeOS
   227  		}
   228  
   229  		seedSnap.EssentialType = essType
   230  		seedSnap.Essential = true
   231  		seedSnap.Required = true
   232  		added[snapName] = true
   233  
   234  		return seedSnap, nil
   235  	}
   236  
   237  	// if there are snaps to seed, core/base needs to be seeded too
   238  	if len(yamlSnaps) != 0 {
   239  		// ensure "snapd" snap is installed first
   240  		if model.Base() != "" || classicWithSnapd {
   241  			if _, err := addEssential("snapd", "", snap.TypeSnapd); err != nil {
   242  				return err
   243  			}
   244  		}
   245  		if !classicWithSnapd {
   246  			if _, err := addEssential(baseSnap, "", snap.TypeBase); err != nil {
   247  				return err
   248  			}
   249  		}
   250  	}
   251  
   252  	if kernelName := model.Kernel(); kernelName != "" {
   253  		if _, err := addEssential(kernelName, model.KernelTrack(), snap.TypeKernel); err != nil {
   254  			return err
   255  		}
   256  	}
   257  
   258  	if gadgetName := model.Gadget(); gadgetName != "" {
   259  		gadget, err := addEssential(gadgetName, model.GadgetTrack(), snap.TypeGadget)
   260  		if err != nil {
   261  			return err
   262  		}
   263  
   264  		// always make sure the base of gadget is installed first
   265  		info, err := readInfo(gadget.Path, gadget.SideInfo)
   266  		if err != nil {
   267  			return err
   268  		}
   269  		gadgetBase := info.Base
   270  		if gadgetBase == "" {
   271  			gadgetBase = "core"
   272  		}
   273  		// Sanity check
   274  		// TODO: do we want to relax this? the new logic would allow
   275  		// but it might just be confusing for now
   276  		if baseSnap != "" && gadgetBase != baseSnap {
   277  			return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base())
   278  		}
   279  		if _, err = addEssential(gadgetBase, "", snap.TypeBase); err != nil {
   280  			return err
   281  		}
   282  	}
   283  
   284  	s.essentialSnapsNum = len(s.snaps)
   285  
   286  	// the rest of the snaps
   287  	for _, sn := range yamlSnaps {
   288  		if added[sn.Name] {
   289  			continue
   290  		}
   291  		seedSnap, err := s.addSnap(sn, "", tm)
   292  		if err != nil {
   293  			return err
   294  		}
   295  		if required.Contains(seedSnap) {
   296  			seedSnap.Required = true
   297  		}
   298  	}
   299  
   300  	return nil
   301  }
   302  
   303  func (s *seed16) UsesSnapdSnap() bool {
   304  	return s.usesSnapdSnap
   305  }
   306  
   307  func (s *seed16) EssentialSnaps() []*Snap {
   308  	return s.snaps[:s.essentialSnapsNum]
   309  }
   310  
   311  func (s *seed16) ModeSnaps(mode string) ([]*Snap, error) {
   312  	if mode != "run" {
   313  		return nil, fmt.Errorf("internal error: Core 16/18 have only run mode, got: %s", mode)
   314  	}
   315  	return s.snaps[s.essentialSnapsNum:], nil
   316  }
   317  
   318  func (s *seed16) NumSnaps() int {
   319  	return len(s.snaps)
   320  }
   321  
   322  func (s *seed16) Iter(f func(sn *Snap) error) error {
   323  	for _, sn := range s.snaps {
   324  		if err := f(sn); err != nil {
   325  			return err
   326  		}
   327  	}
   328  	return nil
   329  }