github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/seed/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 seed 21 22 /* ATTN this should *not* use: 23 24 * dirs package: it is passed an explicit directory to work on 25 26 * release.OnClassic: it assumes classic based on the model classic 27 option; consistency between system and model can/must be enforced 28 elsewhere 29 30 */ 31 32 import ( 33 "fmt" 34 "path/filepath" 35 36 "github.com/snapcore/snapd/asserts" 37 "github.com/snapcore/snapd/asserts/snapasserts" 38 "github.com/snapcore/snapd/osutil" 39 "github.com/snapcore/snapd/seed/internal" 40 "github.com/snapcore/snapd/snap" 41 "github.com/snapcore/snapd/snap/channel" 42 "github.com/snapcore/snapd/snap/naming" 43 "github.com/snapcore/snapd/timings" 44 ) 45 46 type seed16 struct { 47 seedDir string 48 49 db asserts.RODatabase 50 51 model *asserts.Model 52 53 yamlSnaps []*internal.Snap16 54 55 essCache map[string]*Snap 56 57 snaps []*Snap 58 essentialSnapsNum int 59 60 usesSnapdSnap bool 61 } 62 63 func (s *seed16) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error { 64 if db == nil { 65 // a db was not provided, create an internal temporary one 66 var err error 67 db, commitTo, err = newMemAssertionsDB(nil) 68 if err != nil { 69 return err 70 } 71 } 72 73 assertSeedDir := filepath.Join(s.seedDir, "assertions") 74 // collect assertions and find model assertion 75 var modelRef *asserts.Ref 76 checkForModel := func(ref *asserts.Ref) error { 77 if ref.Type == asserts.ModelType { 78 if modelRef != nil && modelRef.Unique() != ref.Unique() { 79 return fmt.Errorf("cannot have multiple model assertions in seed") 80 } 81 modelRef = ref 82 } 83 return nil 84 } 85 86 batch, err := loadAssertions(assertSeedDir, checkForModel) 87 if err != nil { 88 return err 89 } 90 91 // verify we have one model assertion 92 if modelRef == nil { 93 return fmt.Errorf("seed must have a model assertion") 94 } 95 96 if err := commitTo(batch); err != nil { 97 return err 98 } 99 100 a, err := modelRef.Resolve(db.Find) 101 if err != nil { 102 return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err) 103 } 104 105 // remember db for later use 106 s.db = db 107 s.model = a.(*asserts.Model) 108 109 return nil 110 } 111 112 func (s *seed16) Model() *asserts.Model { 113 if s.model == nil { 114 panic("internal error: model assertion unset (LoadAssertions not called)") 115 } 116 return s.model 117 } 118 119 func (s *seed16) Brand() (*asserts.Account, error) { 120 return findBrand(s, s.db) 121 } 122 123 func (s *seed16) addSnap(sn *internal.Snap16, pinnedTrack string, cache map[string]*Snap, tm timings.Measurer) (*Snap, error) { 124 path := filepath.Join(s.seedDir, "snaps", sn.File) 125 126 seedSnap := cache[path] 127 if seedSnap == nil { 128 snapChannel := sn.Channel 129 if pinnedTrack != "" { 130 var err error 131 snapChannel, err = channel.ResolvePinned(pinnedTrack, snapChannel) 132 if err != nil { 133 // fallback to using the pinned track directly 134 snapChannel = pinnedTrack 135 } 136 } 137 seedSnap = &Snap{ 138 Path: path, 139 Channel: snapChannel, 140 Classic: sn.Classic, 141 DevMode: sn.DevMode, 142 } 143 144 var sideInfo snap.SideInfo 145 if sn.Unasserted { 146 sideInfo.RealName = sn.Name 147 } else { 148 var si *snap.SideInfo 149 var err error 150 timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", sn.Name), func(nested timings.Measurer) { 151 si, err = snapasserts.DeriveSideInfo(path, s.db) 152 }) 153 if asserts.IsNotFound(err) { 154 return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path) 155 } 156 if err != nil { 157 return nil, err 158 } 159 sideInfo = *si 160 sideInfo.Private = sn.Private 161 // TODO: consider whether to use this if we have links? 162 sideInfo.EditedContact = sn.Contact 163 } 164 165 seedSnap.SideInfo = &sideInfo 166 if cache != nil { 167 cache[path] = seedSnap 168 } 169 } 170 171 s.snaps = append(s.snaps, seedSnap) 172 173 return seedSnap, nil 174 } 175 176 type essentialSnapMissingError struct { 177 SnapName string 178 } 179 180 func (e *essentialSnapMissingError) Error() string { 181 return fmt.Sprintf("essential snap %q required by the model is missing in the seed", e.SnapName) 182 } 183 184 func (s *seed16) loadYaml() error { 185 if s.yamlSnaps != nil { 186 return nil 187 } 188 189 seedYamlFile := filepath.Join(s.seedDir, "seed.yaml") 190 if !osutil.FileExists(seedYamlFile) { 191 return ErrNoMeta 192 } 193 194 seedYaml, err := internal.ReadSeedYaml(seedYamlFile) 195 if err != nil { 196 return err 197 } 198 s.yamlSnaps = seedYaml.Snaps 199 200 return nil 201 } 202 203 func (s *seed16) resetSnaps() { 204 // setup essential snaps cache 205 if s.essCache == nil { 206 // 4 = snapd+base+kernel+gadget 207 s.essCache = make(map[string]*Snap, 4) 208 } 209 210 s.snaps = nil 211 s.essentialSnapsNum = 0 212 } 213 214 func (s *seed16) loadEssentialMeta(essentialTypes []snap.Type, required *naming.SnapSet, added map[string]bool, tm timings.Measurer) error { 215 model := s.Model() 216 217 seeding := make(map[string]*internal.Snap16, len(s.yamlSnaps)) 218 for _, sn := range s.yamlSnaps { 219 seeding[sn.Name] = sn 220 } 221 222 classic := model.Classic() 223 _, usesSnapdSnap := seeding["snapd"] 224 usesSnapdSnap = usesSnapdSnap || required.Contains(naming.Snap("snapd")) 225 s.usesSnapdSnap = usesSnapdSnap 226 227 baseSnap := "core" 228 classicWithSnapd := false 229 if model.Base() != "" { 230 baseSnap = model.Base() 231 } 232 if classic && s.usesSnapdSnap { 233 classicWithSnapd = true 234 // there is no system-wide base as such 235 // if there is a gadget we will install its base first though 236 baseSnap = "" 237 } 238 239 // add the essential snaps 240 addEssential := func(snapName string, pinnedTrack string, essType snap.Type) (*Snap, error) { 241 // be idempotent 242 if added[snapName] { 243 return nil, nil 244 } 245 246 // filter if required 247 if essentialTypes != nil { 248 skip := true 249 for _, t := range essentialTypes { 250 if t == essType { 251 skip = false 252 break 253 } 254 } 255 if skip { 256 return nil, nil 257 } 258 } 259 260 yamlSnap := seeding[snapName] 261 if yamlSnap == nil { 262 return nil, &essentialSnapMissingError{SnapName: snapName} 263 } 264 265 seedSnap, err := s.addSnap(yamlSnap, pinnedTrack, s.essCache, tm) 266 if err != nil { 267 return nil, err 268 } 269 270 if essType == snap.TypeBase && snapName == "core" { 271 essType = snap.TypeOS 272 } 273 274 seedSnap.EssentialType = essType 275 seedSnap.Essential = true 276 seedSnap.Required = true 277 added[snapName] = true 278 279 return seedSnap, nil 280 } 281 282 // if there are snaps to seed, core/base needs to be seeded too 283 if len(s.yamlSnaps) != 0 { 284 // ensure "snapd" snap is installed first 285 if model.Base() != "" || classicWithSnapd { 286 if _, err := addEssential("snapd", "", snap.TypeSnapd); err != nil { 287 return err 288 } 289 } 290 if !classicWithSnapd { 291 if _, err := addEssential(baseSnap, "", snap.TypeBase); err != nil { 292 return err 293 } 294 } 295 } 296 297 if kernelName := model.Kernel(); kernelName != "" { 298 if _, err := addEssential(kernelName, model.KernelTrack(), snap.TypeKernel); err != nil { 299 return err 300 } 301 } 302 303 if gadgetName := model.Gadget(); gadgetName != "" { 304 gadget, err := addEssential(gadgetName, model.GadgetTrack(), snap.TypeGadget) 305 if err != nil { 306 return err 307 } 308 // not skipped 309 if gadget != nil { 310 311 // always make sure the base of gadget is installed first 312 info, err := readInfo(gadget.Path, gadget.SideInfo) 313 if err != nil { 314 return err 315 } 316 gadgetBase := info.Base 317 if gadgetBase == "" { 318 gadgetBase = "core" 319 } 320 // Sanity check 321 // TODO: do we want to relax this? the new logic would allow 322 // but it might just be confusing for now 323 if baseSnap != "" && gadgetBase != baseSnap { 324 return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base()) 325 } 326 if _, err = addEssential(gadgetBase, "", snap.TypeBase); err != nil { 327 return err 328 } 329 } 330 } 331 332 s.essentialSnapsNum = len(s.snaps) 333 334 return nil 335 } 336 337 func (s *seed16) LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error { 338 model := s.Model() 339 340 if err := s.loadYaml(); err != nil { 341 return err 342 } 343 344 required := naming.NewSnapSet(model.RequiredWithEssentialSnaps()) 345 added := make(map[string]bool, 3) 346 347 s.resetSnaps() 348 349 return s.loadEssentialMeta(essentialTypes, required, added, tm) 350 } 351 352 func (s *seed16) LoadMeta(tm timings.Measurer) error { 353 model := s.Model() 354 355 if err := s.loadYaml(); err != nil { 356 return err 357 } 358 359 required := naming.NewSnapSet(model.RequiredWithEssentialSnaps()) 360 added := make(map[string]bool, 3) 361 362 s.resetSnaps() 363 364 if err := s.loadEssentialMeta(nil, required, added, tm); err != nil { 365 return err 366 } 367 368 // the rest of the snaps 369 for _, sn := range s.yamlSnaps { 370 if added[sn.Name] { 371 continue 372 } 373 seedSnap, err := s.addSnap(sn, "", nil, tm) 374 if err != nil { 375 return err 376 } 377 if required.Contains(seedSnap) { 378 seedSnap.Required = true 379 } 380 } 381 382 return nil 383 } 384 385 func (s *seed16) UsesSnapdSnap() bool { 386 return s.usesSnapdSnap 387 } 388 389 func (s *seed16) EssentialSnaps() []*Snap { 390 return s.snaps[:s.essentialSnapsNum] 391 } 392 393 func (s *seed16) ModeSnaps(mode string) ([]*Snap, error) { 394 if mode != "run" { 395 return nil, fmt.Errorf("internal error: Core 16/18 have only run mode, got: %s", mode) 396 } 397 return s.snaps[s.essentialSnapsNum:], nil 398 } 399 400 func (s *seed16) NumSnaps() int { 401 return len(s.snaps) 402 } 403 404 func (s *seed16) Iter(f func(sn *Snap) error) error { 405 for _, sn := range s.snaps { 406 if err := f(sn); err != nil { 407 return err 408 } 409 } 410 return nil 411 }