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  }