代码拉取完成,页面将自动刷新
同步操作将从 tupelo-shen/mysnapd 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2016-2022 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package asserts
import (
"fmt"
"regexp"
"strings"
"time"
"gitee.com/mysnapcore/mysnapd/snap/channel"
"gitee.com/mysnapcore/mysnapd/snap/naming"
"gitee.com/mysnapcore/mysnapd/strutil"
)
// TODO: for ModelSnap
// * consider moving snap.Type out of snap and using it in ModelSnap
// but remember assertions use "core" (never "os") for TypeOS
// * consider having a first-class Presence type
// ModelSnap holds the details about a snap specified by a model assertion.
type ModelSnap struct {
Name string
SnapID string
// SnapType is one of: app|base|gadget|kernel|core, default is app
SnapType string
// Modes in which the snap must be made available
Modes []string
// DefaultChannel is the initial tracking channel,
// default is latest/stable in an extended model
DefaultChannel string
// PinnedTrack is a pinned track for the snap, if set DefaultChannel
// cannot be set at the same time (Core 18 models feature)
PinnedTrack string
// Presence is one of: required|optional
Presence string
// Classic indicates that this classic snap is intentionally
// included in a classic model
Classic bool
}
// SnapName implements naming.SnapRef.
func (s *ModelSnap) SnapName() string {
return s.Name
}
// ID implements naming.SnapRef.
func (s *ModelSnap) ID() string {
return s.SnapID
}
type modelSnaps struct {
snapd *ModelSnap
base *ModelSnap
gadget *ModelSnap
kernel *ModelSnap
snapsNoEssential []*ModelSnap
}
func (ms *modelSnaps) list() (allSnaps []*ModelSnap, requiredWithEssentialSnaps []naming.SnapRef, numEssentialSnaps int) {
addSnap := func(snap *ModelSnap, essentialSnap int) {
if snap == nil {
return
}
numEssentialSnaps += essentialSnap
allSnaps = append(allSnaps, snap)
if snap.Presence == "required" {
requiredWithEssentialSnaps = append(requiredWithEssentialSnaps, snap)
}
}
addSnap(ms.snapd, 1)
addSnap(ms.kernel, 1)
addSnap(ms.base, 1)
addSnap(ms.gadget, 1)
for _, snap := range ms.snapsNoEssential {
addSnap(snap, 0)
}
return allSnaps, requiredWithEssentialSnaps, numEssentialSnaps
}
var (
essentialSnapModes = []string{"run", "ephemeral"}
defaultModes = []string{"run"}
)
func checkExtendedSnaps(extendedSnaps interface{}, base string, grade ModelGrade, modelIsClassic bool) (*modelSnaps, error) {
const wrongHeaderType = `"snaps" header must be a list of maps`
entries, ok := extendedSnaps.([]interface{})
if !ok {
return nil, fmt.Errorf(wrongHeaderType)
}
var modelSnaps modelSnaps
seen := make(map[string]bool, len(entries))
seenIDs := make(map[string]string, len(entries))
for _, entry := range entries {
snap, ok := entry.(map[string]interface{})
if !ok {
return nil, fmt.Errorf(wrongHeaderType)
}
modelSnap, err := checkModelSnap(snap, grade, modelIsClassic)
if err != nil {
return nil, err
}
if seen[modelSnap.Name] {
return nil, fmt.Errorf("cannot list the same snap %q multiple times", modelSnap.Name)
}
seen[modelSnap.Name] = true
// at this time we do not support parallel installing
// from model/seed
if snapID := modelSnap.SnapID; snapID != "" {
if underName := seenIDs[snapID]; underName != "" {
return nil, fmt.Errorf("cannot specify the same snap id %q multiple times, specified for snaps %q and %q", snapID, underName, modelSnap.Name)
}
seenIDs[snapID] = modelSnap.Name
}
essential := false
switch {
case modelSnap.SnapType == "snapd":
// TODO: allow to be explicit only in grade: dangerous?
essential = true
if modelSnaps.snapd != nil {
return nil, fmt.Errorf("cannot specify multiple snapd snaps: %q and %q", modelSnaps.snapd.Name, modelSnap.Name)
}
modelSnaps.snapd = modelSnap
case modelSnap.SnapType == "kernel":
essential = true
if modelSnaps.kernel != nil {
return nil, fmt.Errorf("cannot specify multiple kernel snaps: %q and %q", modelSnaps.kernel.Name, modelSnap.Name)
}
modelSnaps.kernel = modelSnap
case modelSnap.SnapType == "gadget":
essential = true
if modelSnaps.gadget != nil {
return nil, fmt.Errorf("cannot specify multiple gadget snaps: %q and %q", modelSnaps.gadget.Name, modelSnap.Name)
}
modelSnaps.gadget = modelSnap
case modelSnap.Name == base:
essential = true
if modelSnap.SnapType != "base" {
return nil, fmt.Errorf(`boot base %q must specify type "base", not %q`, base, modelSnap.SnapType)
}
modelSnaps.base = modelSnap
}
if essential {
if len(modelSnap.Modes) != 0 || modelSnap.Presence != "" {
return nil, fmt.Errorf("essential snaps are always available, cannot specify modes or presence for snap %q", modelSnap.Name)
}
modelSnap.Modes = essentialSnapModes
}
if len(modelSnap.Modes) == 0 {
modelSnap.Modes = defaultModes
}
if modelSnap.Classic && (len(modelSnap.Modes) != 1 || modelSnap.Modes[0] != "run") {
return nil, fmt.Errorf("classic snap %q not allowed outside of run mode: %v", modelSnap.Name, modelSnap.Modes)
}
if modelSnap.Presence == "" {
modelSnap.Presence = "required"
}
if !essential {
modelSnaps.snapsNoEssential = append(modelSnaps.snapsNoEssential, modelSnap)
}
}
return &modelSnaps, nil
}
var (
validSnapTypes = []string{"app", "base", "gadget", "kernel", "core", "snapd"}
validSnapMode = regexp.MustCompile("^[a-z][-a-z]+$")
validSnapPresences = []string{"required", "optional"}
)
func checkModelSnap(snap map[string]interface{}, grade ModelGrade, modelIsClassic bool) (*ModelSnap, error) {
name, err := checkNotEmptyStringWhat(snap, "name", "of snap")
if err != nil {
return nil, err
}
if err := naming.ValidateSnap(name); err != nil {
return nil, fmt.Errorf("invalid snap name %q", name)
}
what := fmt.Sprintf("of snap %q", name)
var snapID string
_, ok := snap["id"]
if ok {
var err error
snapID, err = checkStringMatchesWhat(snap, "id", what, naming.ValidSnapID)
if err != nil {
return nil, err
}
} else {
// snap ids are optional with grade dangerous to allow working
// with local/not pushed yet to the store snaps
if grade != ModelDangerous {
return nil, fmt.Errorf(`"id" %s is mandatory for %s grade model`, what, grade)
}
}
typ, err := checkOptionalStringWhat(snap, "type", what)
if err != nil {
return nil, err
}
if typ == "" {
typ = "app"
}
if !strutil.ListContains(validSnapTypes, typ) {
return nil, fmt.Errorf("type of snap %q must be one of %s", name, strings.Join(validSnapTypes, "|"))
}
modes, err := checkStringListInMap(snap, "modes", fmt.Sprintf("%q %s", "modes", what), validSnapMode)
if err != nil {
return nil, err
}
defaultChannel, err := checkOptionalStringWhat(snap, "default-channel", what)
if err != nil {
return nil, err
}
if defaultChannel == "" {
defaultChannel = "latest/stable"
}
defCh, err := channel.ParseVerbatim(defaultChannel, "-")
if err != nil {
return nil, fmt.Errorf("invalid default channel for snap %q: %v", name, err)
}
if defCh.Track == "" {
return nil, fmt.Errorf("default channel for snap %q must specify a track", name)
}
presence, err := checkOptionalStringWhat(snap, "presence", what)
if err != nil {
return nil, err
}
if presence != "" && !strutil.ListContains(validSnapPresences, presence) {
return nil, fmt.Errorf("presence of snap %q must be one of required|optional", name)
}
isClassic, err := checkOptionalBoolWhat(snap, "classic", what)
if err != nil {
return nil, err
}
if isClassic && !modelIsClassic {
return nil, fmt.Errorf("snap %q cannot be classic in non-classic model", name)
}
if isClassic && typ != "app" {
return nil, fmt.Errorf("snap %q cannot be classic with type %q instead of app", name, typ)
}
return &ModelSnap{
Name: name,
SnapID: snapID,
SnapType: typ,
Modes: modes, // can be empty
DefaultChannel: defaultChannel,
Presence: presence, // can be empty
Classic: isClassic,
}, nil
}
// unextended case support
func checkSnapWithTrack(headers map[string]interface{}, which string) (*ModelSnap, error) {
_, ok := headers[which]
if !ok {
return nil, nil
}
value, ok := headers[which].(string)
if !ok {
return nil, fmt.Errorf(`%q header must be a string`, which)
}
l := strings.SplitN(value, "=", 2)
name := l[0]
track := ""
if err := validateSnapName(name, which); err != nil {
return nil, err
}
if len(l) > 1 {
track = l[1]
if strings.Count(track, "/") != 0 {
return nil, fmt.Errorf(`%q channel selector must be a track name only`, which)
}
channelRisks := []string{"stable", "candidate", "beta", "edge"}
if strutil.ListContains(channelRisks, track) {
return nil, fmt.Errorf(`%q channel selector must be a track name`, which)
}
}
return &ModelSnap{
Name: name,
SnapType: which,
Modes: defaultModes,
PinnedTrack: track,
Presence: "required",
}, nil
}
func validateSnapName(name string, headerName string) error {
if err := naming.ValidateSnap(name); err != nil {
return fmt.Errorf("invalid snap name in %q header: %s", headerName, name)
}
return nil
}
func checkRequiredSnap(name string, headerName string, snapType string) (*ModelSnap, error) {
if err := validateSnapName(name, headerName); err != nil {
return nil, err
}
return &ModelSnap{
Name: name,
SnapType: snapType,
Modes: defaultModes,
Presence: "required",
}, nil
}
// ModelGrade characterizes the security of the model which then
// controls related policy.
type ModelGrade string
const (
ModelGradeUnset ModelGrade = "unset"
// ModelSecured implies mandatory full disk encryption and secure boot.
ModelSecured ModelGrade = "secured"
// ModelSigned implies all seed snaps are signed and mentioned
// in the model, i.e. no unasserted or extra snaps.
ModelSigned ModelGrade = "signed"
// ModelDangerous allows unasserted snaps and extra snaps.
ModelDangerous ModelGrade = "dangerous"
)
// StorageSafety characterizes the requested storage safety of
// the model which then controls what encryption is used
type StorageSafety string
const (
StorageSafetyUnset StorageSafety = "unset"
// StorageSafetyEncrypted implies mandatory full disk encryption.
StorageSafetyEncrypted StorageSafety = "encrypted"
// StorageSafetyPreferEncrypted implies full disk
// encryption when the system supports it.
StorageSafetyPreferEncrypted StorageSafety = "prefer-encrypted"
// StorageSafetyPreferUnencrypted implies no full disk
// encryption by default even if the system supports
// encryption.
StorageSafetyPreferUnencrypted StorageSafety = "prefer-unencrypted"
)
var validStorageSafeties = []string{string(StorageSafetyEncrypted), string(StorageSafetyPreferEncrypted), string(StorageSafetyPreferUnencrypted)}
var validModelGrades = []string{string(ModelSecured), string(ModelSigned), string(ModelDangerous)}
// gradeToCode encodes grades into 32 bits, trying to be slightly future-proof:
// * lower 16 bits are reserved
// * in the higher bits use the sequence 1, 8, 16 to have some space
// to possibly add new grades in between
var gradeToCode = map[ModelGrade]uint32{
ModelGradeUnset: 0,
ModelDangerous: 0x10000,
ModelSigned: 0x80000,
ModelSecured: 0x100000,
// reserved by secboot to measure classic models
// "ClassicModelGradeMask": 0x80000000
}
// Code returns a bit representation of the grade, for example for
// measuring it in a full disk encryption implementation.
func (mg ModelGrade) Code() uint32 {
code, ok := gradeToCode[mg]
if !ok {
panic(fmt.Sprintf("unknown model grade: %s", mg))
}
return code
}
// Model holds a model assertion, which is a statement by a brand
// about the properties of a device model.
type Model struct {
assertionBase
classic bool
baseSnap *ModelSnap
gadgetSnap *ModelSnap
kernelSnap *ModelSnap
grade ModelGrade
storageSafety StorageSafety
allSnaps []*ModelSnap
// consumers of this info should care only about snap identity =>
// snapRef
requiredWithEssentialSnaps []naming.SnapRef
numEssentialSnaps int
serialAuthority []string
sysUserAuthority []string
timestamp time.Time
}
// BrandID returns the brand identifier. Same as the authority id.
func (mod *Model) BrandID() string {
return mod.HeaderString("brand-id")
}
// Model returns the model name identifier.
func (mod *Model) Model() string {
return mod.HeaderString("model")
}
// DisplayName returns the human-friendly name of the model or
// falls back to Model if this was not set.
func (mod *Model) DisplayName() string {
display := mod.HeaderString("display-name")
if display == "" {
return mod.Model()
}
return display
}
// Series returns the series of the core software the model uses.
func (mod *Model) Series() string {
return mod.HeaderString("series")
}
// Classic returns whether the model is a classic system.
func (mod *Model) Classic() bool {
return mod.classic
}
// Distribution returns the linux distro specified in the model.
func (mod *Model) Distribution() string {
return mod.HeaderString("distribution")
}
// Architecture returns the architecture the model is based on.
func (mod *Model) Architecture() string {
return mod.HeaderString("architecture")
}
// Grade returns the stability grade of the model. Will be ModelGradeUnset
// for Core 16/18 models.
func (mod *Model) Grade() ModelGrade {
return mod.grade
}
// StorageSafety returns the storage safety for the model. Will be
// StorageSafetyUnset for Core 16/18 models.
func (mod *Model) StorageSafety() StorageSafety {
return mod.storageSafety
}
// GadgetSnap returns the details of the gadget snap the model uses.
func (mod *Model) GadgetSnap() *ModelSnap {
return mod.gadgetSnap
}
// Gadget returns the gadget snap the model uses.
func (mod *Model) Gadget() string {
if mod.gadgetSnap == nil {
return ""
}
return mod.gadgetSnap.Name
}
// GadgetTrack returns the gadget track the model uses.
// XXX this should go away
func (mod *Model) GadgetTrack() string {
if mod.gadgetSnap == nil {
return ""
}
return mod.gadgetSnap.PinnedTrack
}
// KernelSnap returns the details of the kernel snap the model uses.
func (mod *Model) KernelSnap() *ModelSnap {
return mod.kernelSnap
}
// Kernel returns the kernel snap the model uses.
// XXX this should go away
func (mod *Model) Kernel() string {
if mod.kernelSnap == nil {
return ""
}
return mod.kernelSnap.Name
}
// KernelTrack returns the kernel track the model uses.
// XXX this should go away
func (mod *Model) KernelTrack() string {
if mod.kernelSnap == nil {
return ""
}
return mod.kernelSnap.PinnedTrack
}
// Base returns the base snap the model uses.
func (mod *Model) Base() string {
return mod.HeaderString("base")
}
// BaseSnap returns the details of the base snap the model uses.
func (mod *Model) BaseSnap() *ModelSnap {
return mod.baseSnap
}
// Store returns the snap store the model uses.
func (mod *Model) Store() string {
return mod.HeaderString("store")
}
// RequiredNoEssentialSnaps returns the snaps that must be installed at all times and cannot be removed for this model, excluding the essential snaps (gadget, kernel, boot base, snapd).
func (mod *Model) RequiredNoEssentialSnaps() []naming.SnapRef {
return mod.requiredWithEssentialSnaps[mod.numEssentialSnaps:]
}
// RequiredWithEssentialSnaps returns the snaps that must be installed at all times and cannot be removed for this model, including any essential snaps (gadget, kernel, boot base, snapd).
func (mod *Model) RequiredWithEssentialSnaps() []naming.SnapRef {
return mod.requiredWithEssentialSnaps
}
// EssentialSnaps returns all essential snaps explicitly mentioned by
// the model.
// They are always returned according to this order with some skipped
// if not mentioned: snapd, kernel, boot base, gadget.
func (mod *Model) EssentialSnaps() []*ModelSnap {
return mod.allSnaps[:mod.numEssentialSnaps]
}
// SnapsWithoutEssential returns all the snaps listed by the model
// without any of the essential snaps (as returned by EssentialSnaps).
// They are returned in the order of mention by the model.
func (mod *Model) SnapsWithoutEssential() []*ModelSnap {
return mod.allSnaps[mod.numEssentialSnaps:]
}
// SerialAuthority returns the authority ids that are accepted as
// signers for serial assertions for this model. It always includes the
// brand of the model.
func (mod *Model) SerialAuthority() []string {
return mod.serialAuthority
}
// SystemUserAuthority returns the authority ids that are accepted as
// signers of system-user assertions for this model. Empty list means
// any, otherwise it always includes the brand of the model.
func (mod *Model) SystemUserAuthority() []string {
return mod.sysUserAuthority
}
// Timestamp returns the time when the model assertion was issued.
func (mod *Model) Timestamp() time.Time {
return mod.timestamp
}
// Implement further consistency checks.
func (mod *Model) checkConsistency(db RODatabase, acck *AccountKey) error {
// TODO: double check trust level of authority depending on class and possibly allowed-modes
return nil
}
// expected interface is implemented
var _ consistencyChecker = (*Model)(nil)
// limit model to only lowercase for now
var validModel = regexp.MustCompile("^[a-zA-Z0-9](?:-?[a-zA-Z0-9])*$")
func checkModel(headers map[string]interface{}) (string, error) {
s, err := checkStringMatches(headers, "model", validModel)
if err != nil {
return "", err
}
// TODO: support the concept of case insensitive/preserving string headers
if strings.ToLower(s) != s {
return "", fmt.Errorf(`"model" header cannot contain uppercase letters`)
}
return s, nil
}
func checkAuthorityMatchesBrand(a Assertion) error {
typeName := a.Type().Name
authorityID := a.AuthorityID()
brand := a.HeaderString("brand-id")
if brand != authorityID {
return fmt.Errorf("authority-id and brand-id must match, %s assertions are expected to be signed by the brand: %q != %q", typeName, authorityID, brand)
}
return nil
}
func checkOptionalSerialAuthority(headers map[string]interface{}, brandID string) ([]string, error) {
ids := []string{brandID}
const name = "serial-authority"
if _, ok := headers[name]; !ok {
return ids, nil
}
if lst, err := checkStringListMatches(headers, name, validAccountID); err == nil {
if !strutil.ListContains(lst, brandID) {
lst = append(ids, lst...)
}
return lst, nil
}
return nil, fmt.Errorf("%q header must be a list of account ids", name)
}
func checkOptionalSystemUserAuthority(headers map[string]interface{}, brandID string) ([]string, error) {
ids := []string{brandID}
const name = "system-user-authority"
v, ok := headers[name]
if !ok {
return ids, nil
}
switch x := v.(type) {
case string:
if x == "*" {
return nil, nil
}
case []interface{}:
lst, err := checkStringListMatches(headers, name, validAccountID)
if err == nil {
if !strutil.ListContains(lst, brandID) {
lst = append(ids, lst...)
}
return lst, nil
}
}
return nil, fmt.Errorf("%q header must be '*' or a list of account ids", name)
}
var (
modelMandatory = []string{"architecture", "gadget", "kernel"}
extendedMandatory = []string{"architecture", "base"}
extendedSnapsConflicting = []string{"gadget", "kernel", "required-snaps"}
classicModelOptional = []string{"architecture", "gadget"}
// The distribution header must be a valid ID according to
// https://www.freedesktop.org/software/systemd/man/os-release.html#ID=
validDistribution = regexp.MustCompile(`^[a-z0-9._-]*$`)
)
func assembleModel(assert assertionBase) (Assertion, error) {
err := checkAuthorityMatchesBrand(&assert)
if err != nil {
return nil, err
}
_, err = checkModel(assert.headers)
if err != nil {
return nil, err
}
classic, err := checkOptionalBool(assert.headers, "classic")
if err != nil {
return nil, err
}
// Core 20 extended snaps header
extendedSnaps, extended := assert.headers["snaps"]
if extended {
for _, conflicting := range extendedSnapsConflicting {
if _, ok := assert.headers[conflicting]; ok {
return nil, fmt.Errorf("cannot specify separate %q header once using the extended snaps header", conflicting)
}
}
} else {
if _, ok := assert.headers["grade"]; ok {
return nil, fmt.Errorf("cannot specify a grade for model without the extended snaps header")
}
if _, ok := assert.headers["storage-safety"]; ok {
return nil, fmt.Errorf("cannot specify storage-safety for model without the extended snaps header")
}
}
if classic && !extended {
if _, ok := assert.headers["kernel"]; ok {
return nil, fmt.Errorf("cannot specify a kernel with a non-extended classic model")
}
if _, ok := assert.headers["base"]; ok {
return nil, fmt.Errorf("cannot specify a base with a non-extended classic model")
}
}
// distribution mandatory for classic with extended snaps, not
// allowed otherwise.
if classic && extended {
_, err := checkStringMatches(assert.headers, "distribution", validDistribution)
if err != nil {
return nil, fmt.Errorf("%v, see distribution ID in os-release spec", err)
}
} else if _, ok := assert.headers["distribution"]; ok {
return nil, fmt.Errorf("cannot specify distribution for model unless it is classic and has an extended snaps header")
}
checker := checkNotEmptyString
toCheck := modelMandatory
if extended {
toCheck = extendedMandatory
} else if classic {
checker = checkOptionalString
toCheck = classicModelOptional
}
for _, h := range toCheck {
if _, err := checker(assert.headers, h); err != nil {
return nil, err
}
}
// base, if provided, must be a valid snap name too
var baseSnap *ModelSnap
base, err := checkOptionalString(assert.headers, "base")
if err != nil {
return nil, err
}
if base != "" {
baseSnap, err = checkRequiredSnap(base, "base", "base")
if err != nil {
return nil, err
}
}
// store is optional but must be a string, defaults to the ubuntu store
if _, err = checkOptionalString(assert.headers, "store"); err != nil {
return nil, err
}
// display-name is optional but must be a string
if _, err = checkOptionalString(assert.headers, "display-name"); err != nil {
return nil, err
}
var modSnaps *modelSnaps
grade := ModelGradeUnset
storageSafety := StorageSafetyUnset
if extended {
gradeStr, err := checkOptionalString(assert.headers, "grade")
if err != nil {
return nil, err
}
if gradeStr != "" && !strutil.ListContains(validModelGrades, gradeStr) {
return nil, fmt.Errorf("grade for model must be %s, not %q", strings.Join(validModelGrades, "|"), gradeStr)
}
grade = ModelSigned
if gradeStr != "" {
grade = ModelGrade(gradeStr)
}
storageSafetyStr, err := checkOptionalString(assert.headers, "storage-safety")
if err != nil {
return nil, err
}
if storageSafetyStr != "" && !strutil.ListContains(validStorageSafeties, storageSafetyStr) {
return nil, fmt.Errorf("storage-safety for model must be %s, not %q", strings.Join(validStorageSafeties, "|"), storageSafetyStr)
}
if storageSafetyStr != "" {
storageSafety = StorageSafety(storageSafetyStr)
} else {
if grade == ModelSecured {
storageSafety = StorageSafetyEncrypted
} else {
storageSafety = StorageSafetyPreferEncrypted
}
}
if grade == ModelSecured && storageSafety != StorageSafetyEncrypted {
return nil, fmt.Errorf(`secured grade model must not have storage-safety overridden, only "encrypted" is valid`)
}
modSnaps, err = checkExtendedSnaps(extendedSnaps, base, grade, classic)
if err != nil {
return nil, err
}
hasKernel := modSnaps.kernel != nil
hasGadget := modSnaps.gadget != nil
if !classic {
if !hasGadget {
return nil, fmt.Errorf(`one "snaps" header entry must specify the model gadget`)
}
if !hasKernel {
return nil, fmt.Errorf(`one "snaps" header entry must specify the model kernel`)
}
} else {
if hasKernel && !hasGadget {
return nil, fmt.Errorf("cannot specify a kernel in an extended classic model without a model gadget")
}
}
if modSnaps.base == nil {
// complete with defaults,
// the assumption is that base names are very stable
// essentially fixed
modSnaps.base = baseSnap
snapID := naming.WellKnownSnapID(modSnaps.base.Name)
if snapID == "" && grade != ModelDangerous {
return nil, fmt.Errorf(`cannot specify not well-known base %q without a corresponding "snaps" header entry`, modSnaps.base.Name)
}
modSnaps.base.SnapID = snapID
modSnaps.base.Modes = essentialSnapModes
modSnaps.base.DefaultChannel = "latest/stable"
}
} else {
modSnaps = &modelSnaps{
base: baseSnap,
}
// kernel/gadget must be valid snap names and can have (optional) tracks
// - validate those
modSnaps.kernel, err = checkSnapWithTrack(assert.headers, "kernel")
if err != nil {
return nil, err
}
modSnaps.gadget, err = checkSnapWithTrack(assert.headers, "gadget")
if err != nil {
return nil, err
}
// required snap must be valid snap names
reqSnaps, err := checkStringList(assert.headers, "required-snaps")
if err != nil {
return nil, err
}
for _, name := range reqSnaps {
reqSnap, err := checkRequiredSnap(name, "required-snaps", "")
if err != nil {
return nil, err
}
modSnaps.snapsNoEssential = append(modSnaps.snapsNoEssential, reqSnap)
}
}
brandID := assert.HeaderString("brand-id")
serialAuthority, err := checkOptionalSerialAuthority(assert.headers, brandID)
if err != nil {
return nil, err
}
sysUserAuthority, err := checkOptionalSystemUserAuthority(assert.headers, brandID)
if err != nil {
return nil, err
}
timestamp, err := checkRFC3339Date(assert.headers, "timestamp")
if err != nil {
return nil, err
}
allSnaps, requiredWithEssentialSnaps, numEssentialSnaps := modSnaps.list()
// NB:
// * core is not supported at this time, it defaults to ubuntu-core
// in prepare-image until rename and/or introduction of the header.
// * some form of allowed-modes, class are postponed,
//
// prepare-image takes care of not allowing them for now
// ignore extra headers and non-empty body for future compatibility
return &Model{
assertionBase: assert,
classic: classic,
baseSnap: modSnaps.base,
gadgetSnap: modSnaps.gadget,
kernelSnap: modSnaps.kernel,
grade: grade,
storageSafety: storageSafety,
allSnaps: allSnaps,
requiredWithEssentialSnaps: requiredWithEssentialSnaps,
numEssentialSnaps: numEssentialSnaps,
serialAuthority: serialAuthority,
sysUserAuthority: sysUserAuthority,
timestamp: timestamp,
}, nil
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。