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 }