github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/seed/seedwriter/seed20.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-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 seedwriter
    21  
    22  import (
    23  	"encoding/json"
    24  	"errors"
    25  	"fmt"
    26  	"os"
    27  	"path/filepath"
    28  
    29  	"github.com/snapcore/snapd/asserts"
    30  	"github.com/snapcore/snapd/seed/internal"
    31  	"github.com/snapcore/snapd/snap"
    32  	"github.com/snapcore/snapd/snap/channel"
    33  	"github.com/snapcore/snapd/snap/naming"
    34  )
    35  
    36  type policy20 struct {
    37  	model *asserts.Model
    38  	opts  *Options
    39  
    40  	warningf func(format string, a ...interface{})
    41  }
    42  
    43  var errNotAllowedExceptForDangerous = errors.New("cannot override channels, add devmode snaps, local snaps, or extra snaps with a model of grade higher than dangerous")
    44  
    45  func (pol *policy20) checkAllowedDangerous() error {
    46  	if pol.model.Grade() != asserts.ModelDangerous {
    47  		return errNotAllowedExceptForDangerous
    48  	}
    49  	return nil
    50  }
    51  
    52  func (pol *policy20) allowsDangerousFeatures() error {
    53  	return pol.checkAllowedDangerous()
    54  }
    55  
    56  func (pol *policy20) checkDefaultChannel(channel.Channel) error {
    57  	return pol.checkAllowedDangerous()
    58  }
    59  
    60  func (pol *policy20) checkSnapChannel(ch channel.Channel, whichSnap string) error {
    61  	return pol.checkAllowedDangerous()
    62  }
    63  
    64  func (pol *policy20) systemSnap() *asserts.ModelSnap {
    65  	return internal.MakeSystemSnap("snapd", "latest/stable", []string{"run", "ephemeral"})
    66  }
    67  
    68  func (pol *policy20) modelSnapDefaultChannel() string {
    69  	// We will use latest/stable as default, model that want something else
    70  	// will need to to speficy a default-channel
    71  	return "latest/stable"
    72  }
    73  
    74  func (pol *policy20) extraSnapDefaultChannel() string {
    75  	// We will use latest/stable as default for consistency with
    76  	// model snaps, this means not taking into account default-tracks
    77  	// by default
    78  	return "latest/stable"
    79  }
    80  
    81  func (pol *policy20) checkBase(info *snap.Info, modes []string, availableByMode map[string]*naming.SnapSet) error {
    82  	base := info.Base
    83  	if base == "" {
    84  		if info.Type() != snap.TypeGadget && info.Type() != snap.TypeApp {
    85  			return nil
    86  		}
    87  		base = "core"
    88  	}
    89  
    90  	if pol.checkAvailable(naming.Snap(base), modes, availableByMode) {
    91  		return nil
    92  	}
    93  
    94  	whichBase := fmt.Sprintf("its base %q", base)
    95  	if base == "core16" {
    96  		if pol.checkAvailable(naming.Snap("core"), modes, availableByMode) {
    97  			return nil
    98  		}
    99  		whichBase += ` (or "core")`
   100  	}
   101  
   102  	return fmt.Errorf("cannot add snap %q without also adding %s explicitly%s", info.SnapName(), whichBase, errorMsgForModesSuffix(modes))
   103  }
   104  
   105  func (pol *policy20) checkAvailable(snapRef naming.SnapRef, modes []string, availableByMode map[string]*naming.SnapSet) bool {
   106  	// checks that snapRef is available in all modes
   107  	for _, mode := range modes {
   108  		byMode := availableByMode[mode]
   109  		if !byMode.Contains(snapRef) {
   110  			if mode == "run" || mode == "ephemeral" {
   111  				// no additional fallback for these
   112  				// cases:
   113  				// * run is not ephemeral,
   114  				//   is covered only by run
   115  				// * ephemeral is only covered by ephemeral
   116  				return false
   117  			}
   118  			// all non-run modes (e.g. recover) are
   119  			// considered ephemeral, as a fallback check
   120  			// if the snap is listed under the ephemeral mode label
   121  			ephem := availableByMode["ephemeral"]
   122  			if ephem == nil || !ephem.Contains(snapRef) {
   123  				return false
   124  			}
   125  		}
   126  	}
   127  	return true
   128  }
   129  
   130  func (pol *policy20) needsImplicitSnaps(map[string]*naming.SnapSet) (bool, error) {
   131  	// no implicit snaps with Core 20
   132  	// TODO: unless we want to support them for extra snaps
   133  	return false, nil
   134  }
   135  
   136  func (pol *policy20) implicitSnaps(map[string]*naming.SnapSet) []*asserts.ModelSnap {
   137  	return nil
   138  }
   139  
   140  func (pol *policy20) implicitExtraSnaps(map[string]*naming.SnapSet) []*OptionsSnap {
   141  	return nil
   142  }
   143  
   144  type tree20 struct {
   145  	grade asserts.ModelGrade
   146  	opts  *Options
   147  
   148  	snapsDirPath string
   149  	systemDir    string
   150  
   151  	systemSnapsDirEnsured bool
   152  }
   153  
   154  func (tr *tree20) mkFixedDirs() error {
   155  	tr.snapsDirPath = filepath.Join(tr.opts.SeedDir, "snaps")
   156  	tr.systemDir = filepath.Join(tr.opts.SeedDir, "systems", tr.opts.Label)
   157  
   158  	if err := os.MkdirAll(tr.snapsDirPath, 0755); err != nil {
   159  		return err
   160  	}
   161  
   162  	return os.MkdirAll(tr.systemDir, 0755)
   163  }
   164  
   165  func (tr *tree20) ensureSystemSnapsDir() (string, error) {
   166  	snapsDir := filepath.Join(tr.systemDir, "snaps")
   167  	if tr.systemSnapsDirEnsured {
   168  		return snapsDir, nil
   169  	}
   170  	if err := os.MkdirAll(snapsDir, 0755); err != nil {
   171  		return "", err
   172  	}
   173  	tr.systemSnapsDirEnsured = true
   174  	return snapsDir, nil
   175  }
   176  
   177  func (tr *tree20) snapPath(sn *SeedSnap) (string, error) {
   178  	var snapsDir string
   179  	if sn.modelSnap != nil {
   180  		snapsDir = tr.snapsDirPath
   181  	} else {
   182  		// extra snap
   183  		var err error
   184  		snapsDir, err = tr.ensureSystemSnapsDir()
   185  		if err != nil {
   186  			return "", err
   187  		}
   188  	}
   189  	return filepath.Join(snapsDir, sn.Info.Filename()), nil
   190  }
   191  
   192  func (tr *tree20) localSnapPath(sn *SeedSnap) (string, error) {
   193  	sysSnapsDir, err := tr.ensureSystemSnapsDir()
   194  	if err != nil {
   195  		return "", err
   196  	}
   197  	return filepath.Join(sysSnapsDir, fmt.Sprintf("%s_%s.snap", sn.SnapName(), sn.Info.Version)), nil
   198  }
   199  
   200  func (tr *tree20) writeAssertions(db asserts.RODatabase, modelRefs []*asserts.Ref, snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error {
   201  	assertsDir := filepath.Join(tr.systemDir, "assertions")
   202  	if err := os.MkdirAll(assertsDir, 0755); err != nil {
   203  		return err
   204  	}
   205  
   206  	writeByRefs := func(fname string, refsGen func(stop <-chan struct{}) <-chan *asserts.Ref) error {
   207  		f, err := os.OpenFile(filepath.Join(assertsDir, fname), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
   208  		if err != nil {
   209  			return err
   210  		}
   211  		defer f.Close()
   212  
   213  		stop := make(chan struct{})
   214  		defer close(stop)
   215  		refs := refsGen(stop)
   216  
   217  		enc := asserts.NewEncoder(f)
   218  		for {
   219  			aRef := <-refs
   220  			if aRef == nil {
   221  				break
   222  			}
   223  			a, err := aRef.Resolve(db.Find)
   224  			if err != nil {
   225  				return fmt.Errorf("internal error: lost saved assertion")
   226  			}
   227  			if err := enc.Encode(a); err != nil {
   228  				return err
   229  			}
   230  		}
   231  		return nil
   232  	}
   233  
   234  	pushRef := func(refs chan<- *asserts.Ref, ref *asserts.Ref, stop <-chan struct{}) bool {
   235  		select {
   236  		case refs <- ref:
   237  			return true
   238  		case <-stop:
   239  			// get unstuck if we error early
   240  			return false
   241  		}
   242  	}
   243  
   244  	modelOnly := func(aRef *asserts.Ref) bool { return aRef.Type == asserts.ModelType }
   245  	excludeModel := func(aRef *asserts.Ref) bool { return aRef.Type != asserts.ModelType }
   246  
   247  	modelRefsGen := func(include func(*asserts.Ref) bool) func(stop <-chan struct{}) <-chan *asserts.Ref {
   248  		return func(stop <-chan struct{}) <-chan *asserts.Ref {
   249  			refs := make(chan *asserts.Ref)
   250  			go func() {
   251  				for _, aRef := range modelRefs {
   252  					if include(aRef) {
   253  						if !pushRef(refs, aRef, stop) {
   254  							return
   255  						}
   256  					}
   257  				}
   258  				close(refs)
   259  			}()
   260  			return refs
   261  		}
   262  	}
   263  
   264  	if err := writeByRefs("../model", modelRefsGen(modelOnly)); err != nil {
   265  		return err
   266  	}
   267  
   268  	if err := writeByRefs("model-etc", modelRefsGen(excludeModel)); err != nil {
   269  		return err
   270  	}
   271  
   272  	snapsRefGen := func(snaps []*SeedSnap) func(stop <-chan struct{}) <-chan *asserts.Ref {
   273  		return func(stop <-chan struct{}) <-chan *asserts.Ref {
   274  			refs := make(chan *asserts.Ref)
   275  			go func() {
   276  				for _, sn := range snaps {
   277  					for _, aRef := range sn.ARefs {
   278  						if !pushRef(refs, aRef, stop) {
   279  							return
   280  						}
   281  					}
   282  				}
   283  				close(refs)
   284  			}()
   285  			return refs
   286  		}
   287  	}
   288  
   289  	if err := writeByRefs("snaps", snapsRefGen(snapsFromModel)); err != nil {
   290  		return err
   291  	}
   292  
   293  	if len(extraSnaps) != 0 {
   294  		if err := writeByRefs("extra-snaps", snapsRefGen(extraSnaps)); err != nil {
   295  			return err
   296  		}
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  func (tr *tree20) writeMeta(snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error {
   303  	var optionsSnaps []*internal.Snap20
   304  
   305  	for _, sn := range snapsFromModel {
   306  		channelOverride := ""
   307  		if sn.Channel != sn.modelSnap.DefaultChannel {
   308  			channelOverride = sn.Channel
   309  		}
   310  		if sn.Info.ID() != "" && channelOverride == "" {
   311  			continue
   312  		}
   313  		unasserted := ""
   314  		if sn.Info.ID() == "" {
   315  			unasserted = filepath.Base(sn.Path)
   316  		}
   317  
   318  		optionsSnaps = append(optionsSnaps, &internal.Snap20{
   319  			Name: sn.SnapName(),
   320  			// even if unasserted != "" SnapID is useful
   321  			// to cross-ref the model entry
   322  			SnapID:     sn.modelSnap.ID(),
   323  			Unasserted: unasserted,
   324  			Channel:    channelOverride,
   325  		})
   326  	}
   327  
   328  	for _, sn := range extraSnaps {
   329  		channel := sn.Channel
   330  		unasserted := ""
   331  		if sn.Info.ID() == "" {
   332  			unasserted = filepath.Base(sn.Path)
   333  			channel = ""
   334  		}
   335  
   336  		optionsSnaps = append(optionsSnaps, &internal.Snap20{
   337  			Name:       sn.SnapName(),
   338  			SnapID:     sn.Info.ID(),
   339  			Unasserted: unasserted,
   340  			Channel:    channel,
   341  		})
   342  	}
   343  
   344  	if len(optionsSnaps) != 0 {
   345  		if tr.grade != asserts.ModelDangerous {
   346  			return fmt.Errorf("internal error: unexpected non-model snap overrides with grade %s", tr.grade)
   347  		}
   348  		options20 := &internal.Options20{Snaps: optionsSnaps}
   349  		if err := options20.Write(filepath.Join(tr.systemDir, "options.yaml")); err != nil {
   350  			return err
   351  		}
   352  	}
   353  
   354  	auxInfos := make(map[string]*internal.AuxInfo20)
   355  
   356  	addAuxInfos := func(seedSnaps []*SeedSnap) {
   357  		for _, sn := range seedSnaps {
   358  			if sn.Info.ID() != "" {
   359  				if sn.Info.Contact != "" || sn.Info.Private {
   360  					auxInfos[sn.Info.ID()] = &internal.AuxInfo20{
   361  						Private: sn.Info.Private,
   362  						Contact: sn.Info.Contact,
   363  					}
   364  				}
   365  			}
   366  		}
   367  	}
   368  
   369  	addAuxInfos(snapsFromModel)
   370  	addAuxInfos(extraSnaps)
   371  
   372  	if len(auxInfos) == 0 {
   373  		// nothing to do
   374  		return nil
   375  	}
   376  
   377  	if _, err := tr.ensureSystemSnapsDir(); err != nil {
   378  		return err
   379  	}
   380  
   381  	f, err := os.OpenFile(filepath.Join(tr.systemDir, "snaps", "aux-info.json"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644)
   382  	if err != nil {
   383  		return err
   384  	}
   385  	defer f.Close()
   386  	enc := json.NewEncoder(f)
   387  
   388  	if err := enc.Encode(auxInfos); err != nil {
   389  		return err
   390  	}
   391  
   392  	return nil
   393  }