github.com/david-imola/snapd@v0.0.0-20210611180407-2de8ddeece6d/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 sideInfo.Contact = sn.Contact 162 } 163 164 seedSnap.SideInfo = &sideInfo 165 if cache != nil { 166 cache[path] = seedSnap 167 } 168 } 169 170 s.snaps = append(s.snaps, seedSnap) 171 172 return seedSnap, nil 173 } 174 175 type essentialSnapMissingError struct { 176 SnapName string 177 } 178 179 func (e *essentialSnapMissingError) Error() string { 180 return fmt.Sprintf("essential snap %q required by the model is missing in the seed", e.SnapName) 181 } 182 183 func (s *seed16) loadYaml() error { 184 if s.yamlSnaps != nil { 185 return nil 186 } 187 188 seedYamlFile := filepath.Join(s.seedDir, "seed.yaml") 189 if !osutil.FileExists(seedYamlFile) { 190 return ErrNoMeta 191 } 192 193 seedYaml, err := internal.ReadSeedYaml(seedYamlFile) 194 if err != nil { 195 return err 196 } 197 s.yamlSnaps = seedYaml.Snaps 198 199 return nil 200 } 201 202 func (s *seed16) resetSnaps() { 203 // setup essential snaps cache 204 if s.essCache == nil { 205 // 4 = snapd+base+kernel+gadget 206 s.essCache = make(map[string]*Snap, 4) 207 } 208 209 s.snaps = nil 210 s.essentialSnapsNum = 0 211 } 212 213 func (s *seed16) loadEssentialMeta(essentialTypes []snap.Type, required *naming.SnapSet, added map[string]bool, tm timings.Measurer) error { 214 model := s.Model() 215 216 seeding := make(map[string]*internal.Snap16, len(s.yamlSnaps)) 217 for _, sn := range s.yamlSnaps { 218 seeding[sn.Name] = sn 219 } 220 221 classic := model.Classic() 222 _, usesSnapdSnap := seeding["snapd"] 223 usesSnapdSnap = usesSnapdSnap || required.Contains(naming.Snap("snapd")) 224 s.usesSnapdSnap = usesSnapdSnap 225 226 baseSnap := "core" 227 classicWithSnapd := false 228 if model.Base() != "" { 229 baseSnap = model.Base() 230 } 231 if classic && s.usesSnapdSnap { 232 classicWithSnapd = true 233 // there is no system-wide base as such 234 // if there is a gadget we will install its base first though 235 baseSnap = "" 236 } 237 238 // add the essential snaps 239 addEssential := func(snapName string, pinnedTrack string, essType snap.Type) (*Snap, error) { 240 // be idempotent 241 if added[snapName] { 242 return nil, nil 243 } 244 245 // filter if required 246 if essentialTypes != nil { 247 skip := true 248 for _, t := range essentialTypes { 249 if t == essType { 250 skip = false 251 break 252 } 253 } 254 if skip { 255 return nil, nil 256 } 257 } 258 259 yamlSnap := seeding[snapName] 260 if yamlSnap == nil { 261 return nil, &essentialSnapMissingError{SnapName: snapName} 262 } 263 264 seedSnap, err := s.addSnap(yamlSnap, pinnedTrack, s.essCache, tm) 265 if err != nil { 266 return nil, err 267 } 268 269 if essType == snap.TypeBase && snapName == "core" { 270 essType = snap.TypeOS 271 } 272 273 seedSnap.EssentialType = essType 274 seedSnap.Essential = true 275 seedSnap.Required = true 276 added[snapName] = true 277 278 return seedSnap, nil 279 } 280 281 // if there are snaps to seed, core/base needs to be seeded too 282 if len(s.yamlSnaps) != 0 { 283 // ensure "snapd" snap is installed first 284 if model.Base() != "" || classicWithSnapd { 285 if _, err := addEssential("snapd", "", snap.TypeSnapd); err != nil { 286 return err 287 } 288 } 289 if !classicWithSnapd { 290 if _, err := addEssential(baseSnap, "", snap.TypeBase); err != nil { 291 return err 292 } 293 } 294 } 295 296 if kernelName := model.Kernel(); kernelName != "" { 297 if _, err := addEssential(kernelName, model.KernelTrack(), snap.TypeKernel); err != nil { 298 return err 299 } 300 } 301 302 if gadgetName := model.Gadget(); gadgetName != "" { 303 gadget, err := addEssential(gadgetName, model.GadgetTrack(), snap.TypeGadget) 304 if err != nil { 305 return err 306 } 307 // not skipped 308 if gadget != nil { 309 310 // always make sure the base of gadget is installed first 311 info, err := readInfo(gadget.Path, gadget.SideInfo) 312 if err != nil { 313 return err 314 } 315 gadgetBase := info.Base 316 if gadgetBase == "" { 317 gadgetBase = "core" 318 } 319 // Sanity check 320 // TODO: do we want to relax this? the new logic would allow 321 // but it might just be confusing for now 322 if baseSnap != "" && gadgetBase != baseSnap { 323 return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base()) 324 } 325 if _, err = addEssential(gadgetBase, "", snap.TypeBase); err != nil { 326 return err 327 } 328 } 329 } 330 331 s.essentialSnapsNum = len(s.snaps) 332 333 return nil 334 } 335 336 func (s *seed16) LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error { 337 model := s.Model() 338 339 if err := s.loadYaml(); err != nil { 340 return err 341 } 342 343 required := naming.NewSnapSet(model.RequiredWithEssentialSnaps()) 344 added := make(map[string]bool, 3) 345 346 s.resetSnaps() 347 348 return s.loadEssentialMeta(essentialTypes, required, added, tm) 349 } 350 351 func (s *seed16) LoadMeta(tm timings.Measurer) error { 352 model := s.Model() 353 354 if err := s.loadYaml(); err != nil { 355 return err 356 } 357 358 required := naming.NewSnapSet(model.RequiredWithEssentialSnaps()) 359 added := make(map[string]bool, 3) 360 361 s.resetSnaps() 362 363 if err := s.loadEssentialMeta(nil, required, added, tm); err != nil { 364 return err 365 } 366 367 // the rest of the snaps 368 for _, sn := range s.yamlSnaps { 369 if added[sn.Name] { 370 continue 371 } 372 seedSnap, err := s.addSnap(sn, "", nil, tm) 373 if err != nil { 374 return err 375 } 376 if required.Contains(seedSnap) { 377 seedSnap.Required = true 378 } 379 } 380 381 return nil 382 } 383 384 func (s *seed16) UsesSnapdSnap() bool { 385 return s.usesSnapdSnap 386 } 387 388 func (s *seed16) EssentialSnaps() []*Snap { 389 return s.snaps[:s.essentialSnapsNum] 390 } 391 392 func (s *seed16) ModeSnaps(mode string) ([]*Snap, error) { 393 if mode != "run" { 394 return nil, fmt.Errorf("internal error: Core 16/18 have only run mode, got: %s", mode) 395 } 396 return s.snaps[s.essentialSnapsNum:], nil 397 } 398 399 func (s *seed16) NumSnaps() int { 400 return len(s.snaps) 401 } 402 403 func (s *seed16) Iter(f func(sn *Snap) error) error { 404 for _, sn := range s.snaps { 405 if err := f(sn); err != nil { 406 return err 407 } 408 } 409 return nil 410 }