Skip to content

Commit

Permalink
feat: improve imager APIs
Browse files Browse the repository at this point in the history
* report the final output path of the asset
* allow 'cmdline' output (just to get the kernel cmdline, e.g. for PXE
  booting)
* support pre-pulled container images for extensions

Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
  • Loading branch information
smira committed Aug 31, 2023
1 parent 2d3ac92 commit 44f59a8
Show file tree
Hide file tree
Showing 10 changed files with 78 additions and 33 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion cmd/installer/cmd/imager/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 18 additions & 13 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{
Expand All @@ -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:
Expand All @@ -134,18 +132,20 @@ 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:
err = i.outInstaller(ctx, outputAssetPath, report)
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{
Expand All @@ -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:
Expand All @@ -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)
}
}

Expand All @@ -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
Expand Down
5 changes: 5 additions & 0 deletions pkg/imager/out.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"time"

Expand Down Expand Up @@ -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})

Expand Down
22 changes: 11 additions & 11 deletions pkg/imager/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
21 changes: 21 additions & 0 deletions pkg/imager/profile/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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{
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions pkg/imager/profile/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const (
OutKindKernel // kernel
OutKindInitramfs // initramfs
OutKindUKI // uki
OutKindCmdline // cmdline
)

//go:generate enumer -type OutFormat -linecomment -text
Expand Down
7 changes: 4 additions & 3 deletions pkg/imager/profile/outputkind_enumer.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 14 additions & 2 deletions pkg/imager/profile/profile.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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 {
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/<user>/installer-amd64-secureboot:{{< release >}}
crane push _out/installer-amd64-secureboot.tar ghcr.io/<user>/installer-amd64-secureboot:{{< release >}}
```

The generated ISO and installer images might be further customized with system extensions, extra kernel command line arguments, etc.
Expand Down

0 comments on commit 44f59a8

Please sign in to comment.