Files
snapd/gadget/partial.go
alfonsosanchezbeato 85812c58a7 many: use laid out types only for writing content (#13019)
* gadget,daemon: use Volume instead of LaidOutVolume when retrieving

traits (we only need the information from the gadget).

* gadget,overlord: use Volume instead of LaidOutVolume when saving

storage traits (we only actually need the gadget info).

* gadget: add function to get disk from installer gadget volume

* gadget: apply installer gadget changes to volumes

instead of applying to the laid out volumes, as in the end what the
installer is providing us is a way to fill gadget information.

* gadget: create temporary wrapper for LaidOutVolumesFromGadget

* o/devicestate: build layouts after matching disk to volumes

returned by the installer, so we make sure that the information we
have received is accurate.

* gadget/install: do not use laid out when encrypting a partition

* gadget: change so Run uses laid out types

only after the partitions have been created.

* gadget: do not use laid out structures directly in FactoryReset

* gadget: move ancillary function to gadgettest, as it is used there only

* gadget: remove some duplicated lines left by mistake

* gadget: pass around yaml index to installer

It is used by muinstaller as it calls create partition functions that
return maps that use this index as key.

* revert prev

* tests/muinstaller: update to latest snapd

* gadget: fix and test OnDiskVolumeFromGadgetVol

* gadget,overlord: set device from installer

* gadget/install: really check error returned by LayoutVolumeStructure

* gadget/install: fix nosecboot builds

* gadget,overlord: minor changes in comments

* gadget: add test for StructFromYamlIndex, add doc strings

* gadget: add some additional tests

* gadget: move up variable definition

* gadget,overlord: move OnDiskVolumeFromGadgetVol to a better place

* gadget: return OnDiskAndGadgetStructurePair pairs from buildPartitionList

* gadget: add method to copy volumes

* gadget,overlord: return a copy from ApplyInstallerVolumesToGadget

instead of modifying the passed around volumes.

* gadget,overlord: address review comments

* daemon,gadget: some renames and error/comments string changes

* gadget: move TestOnDiskVolumeFromGadgetVol to the right place

* gadget: add case for testing volume copy

* gadget/install: prevent reading twice gadget.yaml in Run

* gadget/install: remove now unneeded dummy function

* tests/muinstaller: update to latest snapd changes

* gadget/install: fix nosecboot tests

* gadget: unexport yamlIdxToStructureIdx

* gadget,overlord: some var name and comment changes

* gadget: add more unit tests for MatchDisksToGadgetVolumes

* gadget,overlord: address some review comments
2023-08-18 15:38:38 +02:00

143 lines
4.0 KiB
Go

// -*- Mode: Go; indent-tabs-mode: t -*-
/*
* Copyright (C) 2023 Canonical Ltd
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package gadget
import "fmt"
// ApplyInstallerVolumesToGadget takes the volume information returned
// by the installer and applies it to the gadget volumes for the
// device to install to and for properties partially defined,
// returning the result in a new Volume map. After that it checks that
// the gadget is now fully specified.
func ApplyInstallerVolumesToGadget(installerVols map[string]*Volume, gadgetVols map[string]*Volume) (map[string]*Volume, error) {
newVols := map[string]*Volume{}
for volName, gv := range gadgetVols {
newV := gv.Copy()
newVols[volName] = newV
insVol := installerVols[volName]
if insVol == nil {
return nil, fmt.Errorf("installer did not provide information for volume %q", volName)
}
// First, retrieve device specified by installer
for i := range newV.Structure {
insStr, err := structureByName(insVol.Structure, newV.Structure[i].Name)
if err != nil {
return nil, err
}
newV.Structure[i].Device = insStr.Device
}
// Next changes are only for partial gadgets
if len(newV.Partial) == 0 {
continue
}
// TODO: partial structure, as it is not clear what will be possible when set
if newV.HasPartial(PartialSchema) {
if insVol.Schema == "" {
return nil, fmt.Errorf("installer did not provide schema for volume %q", volName)
}
newV.Schema = insVol.Schema
}
if newV.HasPartial(PartialFilesystem) {
if err := applyPartialFilesystem(insVol, newV, volName); err != nil {
return nil, err
}
}
if newV.HasPartial(PartialSize) {
if err := applyPartialSize(insVol, newV, volName); err != nil {
return nil, err
}
}
// The only thing that can still be partial is the structure
if newV.HasPartial(PartialStructure) {
newV.Partial = []PartialProperty{PartialStructure}
} else {
newV.Partial = []PartialProperty{}
}
// Now validate finalized volume
if err := validateVolume(newV); err != nil {
return nil, fmt.Errorf("finalized volume %q is wrong: %v", newV.Name, err)
}
}
return newVols, nil
}
func applyPartialFilesystem(insVol *Volume, gadgetVol *Volume, volName string) error {
for sidx := range gadgetVol.Structure {
vs := &gadgetVol.Structure[sidx]
if vs.Filesystem != "" || !vs.HasFilesystem() {
continue
}
insStr, err := structureByName(insVol.Structure, vs.Name)
if err != nil {
return err
}
if insStr.Filesystem == "" {
return fmt.Errorf("installer did not provide filesystem for structure %q in volume %q", vs.Name, volName)
}
vs.Filesystem = insStr.Filesystem
}
return nil
}
func applyPartialSize(insVol *Volume, gadgetVol *Volume, volName string) error {
for sidx := range gadgetVol.Structure {
vs := &gadgetVol.Structure[sidx]
if !vs.hasPartialSize() {
continue
}
insStr, err := structureByName(insVol.Structure, vs.Name)
if err != nil {
return err
}
if insStr.Size == 0 {
return fmt.Errorf("installer did not provide size for structure %q in volume %q", vs.Name, volName)
}
if insStr.Offset == nil {
return fmt.Errorf("installer did not provide offset for structure %q in volume %q", vs.Name, volName)
}
vs.Size = insStr.Size
vs.Offset = insStr.Offset
}
return nil
}
func structureByName(vss []VolumeStructure, name string) (*VolumeStructure, error) {
for sidx := range vss {
if vss[sidx].Name == name {
return &vss[sidx], nil
}
}
return nil, fmt.Errorf("cannot find structure %q", name)
}