github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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  }