github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/seed/seedwriter/seed20.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2019 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 "encoding/json" 24 "errors" 25 "fmt" 26 "os" 27 "path/filepath" 28 "regexp" 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 ) 36 37 var validSystemLabel = regexp.MustCompile("^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$") 38 39 func validateSystemLabel(label string) error { 40 if !validSystemLabel.MatchString(label) { 41 return fmt.Errorf("system label contains invalid characters: %s", label) 42 } 43 return nil 44 } 45 46 type policy20 struct { 47 model *asserts.Model 48 opts *Options 49 50 warningf func(format string, a ...interface{}) 51 } 52 53 var errNotAllowedExceptForDangerous = errors.New("cannot override channels, add local snaps or extra snaps with a model of grade higher than dangerous") 54 55 func (pol *policy20) checkAllowedDangerous() error { 56 if pol.model.Grade() != asserts.ModelDangerous { 57 return errNotAllowedExceptForDangerous 58 } 59 return nil 60 } 61 62 func (pol *policy20) allowsDangerousFeatures() error { 63 return pol.checkAllowedDangerous() 64 } 65 66 func (pol *policy20) checkDefaultChannel(channel.Channel) error { 67 // TODO: consider allowing some channel overrides for >=signed 68 // Core 20 models? 69 return pol.checkAllowedDangerous() 70 } 71 72 func (pol *policy20) checkSnapChannel(ch channel.Channel, whichSnap string) error { 73 // TODO: consider allowing some channel overrides for >=signed 74 // Core 20 models? 75 return pol.checkAllowedDangerous() 76 } 77 78 func (pol *policy20) systemSnap() *asserts.ModelSnap { 79 return internal.MakeSystemSnap("snapd", "latest/stable", []string{"run", "ephemeral"}) 80 } 81 82 func (pol *policy20) modelSnapDefaultChannel() string { 83 // We will use latest/stable as default, model that want something else 84 // will need to to speficy a default-channel 85 return "latest/stable" 86 } 87 88 func (pol *policy20) extraSnapDefaultChannel() string { 89 // We will use latest/stable as default 90 // TODO: consider using just "stable" for these? 91 return "latest/stable" 92 } 93 94 func (pol *policy20) checkBase(info *snap.Info, availableSnaps *naming.SnapSet) error { 95 base := info.Base 96 if base == "" { 97 if info.Type() != snap.TypeGadget && info.Type() != snap.TypeApp { 98 return nil 99 } 100 base = "core" 101 } 102 103 if availableSnaps.Contains(naming.Snap(base)) { 104 return nil 105 } 106 107 whichBase := fmt.Sprintf("its base %q", base) 108 if base == "core16" { 109 if availableSnaps.Contains(naming.Snap("core")) { 110 return nil 111 } 112 whichBase += ` (or "core")` 113 } 114 115 return fmt.Errorf("cannot add snap %q without also adding %s explicitly", info.SnapName(), whichBase) 116 } 117 118 func (pol *policy20) needsImplicitSnaps(*naming.SnapSet) (bool, error) { 119 // no implicit snaps with Core 20 120 // TODO: unless we want to support them for extra snaps 121 return false, nil 122 } 123 124 func (pol *policy20) implicitSnaps(*naming.SnapSet) []*asserts.ModelSnap { 125 return nil 126 } 127 128 func (pol *policy20) implicitExtraSnaps(*naming.SnapSet) []*OptionsSnap { 129 return nil 130 } 131 132 type tree20 struct { 133 opts *Options 134 135 snapsDirPath string 136 systemDir string 137 138 systemSnapsDirEnsured bool 139 } 140 141 func (tr *tree20) mkFixedDirs() error { 142 tr.snapsDirPath = filepath.Join(tr.opts.SeedDir, "snaps") 143 tr.systemDir = filepath.Join(tr.opts.SeedDir, "systems", tr.opts.Label) 144 145 if err := os.MkdirAll(tr.snapsDirPath, 0755); err != nil { 146 return err 147 } 148 149 return os.MkdirAll(tr.systemDir, 0755) 150 } 151 152 func (tr *tree20) ensureSystemSnapsDir() (string, error) { 153 snapsDir := filepath.Join(tr.systemDir, "snaps") 154 if tr.systemSnapsDirEnsured { 155 return snapsDir, nil 156 } 157 if err := os.MkdirAll(snapsDir, 0755); err != nil { 158 return "", err 159 } 160 tr.systemSnapsDirEnsured = true 161 return snapsDir, nil 162 } 163 164 func (tr *tree20) snapPath(sn *SeedSnap) (string, error) { 165 var snapsDir string 166 if sn.modelSnap != nil { 167 snapsDir = tr.snapsDirPath 168 } else { 169 // extra snap 170 var err error 171 snapsDir, err = tr.ensureSystemSnapsDir() 172 if err != nil { 173 return "", err 174 } 175 } 176 return filepath.Join(snapsDir, sn.Info.Filename()), nil 177 } 178 179 func (tr *tree20) localSnapPath(sn *SeedSnap) (string, error) { 180 sysSnapsDir, err := tr.ensureSystemSnapsDir() 181 if err != nil { 182 return "", err 183 } 184 return filepath.Join(sysSnapsDir, fmt.Sprintf("%s_%s.snap", sn.SnapName(), sn.Info.Version)), nil 185 } 186 187 func (tr *tree20) writeAssertions(db asserts.RODatabase, modelRefs []*asserts.Ref, snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error { 188 assertsDir := filepath.Join(tr.systemDir, "assertions") 189 if err := os.MkdirAll(assertsDir, 0755); err != nil { 190 return err 191 } 192 193 writeByRefs := func(fname string, refsGen func(stop <-chan struct{}) <-chan *asserts.Ref) error { 194 f, err := os.OpenFile(filepath.Join(assertsDir, fname), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) 195 if err != nil { 196 return err 197 } 198 defer f.Close() 199 200 stop := make(chan struct{}) 201 defer close(stop) 202 refs := refsGen(stop) 203 204 enc := asserts.NewEncoder(f) 205 for { 206 aRef := <-refs 207 if aRef == nil { 208 break 209 } 210 a, err := aRef.Resolve(db.Find) 211 if err != nil { 212 return fmt.Errorf("internal error: lost saved assertion") 213 } 214 if err := enc.Encode(a); err != nil { 215 return err 216 } 217 } 218 return nil 219 } 220 221 pushRef := func(refs chan<- *asserts.Ref, ref *asserts.Ref, stop <-chan struct{}) bool { 222 select { 223 case refs <- ref: 224 return true 225 case <-stop: 226 // get unstuck if we error early 227 return false 228 } 229 } 230 231 modelOnly := func(aRef *asserts.Ref) bool { return aRef.Type == asserts.ModelType } 232 excludeModel := func(aRef *asserts.Ref) bool { return aRef.Type != asserts.ModelType } 233 234 modelRefsGen := func(include func(*asserts.Ref) bool) func(stop <-chan struct{}) <-chan *asserts.Ref { 235 return func(stop <-chan struct{}) <-chan *asserts.Ref { 236 refs := make(chan *asserts.Ref) 237 go func() { 238 for _, aRef := range modelRefs { 239 if include(aRef) { 240 if !pushRef(refs, aRef, stop) { 241 return 242 } 243 } 244 } 245 close(refs) 246 }() 247 return refs 248 } 249 } 250 251 if err := writeByRefs("../model", modelRefsGen(modelOnly)); err != nil { 252 return err 253 } 254 255 if err := writeByRefs("model-etc", modelRefsGen(excludeModel)); err != nil { 256 return err 257 } 258 259 snapsRefGen := func(snaps []*SeedSnap) func(stop <-chan struct{}) <-chan *asserts.Ref { 260 return func(stop <-chan struct{}) <-chan *asserts.Ref { 261 refs := make(chan *asserts.Ref) 262 go func() { 263 for _, sn := range snaps { 264 for _, aRef := range sn.ARefs { 265 if !pushRef(refs, aRef, stop) { 266 return 267 } 268 } 269 } 270 close(refs) 271 }() 272 return refs 273 } 274 } 275 276 if err := writeByRefs("snaps", snapsRefGen(snapsFromModel)); err != nil { 277 return err 278 } 279 280 if len(extraSnaps) != 0 { 281 if err := writeByRefs("extra-snaps", snapsRefGen(extraSnaps)); err != nil { 282 return err 283 } 284 } 285 286 return nil 287 } 288 289 func (tr *tree20) writeMeta(snapsFromModel []*SeedSnap, extraSnaps []*SeedSnap) error { 290 var optionsSnaps []*internal.Snap20 291 292 for _, sn := range snapsFromModel { 293 channelOverride := "" 294 if sn.Channel != sn.modelSnap.DefaultChannel { 295 channelOverride = sn.Channel 296 } 297 if sn.Info.ID() != "" && channelOverride == "" { 298 continue 299 } 300 unasserted := "" 301 if sn.Info.ID() == "" { 302 unasserted = filepath.Base(sn.Path) 303 } 304 305 optionsSnaps = append(optionsSnaps, &internal.Snap20{ 306 Name: sn.SnapName(), 307 // even if unasserted != "" SnapID is useful 308 // to cross-ref the model entry 309 SnapID: sn.modelSnap.ID(), 310 Unasserted: unasserted, 311 Channel: channelOverride, 312 }) 313 } 314 315 for _, sn := range extraSnaps { 316 channel := sn.Channel 317 unasserted := "" 318 if sn.Info.ID() == "" { 319 unasserted = filepath.Base(sn.Path) 320 channel = "" 321 } 322 323 optionsSnaps = append(optionsSnaps, &internal.Snap20{ 324 Name: sn.SnapName(), 325 SnapID: sn.Info.ID(), 326 Unasserted: unasserted, 327 Channel: channel, 328 }) 329 } 330 331 if len(optionsSnaps) != 0 { 332 // XXX internal error if we get here and grade != dangerous 333 options20 := &internal.Options20{Snaps: optionsSnaps} 334 if err := options20.Write(filepath.Join(tr.systemDir, "options.yaml")); err != nil { 335 return err 336 } 337 } 338 339 auxInfos := make(map[string]*internal.AuxInfo20) 340 341 addAuxInfos := func(seedSnaps []*SeedSnap) { 342 for _, sn := range seedSnaps { 343 if sn.Info.ID() != "" { 344 if sn.Info.Contact != "" || sn.Info.Private { 345 auxInfos[sn.Info.ID()] = &internal.AuxInfo20{ 346 Private: sn.Info.Private, 347 Contact: sn.Info.Contact, 348 } 349 } 350 } 351 } 352 } 353 354 addAuxInfos(snapsFromModel) 355 addAuxInfos(extraSnaps) 356 357 if len(auxInfos) == 0 { 358 // nothing to do 359 return nil 360 } 361 362 if _, err := tr.ensureSystemSnapsDir(); err != nil { 363 return err 364 } 365 366 f, err := os.OpenFile(filepath.Join(tr.systemDir, "snaps", "aux-info.json"), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) 367 if err != nil { 368 return err 369 } 370 defer f.Close() 371 enc := json.NewEncoder(f) 372 373 if err := enc.Encode(auxInfos); err != nil { 374 return err 375 } 376 377 return nil 378 }