gopkg.in/ubuntu-core/snappy.v0@v0.0.0-20210902073436-25a8614f10a6/seed/seedwriter/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 seedwriter
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"os"
    26  	"path/filepath"
    27  	"sort"
    28  	"strings"
    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  	"github.com/snapcore/snapd/strutil"
    36  )
    37  
    38  type policy16 struct {
    39  	model *asserts.Model
    40  	opts  *Options
    41  
    42  	warningf func(format string, a ...interface{})
    43  
    44  	needsCore   []string
    45  	needsCore16 []string
    46  }
    47  
    48  func (pol *policy16) allowsDangerousFeatures() error {
    49  	// Core 16/18 have allowed dangerous features without constraints
    50  	return nil
    51  }
    52  
    53  func (pol *policy16) checkDefaultChannel(channel.Channel) error {
    54  	// Core 16 has no constraints on the default channel
    55  	return nil
    56  }
    57  
    58  func (pol *policy16) checkSnapChannel(_ channel.Channel, whichSnap string) error {
    59  	// Core 16 has no constraints on snap channel overrides
    60  	return nil
    61  }
    62  
    63  func makeSystemSnap(snapName string) *asserts.ModelSnap {
    64  	return internal.MakeSystemSnap(snapName, "", []string{"run"})
    65  }
    66  
    67  func (pol *policy16) systemSnap() *asserts.ModelSnap {
    68  	if pol.model.Classic() {
    69  		// no predefined system snap, infer later
    70  		return nil
    71  	}
    72  	snapName := "core"
    73  	if pol.model.Base() != "" {
    74  		snapName = "snapd"
    75  	}
    76  	return makeSystemSnap(snapName)
    77  }
    78  
    79  func (pol *policy16) modelSnapDefaultChannel() string {
    80  	// We will use latest or the current default track at image build time
    81  	return "stable"
    82  }
    83  
    84  func (pol *policy16) extraSnapDefaultChannel() string {
    85  	// We will use latest or the current default track at image build time
    86  	return "stable"
    87  }
    88  
    89  func (pol *policy16) checkBase(info *snap.Info, modes []string, availableByMode map[string]*naming.SnapSet) error {
    90  	availableSnaps := availableByMode["run"]
    91  	// snap needs no base (or it simply needs core which is never listed explicitly): nothing to do
    92  	if info.Base == "" {
    93  		if info.Type() == snap.TypeGadget || info.Type() == snap.TypeApp {
    94  			// remember to make sure we have core installed
    95  			pol.needsCore = append(pol.needsCore, info.SnapName())
    96  		}
    97  		return nil
    98  	}
    99  
   100  	if availableSnaps.Contains(naming.Snap(info.Base)) {
   101  		return nil
   102  	}
   103  
   104  	if info.Base == "core16" {
   105  		// check at the end
   106  		pol.needsCore16 = append(pol.needsCore16, info.SnapName())
   107  		return nil
   108  	}
   109  
   110  	return fmt.Errorf("cannot add snap %q without also adding its base %q explicitly", info.SnapName(), info.Base)
   111  }
   112  
   113  func (pol *policy16) checkAvailable(snapRef naming.SnapRef, modes []string, availableByMode map[string]*naming.SnapSet) bool {
   114  	availableSnaps := availableByMode["run"]
   115  	return availableSnaps.Contains(snapRef)
   116  }
   117  
   118  func (pol *policy16) needsImplicitSnaps(availableByMode map[string]*naming.SnapSet) (bool, error) {
   119  	availableSnaps := availableByMode["run"]
   120  	// do we need to add implicitly either snapd (or core)
   121  	hasCore := availableSnaps.Contains(naming.Snap("core"))
   122  	if len(pol.needsCore) != 0 && !hasCore {
   123  		if pol.model.Base() != "" {
   124  			pol.warningf("model has base %q but some snaps (%s) require \"core\" as base as well, for compatibility it was added implicitly, adding \"core\" explicitly is recommended", pol.model.Base(), strutil.Quoted(pol.needsCore))
   125  		}
   126  		return true, nil
   127  	}
   128  
   129  	if len(pol.needsCore16) != 0 && !hasCore {
   130  		return false, fmt.Errorf(`cannot use %s requiring base "core16" without adding "core16" (or "core") explicitly`, strutil.Quoted(pol.needsCore16))
   131  	}
   132  
   133  	if pol.model.Classic() && !availableSnaps.Empty() {
   134  		return true, nil
   135  	}
   136  
   137  	return false, nil
   138  }
   139  
   140  func (pol *policy16) implicitSnaps(availableByMode map[string]*naming.SnapSet) []*asserts.ModelSnap {
   141  	availableSnaps := availableByMode["run"]
   142  	if len(pol.needsCore) != 0 && !availableSnaps.Contains(naming.Snap("core")) {
   143  		return []*asserts.ModelSnap{makeSystemSnap("core")}
   144  	}
   145  	if pol.model.Classic() && !availableSnaps.Empty() {
   146  		return []*asserts.ModelSnap{makeSystemSnap("snapd")}
   147  	}
   148  	return nil
   149  }
   150  
   151  func (pol *policy16) implicitExtraSnaps(availableByMode map[string]*naming.SnapSet) []*OptionsSnap {
   152  	availableSnaps := availableByMode["run"]
   153  	if len(pol.needsCore) != 0 && !availableSnaps.Contains(naming.Snap("core")) {
   154  		return []*OptionsSnap{{Name: "core"}}
   155  	}
   156  	return nil
   157  }
   158  
   159  type tree16 struct {
   160  	opts *Options
   161  
   162  	snapsDirPath string
   163  }
   164  
   165  func (tr *tree16) mkFixedDirs() error {
   166  	tr.snapsDirPath = filepath.Join(tr.opts.SeedDir, "snaps")
   167  	return os.MkdirAll(tr.snapsDirPath, 0755)
   168  }
   169  
   170  func (tr *tree16) snapPath(sn *SeedSnap) (string, error) {
   171  	return filepath.Join(tr.snapsDirPath, sn.Info.Filename()), nil
   172  }
   173  
   174  func (tr *tree16) localSnapPath(sn *SeedSnap) (string, error) {
   175  	return filepath.Join(tr.snapsDirPath, sn.Info.Filename()), nil
   176  }
   177  
   178  func (tr *tree16) writeAssertions(db asserts.RODatabase, modelRefs []*asserts.Ref, snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error {
   179  	seedAssertsDir := filepath.Join(tr.opts.SeedDir, "assertions")
   180  	if err := os.MkdirAll(seedAssertsDir, 0755); err != nil {
   181  		return err
   182  	}
   183  
   184  	writeByRefs := func(aRefs []*asserts.Ref) error {
   185  		for _, aRef := range aRefs {
   186  			var afn string
   187  			// the names don't matter in practice as long as they don't conflict
   188  			if aRef.Type == asserts.ModelType {
   189  				afn = "model"
   190  			} else {
   191  				afn = fmt.Sprintf("%s.%s", strings.Join(aRef.PrimaryKey, ","), aRef.Type.Name)
   192  			}
   193  			a, err := aRef.Resolve(db.Find)
   194  			if err != nil {
   195  				return fmt.Errorf("internal error: lost saved assertion")
   196  			}
   197  			if err = ioutil.WriteFile(filepath.Join(seedAssertsDir, afn), asserts.Encode(a), 0644); err != nil {
   198  				return err
   199  			}
   200  		}
   201  		return nil
   202  	}
   203  
   204  	if err := writeByRefs(modelRefs); err != nil {
   205  		return err
   206  	}
   207  
   208  	for _, sn := range snapsFromModel {
   209  		if err := writeByRefs(sn.ARefs); err != nil {
   210  			return err
   211  		}
   212  	}
   213  
   214  	for _, sn := range extraSnaps {
   215  		if err := writeByRefs(sn.ARefs); err != nil {
   216  			return err
   217  		}
   218  	}
   219  
   220  	return nil
   221  }
   222  
   223  func (tr *tree16) writeMeta(snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error {
   224  	var seedYaml internal.Seed16
   225  
   226  	seedSnaps := make(seedSnapsByType, len(snapsFromModel)+len(extraSnaps))
   227  	copy(seedSnaps, snapsFromModel)
   228  	copy(seedSnaps[len(snapsFromModel):], extraSnaps)
   229  
   230  	sort.Stable(seedSnaps)
   231  
   232  	seedYaml.Snaps = make([]*internal.Snap16, len(seedSnaps))
   233  	for i, sn := range seedSnaps {
   234  		info := sn.Info
   235  		channel := sn.Channel
   236  		unasserted := info.SnapID == ""
   237  		if unasserted {
   238  			// Core 16/18 don't set a channel in the seed
   239  			// for unasserted snaps
   240  			channel = ""
   241  		}
   242  		seedYaml.Snaps[i] = &internal.Snap16{
   243  			Name:    info.SnapName(),
   244  			SnapID:  info.SnapID, // cross-ref
   245  			Channel: channel,
   246  			File:    filepath.Base(sn.Path),
   247  			DevMode: info.NeedsDevMode(),
   248  			Classic: info.NeedsClassic(),
   249  			// TODO: set this only if the snap has no links?
   250  			Contact: info.Contact(),
   251  			// no assertions for this snap were put in the seed
   252  			Unasserted: unasserted,
   253  		}
   254  	}
   255  
   256  	seedFn := filepath.Join(tr.opts.SeedDir, "seed.yaml")
   257  	if err := seedYaml.Write(seedFn); err != nil {
   258  		return fmt.Errorf("cannot write seed.yaml: %v", err)
   259  	}
   260  
   261  	return nil
   262  }