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