-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for the new `org.osbuild.bootupd` stage that got added in osbuild/osbuild#1519
- Loading branch information
Showing
2 changed files
with
220 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package osbuild | ||
|
||
import ( | ||
"fmt" | ||
"sort" | ||
) | ||
|
||
type BootupdStageOptionsBios struct { | ||
Device string `json:"device"` | ||
Partition int `json:"partition,omitempty"` | ||
} | ||
|
||
type BootupdStageOptions struct { | ||
Deployment *OSTreeDeployment `json:"deployment,omitempty"` | ||
StaticConfigs bool `json:"static-configs"` | ||
Bios *BootupdStageOptionsBios `json:"bios,omitempty"` | ||
} | ||
|
||
func (BootupdStageOptions) isStageOptions() {} | ||
|
||
func (opts *BootupdStageOptions) validate(devices map[string]Device) error { | ||
if opts.Bios != nil && opts.Bios.Device != "" { | ||
if _, ok := devices[opts.Bios.Device]; !ok { | ||
var devnames []string | ||
for devname := range devices { | ||
devnames = append(devnames, devname) | ||
} | ||
sort.Strings(devnames) | ||
return fmt.Errorf("cannot find expected device %q for bootupd bios option in %v", opts.Bios.Device, devnames) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// validateBootupdMounts ensures that all required mounts for the bootup | ||
// stage are generated. Right now the stage requires root, boot and boot/efi | ||
// to find all the bootloader configs | ||
func validateBootupdMounts(mounts []Mount) error { | ||
requiredMounts := map[string]bool{ | ||
"/": true, | ||
"/boot": true, | ||
"/boot/efi": true, | ||
} | ||
for _, mnt := range mounts { | ||
delete(requiredMounts, mnt.Target) | ||
} | ||
if len(requiredMounts) != 0 { | ||
var missingMounts []string | ||
for mnt := range requiredMounts { | ||
missingMounts = append(missingMounts, mnt) | ||
} | ||
sort.Strings(missingMounts) | ||
return fmt.Errorf("required mounts for bootupd stage %v missing", missingMounts) | ||
} | ||
return nil | ||
} | ||
|
||
// NewBootupdStage creates a new stage for the org.osbuild.bootupd stage. It | ||
// requires a mount setup of "/", "/boot" and "/boot/efi" right now so that | ||
// bootupd can find and install all required bootloader bits. | ||
func NewBootupdStage(opts *BootupdStageOptions, devices *Devices, mounts *Mounts) (*Stage, error) { | ||
if err := validateBootupdMounts(*mounts); err != nil { | ||
return nil, err | ||
} | ||
if err := opts.validate(*devices); err != nil { | ||
return nil, err | ||
} | ||
|
||
return &Stage{ | ||
Type: "org.osbuild.bootupd", | ||
Options: opts, | ||
Devices: *devices, | ||
Mounts: *mounts, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
package osbuild_test | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/osbuild/images/pkg/osbuild" | ||
) | ||
|
||
func makeOsbuildMounts(targets ...string) osbuild.Mounts { | ||
var mnts []osbuild.Mount | ||
for _, target := range targets { | ||
mnts = append(mnts, osbuild.Mount{ | ||
Type: "org.osbuild.ext4", | ||
Name: "mnt-" + target, | ||
Source: "dev-" + target, | ||
Target: target, | ||
}) | ||
} | ||
return mnts | ||
} | ||
|
||
func makeOsbuildDevices(devnames ...string) osbuild.Devices { | ||
devices := make(map[string]osbuild.Device) | ||
for _, devname := range devnames { | ||
devices[devname] = osbuild.Device{ | ||
Type: "orgosbuild.loopback", | ||
} | ||
} | ||
return devices | ||
} | ||
|
||
func TestBootupdStageNewHappy(t *testing.T) { | ||
opts := &osbuild.BootupdStageOptions{ | ||
StaticConfigs: true, | ||
} | ||
devices := makeOsbuildDevices("dev-/", "dev-/boot", "dev-/boot/efi") | ||
mounts := makeOsbuildMounts("/", "/boot", "/boot/efi") | ||
|
||
expectedStage := &osbuild.Stage{ | ||
Type: "org.osbuild.bootupd", | ||
Options: opts, | ||
Devices: devices, | ||
Mounts: mounts, | ||
} | ||
stage, err := osbuild.NewBootupdStage(opts, &devices, &mounts) | ||
require.Nil(t, err) | ||
assert.Equal(t, stage, expectedStage) | ||
} | ||
|
||
func TestBootupdStageMissingMounts(t *testing.T) { | ||
opts := &osbuild.BootupdStageOptions{ | ||
StaticConfigs: true, | ||
} | ||
devices := makeOsbuildDevices("dev-/") | ||
mounts := makeOsbuildMounts("/") | ||
|
||
stage, err := osbuild.NewBootupdStage(opts, &devices, &mounts) | ||
assert.ErrorContains(t, err, "required mounts for bootupd stage [/boot /boot/efi] missing") | ||
require.Nil(t, stage) | ||
} | ||
|
||
func TestBootupdStageMissingDevice(t *testing.T) { | ||
opts := &osbuild.BootupdStageOptions{ | ||
Bios: &osbuild.BootupdStageOptionsBios{ | ||
Device: "disk", | ||
}, | ||
} | ||
devices := makeOsbuildDevices("dev-/", "dev-/boot", "dev-/boot/efi") | ||
mounts := makeOsbuildMounts("/", "/boot", "/boot/efi") | ||
|
||
stage, err := osbuild.NewBootupdStage(opts, &devices, &mounts) | ||
assert.ErrorContains(t, err, `cannot find expected device "disk" for bootupd bios option in [dev-/ dev-/boot dev-/boot/efi]`) | ||
require.Nil(t, stage) | ||
} | ||
|
||
func TestBootupdStageJsonHappy(t *testing.T) { | ||
opts := &osbuild.BootupdStageOptions{ | ||
Deployment: &osbuild.OSTreeDeployment{ | ||
OSName: "default", | ||
Ref: "ostree/1/1/0", | ||
}, | ||
StaticConfigs: true, | ||
Bios: &osbuild.BootupdStageOptionsBios{ | ||
Device: "disk", | ||
}, | ||
} | ||
devices := makeOsbuildDevices("disk", "dev-/", "dev-/boot", "dev-/boot/efi") | ||
mounts := makeOsbuildMounts("/", "/boot", "/boot/efi") | ||
|
||
stage, err := osbuild.NewBootupdStage(opts, &devices, &mounts) | ||
require.Nil(t, err) | ||
stageJson, err := json.MarshalIndent(stage, "", " ") | ||
require.Nil(t, err) | ||
assert.Equal(t, string(stageJson), `{ | ||
"type": "org.osbuild.bootupd", | ||
"options": { | ||
"deployment": { | ||
"osname": "default", | ||
"ref": "ostree/1/1/0" | ||
}, | ||
"static-configs": true, | ||
"bios": { | ||
"device": "disk" | ||
} | ||
}, | ||
"devices": { | ||
"dev-/": { | ||
"type": "orgosbuild.loopback" | ||
}, | ||
"dev-/boot": { | ||
"type": "orgosbuild.loopback" | ||
}, | ||
"dev-/boot/efi": { | ||
"type": "orgosbuild.loopback" | ||
}, | ||
"disk": { | ||
"type": "orgosbuild.loopback" | ||
} | ||
}, | ||
"mounts": [ | ||
{ | ||
"name": "mnt-/", | ||
"type": "org.osbuild.ext4", | ||
"source": "dev-/", | ||
"target": "/" | ||
}, | ||
{ | ||
"name": "mnt-/boot", | ||
"type": "org.osbuild.ext4", | ||
"source": "dev-/boot", | ||
"target": "/boot" | ||
}, | ||
{ | ||
"name": "mnt-/boot/efi", | ||
"type": "org.osbuild.ext4", | ||
"source": "dev-/boot/efi", | ||
"target": "/boot/efi" | ||
} | ||
] | ||
}`) | ||
} |