github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/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 snaps []*Snap 54 essentialSnapsNum int 55 56 usesSnapdSnap bool 57 } 58 59 func (s *seed16) LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error { 60 if db == nil { 61 // a db was not provided, create an internal temporary one 62 var err error 63 db, commitTo, err = newMemAssertionsDB() 64 if err != nil { 65 return err 66 } 67 } 68 69 assertSeedDir := filepath.Join(s.seedDir, "assertions") 70 // collect assertions and find model assertion 71 var modelRef *asserts.Ref 72 checkForModel := func(ref *asserts.Ref) error { 73 if ref.Type == asserts.ModelType { 74 if modelRef != nil && modelRef.Unique() != ref.Unique() { 75 return fmt.Errorf("cannot have multiple model assertions in seed") 76 } 77 modelRef = ref 78 } 79 return nil 80 } 81 82 batch, err := loadAssertions(assertSeedDir, checkForModel) 83 if err != nil { 84 return err 85 } 86 87 // verify we have one model assertion 88 if modelRef == nil { 89 return fmt.Errorf("seed must have a model assertion") 90 } 91 92 if err := commitTo(batch); err != nil { 93 return err 94 } 95 96 a, err := modelRef.Resolve(db.Find) 97 if err != nil { 98 return fmt.Errorf("internal error: cannot find just added assertion %v: %v", modelRef, err) 99 } 100 101 // remember db for later use 102 s.db = db 103 s.model = a.(*asserts.Model) 104 105 return nil 106 } 107 108 func (s *seed16) Model() *asserts.Model { 109 if s.model == nil { 110 panic("internal error: model assertion unset (LoadAssertions not called)") 111 } 112 return s.model 113 } 114 115 func (s *seed16) Brand() (*asserts.Account, error) { 116 return findBrand(s, s.db) 117 } 118 119 func (s *seed16) addSnap(sn *internal.Snap16, pinnedTrack string, tm timings.Measurer) (*Snap, error) { 120 path := filepath.Join(s.seedDir, "snaps", sn.File) 121 snapChannel := sn.Channel 122 if pinnedTrack != "" { 123 var err error 124 snapChannel, err = channel.ResolvePinned(pinnedTrack, snapChannel) 125 if err != nil { 126 // fallback to using the pinned track directly 127 snapChannel = pinnedTrack 128 } 129 } 130 seedSnap := &Snap{ 131 Path: path, 132 Channel: snapChannel, 133 Classic: sn.Classic, 134 DevMode: sn.DevMode, 135 } 136 137 var sideInfo snap.SideInfo 138 if sn.Unasserted { 139 sideInfo.RealName = sn.Name 140 } else { 141 var si *snap.SideInfo 142 var err error 143 timings.Run(tm, "derive-side-info", fmt.Sprintf("hash and derive side info for snap %q", sn.Name), func(nested timings.Measurer) { 144 si, err = snapasserts.DeriveSideInfo(path, s.db) 145 }) 146 if asserts.IsNotFound(err) { 147 return nil, fmt.Errorf("cannot find signatures with metadata for snap %q (%q)", sn.Name, path) 148 } 149 if err != nil { 150 return nil, err 151 } 152 sideInfo = *si 153 sideInfo.Private = sn.Private 154 sideInfo.Contact = sn.Contact 155 } 156 157 seedSnap.SideInfo = &sideInfo 158 159 s.snaps = append(s.snaps, seedSnap) 160 161 return seedSnap, nil 162 } 163 164 type essentialSnapMissingError struct { 165 SnapName string 166 } 167 168 func (e *essentialSnapMissingError) Error() string { 169 return fmt.Sprintf("essential snap %q required by the model is missing in the seed", e.SnapName) 170 } 171 172 func (s *seed16) LoadMeta(tm timings.Measurer) error { 173 model := s.Model() 174 175 seedYamlFile := filepath.Join(s.seedDir, "seed.yaml") 176 if !osutil.FileExists(seedYamlFile) { 177 return ErrNoMeta 178 } 179 180 seedYaml, err := internal.ReadSeedYaml(seedYamlFile) 181 if err != nil { 182 return err 183 } 184 yamlSnaps := seedYaml.Snaps 185 186 required := naming.NewSnapSet(model.RequiredWithEssentialSnaps()) 187 seeding := make(map[string]*internal.Snap16, len(yamlSnaps)) 188 for _, sn := range yamlSnaps { 189 seeding[sn.Name] = sn 190 } 191 added := make(map[string]bool, 3) 192 classic := model.Classic() 193 _, usesSnapdSnap := seeding["snapd"] 194 usesSnapdSnap = usesSnapdSnap || required.Contains(naming.Snap("snapd")) 195 s.usesSnapdSnap = usesSnapdSnap 196 197 baseSnap := "core" 198 classicWithSnapd := false 199 if model.Base() != "" { 200 baseSnap = model.Base() 201 } 202 if classic && s.usesSnapdSnap { 203 classicWithSnapd = true 204 // there is no system-wide base as such 205 // if there is a gadget we will install its base first though 206 baseSnap = "" 207 } 208 209 // add the essential snaps 210 addEssential := func(snapName string, pinnedTrack string, essType snap.Type) (*Snap, error) { 211 // be idempotent 212 if added[snapName] { 213 return nil, nil 214 } 215 yamlSnap := seeding[snapName] 216 if yamlSnap == nil { 217 return nil, &essentialSnapMissingError{SnapName: snapName} 218 } 219 220 seedSnap, err := s.addSnap(yamlSnap, pinnedTrack, tm) 221 if err != nil { 222 return nil, err 223 } 224 225 if essType == snap.TypeBase && snapName == "core" { 226 essType = snap.TypeOS 227 } 228 229 seedSnap.EssentialType = essType 230 seedSnap.Essential = true 231 seedSnap.Required = true 232 added[snapName] = true 233 234 return seedSnap, nil 235 } 236 237 // if there are snaps to seed, core/base needs to be seeded too 238 if len(yamlSnaps) != 0 { 239 // ensure "snapd" snap is installed first 240 if model.Base() != "" || classicWithSnapd { 241 if _, err := addEssential("snapd", "", snap.TypeSnapd); err != nil { 242 return err 243 } 244 } 245 if !classicWithSnapd { 246 if _, err := addEssential(baseSnap, "", snap.TypeBase); err != nil { 247 return err 248 } 249 } 250 } 251 252 if kernelName := model.Kernel(); kernelName != "" { 253 if _, err := addEssential(kernelName, model.KernelTrack(), snap.TypeKernel); err != nil { 254 return err 255 } 256 } 257 258 if gadgetName := model.Gadget(); gadgetName != "" { 259 gadget, err := addEssential(gadgetName, model.GadgetTrack(), snap.TypeGadget) 260 if err != nil { 261 return err 262 } 263 264 // always make sure the base of gadget is installed first 265 info, err := readInfo(gadget.Path, gadget.SideInfo) 266 if err != nil { 267 return err 268 } 269 gadgetBase := info.Base 270 if gadgetBase == "" { 271 gadgetBase = "core" 272 } 273 // Sanity check 274 // TODO: do we want to relax this? the new logic would allow 275 // but it might just be confusing for now 276 if baseSnap != "" && gadgetBase != baseSnap { 277 return fmt.Errorf("cannot use gadget snap because its base %q is different from model base %q", gadgetBase, model.Base()) 278 } 279 if _, err = addEssential(gadgetBase, "", snap.TypeBase); err != nil { 280 return err 281 } 282 } 283 284 s.essentialSnapsNum = len(s.snaps) 285 286 // the rest of the snaps 287 for _, sn := range yamlSnaps { 288 if added[sn.Name] { 289 continue 290 } 291 seedSnap, err := s.addSnap(sn, "", tm) 292 if err != nil { 293 return err 294 } 295 if required.Contains(seedSnap) { 296 seedSnap.Required = true 297 } 298 } 299 300 return nil 301 } 302 303 func (s *seed16) UsesSnapdSnap() bool { 304 return s.usesSnapdSnap 305 } 306 307 func (s *seed16) EssentialSnaps() []*Snap { 308 return s.snaps[:s.essentialSnapsNum] 309 } 310 311 func (s *seed16) ModeSnaps(mode string) ([]*Snap, error) { 312 if mode != "run" { 313 return nil, fmt.Errorf("internal error: Core 16/18 have only run mode, got: %s", mode) 314 } 315 return s.snaps[s.essentialSnapsNum:], nil 316 } 317 318 func (s *seed16) NumSnaps() int { 319 return len(s.snaps) 320 } 321 322 func (s *seed16) Iter(f func(sn *Snap) error) error { 323 for _, sn := range s.snaps { 324 if err := f(sn); err != nil { 325 return err 326 } 327 } 328 return nil 329 }