build(deps): bump the go-deps group across 1 directory with 10 updates

Bumps the go-deps group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [cloud.google.com/go/compute](https://github.com/googleapis/google-cloud-go) | `1.27.1` | `1.27.3` |
| [cloud.google.com/go/storage](https://github.com/googleapis/google-cloud-go) | `1.42.0` | `1.43.0` |
| [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) | `1.6.0` | `1.7.0` |
| [github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage](https://github.com/Azure/azure-sdk-for-go) | `1.5.0` | `1.6.0` |
| [github.com/aws/aws-sdk-go](https://github.com/aws/aws-sdk-go) | `1.54.10` | `1.54.18` |
| [github.com/gophercloud/gophercloud](https://github.com/gophercloud/gophercloud) | `1.12.0` | `1.13.0` |
| [github.com/openshift-online/ocm-sdk-go](https://github.com/openshift-online/ocm-sdk-go) | `0.1.425` | `0.1.429` |
| [github.com/osbuild/images](https://github.com/osbuild/images) | `0.69.0` | `0.70.0` |



Updates `cloud.google.com/go/compute` from 1.27.1 to 1.27.3
- [Release notes](https://github.com/googleapis/google-cloud-go/releases)
- [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-cloud-go/compare/pubsub/v1.27.1...compute/v1.27.3)

Updates `cloud.google.com/go/storage` from 1.42.0 to 1.43.0
- [Release notes](https://github.com/googleapis/google-cloud-go/releases)
- [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-cloud-go/compare/spanner/v1.42.0...spanner/v1.43.0)

Updates `github.com/Azure/azure-sdk-for-go/sdk/azidentity` from 1.6.0 to 1.7.0
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.6.0...sdk/azcore/v1.7.0)

Updates `github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage` from 1.5.0 to 1.6.0
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.5.0...sdk/azcore/v1.6.0)

Updates `github.com/aws/aws-sdk-go` from 1.54.10 to 1.54.18
- [Release notes](https://github.com/aws/aws-sdk-go/releases)
- [Commits](https://github.com/aws/aws-sdk-go/compare/v1.54.10...v1.54.18)

Updates `github.com/gophercloud/gophercloud` from 1.12.0 to 1.13.0
- [Release notes](https://github.com/gophercloud/gophercloud/releases)
- [Changelog](https://github.com/gophercloud/gophercloud/blob/v1.13.0/CHANGELOG.md)
- [Commits](https://github.com/gophercloud/gophercloud/compare/v1.12.0...v1.13.0)

Updates `github.com/openshift-online/ocm-sdk-go` from 0.1.425 to 0.1.429
- [Release notes](https://github.com/openshift-online/ocm-sdk-go/releases)
- [Changelog](https://github.com/openshift-online/ocm-sdk-go/blob/main/CHANGES.md)
- [Commits](https://github.com/openshift-online/ocm-sdk-go/compare/v0.1.425...v0.1.429)

Updates `github.com/osbuild/images` from 0.69.0 to 0.70.0
- [Release notes](https://github.com/osbuild/images/releases)
- [Commits](https://github.com/osbuild/images/compare/v0.69.0...v0.70.0)

Updates `golang.org/x/sys` from 0.21.0 to 0.22.0
- [Commits](https://github.com/golang/sys/compare/v0.21.0...v0.22.0)

Updates `google.golang.org/api` from 0.186.0 to 0.188.0
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.186.0...v0.188.0)

---
updated-dependencies:
- dependency-name: cloud.google.com/go/compute
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: cloud.google.com/go/storage
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/aws/aws-sdk-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: github.com/gophercloud/gophercloud
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: github.com/openshift-online/ocm-sdk-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-deps
- dependency-name: github.com/osbuild/images
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: golang.org/x/sys
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-deps
...

Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
dependabot[bot] 2024-07-11 04:55:34 +00:00 committed by Tomáš Hozza
parent c6d669f2cc
commit 4ba7085068
153 changed files with 6445 additions and 1316 deletions

View file

@ -3,11 +3,13 @@ package disk
import (
"fmt"
"math/rand"
"strings"
"reflect"
"github.com/google/uuid"
)
const DefaultBtrfsCompression = "zstd:1"
type Btrfs struct {
UUID string
Label string
@ -15,6 +17,14 @@ type Btrfs struct {
Subvolumes []BtrfsSubvolume
}
func init() {
payloadEntityMap["btrfs"] = reflect.TypeOf(Btrfs{})
}
func (b *Btrfs) EntityName() string {
return "btrfs"
}
func (b *Btrfs) IsContainer() bool {
return true
}
@ -61,6 +71,7 @@ func (b *Btrfs) CreateMountpoint(mountpoint string, size uint64) (Entity, error)
GroupID: 0,
UUID: b.UUID, // subvolumes inherit UUID of main volume
Name: name,
Compress: DefaultBtrfsCompression,
}
b.Subvolumes = append(b.Subvolumes, subvolume)
@ -75,6 +86,10 @@ func (b *Btrfs) GenUUID(rng *rand.Rand) {
if b.UUID == "" {
b.UUID = uuid.Must(newRandomUUIDFromReader(rng)).String()
}
for i := range b.Subvolumes {
b.Subvolumes[i].UUID = b.UUID
}
}
type BtrfsSubvolume struct {
@ -82,8 +97,8 @@ type BtrfsSubvolume struct {
Size uint64
Mountpoint string
GroupID uint64
MntOps string
Compress string
ReadOnly bool
// UUID of the parent volume
UUID string
@ -103,7 +118,7 @@ func (bs *BtrfsSubvolume) Clone() Entity {
Size: bs.Size,
Mountpoint: bs.Mountpoint,
GroupID: bs.GroupID,
MntOps: bs.MntOps,
Compress: bs.Compress,
UUID: bs.UUID,
}
}
@ -149,7 +164,16 @@ func (bs *BtrfsSubvolume) GetFSTabOptions() FSTabOptions {
return FSTabOptions{}
}
ops := strings.Join([]string{bs.MntOps, fmt.Sprintf("subvol=%s", bs.Name)}, ",")
if bs.Name == "" {
panic(fmt.Errorf("internal error: BtrfsSubvolume.GetFSTabOptions() for %+v called without a name", bs))
}
ops := fmt.Sprintf("subvol=%s", bs.Name)
if bs.Compress != "" {
ops += fmt.Sprintf(",compress=%s", bs.Compress)
}
if bs.ReadOnly {
ops += ",ro"
}
return FSTabOptions{
MntOps: ops,
Freq: 0,

View file

@ -20,8 +20,11 @@ import (
"encoding/hex"
"io"
"math/rand"
"reflect"
"strings"
"github.com/google/uuid"
"golang.org/x/exp/slices"
)
const (
@ -60,6 +63,15 @@ type Entity interface {
Clone() Entity
}
type PayloadEntity interface {
Entity
// EntityName is the type name of the Entity, used for marshaling
EntityName() string
}
var payloadEntityMap = map[string]reflect.Type{}
// Container is the interface for entities that can contain other entities.
// Together with the base Entity interface this allows to model a generic
// entity tree of theoretically arbitrary depth and width.
@ -143,6 +155,16 @@ type FSTabOptions struct {
PassNo uint64
}
// ReadOnly returns true is the filesystem is mounted read-only
func (o FSTabOptions) ReadOnly() bool {
opts := strings.Split(o.MntOps, ",")
// filesystem is mounted read-only if:
// - there's ro (because rw is the default)
// - AND there's no rw (because rw overrides ro)
return slices.Contains(opts, "ro") && !slices.Contains(opts, "rw")
}
// uuid generator helpers
// GeneratesnewRandomUUIDFromReader generates a new random UUID (version

View file

@ -2,6 +2,7 @@ package disk
import (
"math/rand"
"reflect"
"github.com/google/uuid"
)
@ -22,6 +23,14 @@ type Filesystem struct {
FSTabPassNo uint64
}
func init() {
payloadEntityMap["filesystem"] = reflect.TypeOf(Filesystem{})
}
func (fs *Filesystem) EntityName() string {
return "filesystem"
}
func (fs *Filesystem) IsContainer() bool {
return false
}

View file

@ -3,6 +3,7 @@ package disk
import (
"fmt"
"math/rand"
"reflect"
"github.com/google/uuid"
@ -36,6 +37,14 @@ type LUKSContainer struct {
Payload Entity
}
func init() {
payloadEntityMap["luks"] = reflect.TypeOf(LUKSContainer{})
}
func (lc *LUKSContainer) EntityName() string {
return "luks"
}
func (lc *LUKSContainer) IsContainer() bool {
return true
}

View file

@ -2,6 +2,7 @@ package disk
import (
"fmt"
"reflect"
"strings"
"github.com/osbuild/images/internal/common"
@ -18,6 +19,14 @@ type LVMVolumeGroup struct {
LogicalVolumes []LVMLogicalVolume
}
func init() {
payloadEntityMap["lvm"] = reflect.TypeOf(LVMVolumeGroup{})
}
func (vg *LVMVolumeGroup) EntityName() string {
return "lvm"
}
func (vg *LVMVolumeGroup) IsContainer() bool {
return true
}

View file

@ -1,7 +1,10 @@
package disk
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
)
type Partition struct {
@ -15,7 +18,7 @@ type Partition struct {
UUID string
// If nil, the partition is raw; It doesn't contain a payload.
Payload Entity
Payload PayloadEntity
}
func (p *Partition) IsContainer() bool {
@ -36,7 +39,7 @@ func (p *Partition) Clone() Entity {
}
if p.Payload != nil {
partition.Payload = p.Payload.Clone()
partition.Payload = p.Payload.Clone().(PayloadEntity)
}
return partition
@ -85,3 +88,53 @@ func (p *Partition) IsPReP() bool {
return p.Type == "41" || p.Type == PRePartitionGUID
}
func (p *Partition) MarshalJSON() ([]byte, error) {
type partAlias Partition
entityName := "no-payload"
if p.Payload != nil {
entityName = p.Payload.EntityName()
}
partWithPayloadType := struct {
partAlias
PayloadType string
}{
partAlias(*p),
entityName,
}
return json.Marshal(partWithPayloadType)
}
func (p *Partition) UnmarshalJSON(data []byte) error {
type partAlias Partition
var partWithoutPayload struct {
partAlias
Payload json.RawMessage
PayloadType string
}
dec := json.NewDecoder(bytes.NewBuffer(data))
if err := dec.Decode(&partWithoutPayload); err != nil {
return fmt.Errorf("cannot build partition from %q: %w", data, err)
}
*p = Partition(partWithoutPayload.partAlias)
// no payload, e.g. bios partiton
if partWithoutPayload.PayloadType == "no-payload" {
return nil
}
entType := payloadEntityMap[partWithoutPayload.PayloadType]
if entType == nil {
return fmt.Errorf("cannot build partition from %q", data)
}
entValP := reflect.New(entType).Elem().Addr()
ent := entValP.Interface()
if err := json.Unmarshal(partWithoutPayload.Payload, &ent); err != nil {
return err
}
p.Payload = ent.(PayloadEntity)
return nil
}

View file

@ -36,6 +36,9 @@ const (
// RawPartitioningMode always creates a raw layout.
RawPartitioningMode PartitioningMode = "raw"
// BtrfsPartitioningMode creates a btrfs layout.
BtfrsPartitioningMode PartitioningMode = "btrfs"
// DefaultPartitioningMode is AutoLVMPartitioningMode and is the empty state
DefaultPartitioningMode PartitioningMode = ""
)
@ -43,14 +46,14 @@ const (
func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.FilesystemCustomization, imageSize uint64, mode PartitioningMode, requiredSizes map[string]uint64, rng *rand.Rand) (*PartitionTable, error) {
newPT := basePT.Clone().(*PartitionTable)
if basePT.features().LVM && mode == RawPartitioningMode {
return nil, fmt.Errorf("raw partitioning mode set for a base partition table with LVM, this is unsupported")
if basePT.features().LVM && (mode == RawPartitioningMode || mode == BtfrsPartitioningMode) {
return nil, fmt.Errorf("%s partitioning mode set for a base partition table with LVM, this is unsupported", mode)
}
// first pass: enlarge existing mountpoints and collect new ones
newMountpoints, _ := newPT.applyCustomization(mountpoints, false)
var ensureLVM bool
var ensureLVM, ensureBtrfs bool
switch mode {
case LVMPartitioningMode:
ensureLVM = true
@ -58,6 +61,8 @@ func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.Filesyste
ensureLVM = false
case DefaultPartitioningMode, AutoLVMPartitioningMode:
ensureLVM = len(newMountpoints) > 0
case BtfrsPartitioningMode:
ensureBtrfs = true
default:
return nil, fmt.Errorf("unsupported partitioning mode %q", mode)
}
@ -66,6 +71,11 @@ func NewPartitionTable(basePT *PartitionTable, mountpoints []blueprint.Filesyste
if err != nil {
return nil, err
}
} else if ensureBtrfs {
err := newPT.ensureBtrfs()
if err != nil {
return nil, err
}
}
// second pass: deal with new mountpoints and newly created ones, after switching to
@ -678,6 +688,67 @@ func (pt *PartitionTable) ensureLVM() error {
return nil
}
// ensureBtrfs will ensure that the root partition is on a btrfs subvolume, i.e. if
// it currently is not, it will wrap it in one
func (pt *PartitionTable) ensureBtrfs() error {
rootPath := entityPath(pt, "/")
if rootPath == nil {
return fmt.Errorf("no root mountpoint for a partition table: %#v", pt)
}
// we need a /boot partition to boot btrfs, ensure one exists
bootPath := entityPath(pt, "/boot")
if bootPath == nil {
_, err := pt.CreateMountpoint("/boot", 512*common.MiB)
if err != nil {
return fmt.Errorf("failed to create /boot partition when ensuring btrfs: %w", err)
}
rootPath = entityPath(pt, "/")
}
parent := rootPath[1] // NB: entityPath has reversed order
if _, ok := parent.(*Btrfs); ok {
return nil
} else if part, ok := parent.(*Partition); ok {
rootMountable, ok := rootPath[0].(Mountable)
if !ok {
return fmt.Errorf("root entity is not mountable: %T, this is a violation of entityPath() contract", rootPath[0])
}
btrfs := &Btrfs{
Label: "root",
Subvolumes: []BtrfsSubvolume{
{
Name: "root",
Mountpoint: "/",
Compress: DefaultBtrfsCompression,
ReadOnly: rootMountable.GetFSTabOptions().ReadOnly(),
},
},
}
// replace the top-level partition payload with a new btrfs filesystem
part.Payload = btrfs
// reset the btrfs partition size - it will be grown later
part.Size = 0
if pt.Type == "gpt" {
part.Type = FilesystemDataGUID
} else {
part.Type = "83"
}
} else {
return fmt.Errorf("unsupported parent for btrfs: %T", parent)
}
return nil
}
type partitionTableFeatures struct {
LVM bool
Btrfs bool

View file

@ -76,11 +76,6 @@ func (img *AnacondaContainerInstaller) InstantiateManifest(m *manifest.Manifest,
anacondaPipeline.ExtraPackages = img.ExtraBasePackages.Include
anacondaPipeline.ExcludePackages = img.ExtraBasePackages.Exclude
anacondaPipeline.ExtraRepos = img.ExtraBasePackages.Repositories
anacondaPipeline.InteractiveDefaultsKickstart = &kickstart.Options{
Users: img.Kickstart.Users,
Groups: img.Kickstart.Groups,
Path: osbuild.KickstartPathOSBuild,
}
anacondaPipeline.Variant = img.Variant
anacondaPipeline.Biosdevname = (img.Platform.GetArch() == arch.ARCH_X86_64)
anacondaPipeline.Checkpoint()
@ -102,11 +97,14 @@ func (img *AnacondaContainerInstaller) InstantiateManifest(m *manifest.Manifest,
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
bootTreePipeline.ISOLabel = img.ISOLabel
ksPath := osbuild.KickstartPathOSBuild
if img.Kickstart != nil && img.Kickstart.Path != "" {
ksPath = img.Kickstart.Path
if img.Kickstart == nil {
img.Kickstart = &kickstart.Options{}
}
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, ksPath)}
if img.Kickstart.Path == "" {
img.Kickstart.Path = osbuild.KickstartPathOSBuild
}
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, img.Kickstart.Path)}
if img.FIPS {
bootTreePipeline.KernelOpts = append(bootTreePipeline.KernelOpts, "fips=1")
}

View file

@ -96,11 +96,13 @@ func (img *AnacondaOSTreeInstaller) InstantiateManifest(m *manifest.Manifest,
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
bootTreePipeline.ISOLabel = img.ISOLabel
ksPath := osbuild.KickstartPathOSBuild
if img.Kickstart != nil && img.Kickstart.Path != "" {
ksPath = img.Kickstart.Path
if img.Kickstart == nil || img.Kickstart.OSTree == nil {
return nil, fmt.Errorf("kickstart options not set for ostree installer")
}
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, ksPath)}
if img.Kickstart.Path == "" {
img.Kickstart.Path = osbuild.KickstartPathOSBuild
}
bootTreePipeline.KernelOpts = []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel), fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, img.Kickstart.Path)}
if img.FIPS {
bootTreePipeline.KernelOpts = append(bootTreePipeline.KernelOpts, "fips=1")
}

View file

@ -85,12 +85,24 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
buildPipeline := manifest.NewBuild(m, runner, repos, nil)
buildPipeline.Checkpoint()
if img.Kickstart != nil && img.Kickstart.Unattended {
if img.Kickstart == nil {
img.Kickstart = &kickstart.Options{}
}
if img.Kickstart.Unattended {
// if we're building an unattended installer, override the
// ISORootKickstart option
img.ISORootKickstart = true
}
if img.ISORootKickstart {
// kickstart file will be in the iso root and not interactive-defaults,
// so let's make sure the kickstart path option is set
if img.Kickstart.Path == "" {
img.Kickstart.Path = osbuild.KickstartPathOSBuild
}
}
anacondaPipeline := manifest.NewAnacondaInstaller(
manifest.AnacondaInstallerTypePayload,
buildPipeline,
@ -109,7 +121,6 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
anacondaPipeline.InteractiveDefaultsKickstart = &kickstart.Options{
Users: img.Kickstart.Users,
Groups: img.Kickstart.Groups,
Path: osbuild.KickstartPathOSBuild,
}
}
anacondaPipeline.Variant = img.Variant
@ -141,14 +152,9 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
bootTreePipeline.UEFIVendor = img.Platform.GetUEFIVendor()
bootTreePipeline.ISOLabel = img.ISOLabel
kspath := osbuild.KickstartPathOSBuild
kernelOpts := []string{fmt.Sprintf("inst.stage2=hd:LABEL=%s", img.ISOLabel)}
if img.ISORootKickstart {
ksPath := osbuild.KickstartPathOSBuild
if img.Kickstart != nil && img.Kickstart.Path != "" {
ksPath = img.Kickstart.Path
}
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, ksPath))
kernelOpts = append(kernelOpts, fmt.Sprintf("inst.ks=hd:LABEL=%s:%s", img.ISOLabel, img.Kickstart.Path))
}
if img.OSCustomizations.FIPS {
kernelOpts = append(kernelOpts, "fips=1")
@ -171,7 +177,7 @@ func (img *AnacondaTarInstaller) InstantiateManifest(m *manifest.Manifest,
isoTreePipeline.Kickstart = img.Kickstart
isoTreePipeline.PayloadPath = tarPath
if img.ISORootKickstart {
isoTreePipeline.Kickstart.Path = kspath
isoTreePipeline.Kickstart.Path = img.Kickstart.Path
}
isoTreePipeline.SquashfsCompression = img.SquashfsCompression

View file

@ -41,8 +41,7 @@ func (opts *BootupdStageOptions) validate(devices map[string]Device) error {
// to find all the bootloader configs
func validateBootupdMounts(mounts []Mount, pf platform.Platform) error {
requiredMounts := map[string]bool{
"/": true,
"/boot": true,
"/": true,
}
if pf.GetUEFIVendor() != "" {
requiredMounts["/boot/efi"] = true
@ -99,6 +98,15 @@ func genMountsForBootupd(source string, pt *disk.PartitionTable) ([]Mount, error
}
mount.Partition = common.ToPtr(idx + 1)
mounts = append(mounts, *mount)
case *disk.Btrfs:
for i := range payload.Subvolumes {
mount, err := genOsbuildMount(source, &payload.Subvolumes[i])
if err != nil {
return nil, err
}
mount.Partition = common.ToPtr(idx + 1)
mounts = append(mounts, *mount)
}
default:
return nil, fmt.Errorf("type %T not supported by bootupd handling yet", part.Payload)
}

View file

@ -1,10 +1,21 @@
package osbuild
func NewBtrfsMount(name, source, target string) *Mount {
type BtrfsMountOptions struct {
Subvol string `json:"subvol,omitempty"`
Compress string `json:"compress,omitempty"`
}
func (b BtrfsMountOptions) isMountOptions() {}
func NewBtrfsMount(name, source, target, subvol, compress string) *Mount {
return &Mount{
Type: "org.osbuild.btrfs",
Name: name,
Source: source,
Target: target,
Options: BtrfsMountOptions{
Subvol: subvol,
Compress: compress,
},
}
}

View file

@ -0,0 +1,73 @@
package osbuild
import (
"github.com/osbuild/images/pkg/disk"
)
type BtrfsSubVolOptions struct {
Subvolumes []BtrfsSubVol `json:"subvolumes"`
}
type BtrfsSubVol struct {
Name string `json:"name"`
}
func (BtrfsSubVolOptions) isStageOptions() {}
func NewBtrfsSubVol(options *BtrfsSubVolOptions, devices *map[string]Device, mounts *[]Mount) *Stage {
return &Stage{
Type: "org.osbuild.btrfs.subvol",
Options: options,
Devices: *devices,
Mounts: *mounts,
}
}
func GenBtrfsSubVolStage(filename string, pt *disk.PartitionTable) *Stage {
var subvolumes []BtrfsSubVol
genStage := func(mnt disk.Mountable, path []disk.Entity) error {
if mnt.GetFSType() != "btrfs" {
return nil
}
btrfs := mnt.(*disk.BtrfsSubvolume)
subvolumes = append(subvolumes, BtrfsSubVol{Name: "/" + btrfs.Name})
return nil
}
_ = pt.ForEachMountable(genStage)
if len(subvolumes) == 0 {
return nil
}
devices, mounts := genBtrfsMountDevices(filename, pt)
return NewBtrfsSubVol(&BtrfsSubVolOptions{subvolumes}, devices, mounts)
}
func genBtrfsMountDevices(filename string, pt *disk.PartitionTable) (*map[string]Device, *[]Mount) {
devices := make(map[string]Device, len(pt.Partitions))
mounts := make([]Mount, 0, len(pt.Partitions))
genMounts := func(ent disk.Entity, path []disk.Entity) error {
if _, isBtrfs := ent.(*disk.Btrfs); !isBtrfs {
return nil
}
stageDevices, name := getDevices(path, filename, false)
mounts = append(mounts, *NewBtrfsMount(name, name, "/", "", ""))
// update devices map with new elements from stageDevices
for devName := range stageDevices {
devices[devName] = stageDevices[devName]
}
return nil
}
_ = pt.ForEachEntity(genMounts)
return &devices, &mounts
}

View file

@ -160,6 +160,8 @@ func deviceName(p disk.Entity) string {
return payload.Name
case *disk.LVMLogicalVolume:
return payload.Name
case *disk.Btrfs:
return "btrfs-" + payload.UUID[:4]
}
panic(fmt.Sprintf("unsupported device type in deviceName: '%T'", p))
}
@ -254,7 +256,11 @@ func genOsbuildMount(source string, mnt disk.Mountable) (*Mount, error) {
case "ext4":
return NewExt4Mount(name, source, mountpoint), nil
case "btrfs":
return NewBtrfsMount(name, source, mountpoint), nil
if subvol, isSubvol := mnt.(*disk.BtrfsSubvolume); isSubvol {
return NewBtrfsMount(name, source, mountpoint, subvol.Name, subvol.Compress), nil
} else {
return nil, fmt.Errorf("mounting bare btrfs partition is unsupported: %s", mountpoint)
}
default:
return nil, fmt.Errorf("unknown fs type " + t)
}

View file

@ -4,6 +4,7 @@ import (
"fmt"
"github.com/google/uuid"
"github.com/osbuild/images/pkg/disk"
)
@ -102,6 +103,11 @@ func GenImagePrepareStages(pt *disk.PartitionTable, filename string, partTool Pa
s = GenMkfsStages(pt, filename)
stages = append(stages, s...)
subvolStage := GenBtrfsSubVolStage(filename, pt)
if subvolStage != nil {
stages = append(stages, subvolStage)
}
return stages
}
@ -117,6 +123,11 @@ func GenImageKernelOptions(pt *disk.PartitionTable) []string {
case *disk.LUKSContainer:
karg := "luks.uuid=" + ent.UUID
cmdline = append(cmdline, karg)
case *disk.BtrfsSubvolume:
if ent.Mountpoint == "/" {
karg := "rootflags=subvol=" + ent.Name
cmdline = append(cmdline, karg)
}
}
return nil
}

View file

@ -1,5 +1,9 @@
package osbuild
import "fmt"
const grubisoStageType = "org.osbuild.grub2.iso"
type GrubISOStageOptions struct {
Product Product `json:"product"`
@ -14,6 +18,30 @@ type GrubISOStageOptions struct {
func (GrubISOStageOptions) isStageOptions() {}
func (o GrubISOStageOptions) validate() error {
// The stage schema marks product.name, product.version, kernel.dir, and
// isolabel as required. Empty values are technically valid according to
// the schema, but here we will consider them invalid.
if o.Product.Name == "" {
return fmt.Errorf("%s: product.name option is required", grubisoStageType)
}
if o.Product.Version == "" {
return fmt.Errorf("%s: product.version option is required", grubisoStageType)
}
if o.Kernel.Dir == "" {
return fmt.Errorf("%s: kernel.dir option is required", grubisoStageType)
}
if o.ISOLabel == "" {
return fmt.Errorf("%s: isolabel option is required", grubisoStageType)
}
return nil
}
type ISOKernel struct {
Dir string `json:"dir"`
@ -23,8 +51,11 @@ type ISOKernel struct {
// Assemble a file system tree for a bootable ISO
func NewGrubISOStage(options *GrubISOStageOptions) *Stage {
if err := options.validate(); err != nil {
panic(err)
}
return &Stage{
Type: "org.osbuild.grub2.iso",
Type: grubisoStageType,
Options: options,
}
}

View file

@ -1,6 +1,7 @@
package osbuild
import (
"fmt"
"strings"
"github.com/osbuild/images/pkg/disk"
@ -12,6 +13,7 @@ import (
func GenMkfsStages(pt *disk.PartitionTable, filename string) []*Stage {
stages := make([]*Stage, 0, len(pt.Partitions))
processedBtrfsPartitions := make(map[string]bool)
genStage := func(mnt disk.Mountable, path []disk.Entity) error {
t := mnt.GetFSType()
var stage *Stage
@ -38,9 +40,23 @@ func GenMkfsStages(pt *disk.PartitionTable, filename string) []*Stage {
}
stage = NewMkfsFATStage(options, stageDevices)
case "btrfs":
// the disk library allows only subvolumes as Mountable, so we need to find the underlying btrfs partition
// and mkfs it
btrfsPart := findBtrfsPartition(path)
if btrfsPart == nil {
panic(fmt.Sprintf("found btrfs subvolume without btrfs partition: %s", mnt.GetMountpoint()))
}
// btrfs partitions can be shared between multiple subvolumes, so we need to make sure we only create
// one
if processedBtrfsPartitions[btrfsPart.UUID] {
return nil
}
processedBtrfsPartitions[btrfsPart.UUID] = true
options := &MkfsBtrfsStageOptions{
UUID: fsSpec.UUID,
Label: fsSpec.Label,
UUID: btrfsPart.UUID,
Label: btrfsPart.Label,
}
stage = NewMkfsBtrfsStage(options, stageDevices)
case "ext4":
@ -60,3 +76,12 @@ func GenMkfsStages(pt *disk.PartitionTable, filename string) []*Stage {
_ = pt.ForEachMountable(genStage) // genStage always returns nil
return stages
}
func findBtrfsPartition(path []disk.Entity) *disk.Btrfs {
for _, e := range path {
if btrfsPartition, ok := e.(*disk.Btrfs); ok {
return btrfsPartition
}
}
return nil
}