github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/seed/internal/seed_yaml.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 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 internal 21 22 import ( 23 "fmt" 24 "io/ioutil" 25 "strings" 26 27 "gopkg.in/yaml.v2" 28 29 "github.com/snapcore/snapd/osutil" 30 "github.com/snapcore/snapd/snap/channel" 31 "github.com/snapcore/snapd/snap/naming" 32 ) 33 34 // Snap16 points to a snap in the seed to install, together with 35 // assertions (or alone if unasserted is true) it will be used to 36 // drive the installation and ultimately set SideInfo/SnapState for it. 37 type Snap16 struct { 38 Name string `yaml:"name"` 39 40 // cross-reference/audit 41 SnapID string `yaml:"snap-id,omitempty"` 42 43 // bits that are orthongonal/not in assertions 44 Channel string `yaml:"channel,omitempty"` 45 DevMode bool `yaml:"devmode,omitempty"` 46 Classic bool `yaml:"classic,omitempty"` 47 48 Private bool `yaml:"private,omitempty"` 49 50 Contact string `yaml:"contact,omitempty"` 51 52 // no assertions are available in the seed for this snap 53 Unasserted bool `yaml:"unasserted,omitempty"` 54 55 File string `yaml:"file"` 56 } 57 58 type Seed16 struct { 59 Snaps []*Snap16 `yaml:"snaps"` 60 } 61 62 func ReadSeedYaml(fn string) (*Seed16, error) { 63 errPrefix := "cannot read seed yaml" 64 65 yamlData, err := ioutil.ReadFile(fn) 66 if err != nil { 67 return nil, fmt.Errorf("%s: %v", errPrefix, err) 68 } 69 70 var seed Seed16 71 if err := yaml.Unmarshal(yamlData, &seed); err != nil { 72 return nil, fmt.Errorf("%s: cannot unmarshal %q: %s", errPrefix, yamlData, err) 73 } 74 75 seenNames := make(map[string]bool, len(seed.Snaps)) 76 // validate 77 for _, sn := range seed.Snaps { 78 if sn == nil { 79 return nil, fmt.Errorf("%s: empty element in seed", errPrefix) 80 } 81 // TODO: check if it's a parallel install explicitly, 82 // need to move *Instance* helpers from snap to naming 83 if err := naming.ValidateSnap(sn.Name); err != nil { 84 return nil, fmt.Errorf("%s: %v", errPrefix, err) 85 } 86 if sn.Channel != "" { 87 if _, err := channel.Parse(sn.Channel, ""); err != nil { 88 return nil, fmt.Errorf("%s: %v", errPrefix, err) 89 } 90 } 91 if sn.File == "" { 92 return nil, fmt.Errorf(`%s: "file" attribute for %q cannot be empty`, errPrefix, sn.Name) 93 } 94 if strings.Contains(sn.File, "/") { 95 return nil, fmt.Errorf("%s: %q must be a filename, not a path", errPrefix, sn.File) 96 } 97 98 // make sure names and file names are unique 99 if seenNames[sn.Name] { 100 return nil, fmt.Errorf("%s: snap name %q must be unique", errPrefix, sn.Name) 101 } 102 seenNames[sn.Name] = true 103 } 104 105 return &seed, nil 106 } 107 108 func (seed *Seed16) Write(seedFn string) error { 109 data, err := yaml.Marshal(&seed) 110 if err != nil { 111 return err 112 } 113 if err := osutil.AtomicWriteFile(seedFn, data, 0644, 0); err != nil { 114 return err 115 } 116 return nil 117 }