Documentation ¶
Index ¶
- Constants
- Variables
- func FindDeviceForStructure(ps *LaidOutStructure) (string, error)
- func IsCompatible(current, new *Info) error
- func IsRoleMBR(ls LaidOutStructure) bool
- func KernelCommandLineFromGadget(gadgetDirOrSnapPath string) (cmdline string, full bool, err error)
- func MockUpdaterForStructure(...) (restore func())
- func ParentDiskFromMountSource(mountSource string) (string, error)
- func SystemDefaults(gadgetDefaults map[string]map[string]interface{}) map[string]interface{}
- func Update(old, new GadgetData, rollbackDirPath string, updatePolicy UpdatePolicyFunc, ...) error
- func UpdatePartitionList(dl *OnDiskVolume) error
- func Validate(info *Info, model Model, extra *ValidationConstraints) error
- func ValidateContent(info *Info, gadgetSnapRootDir, kernelSnapRootDir string) error
- type Connection
- type ConnectionPlug
- type ConnectionSlot
- type ContentChange
- type ContentChangeAction
- type ContentObserver
- type ContentOperation
- type ContentUpdateObserver
- type GadgetData
- type Info
- func InfoFromGadgetYaml(gadgetYaml []byte, model Model) (*Info, error)
- func ReadInfo(gadgetSnapRootDir string, model Model) (*Info, error)
- func ReadInfoAndValidate(gadgetSnapRootDir string, model Model, ...) (*Info, error)
- func ReadInfoFromSnapFile(snapf snap.Container, model Model) (*Info, error)
- func ReadInfoFromSnapFileNoValidate(snapf snap.Container, model Model) (*Info, error)
- type LaidOutContent
- type LaidOutStructure
- type LaidOutVolume
- type LayoutConstraints
- type Model
- type MountedFilesystemWriter
- type OnDiskStructure
- type OnDiskVolume
- type PartiallyLaidOutVolume
- type RawStructureWriter
- type RelativeOffset
- type ResolvedContent
- type ResolvedContentFilterFunc
- type UpdatePolicyFunc
- type Updater
- type ValidationConstraints
- type Volume
- type VolumeContent
- type VolumeStructure
- type VolumeUpdate
Constants ¶
const ( SystemBoot = "system-boot" SystemData = "system-data" SystemSeed = "system-seed" SystemSave = "system-save" )
const ( // SizeMBR is the maximum byte size of a structure of role 'mbr' SizeMBR = quantity.Size(446) // SizeLBA48Pointer is the byte size of a pointer value written at the // location described by 'offset-write' SizeLBA48Pointer = quantity.Size(4) )
const ( ContentWrite ContentOperation = iota ContentUpdate ContentRollback ChangeAbort ContentChangeAction = iota ChangeApply ChangeIgnore )
Variables ¶
var ( // default positioning constraints that match ubuntu-image DefaultConstraints = LayoutConstraints{ NonMBRStartOffset: 1 * quantity.OffsetMiB, } )
var ErrDeviceNotFound = errors.New("device not found")
var ErrMountNotFound = errors.New("mount point not found")
var ErrNoFilesystemDefined = errors.New("no filesystem defined")
var ErrNoKernelCommandline = errors.New("no kernel command line in the gadget")
var (
ErrNoUpdate = errors.New("nothing to update")
)
Functions ¶
func FindDeviceForStructure ¶
func FindDeviceForStructure(ps *LaidOutStructure) (string, error)
FindDeviceForStructure attempts to find an existing block device matching given volume structure, by inspecting its name and, optionally, the filesystem label. Assumes that the host's udev has set up device symlinks correctly.
func IsCompatible ¶
IsCompatible checks whether the current and an update are compatible. Returns nil or an error describing the incompatibility.
func IsRoleMBR ¶
func IsRoleMBR(ls LaidOutStructure) bool
IsRoleMBR returns whether a structure's role is MBR or not. meh this function is weirdly placed, not sure what to do w/o making schemaMBR constant exported
func KernelCommandLineFromGadget ¶
KernelCommandLineFromGadget returns the desired kernel command line provided by the gadget. The full flag indicates whether the gadget provides a full command line or just the extra parameters that will be appended to the static ones. An ErrNoKernelCommandline is returned when thea gadget does not set any kernel command line.
func MockUpdaterForStructure ¶
func MockUpdaterForStructure(mock func(ps *LaidOutStructure, rootDir, rollbackDir string, observer ContentUpdateObserver) (Updater, error)) (restore func())
MockUpdaterForStructure replace internal call with a mocked one, for use in tests only
func ParentDiskFromMountSource ¶
ParentDiskFromMountSource will find the parent disk device for the given partition. E.g. /dev/nvmen0n1p5 -> /dev/nvme0n1.
When the mount source is a symlink, it is resolved to the actual device that is mounted. Should the device be one created by device mapper, it is followed up to the actual underlying block device. As an example, this is how devices are followed with a /writable mounted from an encrypted volume:
/dev/mapper/ubuntu-data-<uuid> (a symlink)
⤷ /dev/dm-0 (set up by device mapper) ⤷ /dev/hda4 (actual partition with the content) ⤷ /dev/hda (returned by this function)
func SystemDefaults ¶
SystemDefaults returns default system configuration from gadget defaults.
func Update ¶
func Update(old, new GadgetData, rollbackDirPath string, updatePolicy UpdatePolicyFunc, observer ContentUpdateObserver) error
Update applies the gadget update given the gadget information and data from old and new revisions. It errors out when the update is not possible or illegal, or a failure occurs at any of the steps. When there is no update, a special error ErrNoUpdate is returned.
Only structures selected by the update policy are part of the update. When the policy is nil, a default one is used. The default policy selects structures in an opt-in manner, only tructures with a higher value of Edition field in the new gadget definition are part of the update.
Data that would be modified during the update is first backed up inside the rollback directory. Should the apply step fail, the modified data is recovered.
The rules for gadget/kernel updates with "$kernel:refs":
- When installing a kernel with assets that have "update: true" there *must* be a matching entry in gadget.yaml. If not we risk bricking the system because the kernel tells us that it *needs* this file to boot but without gadget.yaml we would not put it anywhere.
- When installing a gadget with "$kernel:ref" content it is okay if this content cannot get resolved as long as there is no "edition" jump. This means adding new "$kernel:ref" without "edition" updates is always possible.
To add a new "$kernel:ref" to gadget/kernel: a. Update gadget and gadget.yaml and add "$kernel:ref" but do not
update edition (if edition update is needed, use epoch)
b. Update kernel and kernel.yaml with new assets. c. snapd will refresh gadget (see rule 2) but refuse to take the
new kernel (rule 1)
d. After step (c) is completed the kernel refresh will now also
work (no more violation of rule 1)
func UpdatePartitionList ¶
func UpdatePartitionList(dl *OnDiskVolume) error
UpdatePartitionList re-reads the partitioning data from the device and updates the volume structures in the specified volume.
func Validate ¶
func Validate(info *Info, model Model, extra *ValidationConstraints) error
Validate checks that the given gadget metadata matches the consistency rules for role usage, labels etc as implied by the model and extra constraints that might be known only at runtime.
func ValidateContent ¶
ValidateContent checks whether the given directory contains valid matching content with respect to the given pre-validated gadget metadata.
Types ¶
type Connection ¶
type Connection struct { Plug ConnectionPlug `yaml:"plug"` Slot ConnectionSlot `yaml:"slot"` }
GadgetConnect describes an interface connection requested by the gadget between seeded snaps. The syntax is of a mapping like:
plug: (<plug-snap-id>|system):plug [slot: (<slot-snap-id>|system):slot]
"system" indicates a system plug or slot. Fully omitting the slot part indicates a system slot with the same name as the plug.
type ConnectionPlug ¶
func (*ConnectionPlug) Empty ¶
func (gcplug *ConnectionPlug) Empty() bool
func (*ConnectionPlug) UnmarshalYAML ¶
func (gcplug *ConnectionPlug) UnmarshalYAML(unmarshal func(interface{}) error) error
type ConnectionSlot ¶
func (*ConnectionSlot) Empty ¶
func (gcslot *ConnectionSlot) Empty() bool
func (*ConnectionSlot) UnmarshalYAML ¶
func (gcslot *ConnectionSlot) UnmarshalYAML(unmarshal func(interface{}) error) error
type ContentChange ¶
type ContentChange struct { // Before is a path to a file containing the original data before the // operation takes place (or took place in case of ContentRollback). Before string // After is a path to a file location of the data applied by the operation. After string }
ContentChange carries paths to files containing the content data being modified by the operation.
type ContentChangeAction ¶
type ContentChangeAction int
type ContentObserver ¶
type ContentObserver interface { // Observe is called to observe an pending or completed action, related // to content being written, updated or being rolled back. In each of // the scenarios, the target path is relative under the root. // // For a file write or update, the source path points to the content // that will be written. When called during rollback, observe call // happens after the original file has been restored (or removed if the // file was added during the update), the source path is empty. // // Returning ChangeApply indicates that the observer agrees for a given // change to be applied. When called with a ContentUpdate or // ContentWrite operation, returning ChangeIgnore indicates that the // change shall be ignored. ChangeAbort is expected to be returned along // with a non-nil error. Observe(op ContentOperation, sourceStruct *LaidOutStructure, targetRootDir, relativeTargetPath string, dataChange *ContentChange) (ContentChangeAction, error) }
ContentObserver allows for observing operations on the content of the gadget structures.
type ContentOperation ¶
type ContentOperation int
type ContentUpdateObserver ¶
type ContentUpdateObserver interface { ContentObserver // BeforeWrite is called when the backups of content that will get // modified during the update are complete and update is ready to be // applied. BeforeWrite() error // Canceled is called when the update has been canceled, or if changes // were written and the update has been reverted. Canceled() error }
ContentUpdateObserver allows for observing update (and potentially a rollback) of the gadget structure content.
type GadgetData ¶
type GadgetData struct { // Info is the gadget metadata Info *Info // XXX: should be GadgetRootDir // RootDir is the root directory of gadget snap data RootDir string // KernelRootDir is the root directory of kernel snap data KernelRootDir string }
GadgetData holds references to a gadget revision metadata and its data directory.
type Info ¶
type Info struct { Volumes map[string]*Volume `yaml:"volumes,omitempty"` // Default configuration for snaps (snap-id => key => value). Defaults map[string]map[string]interface{} `yaml:"defaults,omitempty"` Connections []Connection `yaml:"connections"` }
func InfoFromGadgetYaml ¶
InfoFromGadgetYaml parses the provided gadget metadata. If model is nil only self-consistency checks are performed. If model is not nil implied values for filesystem labels will be set as well, based on whether the model is for classic, UC16/18 or UC20. UC gadget metadata is expected to have volumes definitions.
func ReadInfo ¶
ReadInfo reads the gadget specific metadata from meta/gadget.yaml in the snap root directory. See ReadInfoAndValidate for a variant that does role-usage consistency validation like Validate.
func ReadInfoAndValidate ¶
func ReadInfoAndValidate(gadgetSnapRootDir string, model Model, validationConstraints *ValidationConstraints) (*Info, error)
ReadInfoAndValidate reads the gadget specific metadata from meta/gadget.yaml in the snap root directory. It also performs role-usage consistency validation as Validate does using the given constraints. See ReadInfo for a variant that does not. See also ValidateContent for further validating the content itself instead of the metadata.
func ReadInfoFromSnapFile ¶
ReadInfoFromSnapFile reads the gadget specific metadata from meta/gadget.yaml in the given snap container. It also performs role-usage consistency validation as Validate does. See ReadInfoFromSnapFileNoValidate for a variant that does not.
func ReadInfoFromSnapFileNoValidate ¶
ReadInfoFromSnapFileNoValidate reads the gadget specific metadata from meta/gadget.yaml in the given snap container. See ReadInfoFromSnapFile for a variant that does role-usage consistency validation like Validate as well.
type LaidOutContent ¶
type LaidOutContent struct { *VolumeContent // StartOffset defines the start offset of this content image StartOffset quantity.Offset // AbsoluteOffsetWrite is the resolved absolute position of offset-write // for this content element within the enclosing volume AbsoluteOffsetWrite *quantity.Offset // Size is the maximum size occupied by this image Size quantity.Size // Index of the content in structure declaration inside gadget YAML Index int }
LaidOutContent describes raw content that has been placed within the encompassing structure and volume
TODO: this can't have "$kernel:" refs at this point, fail in validate
for bare structures with "$kernel:" refs
func (LaidOutContent) String ¶
func (p LaidOutContent) String() string
type LaidOutStructure ¶
type LaidOutStructure struct { *VolumeStructure // StartOffset defines the start offset of the structure within the // enclosing volume StartOffset quantity.Offset // AbsoluteOffsetWrite is the resolved absolute position of offset-write // for this structure element within the enclosing volume AbsoluteOffsetWrite *quantity.Offset // Index of the structure definition in gadget YAML Index int // LaidOutContent is a list of raw content inside the structure LaidOutContent []LaidOutContent // ResolvedContent is a list of filesystem content that has all // relative paths or references resolved ResolvedContent []ResolvedContent }
LaidOutStructure describes a VolumeStructure that has been placed within the volume
func ShiftStructureTo ¶
func ShiftStructureTo(ps LaidOutStructure, offset quantity.Offset) LaidOutStructure
ShiftStructureTo translates the starting offset of a laid out structure and its content to the provided offset.
func (LaidOutStructure) String ¶
func (p LaidOutStructure) String() string
type LaidOutVolume ¶
type LaidOutVolume struct { *Volume // Size is the total size of the volume Size quantity.Size // LaidOutStructure is a list of structures within the volume, sorted // by their start offsets LaidOutStructure []LaidOutStructure // RootDir is the root directory for volume data RootDir string }
LaidOutVolume defines the size of a volume and arrangement of all the structures within it
func LaidOutSystemVolumeFromGadget ¶
func LaidOutSystemVolumeFromGadget(gadgetRoot, kernelRoot string, model Model) (*LaidOutVolume, error)
LaidOutSystemVolumeFromGadget takes a gadget rootdir and lays out the partitions as specified. It returns one specific volume, which is the volume on which system-* roles/partitions exist, all other volumes are assumed to already be flashed and managed separately at image build/flash time, while the system-* roles can be manipulated on the returned volume during install mode.
func LayoutVolume ¶
func LayoutVolume(gadgetRootDir, kernelRootDir string, volume *Volume, constraints LayoutConstraints) (*LaidOutVolume, error)
LayoutVolume attempts to completely lay out the volume, that is the structures and their content, using provided constraints
type LayoutConstraints ¶
type LayoutConstraints struct { // NonMBRStartOffset is the default start offset of non-MBR structure in // the volume. NonMBRStartOffset quantity.Offset // SkipResolveContent will skip resolving content paths // and `$kernel:` style references SkipResolveContent bool }
LayoutConstraints defines the constraints for arranging structures within a volume
type Model ¶
type Model interface { Classic() bool Grade() asserts.ModelGrade }
Model carries characteristics about the model that are relevant to gadget. Note *asserts.Model implements this, and that's the expected use case.
type MountedFilesystemWriter ¶
type MountedFilesystemWriter struct {
// contains filtered or unexported fields
}
MountedFilesystemWriter assists in writing contents of a structure to a mounted filesystem.
func NewMountedFilesystemWriter ¶
func NewMountedFilesystemWriter(ps *LaidOutStructure, observer ContentObserver) (*MountedFilesystemWriter, error)
NewMountedFilesystemWriter returns a writer capable of writing provided structure, with content of the structure stored in the given root directory.
func (*MountedFilesystemWriter) Write ¶
func (m *MountedFilesystemWriter) Write(whereDir string, preserve []string) error
Write writes structure data into provided directory. All existing files are overwritten, unless their paths, relative to target directory, are listed in the preserve list. Permission bits and ownership of updated entries is not preserved.
type OnDiskStructure ¶
type OnDiskStructure struct { LaidOutStructure // Node identifies the device node of the block device. Node string // Size of the on disk structure, which is at least equal to the // LaidOutStructure.Size but may be bigger if the partition was // expanded. Size quantity.Size }
OnDiskStructure represents a gadget structure laid on a block device.
type OnDiskVolume ¶
type OnDiskVolume struct { Structure []OnDiskStructure ID string Device string Schema string // size in bytes Size quantity.Size // sector size in bytes SectorSize quantity.Size }
OnDiskVolume holds information about the disk device including its partitioning schema, the partition table, and the structure layout it contains.
func OnDiskVolumeFromDevice ¶
func OnDiskVolumeFromDevice(device string) (*OnDiskVolume, error)
OnDiskVolumeFromDevice obtains the partitioning and filesystem information from the block device.
type PartiallyLaidOutVolume ¶
type PartiallyLaidOutVolume struct { *Volume // LaidOutStructure is a list of structures within the volume, sorted // by their start offsets LaidOutStructure []LaidOutStructure }
PartiallyLaidOutVolume defines the layout of volume structures, but lacks the details about the layout of raw image content within the bare structures.
func LayoutVolumePartially ¶
func LayoutVolumePartially(volume *Volume, constraints LayoutConstraints) (*PartiallyLaidOutVolume, error)
LayoutVolumePartially attempts to lay out only the structures in the volume using provided constraints
type RawStructureWriter ¶
type RawStructureWriter struct {
// contains filtered or unexported fields
}
RawStructureWriter implements support for writing raw (bare) structures.
func NewRawStructureWriter ¶
func NewRawStructureWriter(contentDir string, ps *LaidOutStructure) (*RawStructureWriter, error)
NewRawStructureWriter returns a writer for the given structure, that will load the structure content data from the provided gadget content directory.
func (*RawStructureWriter) Write ¶
func (r *RawStructureWriter) Write(out io.WriteSeeker) error
Write will write whole contents of a structure into the output stream.
type RelativeOffset ¶
type RelativeOffset struct { // RelativeTo names the structure relative to which the location of the // address write will be calculated. RelativeTo string // Offset is a 32-bit value Offset quantity.Offset }
RelativeOffset describes an offset where structure data is written at. The position can be specified as byte-offset relative to the start of another named structure.
func (*RelativeOffset) String ¶
func (r *RelativeOffset) String() string
func (*RelativeOffset) UnmarshalYAML ¶
func (s *RelativeOffset) UnmarshalYAML(unmarshal func(interface{}) error) error
type ResolvedContent ¶
type ResolvedContent struct { *VolumeContent // ResolvedSource is the absolute path of the Source after resolving // any references (e.g. to a "$kernel:" snap). ResolvedSource string // KernelUpdate is true if this content comes from the kernel // and has the "Update" property set KernelUpdate bool }
type ResolvedContentFilterFunc ¶
type ResolvedContentFilterFunc func(*ResolvedContent) bool
ResolvedContentFilterFunc is a callback that evaluates the given ResolvedContent and returns true if it should be applied as part of an update. This is relevant for e.g. asset updates that come from the kernel snap.
func KernelUpdatePolicy ¶
func KernelUpdatePolicy(from, to *LaidOutStructure) (bool, ResolvedContentFilterFunc)
KernelUpdatePolicy implements the update policy for kernel asset updates.
This is called when there is a kernel->kernel refresh for kernels that contain bootloader assets. In this case all bootloader assets that are marked as "update: true" in the kernel.yaml need updating.
But any non-kernel assets need to be ignored, they will be handled by the regular gadget->gadget update mechanism and policy.
func RemodelUpdatePolicy ¶
func RemodelUpdatePolicy(from, to *LaidOutStructure) (bool, ResolvedContentFilterFunc)
RemodelUpdatePolicy implements the update policy of a remodel scenario. The policy selects all non-MBR structures for the update.
type UpdatePolicyFunc ¶
type UpdatePolicyFunc func(from, to *LaidOutStructure) (bool, ResolvedContentFilterFunc)
UpdatePolicyFunc is a callback that evaluates the provided pair of (potentially not yet resolved) structures and returns true when the pair should be part of an update. It may also return a filter function for the resolved content when not all of the content should be applied as part of the update (e.g. when updating assets from the kernel snap).
type Updater ¶
type Updater interface { // Update applies the update or errors out on failures. When no actual // update was applied because the new content is identical a special // ErrNoUpdate is returned. Update() error // Backup prepares a backup copy of data that will be modified by // Update() Backup() error // Rollback restores data modified by update Rollback() error }
type ValidationConstraints ¶
type ValidationConstraints struct { // EncryptedData when true indicates that the gadget will be used on a // device where the data partition will be encrypted. EncryptedData bool }
ValidationConstraints carries extra constraints on top of those implied by the model to use for gadget validation. They might be constraints that are determined only at runtime.
type Volume ¶
type Volume struct { // Schema describes the schema used for the volume Schema string `yaml:"schema"` // Bootloader names the bootloader used by the volume Bootloader string `yaml:"bootloader"` // ID is a 2-hex digit disk ID or GPT GUID ID string `yaml:"id"` // Structure describes the structures that are part of the volume Structure []VolumeStructure `yaml:"structure"` }
Volume defines the structure and content for the image to be written into a block device.
type VolumeContent ¶
type VolumeContent struct { // UnresovedSource is the data of the partition relative to // the gadget base directory UnresolvedSource string `yaml:"source"` // Target is the location of the data inside the root filesystem Target string `yaml:"target"` // Image names the image, relative to gadget base directory, to be used // for a 'bare' type structure Image string `yaml:"image"` // Offset the image is written at Offset *quantity.Offset `yaml:"offset"` // OffsetWrite describes a 32-bit address, within the volume, at which // the offset of current image will be written. The position may be // specified as a byte offset relative to the start of a named structure OffsetWrite *RelativeOffset `yaml:"offset-write"` // Size of the image, when empty size is calculated by looking at the // image Size quantity.Size `yaml:"size"` Unpack bool `yaml:"unpack"` }
VolumeContent defines the contents of the structure. The content can be either files within a filesystem described by the structure or raw images written into the area of a bare structure.
func (VolumeContent) String ¶
func (vc VolumeContent) String() string
type VolumeStructure ¶
type VolumeStructure struct { // Name, when non empty, provides the name of the structure Name string `yaml:"name"` // Label provides the filesystem label Label string `yaml:"filesystem-label"` // Offset defines a starting offset of the structure Offset *quantity.Offset `yaml:"offset"` // OffsetWrite describes a 32-bit address, within the volume, at which // the offset of current structure will be written. The position may be // specified as a byte offset relative to the start of a named structure OffsetWrite *RelativeOffset `yaml:"offset-write"` // Size of the structure Size quantity.Size `yaml:"size"` // Type of the structure, which can be 2-hex digit MBR partition, // 36-char GUID partition, comma separated <mbr>,<guid> for hybrid // partitioning schemes, or 'bare' when the structure is not considered // a partition. // // For backwards compatibility type 'mbr' is also accepted, and the // structure is treated as if it is of role 'mbr'. Type string `yaml:"type"` // Role describes the role of given structure, can be one of // 'mbr', 'system-data', 'system-boot', 'system-boot-image', // 'system-boot-select' or 'system-recovery-select'. Structures of type 'mbr', must have a // size of 446 bytes and must start at 0 offset. Role string `yaml:"role"` // ID is the GPT partition ID ID string `yaml:"id"` // Filesystem used for the partition, 'vfat', 'ext4' or 'none' for // structures of type 'bare' Filesystem string `yaml:"filesystem"` // Content of the structure Content []VolumeContent `yaml:"content"` Update VolumeUpdate `yaml:"update"` }
VolumeStructure describes a single structure inside a volume. A structure can represent a partition, Master Boot Record, or any other contiguous range within the volume.
func (*VolumeStructure) HasFilesystem ¶
func (vs *VolumeStructure) HasFilesystem() bool
HasFilesystem returns true if the structure is using a filesystem.
func (*VolumeStructure) IsPartition ¶
func (vs *VolumeStructure) IsPartition() bool
IsPartition returns true when the structure describes a partition in a block device.