github.com/bugraaydogar/snapd@v0.0.0-20210315170335-8c70bb858939/seed/internal/options20.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019 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 // Snap20 carries options for a model snap or an extra snap 35 // in grade: dangerous. 36 type Snap20 struct { 37 Name string `yaml:"name"` 38 // id and unasserted can be both set, in which case it only 39 // cross-references the model 40 SnapID string `yaml:"id,omitempty"` 41 42 // Unasserted has the filename for an unasserted local snap 43 Unasserted string `yaml:"unasserted,omitempty"` 44 45 Channel string `yaml:"channel,omitempty"` 46 // TODO: DevMode bool `yaml:"devmode,omitempty"` 47 } 48 49 // SnapName implements naming.SnapRef. 50 func (sn *Snap20) SnapName() string { 51 return sn.Name 52 } 53 54 // ID implements naming.SnapRef. 55 func (sn *Snap20) ID() string { 56 return sn.SnapID 57 } 58 59 type Options20 struct { 60 Snaps []*Snap20 `yaml:"snaps"` 61 } 62 63 func ReadOptions20(optionsFn string) (*Options20, error) { 64 errPrefix := "cannot read grade dangerous options yaml" 65 66 yamlData, err := ioutil.ReadFile(optionsFn) 67 if err != nil { 68 return nil, fmt.Errorf("%s: %v", errPrefix, err) 69 } 70 71 var options Options20 72 if err := yaml.Unmarshal(yamlData, &options); err != nil { 73 return nil, fmt.Errorf("%s: cannot unmarshal %q: %s", errPrefix, yamlData, err) 74 } 75 76 seenNames := make(map[string]bool, len(options.Snaps)) 77 // validate 78 for _, sn := range options.Snaps { 79 if sn == nil { 80 return nil, fmt.Errorf("%s: empty snaps element", errPrefix) 81 } 82 // TODO: check if it's a parallel install explicitly, 83 // need to move *Instance* helpers from snap to naming 84 if err := naming.ValidateSnap(sn.Name); err != nil { 85 return nil, fmt.Errorf("%s: %v", errPrefix, err) 86 } 87 if sn.SnapID == "" && sn.Channel == "" && sn.Unasserted == "" { 88 return nil, fmt.Errorf("%s: at least one of id, channel or unasserted must be set for snap %q", errPrefix, sn.Name) 89 } 90 if sn.SnapID != "" { 91 if err := naming.ValidateSnapID(sn.SnapID); err != nil { 92 return nil, fmt.Errorf("%s: %v", errPrefix, err) 93 } 94 } 95 if sn.Channel != "" { 96 if _, err := channel.Parse(sn.Channel, ""); err != nil { 97 return nil, fmt.Errorf("%s: %v", errPrefix, err) 98 } 99 } 100 if sn.Unasserted != "" && strings.Contains(sn.Unasserted, "/") { 101 return nil, fmt.Errorf("%s: %q must be a filename, not a path", errPrefix, sn.Unasserted) 102 } 103 104 // make sure names and file names are unique 105 if seenNames[sn.Name] { 106 return nil, fmt.Errorf("%s: snap name %q must be unique", errPrefix, sn.Name) 107 } 108 seenNames[sn.Name] = true 109 } 110 111 return &options, nil 112 } 113 114 func (options *Options20) Write(optionsFn string) error { 115 data, err := yaml.Marshal(options) 116 if err != nil { 117 return err 118 } 119 if err := osutil.AtomicWriteFile(optionsFn, data, 0644, 0); err != nil { 120 return err 121 } 122 return nil 123 }