Ai
1 Star 0 Fork 1

mysnapcore/mysnapd

forked from tupelo-shen/mysnapd 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
model.go 26.74 KB
一键复制 编辑 原始数据 按行查看 历史
tupelo-shen 提交于 2022-11-07 17:55 +08:00 . fix: aspects & asserts commit
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
// -*- 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
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/mysnapcore/mysnapd.git
git@gitee.com:mysnapcore/mysnapd.git
mysnapcore
mysnapd
mysnapd
v0.1.0

搜索帮助