github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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, availableSnaps *naming.SnapSet) error {
    90  	// snap needs no base (or it simply needs core which is never listed explicitly): nothing to do
    91  	if info.Base == "" {
    92  		if info.Type() == snap.TypeGadget || info.Type() == snap.TypeApp {
    93  			// remember to make sure we have core installed
    94  			pol.needsCore = append(pol.needsCore, info.SnapName())
    95  		}
    96  		return nil
    97  	}
    98  
    99  	if availableSnaps.Contains(naming.Snap(info.Base)) {
   100  		return nil
   101  	}
   102  
   103  	if info.Base == "core16" {
   104  		// check at the end
   105  		pol.needsCore16 = append(pol.needsCore16, info.SnapName())
   106  		return nil
   107  	}
   108  
   109  	return fmt.Errorf("cannot add snap %q without also adding its base %q explicitly", info.SnapName(), info.Base)
   110  }
   111  
   112  func (pol *policy16) needsImplicitSnaps(availableSnaps *naming.SnapSet) (bool, error) {
   113  	// do we need to add implicitly either snapd (or core)
   114  	hasCore := availableSnaps.Contains(naming.Snap("core"))
   115  	if len(pol.needsCore) != 0 && !hasCore {
   116  		if pol.model.Base() != "" {
   117  			// TODO: later turn this into an error? for sure for UC20
   118  			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))
   119  		}
   120  		return true, nil
   121  	}
   122  
   123  	if len(pol.needsCore16) != 0 && !hasCore {
   124  		return false, fmt.Errorf(`cannot use %s requiring base "core16" without adding "core16" (or "core") explicitly`, strutil.Quoted(pol.needsCore16))
   125  	}
   126  
   127  	if pol.model.Classic() && !availableSnaps.Empty() {
   128  		return true, nil
   129  	}
   130  
   131  	return false, nil
   132  }
   133  
   134  func (pol *policy16) implicitSnaps(availableSnaps *naming.SnapSet) []*asserts.ModelSnap {
   135  	if len(pol.needsCore) != 0 && !availableSnaps.Contains(naming.Snap("core")) {
   136  		return []*asserts.ModelSnap{makeSystemSnap("core")}
   137  	}
   138  	if pol.model.Classic() && !availableSnaps.Empty() {
   139  		return []*asserts.ModelSnap{makeSystemSnap("snapd")}
   140  	}
   141  	return nil
   142  }
   143  
   144  func (pol *policy16) implicitExtraSnaps(availableSnaps *naming.SnapSet) []*OptionsSnap {
   145  	if len(pol.needsCore) != 0 && !availableSnaps.Contains(naming.Snap("core")) {
   146  		return []*OptionsSnap{{Name: "core"}}
   147  	}
   148  	return nil
   149  }
   150  
   151  type tree16 struct {
   152  	opts *Options
   153  
   154  	snapsDirPath string
   155  }
   156  
   157  func (tr *tree16) mkFixedDirs() error {
   158  	tr.snapsDirPath = filepath.Join(tr.opts.SeedDir, "snaps")
   159  	return os.MkdirAll(tr.snapsDirPath, 0755)
   160  }
   161  
   162  func (tr *tree16) snapPath(sn *SeedSnap) (string, error) {
   163  	return filepath.Join(tr.snapsDirPath, sn.Info.Filename()), nil
   164  }
   165  
   166  func (tr *tree16) localSnapPath(sn *SeedSnap) (string, error) {
   167  	return filepath.Join(tr.snapsDirPath, sn.Info.Filename()), nil
   168  }
   169  
   170  func (tr *tree16) writeAssertions(db asserts.RODatabase, modelRefs []*asserts.Ref, snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error {
   171  	seedAssertsDir := filepath.Join(tr.opts.SeedDir, "assertions")
   172  	if err := os.MkdirAll(seedAssertsDir, 0755); err != nil {
   173  		return err
   174  	}
   175  
   176  	writeByRefs := func(aRefs []*asserts.Ref) error {
   177  		for _, aRef := range aRefs {
   178  			var afn string
   179  			// the names don't matter in practice as long as they don't conflict
   180  			if aRef.Type == asserts.ModelType {
   181  				afn = "model"
   182  			} else {
   183  				afn = fmt.Sprintf("%s.%s", strings.Join(aRef.PrimaryKey, ","), aRef.Type.Name)
   184  			}
   185  			a, err := aRef.Resolve(db.Find)
   186  			if err != nil {
   187  				return fmt.Errorf("internal error: lost saved assertion")
   188  			}
   189  			if err = ioutil.WriteFile(filepath.Join(seedAssertsDir, afn), asserts.Encode(a), 0644); err != nil {
   190  				return err
   191  			}
   192  		}
   193  		return nil
   194  	}
   195  
   196  	if err := writeByRefs(modelRefs); err != nil {
   197  		return err
   198  	}
   199  
   200  	for _, sn := range snapsFromModel {
   201  		if err := writeByRefs(sn.ARefs); err != nil {
   202  			return err
   203  		}
   204  	}
   205  
   206  	for _, sn := range extraSnaps {
   207  		if err := writeByRefs(sn.ARefs); err != nil {
   208  			return err
   209  		}
   210  	}
   211  
   212  	return nil
   213  }
   214  
   215  func (tr *tree16) writeMeta(snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error {
   216  	var seedYaml internal.Seed16
   217  
   218  	seedSnaps := make(seedSnapsByType, len(snapsFromModel)+len(extraSnaps))
   219  	copy(seedSnaps, snapsFromModel)
   220  	copy(seedSnaps[len(snapsFromModel):], extraSnaps)
   221  
   222  	sort.Stable(seedSnaps)
   223  
   224  	seedYaml.Snaps = make([]*internal.Snap16, len(seedSnaps))
   225  	for i, sn := range seedSnaps {
   226  		info := sn.Info
   227  		channel := sn.Channel
   228  		unasserted := info.SnapID == ""
   229  		if unasserted {
   230  			// Core 16/18 don't set a channel in the seed
   231  			// for unasserted snaps
   232  			channel = ""
   233  		}
   234  		seedYaml.Snaps[i] = &internal.Snap16{
   235  			Name:    info.SnapName(),
   236  			SnapID:  info.SnapID, // cross-ref
   237  			Channel: channel,
   238  			File:    filepath.Base(sn.Path),
   239  			DevMode: info.NeedsDevMode(),
   240  			Classic: info.NeedsClassic(),
   241  			Contact: info.Contact,
   242  			// no assertions for this snap were put in the seed
   243  			Unasserted: unasserted,
   244  		}
   245  	}
   246  
   247  	seedFn := filepath.Join(tr.opts.SeedDir, "seed.yaml")
   248  	if err := seedYaml.Write(seedFn); err != nil {
   249  		return fmt.Errorf("cannot write seed.yaml: %v", err)
   250  	}
   251  
   252  	return nil
   253  }