osbuild2: new stages and osbuild features
Stages: - org.osbuild.copy - org.osbuild.truncate - org.osbuild.sfdisk - org.osbuild.qemu - org.osbuild.mkfs.btrfs - org.osbuild.mkfs.ext4 - org.osbuild.mkfs.fat - org.osbuild.mkfs.xfs - org.osbuild.grub2.inst Stages can now have devices and mounts in addition to options and inputs. Devices: - org.osbuild.loopback Mounts: - org.osbuild.btrfs - org.osbuild.ext4 - org.osbuild.fat - org.osbuild.xfs
This commit is contained in:
parent
4cf26bb628
commit
e85fc3b48c
17 changed files with 510 additions and 0 deletions
9
internal/osbuild2/btrfs_mount.go
Normal file
9
internal/osbuild2/btrfs_mount.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package osbuild2
|
||||
|
||||
func NewBtrfsMount(source, target string) *Mount {
|
||||
return &Mount{
|
||||
Type: "org.osbuild.btrfs",
|
||||
Source: source,
|
||||
Target: target,
|
||||
}
|
||||
}
|
||||
46
internal/osbuild2/copy_stage.go
Normal file
46
internal/osbuild2/copy_stage.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package osbuild2
|
||||
|
||||
// Stage to copy items from inputs to mount points or the tree. Multiple items
|
||||
// can be copied. The source and destination is a URL.
|
||||
|
||||
type CopyStageOptions struct {
|
||||
Paths []CopyStagePath `json:"paths"`
|
||||
}
|
||||
|
||||
type CopyStagePath struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
}
|
||||
|
||||
func (CopyStageOptions) isStageOptions() {}
|
||||
|
||||
type CopyStageInputs map[string]CopyStageInput
|
||||
|
||||
type CopyStageInput struct {
|
||||
inputCommon
|
||||
References CopyStageReferences `json:"references"`
|
||||
}
|
||||
|
||||
func (CopyStageInputs) isStageInputs() {}
|
||||
|
||||
type CopyStageReferences []string
|
||||
|
||||
func (CopyStageReferences) isReferences() {}
|
||||
|
||||
type CopyStageDevices map[string]Device
|
||||
|
||||
func (CopyStageDevices) isStageDevices() {}
|
||||
|
||||
type CopyStageMounts map[string]Mount
|
||||
|
||||
func (CopyStageMounts) isStageMounts() {}
|
||||
|
||||
func NewCopyStage(options *CopyStageOptions, inputs *CopyStageInputs, devices *CopyStageDevices, mounts *CopyStageMounts) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.copy",
|
||||
Options: options,
|
||||
Inputs: inputs,
|
||||
Devices: devices,
|
||||
Mounts: mounts,
|
||||
}
|
||||
}
|
||||
14
internal/osbuild2/device.go
Normal file
14
internal/osbuild2/device.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package osbuild2
|
||||
|
||||
type Devices interface {
|
||||
isStageDevices()
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Type string `json:"type"`
|
||||
Options DeviceOptions `json:"options"`
|
||||
}
|
||||
|
||||
type DeviceOptions interface {
|
||||
isDeviceOptions()
|
||||
}
|
||||
9
internal/osbuild2/ext4_mount.go
Normal file
9
internal/osbuild2/ext4_mount.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package osbuild2
|
||||
|
||||
func NewExt4Mount(source, target string) *Mount {
|
||||
return &Mount{
|
||||
Type: "org.osbuild.ext4",
|
||||
Source: source,
|
||||
Target: target,
|
||||
}
|
||||
}
|
||||
9
internal/osbuild2/fat_mount.go
Normal file
9
internal/osbuild2/fat_mount.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package osbuild2
|
||||
|
||||
func NewFATMount(source, target string) *Mount {
|
||||
return &Mount{
|
||||
Type: "org.osbuild.fat",
|
||||
Source: source,
|
||||
Target: target,
|
||||
}
|
||||
}
|
||||
94
internal/osbuild2/grub2_inst_stage.go
Normal file
94
internal/osbuild2/grub2_inst_stage.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Install the grub2 boot loader for non-UEFI systems or hybrid boot
|
||||
|
||||
type Grub2InstStageOptions struct {
|
||||
// Filename of the disk image
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// Platform of the target system
|
||||
Platform string `json:"platform"`
|
||||
|
||||
Location uint64 `json:"location,omitempty"`
|
||||
|
||||
// How to obtain the GRUB core image
|
||||
Core CoreMkImage `json:"core"`
|
||||
|
||||
// Location of grub config
|
||||
Prefix PrefixPartition `json:"prefix"`
|
||||
|
||||
// Sector size (in bytes)
|
||||
SectorSize *uint64 `json:"sector-size,omitempty"`
|
||||
}
|
||||
|
||||
func (Grub2InstStageOptions) isStageOptions() {}
|
||||
|
||||
// Generate the core image via grub-mkimage
|
||||
type CoreMkImage struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
PartLabel string `json:"partlabel"`
|
||||
|
||||
Filesystem string `json:"filesystem"`
|
||||
}
|
||||
|
||||
// Grub2 config on a specific partition, e.g. (,gpt3)/boot
|
||||
type PrefixPartition struct {
|
||||
Type string `json:"type"`
|
||||
|
||||
PartLabel string `json:"partlabel"`
|
||||
|
||||
// The partition number, starting at zero
|
||||
Number uint `json:"number"`
|
||||
|
||||
// Location of the grub config inside the partition
|
||||
Path string `json:"path"`
|
||||
}
|
||||
|
||||
func NewGrub2InstStage(options *Grub2InstStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.grub2.inst",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// alias for custom marshaller
|
||||
type grub2instStageOptions Grub2InstStageOptions
|
||||
|
||||
func (options Grub2InstStageOptions) MarshalJSON() ([]byte, error) {
|
||||
g2options := grub2instStageOptions(options)
|
||||
|
||||
valueIn := func(v string, options []string) bool {
|
||||
for _, o := range options {
|
||||
if v == o {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// verify enum values
|
||||
if g2options.Core.Type != "mkimage" {
|
||||
return nil, fmt.Errorf("org.osbuild.grub2.inst: invalid value %q for core.type", g2options.Core.Type)
|
||||
}
|
||||
if !valueIn(g2options.Core.PartLabel, []string{"gpt", "dos"}) {
|
||||
return nil, fmt.Errorf("org.osbuild.grub2.inst: invalid value %q for core.partlabel", g2options.Core.PartLabel)
|
||||
}
|
||||
if !valueIn(g2options.Core.Filesystem, []string{"ext4", "xfs", "btrfs"}) {
|
||||
return nil, fmt.Errorf("org.osbuild.grub2.inst: invalid value %q for core.filesystem", g2options.Core.Filesystem)
|
||||
}
|
||||
|
||||
if g2options.Prefix.Type != "partition" {
|
||||
return nil, fmt.Errorf("org.osbuild.grub2.inst: invalid value %q for prefix.type", g2options.Prefix.Type)
|
||||
}
|
||||
if !valueIn(g2options.Prefix.PartLabel, []string{"gpt", "dos"}) {
|
||||
return nil, fmt.Errorf("org.osbuild.grub2.inst: invalid value %q for core.partlabel", g2options.Core.PartLabel)
|
||||
}
|
||||
|
||||
return json.Marshal(g2options)
|
||||
}
|
||||
26
internal/osbuild2/loopback_device.go
Normal file
26
internal/osbuild2/loopback_device.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package osbuild2
|
||||
|
||||
// Expose a file (or part of it) as a device node
|
||||
|
||||
type LoopbackDeviceOptions struct {
|
||||
// File to associate with the loopback device
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// Start of the data segment
|
||||
Start uint64 `json:"start,omitempty"`
|
||||
|
||||
// Size limit of the data segment (in sectors)
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
|
||||
// Sector size (in bytes)
|
||||
SectorSize *uint64 `json:"sector-size,omitempty"`
|
||||
}
|
||||
|
||||
func (LoopbackDeviceOptions) isDeviceOptions() {}
|
||||
|
||||
func NewLoopbackDevice(options *LoopbackDeviceOptions) *Device {
|
||||
return &Device{
|
||||
Type: "org.osbuild.loopback",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
22
internal/osbuild2/mkfs_btrfs_stage.go
Normal file
22
internal/osbuild2/mkfs_btrfs_stage.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package osbuild2
|
||||
|
||||
type MkfsBtrfsStageOptions struct {
|
||||
UUID string `json:"uuid"`
|
||||
Label string `json:"label,omitempty"`
|
||||
}
|
||||
|
||||
func (MkfsBtrfsStageOptions) isStageOptions() {}
|
||||
|
||||
type MkfsBtrfsStageDevices struct {
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
func (MkfsBtrfsStageDevices) isStageDevices() {}
|
||||
|
||||
func NewMkfsBtrfsStage(options *MkfsBtrfsStageOptions, devices *MkfsBtrfsStageDevices) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.mkfs.btrfs",
|
||||
Options: options,
|
||||
Devices: devices,
|
||||
}
|
||||
}
|
||||
22
internal/osbuild2/mkfs_ext4_stage.go
Normal file
22
internal/osbuild2/mkfs_ext4_stage.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package osbuild2
|
||||
|
||||
type MkfsExt4StageOptions struct {
|
||||
UUID string `json:"uuid"`
|
||||
Label string `json:"label,omitempty"`
|
||||
}
|
||||
|
||||
func (MkfsExt4StageOptions) isStageOptions() {}
|
||||
|
||||
type MkfsExt4StageDevices struct {
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
func (MkfsExt4StageDevices) isStageDevices() {}
|
||||
|
||||
func NewMkfsExt4Stage(options *MkfsExt4StageOptions, devices *MkfsExt4StageDevices) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.mkfs.ext4",
|
||||
Options: options,
|
||||
Devices: devices,
|
||||
}
|
||||
}
|
||||
23
internal/osbuild2/mkfs_fat_stage.go
Normal file
23
internal/osbuild2/mkfs_fat_stage.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package osbuild2
|
||||
|
||||
type MkfsFATStageOptions struct {
|
||||
VolID string `json:"volid"`
|
||||
Label string `json:"label,omitempty"`
|
||||
FATSize *int `json:"fat-size,omitempty"`
|
||||
}
|
||||
|
||||
func (MkfsFATStageOptions) isStageOptions() {}
|
||||
|
||||
type MkfsFATStageDevices struct {
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
func (MkfsFATStageDevices) isStageDevices() {}
|
||||
|
||||
func NewMkfsFATStage(options *MkfsFATStageOptions, devices *MkfsFATStageDevices) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.mkfs.fat",
|
||||
Options: options,
|
||||
Devices: devices,
|
||||
}
|
||||
}
|
||||
22
internal/osbuild2/mkfs_xfs_stage.go
Normal file
22
internal/osbuild2/mkfs_xfs_stage.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package osbuild2
|
||||
|
||||
type MkfsXfsStageOptions struct {
|
||||
UUID string `json:"uuid"`
|
||||
Label string `json:"label,omitempty"`
|
||||
}
|
||||
|
||||
func (MkfsXfsStageOptions) isStageOptions() {}
|
||||
|
||||
type MkfsXfsStageDevices struct {
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
func (MkfsXfsStageDevices) isStageDevices() {}
|
||||
|
||||
func NewMkfsXfsStage(options *MkfsXfsStageOptions, devices *MkfsXfsStageDevices) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.mkfs.xfs",
|
||||
Options: options,
|
||||
Devices: devices,
|
||||
}
|
||||
}
|
||||
16
internal/osbuild2/mount.go
Normal file
16
internal/osbuild2/mount.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package osbuild2
|
||||
|
||||
type Mounts interface {
|
||||
isStageMounts()
|
||||
}
|
||||
|
||||
type Mount struct {
|
||||
Type string `json:"type"`
|
||||
Source string `json:"source"`
|
||||
Target string `json:"target"`
|
||||
Options *MountOptions `json:"options,omitempty"`
|
||||
}
|
||||
|
||||
type MountOptions interface {
|
||||
isMountOptions()
|
||||
}
|
||||
85
internal/osbuild2/qemu_stage.go
Normal file
85
internal/osbuild2/qemu_stage.go
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Convert a disk image to a different format.
|
||||
//
|
||||
// Some formats support format-specific options:
|
||||
// qcow2: The compatibility version can be specified via 'compat'
|
||||
|
||||
type QEMUStageOptions struct {
|
||||
// Filename for resulting image
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// Image format and options
|
||||
Format QEMUFormatOptions `json:"format"`
|
||||
}
|
||||
|
||||
type QEMUFormatOptions interface {
|
||||
isQEMUFormatOptions()
|
||||
}
|
||||
|
||||
type Qcow2Options struct {
|
||||
// The type of the format must be 'qcow2'
|
||||
Type string `json:"type"`
|
||||
|
||||
// The qcow2-compatibility-version to use
|
||||
Compat string `json:"compat"`
|
||||
}
|
||||
|
||||
func (Qcow2Options) isQEMUFormatOptions() {}
|
||||
|
||||
func (QEMUStageOptions) isStageOptions() {}
|
||||
|
||||
type QEMUStageInputs struct {
|
||||
Image *QEMUStageInput `json:"image"`
|
||||
}
|
||||
|
||||
func (QEMUStageInputs) isStageInputs() {}
|
||||
|
||||
type QEMUStageInput struct {
|
||||
inputCommon
|
||||
References QEMUStageReferences `json:"references"`
|
||||
}
|
||||
|
||||
func (QEMUStageInput) isStageInput() {}
|
||||
|
||||
type QEMUStageReferences map[string]QEMUFile
|
||||
|
||||
func (QEMUStageReferences) isReferences() {}
|
||||
|
||||
type QEMUFile struct {
|
||||
Metadata FileMetadata `json:"metadata,omitempty"`
|
||||
File string `json:"file,omitempty"`
|
||||
}
|
||||
|
||||
type FileMetadata map[string]interface{}
|
||||
|
||||
// NewQEMUStage creates a new QEMU Stage object.
|
||||
func NewQEMUStage(options *QEMUStageOptions, inputs *QEMUStageInputs) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.qemu",
|
||||
Options: options,
|
||||
Inputs: inputs,
|
||||
}
|
||||
}
|
||||
|
||||
// alias for custom marshaller
|
||||
type qemuStageOptions QEMUStageOptions
|
||||
|
||||
// Custom marshaller for validating
|
||||
func (options QEMUStageOptions) MarshalJSON() ([]byte, error) {
|
||||
switch o := options.Format.(type) {
|
||||
case Qcow2Options:
|
||||
if o.Type != "qcow2" {
|
||||
return nil, fmt.Errorf("invalid format type %q for qcow2 options", o.Type)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown format options in QEMU stage: %#v", options.Format)
|
||||
}
|
||||
|
||||
return json.Marshal(qemuStageOptions(options))
|
||||
}
|
||||
51
internal/osbuild2/sfdisk_stage.go
Normal file
51
internal/osbuild2/sfdisk_stage.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package osbuild2
|
||||
|
||||
// Partition a target using sfdisk(8)
|
||||
|
||||
type SfdiskStageOptions struct {
|
||||
// The type of the partition table
|
||||
Label string `json:"label"`
|
||||
|
||||
// UUID for the disk image's partition table
|
||||
UUID string `json:"uuid"`
|
||||
|
||||
// Partition layout
|
||||
Partitions []Partition `json:"partitions,omitempty"`
|
||||
}
|
||||
|
||||
func (SfdiskStageOptions) isStageOptions() {}
|
||||
|
||||
// Description of a partition
|
||||
type Partition struct {
|
||||
// Mark the partition as bootable (dos)
|
||||
Bootable bool `json:"bootable,omitempty"`
|
||||
|
||||
// The partition name (GPT)
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The size of the partition
|
||||
Size uint64 `json:"size,omitempty"`
|
||||
|
||||
// The start offset of the partition
|
||||
Start uint64 `json:"start,omitempty"`
|
||||
|
||||
// The partition type (UUID or identifier)
|
||||
Type string `json:"type,omitempty"`
|
||||
|
||||
// UUID of the partition (GPT)
|
||||
UUID string `json:"uuid,omitempty"`
|
||||
}
|
||||
|
||||
type SfdiskStageDevices struct {
|
||||
Device Device `json:"device"`
|
||||
}
|
||||
|
||||
func (SfdiskStageDevices) isStageDevices() {}
|
||||
|
||||
func NewSfdiskStage(options *SfdiskStageOptions, devices *SfdiskStageDevices) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.sfdisk",
|
||||
Options: options,
|
||||
Devices: devices,
|
||||
}
|
||||
}
|
||||
|
|
@ -14,6 +14,8 @@ type Stage struct {
|
|||
|
||||
Inputs Inputs `json:"inputs,omitempty"`
|
||||
Options StageOptions `json:"options,omitempty"`
|
||||
Devices Devices `json:"devices,omitempty"`
|
||||
Mounts Mounts `json:"mounts,omitempty"`
|
||||
}
|
||||
|
||||
// Collection of Inputs for a Stage
|
||||
|
|
@ -55,6 +57,8 @@ type rawStage struct {
|
|||
Type string `json:"type"`
|
||||
Options json.RawMessage `json:"options"`
|
||||
Inputs json.RawMessage `json:"inputs"`
|
||||
Devices json.RawMessage `json:"devices"`
|
||||
Mounts json.RawMessage `json:"mounts"`
|
||||
}
|
||||
|
||||
// UnmarshalJSON unmarshals JSON into a Stage object. Each type of stage has
|
||||
|
|
@ -66,6 +70,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
|
|||
}
|
||||
var options StageOptions
|
||||
var inputs Inputs
|
||||
var devices Devices
|
||||
var mounts Mounts
|
||||
switch rawStage.Type {
|
||||
case "org.osbuild.authselect":
|
||||
options = new(AuthselectStageOptions)
|
||||
|
|
@ -129,6 +135,31 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
|
|||
options = new(OSTreeInitStageOptions)
|
||||
case "org.osbuild.ostree.preptree":
|
||||
options = new(OSTreePrepTreeStageOptions)
|
||||
case "org.osbuild.truncate":
|
||||
options = new(TruncateStageOptions)
|
||||
case "org.osbuild.sfdisk":
|
||||
options = new(SfdiskStageOptions)
|
||||
devices = new(SfdiskStageDevices)
|
||||
case "org.osbuild.copy":
|
||||
options = new(CopyStageOptions)
|
||||
inputs = new(CopyStageInputs)
|
||||
devices = new(CopyStageDevices)
|
||||
mounts = new(CopyStageMounts)
|
||||
case "org.osbuild.mkfs.btrfs":
|
||||
options = new(MkfsBtrfsStageOptions)
|
||||
devices = new(MkfsBtrfsStageDevices)
|
||||
case "org.osbuild.mkfs.ext4":
|
||||
options = new(MkfsExt4StageOptions)
|
||||
devices = new(MkfsExt4StageDevices)
|
||||
case "org.osbuild.mkfs.fat":
|
||||
options = new(MkfsFATStageOptions)
|
||||
devices = new(MkfsFATStageDevices)
|
||||
case "org.osbuild.mkfs.xfs":
|
||||
options = new(MkfsXfsStageOptions)
|
||||
devices = new(MkfsXfsStageDevices)
|
||||
case "org.osbuild.qemu":
|
||||
options = new(QEMUStageOptions)
|
||||
inputs = new(QEMUStageInputs)
|
||||
default:
|
||||
return fmt.Errorf("unexpected stage type: %s", rawStage.Type)
|
||||
}
|
||||
|
|
@ -144,6 +175,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
|
|||
stage.Type = rawStage.Type
|
||||
stage.Options = options
|
||||
stage.Inputs = inputs
|
||||
stage.Devices = devices
|
||||
stage.Mounts = mounts
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
20
internal/osbuild2/truncate_stage.go
Normal file
20
internal/osbuild2/truncate_stage.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package osbuild2
|
||||
|
||||
// Create, shrink, or extend a file
|
||||
|
||||
type TruncateStageOptions struct {
|
||||
// Image filename
|
||||
Filename string `json:"filename"`
|
||||
|
||||
// Desired size
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
func (TruncateStageOptions) isStageOptions() {}
|
||||
|
||||
func NewTruncateStage(options *TruncateStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.truncate",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
9
internal/osbuild2/xfs_mount.go
Normal file
9
internal/osbuild2/xfs_mount.go
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package osbuild2
|
||||
|
||||
func NewXfsMount(source, target string) *Mount {
|
||||
return &Mount{
|
||||
Type: "org.osbuild.xfs",
|
||||
Source: source,
|
||||
Target: target,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue