Skip to content

Commit

Permalink
feat: implement revert for sd-boot
Browse files Browse the repository at this point in the history
Implement revert for sd-boot.

Signed-off-by: Noel Georgi <git@frezbo.dev>
  • Loading branch information
frezbo committed Jun 22, 2023
1 parent d8b0903 commit e3f3f57
Show file tree
Hide file tree
Showing 16 changed files with 276 additions and 176 deletions.
2 changes: 0 additions & 2 deletions .drone.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -436,8 +436,6 @@ local default_pipeline_steps = [
local integration_qemu = Step('e2e-qemu', privileged=true, depends_on=[load_artifacts], environment={ IMAGE_REGISTRY: local_registry });

local integration_qemu_trusted_boot = Step('e2e-qemu-trusted-boot', target='e2e-qemu', privileged=true, depends_on=[load_artifacts], environment={
// TODO: frezbo: do we need the full suite here?
SHORT_INTEGRATION_TEST: 'yes',
IMAGE_REGISTRY: local_registry,
VIA_MAINTENANCE_MODE: "true",
WITH_TRUSTED_BOOT: "true",
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,7 @@ RUN chmod +x /rootfs/sbin/dashboard
# symlinks to avoid accidentally cleaning them up.
COPY ./hack/cleanup.sh /toolchain/bin/cleanup.sh
RUN cleanup.sh /rootfs
RUN mkdir -pv /rootfs/{boot,etc/cri/conf.d/hosts,lib/firmware,usr/local/share,usr/share/zoneinfo/Etc,mnt,system,opt}
RUN mkdir -pv /rootfs/{boot/EFI,etc/cri/conf.d/hosts,lib/firmware,usr/local/share,usr/share/zoneinfo/Etc,mnt,system,opt}
COPY --chmod=0644 hack/zoneinfo/Etc/UTC /rootfs/usr/share/zoneinfo/Etc/UTC
RUN ln -s /usr/share/zoneinfo/Etc/UTC /rootfs/etc/localtime
COPY --chmod=0644 hack/nfsmount.conf /rootfs/etc/nfsmount.conf
Expand Down
3 changes: 2 additions & 1 deletion cmd/installer/pkg/iso.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/makefs"
"github.com/siderolabs/talos/pkg/version"
)

const (
Expand Down Expand Up @@ -107,7 +108,7 @@ func CreateUKIISO(iso, dir, arch string) error {
return err
}

if _, err := cmd.Run("mcopy", "-i", efiBootImg, filepath.Join(dir, "vmlinuz.efi.signed"), "::EFI/Linux/talos-A.efi"); err != nil {
if _, err := cmd.Run("mcopy", "-i", efiBootImg, filepath.Join(dir, "vmlinuz.efi.signed"), fmt.Sprintf("::EFI/Linux/Talos-%s.efi", version.Tag)); err != nil {
return err
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,22 @@ package grub
import (
"fmt"
"strings"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader"
)

// flipBootLabel flips the boot label.
func flipBootLabel(e BootLabel) (BootLabel, error) {
switch e {
case BootA:
return BootB, nil
case BootB:
return BootA, nil
case BootReset:
fallthrough
default:
return "", fmt.Errorf("invalid entry: %s", e)
}
}

// Flip flips the default boot label.
func (c *Config) flip() error {
if _, exists := c.Entries[c.Default]; !exists {
Expand All @@ -19,7 +31,7 @@ func (c *Config) flip() error {

current := c.Default

next, err := bootloader.FlipBootLabel(c.Default)
next, err := flipBootLabel(c.Default)
if err != nil {
return err
}
Expand All @@ -35,15 +47,15 @@ func (c *Config) PreviousLabel() string {
return string(c.Fallback)
}

// ParseBootLabel parses the given human-readable boot label to a bootloader.BootLabel.
func ParseBootLabel(name string) (bootloader.BootLabel, error) {
// ParseBootLabel parses the given human-readable boot label to a BootLabel.
func ParseBootLabel(name string) (BootLabel, error) {
switch {
case strings.HasPrefix(name, string(bootloader.BootA)):
return bootloader.BootA, nil
case strings.HasPrefix(name, string(bootloader.BootB)):
return bootloader.BootB, nil
case strings.HasPrefix(name, string(BootA)):
return BootA, nil
case strings.HasPrefix(name, string(BootB)):
return BootB, nil
case strings.HasPrefix(name, "Reset"):
return bootloader.BootReset, nil
return BootReset, nil
default:
return "", fmt.Errorf("could not parse boot entry from name: %s", name)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,18 @@ import (
"github.com/siderolabs/talos/pkg/machinery/constants"
)

// BootLabel represents a boot label, e.g. A or B.
type BootLabel string

const (
// ConfigPath is the path to the grub config.
ConfigPath = constants.BootMountPoint + "/grub/grub.cfg"
// BootA is a bootloader label.
BootA BootLabel = "A"
// BootB is a bootloader label.
BootB BootLabel = "B"
// BootReset is a bootloader label.
BootReset BootLabel = "Reset"
)

const (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import (
"fmt"
"os"
"regexp"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader"
)

var (
Expand Down Expand Up @@ -47,7 +45,7 @@ func Decode(c []byte) (*Config, error) {
return nil, fmt.Errorf("found multiple fallback entries")
}

var fallbackEntry bootloader.BootLabel
var fallbackEntry BootLabel

if len(fallbackEntryMatches) == 1 {
if len(fallbackEntryMatches[0]) != 2 {
Expand Down Expand Up @@ -85,8 +83,8 @@ func Decode(c []byte) (*Config, error) {
return &conf, nil
}

func parseEntries(conf []byte) (map[bootloader.BootLabel]MenuEntry, error) {
entries := make(map[bootloader.BootLabel]MenuEntry)
func parseEntries(conf []byte) (map[BootLabel]MenuEntry, error) {
entries := make(map[BootLabel]MenuEntry)

matches := menuEntryRegex.FindAllSubmatch(conf, -1)
for _, m := range matches {
Expand All @@ -108,7 +106,7 @@ func parseEntries(conf []byte) (map[bootloader.BootLabel]MenuEntry, error) {
return nil, err
}

if bootEntry == bootloader.BootReset {
if bootEntry == BootReset {
continue
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@ import (
"fmt"
"path/filepath"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/version"
)

// Config represents a grub configuration file (grub.cfg).
type Config struct {
Default bootloader.BootLabel
Fallback bootloader.BootLabel
Entries map[bootloader.BootLabel]MenuEntry
Default BootLabel
Fallback BootLabel
Entries map[BootLabel]MenuEntry
}

// MenuEntry represents a grub menu entry in the grub config file.
Expand All @@ -36,8 +35,8 @@ func (e bootloaderNotInstalledError) Error() string {
// NewConfig creates a new grub configuration (nothing is written to disk).
func NewConfig() *Config {
return &Config{
Default: bootloader.BootA,
Entries: map[bootloader.BootLabel]MenuEntry{},
Default: BootA,
Entries: map[BootLabel]MenuEntry{},
}
}

Expand All @@ -48,7 +47,7 @@ func (c *Config) UEFIBoot() bool {
}

// Put puts a new menu entry to the grub config (nothing is written to disk).
func (c *Config) Put(entry bootloader.BootLabel, cmdline string) error {
func (c *Config) Put(entry BootLabel, cmdline string) error {
c.Entries[entry] = buildMenuEntry(entry, cmdline)

return nil
Expand All @@ -72,7 +71,7 @@ func (c *Config) validate() error {
return nil
}

func buildMenuEntry(entry bootloader.BootLabel, cmdline string) MenuEntry {
func buildMenuEntry(entry BootLabel, cmdline string) MenuEntry {
return MenuEntry{
Name: fmt.Sprintf("%s - %s", entry, version.Short()),
Linux: filepath.Join("/", string(entry), constants.KernelAsset),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/bootloader"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub"
"github.com/siderolabs/talos/pkg/version"
)
Expand All @@ -35,18 +34,18 @@ func TestDecode(t *testing.T) {
conf, err := grub.Decode(grubCfg)
assert.NoError(t, err)

assert.Equal(t, bootloader.BootA, conf.Default)
assert.Equal(t, bootloader.BootB, conf.Fallback)
assert.Equal(t, grub.BootA, conf.Default)
assert.Equal(t, grub.BootB, conf.Fallback)

assert.Len(t, conf.Entries, 2)

a := conf.Entries[bootloader.BootA]
a := conf.Entries[grub.BootA]
assert.Equal(t, "A - v1", a.Name)
assert.True(t, strings.HasPrefix(a.Linux, "/A/"))
assert.True(t, strings.HasPrefix(a.Initrd, "/A/"))
assert.Equal(t, "cmdline A", a.Cmdline)

b := conf.Entries[bootloader.BootB]
b := conf.Entries[grub.BootB]
assert.Equal(t, "B - v2", b.Name)
assert.Equal(t, "cmdline B", b.Cmdline)
assert.True(t, strings.HasPrefix(b.Linux, "/B/"))
Expand All @@ -55,8 +54,8 @@ func TestDecode(t *testing.T) {

func TestEncodeDecode(t *testing.T) {
config := grub.NewConfig()
require.NoError(t, config.Put(bootloader.BootA, "talos.platform=metal talos.config=https://my-metadata.server/talos/config?hostname=${hostname}&mac=${mac}"))
require.NoError(t, config.Put(bootloader.BootB, "talos.platform=metal talos.config=https://my-metadata.server/talos/config?uuid=${uuid}"))
require.NoError(t, config.Put(grub.BootA, "talos.platform=metal talos.config=https://my-metadata.server/talos/config?hostname=${hostname}&mac=${mac}"))
require.NoError(t, config.Put(grub.BootB, "talos.platform=metal talos.config=https://my-metadata.server/talos/config?uuid=${uuid}"))

var b bytes.Buffer

Expand All @@ -73,15 +72,15 @@ func TestEncodeDecode(t *testing.T) {
func TestParseBootLabel(t *testing.T) {
label, err := grub.ParseBootLabel("A - v1")
assert.NoError(t, err)
assert.Equal(t, bootloader.BootA, label)
assert.Equal(t, grub.BootA, label)

label, err = grub.ParseBootLabel("B - v2")
assert.NoError(t, err)
assert.Equal(t, bootloader.BootB, label)
assert.Equal(t, grub.BootB, label)

label, err = grub.ParseBootLabel("Reset Talos installation and return to maintenance mode\n")
assert.NoError(t, err)
assert.Equal(t, bootloader.BootReset, label)
assert.Equal(t, grub.BootReset, label)

_, err = grub.ParseBootLabel("C - v3")
assert.Error(t, err)
Expand All @@ -103,7 +102,7 @@ func TestWrite(t *testing.T) {
t.Cleanup(func() { require.NoError(t, os.Remove(tempFile.Name())) })

config := grub.NewConfig()
require.NoError(t, config.Put(bootloader.BootA, "cmdline A"))
require.NoError(t, config.Put(grub.BootA, "cmdline A"))

err := config.Write(tempFile.Name())
assert.NoError(t, err)
Expand All @@ -114,29 +113,29 @@ func TestWrite(t *testing.T) {

func TestPut(t *testing.T) {
config := grub.NewConfig()
require.NoError(t, config.Put(bootloader.BootA, "cmdline A"))
require.NoError(t, config.Put(grub.BootA, "cmdline A"))

err := config.Put(bootloader.BootB, "cmdline B")
err := config.Put(grub.BootB, "cmdline B")

assert.NoError(t, err)

assert.Len(t, config.Entries, 2)
assert.Equal(t, "cmdline B", config.Entries[bootloader.BootB].Cmdline)
assert.Equal(t, "cmdline B", config.Entries[grub.BootB].Cmdline)

err = config.Put(bootloader.BootA, "cmdline A 2")
err = config.Put(grub.BootA, "cmdline A 2")
assert.NoError(t, err)

assert.Equal(t, "cmdline A 2", config.Entries[bootloader.BootA].Cmdline)
assert.Equal(t, "cmdline A 2", config.Entries[grub.BootA].Cmdline)
}

//nolint:errcheck
func TestFallback(t *testing.T) {
config := grub.NewConfig()
require.NoError(t, config.Put(bootloader.BootA, "cmdline A"))
require.NoError(t, config.Put(grub.BootA, "cmdline A"))

_ = config.Put(bootloader.BootB, "cmdline B")
_ = config.Put(grub.BootB, "cmdline B")

config.Fallback = bootloader.BootB
config.Fallback = grub.BootB

var buf bytes.Buffer
_ = config.Encode(&buf)
Expand Down Expand Up @@ -233,9 +232,9 @@ func TestBackwardsCompat(t *testing.T) {
var buf bytes.Buffer

config := grub.NewConfig()
require.NoError(t, config.Put(bootloader.BootA, "cmdline A"))
require.NoError(t, config.Put(bootloader.BootB, "cmdline B"))
config.Default = bootloader.BootB
require.NoError(t, config.Put(grub.BootA, "cmdline A"))
require.NoError(t, config.Put(grub.BootB, "cmdline B"))
config.Default = grub.BootB

err := config.Encode(&buf)
assert.NoError(t, err)
Expand Down
Loading

0 comments on commit e3f3f57

Please sign in to comment.