diff --git a/Makefile b/Makefile index c731a7baa..257aa813d 100644 --- a/Makefile +++ b/Makefile @@ -326,7 +326,7 @@ secureboot-installer: ## Builds UEFI only installer which uses UKI and push it t @$(MAKE) image-secureboot-installer IMAGER_ARGS="--base-installer-image $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG)" @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ arch=$$(basename "$${platform}") && \ - crane push $(ARTIFACTS)/metal-$${arch}-secureboot-installer.tar $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG)-$${arch}-secureboot ; \ + crane push $(ARTIFACTS)/installer-$${arch}-secureboot.tar $(REGISTRY_AND_USERNAME)/installer:$(IMAGE_TAG)-$${arch}-secureboot ; \ done .PHONY: talosctl-cni-bundle diff --git a/cmd/installer/cmd/imager/root.go b/cmd/installer/cmd/imager/root.go index dc6cb117a..11ec38c18 100644 --- a/cmd/installer/cmd/imager/root.go +++ b/cmd/installer/cmd/imager/root.go @@ -105,7 +105,7 @@ var rootCmd = &cobra.Command{ return err } - if err = imager.Execute(ctx, cmdFlags.OutputPath, report); err != nil { + if _, err = imager.Execute(ctx, cmdFlags.OutputPath, report); err != nil { report.Report(reporter.Update{ Message: err.Error(), Status: reporter.StatusError, diff --git a/pkg/imager/imager.go b/pkg/imager/imager.go index 47a187d2d..f42e2a5b3 100644 --- a/pkg/imager/imager.go +++ b/pkg/imager/imager.go @@ -80,12 +80,10 @@ func New(prof profile.Profile) (*Imager, error) { // Execute image generation. // //nolint:gocyclo,cyclop -func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporter.Reporter) error { - var err error - +func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporter.Reporter) (outputAssetPath string, err error) { i.tempDir, err = os.MkdirTemp("", "imager") if err != nil { - return fmt.Errorf("failed to create temporary directory: %w", err) + return "", fmt.Errorf("failed to create temporary directory: %w", err) } defer os.RemoveAll(i.tempDir) //nolint:errcheck @@ -97,17 +95,17 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte // 0. Dump the profile. if err = i.prof.Dump(os.Stderr); err != nil { - return err + return "", err } // 1. Transform `initramfs.xz` with system extensions if err = i.buildInitramfs(ctx, report); err != nil { - return err + return "", err } // 2. Prepare kernel arguments. if err = i.buildCmdline(); err != nil { - return err + return "", err } report.Report(reporter.Update{ @@ -118,12 +116,12 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte // 3. Build UKI if Secure Boot is enabled. if i.prof.SecureBootEnabled() { if err = i.buildUKI(report); err != nil { - return err + return "", err } } // 4. Build the output. - outputAssetPath := filepath.Join(outputPath, i.prof.OutputPath()) + outputAssetPath = filepath.Join(outputPath, i.prof.OutputPath()) switch i.prof.Output.Kind { case profile.OutKindISO: @@ -134,6 +132,8 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte err = i.outUKI(outputAssetPath, report) case profile.OutKindInitramfs: err = i.outInitramfs(outputAssetPath, report) + case profile.OutKindCmdline: + err = i.outCmdline(outputAssetPath) case profile.OutKindImage: err = i.outImage(ctx, outputAssetPath, report) case profile.OutKindInstaller: @@ -141,11 +141,11 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte case profile.OutKindUnknown: fallthrough default: - return fmt.Errorf("unknown output kind: %s", i.prof.Output.Kind) + return "", fmt.Errorf("unknown output kind: %s", i.prof.Output.Kind) } if err != nil { - return err + return "", err } report.Report(reporter.Update{ @@ -157,7 +157,7 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte switch i.prof.Output.OutFormat { case profile.OutFormatRaw: // do nothing - return nil + return outputAssetPath, nil case profile.OutFormatXZ: return i.postProcessXz(outputAssetPath, report) case profile.OutFormatGZ: @@ -167,7 +167,7 @@ func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporte case profile.OutFormatUnknown: fallthrough default: - return fmt.Errorf("unknown output format: %s", i.prof.Output.OutFormat) + return "", fmt.Errorf("unknown output format: %s", i.prof.Output.OutFormat) } } @@ -185,6 +185,11 @@ func (i *Imager) buildInitramfs(ctx context.Context, report *reporter.Reporter) return nil } + if i.prof.Output.Kind == profile.OutKindCmdline || i.prof.Output.Kind == profile.OutKindKernel { + // these outputs don't use initramfs image + return nil + } + printf := progressPrintf(report, reporter.Update{Message: "rebuilding initramfs with system extensions...", Status: reporter.StatusRunning}) // copy the initramfs to a temporary location, as it's going to be modified during the extension build process diff --git a/pkg/imager/out.go b/pkg/imager/out.go index c28f4ae7c..e34c8fb56 100644 --- a/pkg/imager/out.go +++ b/pkg/imager/out.go @@ -8,6 +8,7 @@ import ( "context" "fmt" "log" + "os" "path/filepath" "time" @@ -67,6 +68,10 @@ func (i *Imager) outUKI(path string, report *reporter.Reporter) error { return nil } +func (i *Imager) outCmdline(path string) error { + return os.WriteFile(path, []byte(i.cmdline), 0o644) +} + func (i *Imager) outISO(path string, report *reporter.Reporter) error { printf := progressPrintf(report, reporter.Update{Message: "building ISO...", Status: reporter.StatusRunning}) diff --git a/pkg/imager/post.go b/pkg/imager/post.go index 58214a19b..e9047e2e0 100644 --- a/pkg/imager/post.go +++ b/pkg/imager/post.go @@ -14,51 +14,51 @@ import ( "github.com/siderolabs/talos/pkg/reporter" ) -func (i *Imager) postProcessTar(filename string, report *reporter.Reporter) error { +func (i *Imager) postProcessTar(filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "processing .tar.gz", Status: reporter.StatusRunning}) dir := filepath.Dir(filename) src := "disk.raw" if err := os.Rename(filename, filepath.Join(dir, src)); err != nil { - return err + return "", err } outPath := filename + ".tar.gz" if _, err := cmd.Run("tar", "-cvf", outPath, "-C", dir, "--sparse", "--use-compress-program=pigz -6", src); err != nil { - return err + return "", err } if err := os.Remove(filepath.Join(dir, src)); err != nil { - return err + return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("archive is ready: %s", outPath), Status: reporter.StatusSucceeded}) - return nil + return outPath, nil } -func (i *Imager) postProcessGz(filename string, report *reporter.Reporter) error { +func (i *Imager) postProcessGz(filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "compressing .gz", Status: reporter.StatusRunning}) if _, err := cmd.Run("pigz", "-6", "-f", filename); err != nil { - return err + return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s.gz", filename), Status: reporter.StatusSucceeded}) - return nil + return filename + ".gz", nil } -func (i *Imager) postProcessXz(filename string, report *reporter.Reporter) error { +func (i *Imager) postProcessXz(filename string, report *reporter.Reporter) (string, error) { report.Report(reporter.Update{Message: "compressing .xz", Status: reporter.StatusRunning}) if _, err := cmd.Run("xz", "-0", "-f", "-T", "0", filename); err != nil { - return err + return "", err } report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s.xz", filename), Status: reporter.StatusSucceeded}) - return nil + return filename + ".xz", nil } diff --git a/pkg/imager/profile/input.go b/pkg/imager/profile/input.go index d189ca952..af7ee39aa 100644 --- a/pkg/imager/profile/input.go +++ b/pkg/imager/profile/input.go @@ -48,6 +48,10 @@ type FileAsset struct { type ContainerAsset struct { // ImageRef is a reference to the container image. ImageRef string `yaml:"imageRef"` + // TarballPath is a path to the .tar format container image contents. + // + // If TarballPath is set, ImageRef is ignored. + TarballPath string `yaml:"tarballPath,omitempty"` } // SecureBootAssets describes secureboot assets. @@ -144,6 +148,10 @@ func fileExists(path string) bool { // Pull the container asset to the path. func (c *ContainerAsset) Pull(ctx context.Context, arch string, printf func(string, ...any)) (v1.Image, error) { + if c.TarballPath != "" { + return nil, fmt.Errorf("pulling tarball container image is not supported") + } + printf("pulling %s...", c.ImageRef) img, err := crane.Pull(c.ImageRef, crane.WithPlatform(&v1.Platform{ @@ -159,6 +167,19 @@ func (c *ContainerAsset) Pull(ctx context.Context, arch string, printf func(stri // Extract the container asset to the path. func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string, printf func(string, ...any)) error { + if c.TarballPath != "" { + in, err := os.Open(c.TarballPath) + if err != nil { + return err + } + + defer in.Close() //nolint:errcheck + + printf("extracting %s...", c.TarballPath) + + return archiver.Untar(ctx, in, destination) + } + img, err := c.Pull(ctx, arch, printf) if err != nil { return err diff --git a/pkg/imager/profile/output.go b/pkg/imager/profile/output.go index fe428f6fb..2b1208d2c 100644 --- a/pkg/imager/profile/output.go +++ b/pkg/imager/profile/output.go @@ -51,6 +51,7 @@ const ( OutKindKernel // kernel OutKindInitramfs // initramfs OutKindUKI // uki + OutKindCmdline // cmdline ) //go:generate enumer -type OutFormat -linecomment -text diff --git a/pkg/imager/profile/outputkind_enumer.go b/pkg/imager/profile/outputkind_enumer.go index 6f5e17246..a7e5fb1a6 100644 --- a/pkg/imager/profile/outputkind_enumer.go +++ b/pkg/imager/profile/outputkind_enumer.go @@ -6,9 +6,9 @@ import ( "fmt" ) -const _OutputKindName = "unknownisoimageinstallerkernelinitramfsuki" +const _OutputKindName = "unknownisoimageinstallerkernelinitramfsukicmdline" -var _OutputKindIndex = [...]uint8{0, 7, 10, 15, 24, 30, 39, 42} +var _OutputKindIndex = [...]uint8{0, 7, 10, 15, 24, 30, 39, 42, 49} func (i OutputKind) String() string { if i < 0 || i >= OutputKind(len(_OutputKindIndex)-1) { @@ -17,7 +17,7 @@ func (i OutputKind) String() string { return _OutputKindName[_OutputKindIndex[i]:_OutputKindIndex[i+1]] } -var _OutputKindValues = []OutputKind{0, 1, 2, 3, 4, 5, 6} +var _OutputKindValues = []OutputKind{0, 1, 2, 3, 4, 5, 6, 7} var _OutputKindNameToValueMap = map[string]OutputKind{ _OutputKindName[0:7]: 0, @@ -27,6 +27,7 @@ var _OutputKindNameToValueMap = map[string]OutputKind{ _OutputKindName[24:30]: 4, _OutputKindName[30:39]: 5, _OutputKindName[39:42]: 6, + _OutputKindName[42:49]: 7, } // OutputKindString retrieves an enum value from the enum constants string name. diff --git a/pkg/imager/profile/profile.go b/pkg/imager/profile/profile.go index dabb93c7b..7d435b284 100644 --- a/pkg/imager/profile/profile.go +++ b/pkg/imager/profile/profile.go @@ -55,7 +55,7 @@ func (p *Profile) SecureBootEnabled() bool { // Validate the profile. // -//nolint:gocyclo +//nolint:gocyclo,cyclop func (p *Profile) Validate() error { if p.Arch != "amd64" && p.Arch != "arm64" { return fmt.Errorf("invalid arch %q", p.Arch) @@ -76,6 +76,8 @@ func (p *Profile) Validate() error { return fmt.Errorf("unknown output kind") case OutKindISO: // ISO supports all kinds of customization + case OutKindCmdline: + // cmdline supports all kinds of customization case OutKindImage: // Image supports all kinds of customization if p.Output.ImageOptions.DiskSize == 0 { @@ -111,6 +113,8 @@ func (p *Profile) Validate() error { } // OutputPath generates the output path for the profile. +// +//nolint:gocyclo func (p *Profile) OutputPath() string { path := p.Platform @@ -132,13 +136,21 @@ func (p *Profile) OutputPath() string { case OutKindImage: path += "." + p.Output.ImageOptions.DiskFormat.String() case OutKindInstaller: - path += "-installer.tar" + path = "installer-" + p.Arch + + if p.SecureBootEnabled() { + path += "-secureboot" + } + + path += ".tar" case OutKindKernel: path = "kernel-" + p.Arch case OutKindInitramfs: path = "initramfs-" + path + ".xz" case OutKindUKI: path += "-uki.efi" + case OutKindCmdline: + path = "cmdline-" + path } return path diff --git a/website/content/v1.6/talos-guides/install/bare-metal-platforms/secureboot.md b/website/content/v1.6/talos-guides/install/bare-metal-platforms/secureboot.md index 5135598b0..de8af10da 100644 --- a/website/content/v1.6/talos-guides/install/bare-metal-platforms/secureboot.md +++ b/website/content/v1.6/talos-guides/install/bare-metal-platforms/secureboot.md @@ -139,13 +139,13 @@ skipped initramfs rebuild (no system extensions) kernel command line: talos.platform=metal console=ttyS0 console=tty0 init_on_alloc=1 slab_nomerge pti=on consoleblank=0 nvme_core.io_timeout=4294967295 printk.devkmsg=on ima_template=ima-ng ima_appraise=fix ima_hash=sha512 lockdown=confidentiality UKI ready installer container image ready -output asset path: /out/metal-amd64-secureboot-installer.tar +output asset path: /out/installer-amd64-secureboot.tar ``` The generated container image should be pushed to some container registry which Talos can access during the installation, e.g.: ```shell -crane push _out/metal-amd64-secureboot-installer.tar ghcr.io//installer-amd64-secureboot:{{< release >}} +crane push _out/installer-amd64-secureboot.tar ghcr.io//installer-amd64-secureboot:{{< release >}} ``` The generated ISO and installer images might be further customized with system extensions, extra kernel command line arguments, etc.