github.com/rigado/snapd@v2.42.5-go-mod+incompatible/seed/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 seed 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 // Snap 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 // TODO: make this internal 38 type Snap16 struct { 39 Name string `yaml:"name"` 40 41 // cross-reference/audit 42 SnapID string `yaml:"snap-id,omitempty"` 43 44 // bits that are orthongonal/not in assertions 45 Channel string `yaml:"channel,omitempty"` 46 DevMode bool `yaml:"devmode,omitempty"` 47 Classic bool `yaml:"classic,omitempty"` 48 49 Private bool `yaml:"private,omitempty"` 50 51 Contact string `yaml:"contact,omitempty"` 52 53 // no assertions are available in the seed for this snap 54 Unasserted bool `yaml:"unasserted,omitempty"` 55 56 File string `yaml:"file"` 57 } 58 59 // TODO: make all of this internal only 60 61 type Seed16 struct { 62 Snaps []*Snap16 `yaml:"snaps"` 63 } 64 65 func ReadYaml(fn string) (*Seed16, error) { 66 errPrefix := "cannot read seed yaml" 67 68 yamlData, err := ioutil.ReadFile(fn) 69 if err != nil { 70 return nil, fmt.Errorf("%s: %v", errPrefix, err) 71 } 72 73 var seed Seed16 74 if err := yaml.Unmarshal(yamlData, &seed); err != nil { 75 return nil, fmt.Errorf("%s: cannot unmarshal %q: %s", errPrefix, yamlData, err) 76 } 77 78 seenNames := make(map[string]bool, len(seed.Snaps)) 79 // validate 80 for _, sn := range seed.Snaps { 81 if sn == nil { 82 return nil, fmt.Errorf("%s: empty element in seed", errPrefix) 83 } 84 // TODO: check if it's a parallel install explicitly, 85 // need to move *Instance* helpers from snap to naming 86 if err := naming.ValidateSnap(sn.Name); err != nil { 87 return nil, fmt.Errorf("%s: %v", errPrefix, err) 88 } 89 if sn.Channel != "" { 90 if _, err := channel.Parse(sn.Channel, ""); err != nil { 91 return nil, fmt.Errorf("%s: %v", errPrefix, err) 92 } 93 } 94 if sn.File == "" { 95 return nil, fmt.Errorf(`%s: "file" attribute for %q cannot be empty`, errPrefix, sn.Name) 96 } 97 if strings.Contains(sn.File, "/") { 98 return nil, fmt.Errorf("%s: %q must be a filename, not a path", errPrefix, sn.File) 99 } 100 101 // make sure names and file names are unique 102 if seenNames[sn.Name] { 103 return nil, fmt.Errorf("%s: snap name %q must be unique", errPrefix, sn.Name) 104 } 105 seenNames[sn.Name] = true 106 } 107 108 return &seed, nil 109 } 110 111 func (seed *Seed16) Write(seedFn string) error { 112 data, err := yaml.Marshal(&seed) 113 if err != nil { 114 return err 115 } 116 if err := osutil.AtomicWriteFile(seedFn, data, 0644, 0); err != nil { 117 return err 118 } 119 return nil 120 }