github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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 }