github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/seed/validate.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 import ( 23 "bytes" 24 "fmt" 25 "path/filepath" 26 "sort" 27 28 "github.com/snapcore/snapd/snap" 29 "github.com/snapcore/snapd/snap/snapfile" 30 "github.com/snapcore/snapd/timings" 31 ) 32 33 type ValidationError struct { 34 // SystemErrors maps system labels ("" for UC16/18) to their validation errors. 35 SystemErrors map[string][]error 36 } 37 38 func newValidationError(label string, err error) *ValidationError { 39 return &ValidationError{SystemErrors: map[string][]error{ 40 label: {err}, 41 }} 42 } 43 44 func (e *ValidationError) addErr(label string, errs ...error) { 45 if e.SystemErrors == nil { 46 e.SystemErrors = make(map[string][]error) 47 } 48 for _, err := range errs { 49 e.SystemErrors[label] = append(e.SystemErrors[label], err) 50 } 51 } 52 53 func (e ValidationError) hasErrors() bool { 54 return len(e.SystemErrors) != 0 55 } 56 57 func (e *ValidationError) Error() string { 58 systems := make([]string, 0, len(e.SystemErrors)) 59 for s := range e.SystemErrors { 60 systems = append(systems, s) 61 } 62 sort.Strings(systems) 63 var buf bytes.Buffer 64 first := true 65 for _, s := range systems { 66 if first { 67 if s == "" { 68 fmt.Fprintf(&buf, "cannot validate seed:") 69 } else { 70 fmt.Fprintf(&buf, "cannot validate seed system %q:", s) 71 } 72 } else { 73 fmt.Fprintf(&buf, "\nand seed system %q:", s) 74 } 75 for _, err := range e.SystemErrors[s] { 76 fmt.Fprintf(&buf, "\n - %s", err) 77 } 78 } 79 return buf.String() 80 } 81 82 // ValidateFromYaml validates the given seed.yaml file and surrounding seed. 83 func ValidateFromYaml(seedYamlFile string) error { 84 // TODO:UC20: support validating also one or multiple UC20 seed systems 85 // introduce ListSystems ? 86 // What about full empty seed dir? 87 seedDir := filepath.Dir(seedYamlFile) 88 89 seed, err := Open(seedDir, "") 90 if err != nil { 91 return newValidationError("", err) 92 } 93 94 if err := seed.LoadAssertions(nil, nil); err != nil { 95 return newValidationError("", err) 96 } 97 98 tm := timings.New(nil) 99 if err := seed.LoadMeta(tm); err != nil { 100 if missingErr, ok := err.(*essentialSnapMissingError); ok { 101 if seed.Model().Classic() && missingErr.SnapName == "core" { 102 err = fmt.Errorf("essential snap core or snapd must be part of the seed") 103 } 104 } 105 return newValidationError("", err) 106 } 107 108 // TODO:UC20: make the NumSnaps/Iter part of Seed 109 seed16 := seed.(*seed16) 110 111 ve := &ValidationError{} 112 // read the snap infos 113 snapInfos := make([]*snap.Info, 0, seed16.NumSnaps()) 114 seed16.Iter(func(sn *Snap) error { 115 snapf, err := snapfile.Open(sn.Path) 116 if err != nil { 117 ve.addErr("", err) 118 } else { 119 info, err := snap.ReadInfoFromSnapFile(snapf, sn.SideInfo) 120 if err != nil { 121 ve.addErr("", fmt.Errorf("cannot use snap %q: %v", sn.Path, err)) 122 } else { 123 snapInfos = append(snapInfos, info) 124 } 125 } 126 return nil 127 }) 128 129 if errs2 := snap.ValidateBasesAndProviders(snapInfos); errs2 != nil { 130 ve.addErr("", errs2...) 131 } 132 if ve.hasErrors() { 133 return ve 134 } 135 136 return nil 137 }