From 2bf924c7be8869f8da869850f1df0e4d82651960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20F=C3=B6rster?= Date: Tue, 30 Jul 2024 15:25:11 +0200 Subject: [PATCH] feat: update ISO VolumeID with Talos version Put Talos version in the ISO volume ID and volumeset ID. Volume ID is restricted on valid characters, while volumeset ID is not restricted (Unicode). Signed-off-by: Andrey Smirnov --- Makefile | 2 +- pkg/imager/iso/grub.go | 20 ++++++++++---- pkg/imager/iso/iso.go | 37 +++++++++++++++++++++++++ pkg/imager/iso/iso_test.go | 40 +++++++++++++++++++++++++++ pkg/imager/iso/uefi.go | 39 ++++++++++++++++++-------- pkg/machinery/imager/quirks/quirks.go | 12 ++++++++ 6 files changed, 133 insertions(+), 17 deletions(-) create mode 100644 pkg/imager/iso/iso_test.go diff --git a/Makefile b/Makefile index fc8cbb92ba..9051bfed2b 100644 --- a/Makefile +++ b/Makefile @@ -378,7 +378,7 @@ image-%: ## Builds the specified image. Valid options are aws, azure, digital-oc @docker pull $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG) @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ arch=$$(basename "$${platform}") && \ - docker run --rm -t -v /dev:/dev -v $(PWD)/$(ARTIFACTS):/secureboot:ro -v $(PWD)/$(ARTIFACTS):/out --network=host --privileged $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG) $* --arch $$arch $(IMAGER_ARGS) ; \ + docker run --rm -t -v /dev:/dev -v $(PWD)/$(ARTIFACTS):/secureboot:ro -v $(PWD)/$(ARTIFACTS):/out -e SOURCE_DATE_EPOCH=$(SOURCE_DATE_EPOCH) --network=host --privileged $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG) $* --arch $$arch $(IMAGER_ARGS) ; \ done images-essential: image-aws image-azure image-gcp image-metal secureboot-installer ## Builds only essential images used in the CI (AWS, GCP, and Metal). diff --git a/pkg/imager/iso/grub.go b/pkg/imager/iso/grub.go index 0e80e804b8..12de88a823 100644 --- a/pkg/imager/iso/grub.go +++ b/pkg/imager/iso/grub.go @@ -86,14 +86,16 @@ func CreateGRUB(printf func(string, ...any), options GRUBOptions) error { printf("creating ISO image") - return grubMkrescue(options.OutPath, options.ScratchDir) + return grubMkrescue(options) } -func grubMkrescue(isoPath, scratchPath string) error { +func grubMkrescue(options GRUBOptions) error { args := []string{ "--compress=xz", - "--output=" + isoPath, - scratchPath, + "--output=" + options.OutPath, + "--verbose", + options.ScratchDir, + "--", } if epoch, ok, err := utils.SourceDateEpoch(); err != nil { @@ -105,12 +107,20 @@ func grubMkrescue(isoPath, scratchPath string) error { } args = append(args, - "--", "-volume_date", "all_file_dates", fmt.Sprintf("=%d", epoch), "-volume_date", "uuid", time.Unix(epoch, 0).Format("2006010215040500"), ) } + if quirks.New(options.Version).SupportsISOLabel() { + label := Label(options.Version, false) + + args = append(args, + "-volid", VolumeID(label), + "-volset-id", label, + ) + } + _, err := cmd.Run("grub-mkrescue", args...) if err != nil { return fmt.Errorf("failed to create ISO: %w", err) diff --git a/pkg/imager/iso/iso.go b/pkg/imager/iso/iso.go index 9154167dc1..076fea6ddd 100644 --- a/pkg/imager/iso/iso.go +++ b/pkg/imager/iso/iso.go @@ -4,3 +4,40 @@ // Package iso contains functions for creating ISO images. package iso + +import "strings" + +// VolumeID returns a valid volume ID for the given label. +func VolumeID(label string) string { + // builds a valid volume ID: 32 chars out of [A-Z0-9_] + label = strings.ToUpper(label) + label = strings.Map(func(r rune) rune { + switch { + case r >= 'A' && r <= 'Z': + return r + case r >= '0' && r <= '9': + return r + case r == '_' || r == '-' || r == '.': + return '_' + default: + return -1 + } + }, label) + + if len(label) > 32 { + label = label[:32] + } + + return label +} + +// Label returns an ISO full label for a given version. +func Label(version string, secureboot bool) string { + label := "Talos-" + + if secureboot { + label += "SB-" + } + + return label + version +} diff --git a/pkg/imager/iso/iso_test.go b/pkg/imager/iso/iso_test.go new file mode 100644 index 0000000000..4dc51af376 --- /dev/null +++ b/pkg/imager/iso/iso_test.go @@ -0,0 +1,40 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package iso_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/siderolabs/talos/pkg/imager/iso" +) + +func TestVolumeID(t *testing.T) { + t.Parallel() + + for _, test := range []struct { + in string + + out string + }{ + { + in: "Talos-v1.7.6", + + out: "TALOS_V1_7_6", + }, + { + in: "Talos-v1.7.6-beta.0", + + out: "TALOS_V1_7_6_BETA_0", + }, + } { + t.Run(test.in, func(t *testing.T) { + t.Parallel() + + assert.Equal(t, test.out, iso.VolumeID(test.in)) + }) + } +} diff --git a/pkg/imager/iso/uefi.go b/pkg/imager/iso/uefi.go index c221930ed1..6cf4d24510 100644 --- a/pkg/imager/iso/uefi.go +++ b/pkg/imager/iso/uefi.go @@ -12,11 +12,13 @@ import ( "os" "path/filepath" "text/template" + "time" "github.com/siderolabs/go-cmd/pkg/cmd" "github.com/siderolabs/talos/pkg/imager/utils" "github.com/siderolabs/talos/pkg/machinery/constants" + "github.com/siderolabs/talos/pkg/machinery/imager/quirks" "github.com/siderolabs/talos/pkg/makefs" ) @@ -185,19 +187,34 @@ func CreateUEFI(printf func(string, ...any), options UEFIOptions) error { printf("creating ISO image") - if _, err := cmd.Run( - "xorriso", - "-as", - "mkisofs", - "-V", - "Talos Secure Boot ISO", - "-e", - "efiboot.img", + args := []string{ + "-as", "mkisofs", + "-e", "efiboot.img", "-no-emul-boot", - "-o", - options.OutPath, + "-o", options.OutPath, options.ScratchDir, - ); err != nil { + "--", + } + + if epoch, ok, err := utils.SourceDateEpoch(); err != nil { + return err + } else if ok { + args = append(args, + "-volume_date", "all_file_dates", fmt.Sprintf("=%d", epoch), + "-volume_date", "uuid", time.Unix(epoch, 0).Format("2006010215040500"), + ) + } + + if quirks.New(options.Version).SupportsISOLabel() { + label := Label(options.Version, true) + + args = append(args, + "-volid", VolumeID(label), + "-volset-id", label, + ) + } + + if _, err := cmd.Run("xorriso", args...); err != nil { return err } diff --git a/pkg/machinery/imager/quirks/quirks.go b/pkg/machinery/imager/quirks/quirks.go index 5c942e8b2e..1b8354ca57 100644 --- a/pkg/machinery/imager/quirks/quirks.go +++ b/pkg/machinery/imager/quirks/quirks.go @@ -75,6 +75,18 @@ func (q Quirks) UseZSTDCompression() bool { return q.v.GTE(minVersionZstd) } +var minVersionISOLabel = semver.MustParse("1.8.0") + +// SupportsISOLabel returns true if the Talos version supports setting the ISO label. +func (q Quirks) SupportsISOLabel() bool { + // if the version doesn't parse, we assume it's latest Talos + if q.v == nil { + return true + } + + return q.v.GTE(minVersionISOLabel) +} + var minVersionMultidoc = semver.MustParse("1.5.0") // SupportsMultidoc returns true if the Talos version supports multidoc machine configs.