mirror of
https://github.com/siderolabs/talos.git
synced 2026-05-05 04:16:21 +02:00
chore: clean up the output of the imager
Use `Progress`, and options to pass around the way messages are written. Fixed some tiny issues in the code, but otherwise no functional changes. To make colored output work with `docker run`, switched back image generation to use volume mount for output (old mode is still functioning, but it's not the default, and it works when docker is not running on the same host). Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>
This commit is contained in:
parent
fb536af4d1
commit
e0f383598e
4
Makefile
4
Makefile
@ -300,7 +300,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 -v /dev:/dev -v $(PWD)/$(ARTIFACTS):/secureboot:ro --network=host --privileged $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG) $* --arch $$arch --tar-to-stdout $(IMAGER_ARGS) | tar xz -C $(ARTIFACTS) ; \
|
||||
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) ; \
|
||||
done
|
||||
|
||||
images-essential: image-aws image-gcp image-metal secureboot-installer ## Builds only essential images used in the CI (AWS, GCP, and Metal).
|
||||
@ -309,7 +309,7 @@ images: image-aws image-azure image-digital-ocean image-exoscale image-gcp image
|
||||
|
||||
sbc-%: ## Builds the specified SBC image. Valid options are rpi_generic, rock64, bananapi_m64, libretech_all_h3_cc_h5, rockpi_4, rockpi_4c, pine64, jetson_nano and nanopi_r4s (e.g. sbc-rpi_generic)
|
||||
@docker pull $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG)
|
||||
@docker run --rm -v /dev:/dev --network=host --privileged $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG) $* --arch arm64 --tar-to-stdout $(IMAGER_ARGS) | tar xz -C $(ARTIFACTS)
|
||||
@docker run --rm -t -v /dev:/dev -v $(PWD)/$(ARTIFACTS):/out --network=host --privileged $(REGISTRY_AND_USERNAME)/imager:$(IMAGE_TAG) $* --arch arm64 $(IMAGER_ARGS)
|
||||
|
||||
sbcs: sbc-rpi_generic sbc-rock64 sbc-bananapi_m64 sbc-libretech_all_h3_cc_h5 sbc-rockpi_4 sbc-rockpi_4c sbc-pine64 sbc-jetson_nano sbc-nanopi_r4s ## Builds all known SBC images (Raspberry Pi 4, Rock64, Banana Pi M64, Radxa ROCK Pi 4, Radxa ROCK Pi 4c, Pine64, Libre Computer Board ALL-H3-CC, Jetson Nano and Nano Pi R4S).
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ package imager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
@ -21,6 +20,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/imager"
|
||||
"github.com/siderolabs/talos/pkg/imager/profile"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/reporter"
|
||||
)
|
||||
|
||||
var cmdFlags struct {
|
||||
@ -38,12 +38,19 @@ var cmdFlags struct {
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands.
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "imager <profile>|-",
|
||||
Short: "Generate various boot assets and images.",
|
||||
Long: ``,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Use: "imager <profile>|-",
|
||||
Short: "Generate various boot assets and images.",
|
||||
Long: ``,
|
||||
Args: cobra.ExactArgs(1),
|
||||
SilenceUsage: true,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cli.WithContext(context.Background(), func(ctx context.Context) error {
|
||||
report := reporter.New()
|
||||
report.Report(reporter.Update{
|
||||
Message: "assembling the finalized profile...",
|
||||
Status: reporter.StatusRunning,
|
||||
})
|
||||
|
||||
baseProfile := args[0]
|
||||
|
||||
var prof profile.Profile
|
||||
@ -98,7 +105,12 @@ var rootCmd = &cobra.Command{
|
||||
return err
|
||||
}
|
||||
|
||||
if err = imager.Execute(ctx, cmdFlags.OutputPath); err != nil {
|
||||
if err = imager.Execute(ctx, cmdFlags.OutputPath, report); err != nil {
|
||||
report.Report(reporter.Update{
|
||||
Message: err.Error(),
|
||||
Status: reporter.StatusError,
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -115,7 +127,6 @@ var rootCmd = &cobra.Command{
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ type Options struct {
|
||||
ImageSecureboot bool
|
||||
Version string
|
||||
BootAssets bootloaderoptions.BootAssets
|
||||
Printf func(string, ...any)
|
||||
}
|
||||
|
||||
// Mode is the install mode.
|
||||
@ -107,7 +108,7 @@ func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("installation of %s complete", version.Tag)
|
||||
i.options.Printf("installation of %s complete", version.Tag)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -132,6 +133,10 @@ func NewInstaller(ctx context.Context, cmdline *procfs.Cmdline, mode Mode, opts
|
||||
i.options.Version = version.Tag
|
||||
}
|
||||
|
||||
if i.options.Printf == nil {
|
||||
i.options.Printf = log.Printf
|
||||
}
|
||||
|
||||
if !i.options.Zero {
|
||||
i.bootloader, err = bootloader.Probe(ctx, i.options.Disk)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
@ -258,6 +263,7 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) {
|
||||
Version: i.options.Version,
|
||||
ImageMode: mode.IsImage(),
|
||||
BootAssets: i.options.BootAssets,
|
||||
Printf: i.options.Printf,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -270,7 +276,7 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("installing U-Boot for %q", b.Name())
|
||||
i.options.Printf("installing U-Boot for %q", b.Name())
|
||||
|
||||
if err = b.Install(i.options.Disk); err != nil {
|
||||
return err
|
||||
|
||||
@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@ -33,6 +32,8 @@ type Manifest struct {
|
||||
Devices map[string]Device
|
||||
Targets map[string][]*Target
|
||||
LegacyBIOSSupport bool
|
||||
|
||||
Printf func(string, ...any)
|
||||
}
|
||||
|
||||
// Device represents device options.
|
||||
@ -53,6 +54,8 @@ func NewManifest(mode Mode, uefiOnlyBoot bool, bootLoaderPresent bool, opts *Opt
|
||||
Devices: map[string]Device{},
|
||||
Targets: map[string][]*Target{},
|
||||
LegacyBIOSSupport: opts.LegacyBIOSSupport,
|
||||
|
||||
Printf: opts.Printf,
|
||||
}
|
||||
|
||||
if opts.Board != constants.BoardNone {
|
||||
@ -248,7 +251,7 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
if device.Zero {
|
||||
if err = partition.Format(device.Device, &partition.FormatOptions{
|
||||
FileSystemType: partition.FilesystemTypeNone,
|
||||
}); err != nil {
|
||||
}, m.Printf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -272,7 +275,7 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("creating new partition table on %s", device.Device)
|
||||
m.Printf("creating new partition table on %s", device.Device)
|
||||
|
||||
gptOpts := []gpt.Option{
|
||||
gpt.WithMarkMBRBootable(m.LegacyBIOSSupport),
|
||||
@ -287,8 +290,8 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("logical/physical block size: %d/%d", pt.Header().LBA.LogicalBlockSize, pt.Header().LBA.PhysicalBlockSize)
|
||||
log.Printf("minimum/optimal I/O size: %d/%d", pt.Header().LBA.MinimalIOSize, pt.Header().LBA.OptimalIOSize)
|
||||
m.Printf("logical/physical block size: %d/%d", pt.Header().LBA.LogicalBlockSize, pt.Header().LBA.PhysicalBlockSize)
|
||||
m.Printf("minimum/optimal I/O size: %d/%d", pt.Header().LBA.MinimalIOSize, pt.Header().LBA.OptimalIOSize)
|
||||
|
||||
if err = pt.Write(); err != nil {
|
||||
return err
|
||||
@ -309,7 +312,7 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
|
||||
if !created {
|
||||
if device.ResetPartitionTable {
|
||||
log.Printf("resetting partition table on %s", device.Device)
|
||||
m.Printf("resetting partition table on %s", device.Device)
|
||||
|
||||
// TODO: how should it work with zero option above?
|
||||
if err = bd.Reset(); err != nil {
|
||||
@ -343,7 +346,7 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
// delete all partitions which are not skipped
|
||||
for _, part := range pt.Partitions().Items() {
|
||||
if _, ok := keepPartitions[part.Name]; !ok {
|
||||
log.Printf("deleting partition %s", part.Name)
|
||||
m.Printf("deleting partition %s", part.Name)
|
||||
|
||||
if err = pt.Delete(part); err != nil {
|
||||
return err
|
||||
@ -363,7 +366,7 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
}
|
||||
|
||||
for i, target := range targets {
|
||||
if err = target.partition(pt, i); err != nil {
|
||||
if err = target.partition(pt, i, m.Printf); err != nil {
|
||||
return fmt.Errorf("failed to partition device: %w", err)
|
||||
}
|
||||
}
|
||||
@ -376,7 +379,7 @@ func (m *Manifest) executeOnDevice(device Device, targets []*Target) (err error)
|
||||
target := target
|
||||
|
||||
err = retry.Constant(time.Minute, retry.WithUnits(100*time.Millisecond)).Retry(func() error {
|
||||
e := target.Format()
|
||||
e := target.Format(m.Printf)
|
||||
if e != nil {
|
||||
if strings.Contains(e.Error(), "No such file or directory") {
|
||||
// workaround problem with partition device not being visible immediately after partitioning
|
||||
@ -422,7 +425,7 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
|
||||
|
||||
if bd, err = blockdevice.Open(device.Device); err != nil {
|
||||
// failed to open the block device, probably it's damaged?
|
||||
log.Printf("warning: skipping preserve contents on %q as block device failed: %s", device.Device, err)
|
||||
m.Printf("warning: skipping preserve contents on %q as block device failed: %s", device.Device, err)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -432,7 +435,7 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
|
||||
|
||||
pt, err := bd.PartitionTable()
|
||||
if err != nil {
|
||||
log.Printf("warning: skipping preserve contents on %q as partition table failed: %s", device.Device, err)
|
||||
m.Printf("warning: skipping preserve contents on %q as partition table failed: %s", device.Device, err)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -473,13 +476,13 @@ func (m *Manifest) preserveContents(device Device, targets []*Target) (err error
|
||||
}
|
||||
|
||||
if sourcePart == nil {
|
||||
log.Printf("warning: failed to preserve contents of %q on %q, as source partition wasn't found", target.Label, device.Device)
|
||||
m.Printf("warning: failed to preserve contents of %q on %q, as source partition wasn't found", target.Label, device.Device)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err = target.SaveContents(device, sourcePart, fileSystemType, fnmatchFilters); err != nil {
|
||||
log.Printf("warning: failed to preserve contents of %q on %q: %s", target.Label, device.Device, err)
|
||||
m.Printf("warning: failed to preserve contents of %q on %q: %s", target.Label, device.Device, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -229,9 +229,10 @@ func (suite *manifestSuite) TestExecuteManifestClean() {
|
||||
suite.skipUnderBuildkit()
|
||||
|
||||
manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Board: constants.BoardNone,
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Board: constants.BoardNone,
|
||||
Printf: suite.T().Logf,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -249,9 +250,10 @@ func (suite *manifestSuite) TestExecuteManifestForce() {
|
||||
suite.skipUnderBuildkit()
|
||||
|
||||
manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Board: constants.BoardNone,
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Board: constants.BoardNone,
|
||||
Printf: suite.T().Logf,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -267,10 +269,11 @@ func (suite *manifestSuite) TestExecuteManifestForce() {
|
||||
// reinstall
|
||||
|
||||
manifest, err = install.NewManifest(install.ModeUpgrade, false, true, &install.Options{
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Zero: true,
|
||||
Board: constants.BoardNone,
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Zero: true,
|
||||
Board: constants.BoardNone,
|
||||
Printf: suite.T().Logf,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -288,9 +291,10 @@ func (suite *manifestSuite) TestExecuteManifestPreserve() {
|
||||
suite.skipUnderBuildkit()
|
||||
|
||||
manifest, err := install.NewManifest(install.ModeInstall, false, false, &install.Options{
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Board: constants.BoardNone,
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: true,
|
||||
Board: constants.BoardNone,
|
||||
Printf: suite.T().Logf,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
@ -306,9 +310,10 @@ func (suite *manifestSuite) TestExecuteManifestPreserve() {
|
||||
// reinstall
|
||||
|
||||
manifest, err = install.NewManifest(install.ModeUpgrade, false, true, &install.Options{
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: false,
|
||||
Board: constants.BoardNone,
|
||||
Disk: suite.loopbackDevice.Name(),
|
||||
Force: false,
|
||||
Board: constants.BoardNone,
|
||||
Printf: suite.T().Logf,
|
||||
})
|
||||
suite.Require().NoError(err)
|
||||
|
||||
|
||||
@ -200,11 +200,11 @@ func (t *Target) Locate(pt *gpt.GPT) (*gpt.Partition, error) {
|
||||
}
|
||||
|
||||
// partition creates a new partition on the specified device.
|
||||
func (t *Target) partition(pt *gpt.GPT, pos int) (err error) {
|
||||
func (t *Target) partition(pt *gpt.GPT, pos int, printf func(string, ...any)) (err error) {
|
||||
if t.Skip {
|
||||
part := pt.Partitions().FindByName(t.Label)
|
||||
if part != nil {
|
||||
log.Printf("skipped %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
|
||||
printf("skipped %s (%s) size %d blocks", t.PartitionName, t.Label, part.Length())
|
||||
|
||||
t.PartitionName, err = part.Path()
|
||||
if err != nil {
|
||||
@ -220,7 +220,7 @@ func (t *Target) partition(pt *gpt.GPT, pos int) (err error) {
|
||||
PartitionType: t.PartitionType,
|
||||
Size: t.Size,
|
||||
LegacyBIOSBootable: t.LegacyBIOSBootable,
|
||||
})
|
||||
}, printf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -231,12 +231,12 @@ func (t *Target) partition(pt *gpt.GPT, pos int) (err error) {
|
||||
}
|
||||
|
||||
// Format creates a filesystem on the device/partition.
|
||||
func (t *Target) Format() error {
|
||||
func (t *Target) Format(printf func(string, ...any)) error {
|
||||
if t.Skip {
|
||||
return nil
|
||||
}
|
||||
|
||||
return partition.Format(t.PartitionName, t.FormatOptions)
|
||||
return partition.Format(t.PartitionName, t.FormatOptions, printf)
|
||||
}
|
||||
|
||||
// GetLabel returns the underlaying partition label.
|
||||
|
||||
@ -136,7 +136,7 @@ type ResetOptions interface {
|
||||
// PartitionTarget provides interface to the disk partition.
|
||||
type PartitionTarget interface {
|
||||
fmt.Stringer
|
||||
Format() error
|
||||
Format(func(string, ...any)) error
|
||||
GetLabel() string
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ package grub
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
@ -43,7 +42,7 @@ menuentry "Reset Talos installation and return to maintenance mode" {
|
||||
`
|
||||
|
||||
// Write the grub configuration to the given file.
|
||||
func (c *Config) Write(path string) error {
|
||||
func (c *Config) Write(path string, printf func(string, ...any)) error {
|
||||
dir := filepath.Dir(path)
|
||||
if err := os.MkdirAll(dir, os.ModeDir); err != nil {
|
||||
return err
|
||||
@ -56,7 +55,7 @@ func (c *Config) Write(path string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("writing %s to disk", path)
|
||||
printf("writing %s to disk", path)
|
||||
|
||||
return os.WriteFile(path, wr.Bytes(), 0o600)
|
||||
}
|
||||
|
||||
@ -103,7 +103,7 @@ func TestWrite(t *testing.T) {
|
||||
config := grub.NewConfig()
|
||||
require.NoError(t, config.Put(grub.BootA, "cmdline A", "v0.0.1"))
|
||||
|
||||
err := config.Write(tempFile.Name())
|
||||
err := config.Write(tempFile.Name(), t.Logf)
|
||||
assert.NoError(t, err)
|
||||
|
||||
written, _ := os.ReadFile(tempFile.Name())
|
||||
|
||||
@ -6,14 +6,12 @@ package grub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/siderolabs/go-blockdevice/blockdevice"
|
||||
"github.com/siderolabs/go-cmd/pkg/cmd"
|
||||
|
||||
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options"
|
||||
"github.com/siderolabs/talos/pkg/imager/utils"
|
||||
@ -36,6 +34,7 @@ func (c *Config) Install(options options.InstallOptions) error {
|
||||
options.BootAssets.FillDefaults(options.Arch)
|
||||
|
||||
if err := utils.CopyFiles(
|
||||
options.Printf,
|
||||
utils.SourceDestination(options.BootAssets.KernelPath, filepath.Join(constants.BootMountPoint, string(c.Default), constants.KernelAsset)),
|
||||
utils.SourceDestination(options.BootAssets.InitramfsPath, filepath.Join(constants.BootMountPoint, string(c.Default), constants.InitramfsAsset)),
|
||||
); err != nil {
|
||||
@ -46,7 +45,7 @@ func (c *Config) Install(options options.InstallOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.Write(ConfigPath); err != nil {
|
||||
if err := c.Write(ConfigPath, options.Printf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -83,13 +82,9 @@ func (c *Config) Install(options options.InstallOptions) error {
|
||||
|
||||
args = append(args, blk)
|
||||
|
||||
log.Printf("executing: grub-install %s", strings.Join(args, " "))
|
||||
options.Printf("executing: grub-install %s", strings.Join(args, " "))
|
||||
|
||||
cmd := exec.Command("grub-install", args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err = cmd.Run(); err != nil {
|
||||
if _, err := cmd.Run("grub-install", args...); err != nil {
|
||||
return fmt.Errorf("failed to install grub: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -69,7 +70,7 @@ func (c *Config) Revert(ctx context.Context) error {
|
||||
return fmt.Errorf("cannot rollback to %q, label does not exist", "")
|
||||
}
|
||||
|
||||
if err := c.Write(ConfigPath); err != nil {
|
||||
if err := c.Write(ConfigPath, log.Printf); err != nil {
|
||||
return fmt.Errorf("failed to revert bootloader: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@ -27,6 +27,9 @@ type InstallOptions struct {
|
||||
|
||||
// Boot assets to install.
|
||||
BootAssets BootAssets
|
||||
|
||||
// Printf-like function to use.
|
||||
Printf func(format string, v ...any)
|
||||
}
|
||||
|
||||
// BootAssets describes the assets to be installed by the booloader.
|
||||
|
||||
@ -157,7 +157,7 @@ func (c *Config) Install(options options.InstallOptions) error {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("removing old UKI: %s", file)
|
||||
options.Printf("removing old UKI: %s", file)
|
||||
|
||||
if err = os.Remove(file); err != nil {
|
||||
return err
|
||||
@ -167,6 +167,7 @@ func (c *Config) Install(options options.InstallOptions) error {
|
||||
options.BootAssets.FillDefaults(options.Arch)
|
||||
|
||||
if err := utils.CopyFiles(
|
||||
options.Printf,
|
||||
utils.SourceDestination(options.BootAssets.UKIPath, filepath.Join(constants.EFIMountPoint, "EFI", "Linux", ukiPath)),
|
||||
utils.SourceDestination(options.BootAssets.SDBootPath, filepath.Join(constants.EFIMountPoint, "EFI", "boot", sdbootFilename)),
|
||||
); err != nil {
|
||||
@ -175,6 +176,8 @@ func (c *Config) Install(options options.InstallOptions) error {
|
||||
|
||||
// don't update EFI variables if we're installing to a loop device
|
||||
if !options.ImageMode {
|
||||
options.Printf("updating EFI variables")
|
||||
|
||||
efiCtx := efivario.NewDefaultContext()
|
||||
|
||||
// set the new entry as a default one
|
||||
|
||||
@ -822,6 +822,7 @@ func partitionAndFormatDisks(logger *log.Logger, r runtime.Runtime) error {
|
||||
m := &installer.Manifest{
|
||||
Devices: map[string]installer.Device{},
|
||||
Targets: map[string][]*installer.Target{},
|
||||
Printf: logger.Printf,
|
||||
}
|
||||
|
||||
for _, disk := range r.Config().Machine().Disks() {
|
||||
@ -1618,7 +1619,7 @@ func ResetSystemDiskSpec(_ runtime.Sequence, data any) (runtime.TaskExecutionFun
|
||||
}
|
||||
|
||||
for _, target := range in.GetSystemDiskTargets() {
|
||||
if err = target.Format(); err != nil {
|
||||
if err = target.Format(logger.Printf); err != nil {
|
||||
return fmt.Errorf("failed wiping partition %s: %w", target, err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,7 +167,7 @@ func SystemMountPointForLabel(ctx context.Context, device *blockdevice.BlockDevi
|
||||
if !o.MountFlags.Check(SkipIfNoFilesystem) {
|
||||
p.fstype = opts.FileSystemType
|
||||
|
||||
return partition.Format(p.source, opts)
|
||||
return partition.Format(p.source, opts, log.Printf)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@ -7,7 +7,6 @@ package partition
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/siderolabs/go-blockdevice/blockdevice"
|
||||
|
||||
@ -28,13 +27,13 @@ func NewFormatOptions(label string) *FormatOptions {
|
||||
}
|
||||
|
||||
// Format zeroes the device and formats it using filesystem type provided.
|
||||
func Format(devname string, t *FormatOptions) error {
|
||||
func Format(devname string, t *FormatOptions, printf func(string, ...any)) error {
|
||||
if t.FileSystemType == FilesystemTypeNone {
|
||||
return zeroPartition(devname)
|
||||
return zeroPartition(devname, printf)
|
||||
}
|
||||
|
||||
opts := []makefs.Option{makefs.WithForce(t.Force), makefs.WithLabel(t.Label)}
|
||||
log.Printf("formatting the partition %q as %q with label %q\n", devname, t.FileSystemType, t.Label)
|
||||
printf("formatting the partition %q as %q with label %q\n", devname, t.FileSystemType, t.Label)
|
||||
|
||||
switch t.FileSystemType {
|
||||
case FilesystemTypeVFAT:
|
||||
@ -47,8 +46,8 @@ func Format(devname string, t *FormatOptions) error {
|
||||
}
|
||||
|
||||
// zeroPartition fills the partition with zeroes.
|
||||
func zeroPartition(devname string) (err error) {
|
||||
log.Printf("zeroing out %q", devname)
|
||||
func zeroPartition(devname string, printf func(string, ...any)) (err error) {
|
||||
printf("zeroing out %q", devname)
|
||||
|
||||
part, err := blockdevice.Open(devname, blockdevice.WithExclusiveLock(true))
|
||||
if err != nil {
|
||||
|
||||
@ -137,7 +137,7 @@ func (suite *manifestSuite) TestZeroPartition() {
|
||||
|
||||
err = partition.Format(part, &partition.FormatOptions{
|
||||
FileSystemType: partition.FilesystemTypeNone,
|
||||
})
|
||||
}, suite.T().Logf)
|
||||
suite.Require().NoError(err)
|
||||
|
||||
// reading 10 times more than what we wrote should still return 0 since the partition is wiped
|
||||
|
||||
@ -6,8 +6,6 @@
|
||||
package partition
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/siderolabs/go-blockdevice/blockdevice/partition/gpt"
|
||||
|
||||
@ -40,8 +38,8 @@ func Locate(pt *gpt.GPT, label string) (*gpt.Partition, error) {
|
||||
|
||||
// Partition creates a new partition on the specified device.
|
||||
// Returns the path to the newly created partition.
|
||||
func Partition(pt *gpt.GPT, pos int, device string, partitionOpts Options) (string, error) {
|
||||
log.Printf("partitioning %s - %s %q\n", device, partitionOpts.PartitionLabel, humanize.Bytes(partitionOpts.Size))
|
||||
func Partition(pt *gpt.GPT, pos int, device string, partitionOpts Options, printf func(string, ...any)) (string, error) {
|
||||
printf("partitioning %s - %s %q\n", device, partitionOpts.PartitionLabel, humanize.Bytes(partitionOpts.Size))
|
||||
|
||||
opts := []gpt.PartitionOption{
|
||||
gpt.WithPartitionType(partitionOpts.PartitionType),
|
||||
@ -66,7 +64,7 @@ func Partition(pt *gpt.GPT, pos int, device string, partitionOpts Options) (stri
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Printf("created %s (%s) size %d blocks", partitionName, partitionOpts.PartitionLabel, part.Length())
|
||||
printf("created %s (%s) size %d blocks", partitionName, partitionOpts.PartitionLabel, part.Length())
|
||||
|
||||
return partitionName, nil
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ package uki
|
||||
import (
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/siderolabs/talos/internal/pkg/secureboot"
|
||||
)
|
||||
@ -23,8 +22,6 @@ func GetSBAT(path string) ([]byte, error) {
|
||||
|
||||
for _, section := range pefile.Sections {
|
||||
if section.Name == string(secureboot.SBAT) {
|
||||
log.Printf("section size: %d", section.Size)
|
||||
|
||||
data, err := section.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -75,7 +75,7 @@ type Builder struct {
|
||||
// - build ephemeral sections (uname, os-release), and other proposed sections
|
||||
// - measure sections, generate signature, and append to the list of sections
|
||||
// - assemble the final UKI file starting from sd-stub and appending generated section.
|
||||
func (builder *Builder) Build() error {
|
||||
func (builder *Builder) Build(printf func(string, ...any)) error {
|
||||
var err error
|
||||
|
||||
builder.scratchDir, err = os.MkdirTemp("", "talos-uki")
|
||||
@ -89,6 +89,8 @@ func (builder *Builder) Build() error {
|
||||
}
|
||||
}()
|
||||
|
||||
printf("signing systemd-boot")
|
||||
|
||||
builder.peSigner, err = pesign.NewSigner(builder.SigningCertPath, builder.SigningKeyPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error initilazing signer: %w", err)
|
||||
@ -99,6 +101,8 @@ func (builder *Builder) Build() error {
|
||||
return fmt.Errorf("error signing sd-boot: %w", err)
|
||||
}
|
||||
|
||||
printf("generating UKI sections")
|
||||
|
||||
// generate and build list of all sections
|
||||
for _, generateSection := range []func() error{
|
||||
builder.generateOSRel,
|
||||
@ -118,11 +122,15 @@ func (builder *Builder) Build() error {
|
||||
}
|
||||
}
|
||||
|
||||
printf("assembling UKI")
|
||||
|
||||
// assemble the final UKI file
|
||||
if err = builder.assemble(); err != nil {
|
||||
return fmt.Errorf("error assembling UKI: %w", err)
|
||||
}
|
||||
|
||||
printf("signing UKI")
|
||||
|
||||
// sign the UKI file
|
||||
return builder.peSigner.Sign(builder.unsignedUKIPath, builder.OutUKIPath)
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ func (builder *Builder) rebuildInitramfs(tempDir string) error {
|
||||
defer pipeW.Close() //nolint:errcheck
|
||||
|
||||
// build cpio image which contains .sqsh images and extensions.yaml
|
||||
cmd1 := exec.Command("cpio", "-H", "newc", "--create", "--reproducible")
|
||||
cmd1 := exec.Command("cpio", "-H", "newc", "--create", "--reproducible", "--quiet")
|
||||
cmd1.Dir = tempDir
|
||||
cmd1.Stdin = listing
|
||||
cmd1.Stdout = pipeW
|
||||
@ -51,7 +51,7 @@ func (builder *Builder) rebuildInitramfs(tempDir string) error {
|
||||
defer destination.Close() //nolint:errcheck
|
||||
|
||||
// append compressed initramfs.sysext to the original initramfs.xz, kernel can read such format
|
||||
cmd2 := exec.Command("xz", "-v", "-C", "crc32", "-0", "-e", "-T", "0", "-z")
|
||||
cmd2 := exec.Command("xz", "-v", "-C", "crc32", "-0", "-e", "-T", "0", "-z", "--quiet")
|
||||
cmd2.Dir = tempDir
|
||||
cmd2.Stdin = pipeR
|
||||
cmd2.Stdout = destination
|
||||
|
||||
@ -8,7 +8,6 @@ package imager
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@ -25,6 +24,7 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/machinery/config/merge"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/machinery/kernel"
|
||||
"github.com/siderolabs/talos/pkg/reporter"
|
||||
"github.com/siderolabs/talos/pkg/version"
|
||||
)
|
||||
|
||||
@ -80,7 +80,7 @@ func New(prof profile.Profile) (*Imager, error) {
|
||||
// Execute image generation.
|
||||
//
|
||||
//nolint:gocyclo,cyclop
|
||||
func (i *Imager) Execute(ctx context.Context, outputPath string) error {
|
||||
func (i *Imager) Execute(ctx context.Context, outputPath string, report *reporter.Reporter) error {
|
||||
var err error
|
||||
|
||||
i.tempDir, err = os.MkdirTemp("", "imager")
|
||||
@ -90,13 +90,18 @@ func (i *Imager) Execute(ctx context.Context, outputPath string) error {
|
||||
|
||||
defer os.RemoveAll(i.tempDir) //nolint:errcheck
|
||||
|
||||
report.Report(reporter.Update{
|
||||
Message: "profile ready:",
|
||||
Status: reporter.StatusSucceeded,
|
||||
})
|
||||
|
||||
// 0. Dump the profile.
|
||||
if err = i.prof.Dump(os.Stderr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 1. Transform `initramfs.xz` with system extensions
|
||||
if err = i.buildInitramfs(ctx); err != nil {
|
||||
if err = i.buildInitramfs(ctx, report); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -105,11 +110,14 @@ func (i *Imager) Execute(ctx context.Context, outputPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("assembled kernel command line: %s", i.cmdline)
|
||||
report.Report(reporter.Update{
|
||||
Message: fmt.Sprintf("kernel command line: %s", i.cmdline),
|
||||
Status: reporter.StatusSucceeded,
|
||||
})
|
||||
|
||||
// 3. Build UKI if Secure Boot is enabled.
|
||||
if i.prof.SecureBootEnabled() {
|
||||
if err = i.buildUKI(); err != nil {
|
||||
if err = i.buildUKI(report); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -117,21 +125,19 @@ func (i *Imager) Execute(ctx context.Context, outputPath string) error {
|
||||
// 4. Build the output.
|
||||
outputAssetPath := filepath.Join(outputPath, i.prof.OutputPath())
|
||||
|
||||
log.Printf("output path: %s", outputAssetPath)
|
||||
|
||||
switch i.prof.Output.Kind {
|
||||
case profile.OutKindISO:
|
||||
err = i.outISO(outputAssetPath)
|
||||
err = i.outISO(outputAssetPath, report)
|
||||
case profile.OutKindKernel:
|
||||
err = i.outKernel(outputAssetPath)
|
||||
err = i.outKernel(outputAssetPath, report)
|
||||
case profile.OutKindUKI:
|
||||
err = i.outUKI(outputAssetPath)
|
||||
err = i.outUKI(outputAssetPath, report)
|
||||
case profile.OutKindInitramfs:
|
||||
err = i.outInitramfs(outputAssetPath)
|
||||
err = i.outInitramfs(outputAssetPath, report)
|
||||
case profile.OutKindImage:
|
||||
err = i.outImage(ctx, outputAssetPath)
|
||||
err = i.outImage(ctx, outputAssetPath, report)
|
||||
case profile.OutKindInstaller:
|
||||
err = i.outInstaller(ctx, outputAssetPath)
|
||||
err = i.outInstaller(ctx, outputAssetPath, report)
|
||||
case profile.OutKindUnknown:
|
||||
fallthrough
|
||||
default:
|
||||
@ -142,17 +148,22 @@ func (i *Imager) Execute(ctx context.Context, outputPath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{
|
||||
Message: fmt.Sprintf("output asset path: %s", outputAssetPath),
|
||||
Status: reporter.StatusSucceeded,
|
||||
})
|
||||
|
||||
// 5. Post-process the output.
|
||||
switch i.prof.Output.OutFormat {
|
||||
case profile.OutFormatRaw:
|
||||
// do nothing
|
||||
return nil
|
||||
case profile.OutFormatXZ:
|
||||
return i.postProcessXz(outputAssetPath)
|
||||
return i.postProcessXz(outputAssetPath, report)
|
||||
case profile.OutFormatGZ:
|
||||
return i.postProcessGz(outputAssetPath)
|
||||
return i.postProcessGz(outputAssetPath, report)
|
||||
case profile.OutFormatTar:
|
||||
return i.postProcessTar(outputAssetPath)
|
||||
return i.postProcessTar(outputAssetPath, report)
|
||||
case profile.OutFormatUnknown:
|
||||
fallthrough
|
||||
default:
|
||||
@ -161,18 +172,25 @@ func (i *Imager) Execute(ctx context.Context, outputPath string) error {
|
||||
}
|
||||
|
||||
// buildInitramfs transforms `initramfs.xz` with system extensions.
|
||||
func (i *Imager) buildInitramfs(ctx context.Context) error {
|
||||
func (i *Imager) buildInitramfs(ctx context.Context, report *reporter.Reporter) error {
|
||||
if len(i.prof.Input.SystemExtensions) == 0 {
|
||||
report.Report(reporter.Update{
|
||||
Message: "skipped initramfs rebuild (no system extensions)",
|
||||
Status: reporter.StatusSkip,
|
||||
})
|
||||
|
||||
// no system extensions, happy path
|
||||
i.initramfsPath = i.prof.Input.Initramfs.Path
|
||||
|
||||
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
|
||||
tempInitramfsPath := filepath.Join(i.tempDir, "initramfs.xz")
|
||||
|
||||
if err := utils.CopyFiles(utils.SourceDestination(i.prof.Input.Initramfs.Path, tempInitramfsPath)); err != nil {
|
||||
if err := utils.CopyFiles(printf, utils.SourceDestination(i.prof.Input.Initramfs.Path, tempInitramfsPath)); err != nil {
|
||||
return fmt.Errorf("failed to copy initramfs: %w", err)
|
||||
}
|
||||
|
||||
@ -188,7 +206,7 @@ func (i *Imager) buildInitramfs(ctx context.Context) error {
|
||||
return fmt.Errorf("failed to create extension directory: %w", err)
|
||||
}
|
||||
|
||||
if err := ext.Extract(ctx, extensionDir, i.prof.Arch); err != nil {
|
||||
if err := ext.Extract(ctx, extensionDir, i.prof.Arch, printf); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -198,10 +216,19 @@ func (i *Imager) buildInitramfs(ctx context.Context) error {
|
||||
InitramfsPath: i.initramfsPath,
|
||||
Arch: i.prof.Arch,
|
||||
ExtensionTreePath: extensionsCheckoutDir,
|
||||
Printf: log.Printf,
|
||||
Printf: printf,
|
||||
}
|
||||
|
||||
return builder.Build()
|
||||
if err := builder.Build(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{
|
||||
Message: "initramfs ready",
|
||||
Status: reporter.StatusSucceeded,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// buildCmdline builds the kernel command line.
|
||||
@ -262,7 +289,9 @@ func (i *Imager) buildCmdline() error {
|
||||
}
|
||||
|
||||
// buildUKI assembles the UKI and signs it.
|
||||
func (i *Imager) buildUKI() error {
|
||||
func (i *Imager) buildUKI(report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "building UKI...", Status: reporter.StatusRunning})
|
||||
|
||||
i.sdBootPath = filepath.Join(i.tempDir, "systemd-boot.efi.signed")
|
||||
i.ukiPath = filepath.Join(i.tempDir, "vmlinuz.efi.signed")
|
||||
|
||||
@ -283,5 +312,14 @@ func (i *Imager) buildUKI() error {
|
||||
OutUKIPath: i.ukiPath,
|
||||
}
|
||||
|
||||
return builder.Build()
|
||||
if err := builder.Build(printf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{
|
||||
Message: "UKI ready",
|
||||
Status: reporter.StatusSucceeded,
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"text/template"
|
||||
@ -37,15 +36,16 @@ var grubCfgTemplate string
|
||||
// CreateGRUB creates a GRUB-based ISO image.
|
||||
//
|
||||
// This iso supports both BIOS and UEFI booting.
|
||||
func CreateGRUB(options GRUBOptions) error {
|
||||
func CreateGRUB(printf func(string, ...any), options GRUBOptions) error {
|
||||
if err := utils.CopyFiles(
|
||||
printf,
|
||||
utils.SourceDestination(options.KernelPath, filepath.Join(options.ScratchDir, "boot", "vmlinuz")),
|
||||
utils.SourceDestination(options.InitramfsPath, filepath.Join(options.ScratchDir, "boot", "initramfs.xz")),
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("creating grub.cfg")
|
||||
printf("creating grub.cfg")
|
||||
|
||||
var grubCfg bytes.Buffer
|
||||
|
||||
@ -76,11 +76,11 @@ func CreateGRUB(options GRUBOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = utils.TouchFiles(options.ScratchDir); err != nil {
|
||||
if err = utils.TouchFiles(printf, options.ScratchDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("creating ISO")
|
||||
printf("creating ISO image")
|
||||
|
||||
return grubMkrescue(options.OutPath, options.ScratchDir)
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ const (
|
||||
// The ISO created supports only booting in UEFI mode, and supports SecureBoot.
|
||||
//
|
||||
//nolint:gocyclo,cyclop
|
||||
func CreateUEFI(options UEFIOptions) error {
|
||||
func CreateUEFI(printf func(string, ...any), options UEFIOptions) error {
|
||||
if err := os.MkdirAll(options.ScratchDir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -60,10 +60,12 @@ func CreateUEFI(options UEFIOptions) error {
|
||||
isoSize = UKIISOSizeARM64
|
||||
}
|
||||
|
||||
if err := utils.CreateRawDisk(efiBootImg, isoSize); err != nil {
|
||||
if err := utils.CreateRawDisk(printf, efiBootImg, isoSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printf("creating vFAT EFI image")
|
||||
|
||||
fopts := []makefs.Option{
|
||||
makefs.WithLabel(constants.EFIPartitionLabel),
|
||||
makefs.WithReproducible(true),
|
||||
@ -130,10 +132,12 @@ func CreateUEFI(options UEFIOptions) error {
|
||||
}
|
||||
|
||||
// fixup directory timestamps recursively
|
||||
if err := utils.TouchFiles(options.ScratchDir); err != nil {
|
||||
if err := utils.TouchFiles(printf, options.ScratchDir); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
printf("creating ISO image")
|
||||
|
||||
if _, err := cmd.Run(
|
||||
"xorriso",
|
||||
"-as",
|
||||
|
||||
@ -28,25 +28,54 @@ import (
|
||||
"github.com/siderolabs/talos/pkg/imager/qemuimg"
|
||||
"github.com/siderolabs/talos/pkg/imager/utils"
|
||||
"github.com/siderolabs/talos/pkg/machinery/constants"
|
||||
"github.com/siderolabs/talos/pkg/reporter"
|
||||
)
|
||||
|
||||
func (i *Imager) outInitramfs(path string) error {
|
||||
return utils.CopyFiles(utils.SourceDestination(i.initramfsPath, path))
|
||||
func (i *Imager) outInitramfs(path string, report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "copying initramfs...", Status: reporter.StatusRunning})
|
||||
|
||||
if err := utils.CopyFiles(printf, utils.SourceDestination(i.initramfsPath, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: "initramfs output ready", Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) outKernel(path string) error {
|
||||
return utils.CopyFiles(utils.SourceDestination(i.prof.Input.Kernel.Path, path))
|
||||
func (i *Imager) outKernel(path string, report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "copying kernel...", Status: reporter.StatusRunning})
|
||||
|
||||
if err := utils.CopyFiles(printf, utils.SourceDestination(i.prof.Input.Kernel.Path, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: "kernel output ready", Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) outUKI(path string) error {
|
||||
return utils.CopyFiles(utils.SourceDestination(i.ukiPath, path))
|
||||
func (i *Imager) outUKI(path string, report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "copying kernel...", Status: reporter.StatusRunning})
|
||||
|
||||
if err := utils.CopyFiles(printf, utils.SourceDestination(i.ukiPath, path)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: "UKI output ready", Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) outISO(path string) error {
|
||||
func (i *Imager) outISO(path string, report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "building ISO...", Status: reporter.StatusRunning})
|
||||
|
||||
scratchSpace := filepath.Join(i.tempDir, "iso")
|
||||
|
||||
var err error
|
||||
|
||||
if i.prof.SecureBootEnabled() {
|
||||
return iso.CreateUEFI(iso.UEFIOptions{
|
||||
err = iso.CreateUEFI(printf, iso.UEFIOptions{
|
||||
UKIPath: i.ukiPath,
|
||||
SDBootPath: i.sdBootPath,
|
||||
|
||||
@ -57,50 +86,70 @@ func (i *Imager) outISO(path string) error {
|
||||
Arch: i.prof.Arch,
|
||||
Version: i.prof.Version,
|
||||
|
||||
ScratchDir: scratchSpace,
|
||||
OutPath: path,
|
||||
})
|
||||
} else {
|
||||
err = iso.CreateGRUB(printf, iso.GRUBOptions{
|
||||
KernelPath: i.prof.Input.Kernel.Path,
|
||||
InitramfsPath: i.initramfsPath,
|
||||
Cmdline: i.cmdline,
|
||||
|
||||
ScratchDir: scratchSpace,
|
||||
OutPath: path,
|
||||
})
|
||||
}
|
||||
|
||||
return iso.CreateGRUB(iso.GRUBOptions{
|
||||
KernelPath: i.prof.Input.Kernel.Path,
|
||||
InitramfsPath: i.initramfsPath,
|
||||
Cmdline: i.cmdline,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ScratchDir: scratchSpace,
|
||||
OutPath: path,
|
||||
})
|
||||
report.Report(reporter.Update{Message: "ISO ready", Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) outImage(ctx context.Context, path string) error {
|
||||
if err := i.buildImage(ctx, path); err != nil {
|
||||
func (i *Imager) outImage(ctx context.Context, path string, report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "creating disk image...", Status: reporter.StatusRunning})
|
||||
|
||||
if err := i.buildImage(ctx, path, printf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch i.prof.Output.ImageOptions.DiskFormat {
|
||||
case profile.DiskFormatRaw:
|
||||
return nil
|
||||
// nothing to do
|
||||
case profile.DiskFormatQCOW2:
|
||||
return qemuimg.Convert("raw", "qcow2", i.prof.Output.ImageOptions.DiskFormatOptions, path)
|
||||
if err := qemuimg.Convert("raw", "qcow2", i.prof.Output.ImageOptions.DiskFormatOptions, path, printf); err != nil {
|
||||
return err
|
||||
}
|
||||
case profile.DiskFormatVPC:
|
||||
return qemuimg.Convert("raw", "vpc", i.prof.Output.ImageOptions.DiskFormatOptions, path)
|
||||
if err := qemuimg.Convert("raw", "vpc", i.prof.Output.ImageOptions.DiskFormatOptions, path, printf); err != nil {
|
||||
return err
|
||||
}
|
||||
case profile.DiskFormatOVA:
|
||||
scratchPath := filepath.Join(i.tempDir, "ova")
|
||||
|
||||
return ova.CreateOVAFromRAW(fmt.Sprintf("%s-%s", i.prof.Platform, i.prof.Arch), path, i.prof.Arch, scratchPath, i.prof.Output.ImageOptions.DiskSize)
|
||||
if err := ova.CreateOVAFromRAW(fmt.Sprintf("%s-%s", i.prof.Platform, i.prof.Arch), path, i.prof.Arch, scratchPath, i.prof.Output.ImageOptions.DiskSize, printf); err != nil {
|
||||
return err
|
||||
}
|
||||
case profile.DiskFormatUnknown:
|
||||
fallthrough
|
||||
default:
|
||||
return fmt.Errorf("unsupported disk format: %s", i.prof.Output.ImageOptions.DiskFormat)
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: "disk image ready", Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) buildImage(ctx context.Context, path string) error {
|
||||
if err := utils.CreateRawDisk(path, i.prof.Output.ImageOptions.DiskSize); err != nil {
|
||||
func (i *Imager) buildImage(ctx context.Context, path string, printf func(string, ...any)) error {
|
||||
if err := utils.CreateRawDisk(printf, path, i.prof.Output.ImageOptions.DiskSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Print("attaching loopback device")
|
||||
printf("attaching loopback device")
|
||||
|
||||
var (
|
||||
loDevice string
|
||||
@ -112,7 +161,7 @@ func (i *Imager) buildImage(ctx context.Context, path string) error {
|
||||
}
|
||||
|
||||
defer func() {
|
||||
log.Println("detaching loopback device")
|
||||
printf("detaching loopback device")
|
||||
|
||||
if e := utils.Lodetach(loDevice); e != nil {
|
||||
log.Println(e)
|
||||
@ -136,6 +185,7 @@ func (i *Imager) buildImage(ctx context.Context, path string) error {
|
||||
UKIPath: i.ukiPath,
|
||||
SDBootPath: i.sdBootPath,
|
||||
},
|
||||
Printf: printf,
|
||||
}
|
||||
|
||||
if opts.Board == "" {
|
||||
@ -144,15 +194,21 @@ func (i *Imager) buildImage(ctx context.Context, path string) error {
|
||||
|
||||
installer, err := install.NewInstaller(ctx, cmdline, install.ModeImage, opts)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to create installer: %w", err)
|
||||
}
|
||||
|
||||
return installer.Install(ctx, install.ModeImage)
|
||||
if err := installer.Install(ctx, install.ModeImage); err != nil {
|
||||
return fmt.Errorf("failed to install: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
func (i *Imager) outInstaller(ctx context.Context, path string) error {
|
||||
baseInstallerImg, err := i.prof.Input.BaseInstaller.Pull(ctx, i.prof.Arch)
|
||||
func (i *Imager) outInstaller(ctx context.Context, path string, report *reporter.Reporter) error {
|
||||
printf := progressPrintf(report, reporter.Update{Message: "building installer...", Status: reporter.StatusRunning})
|
||||
|
||||
baseInstallerImg, err := i.prof.Input.BaseInstaller.Pull(ctx, i.prof.Arch, printf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -169,6 +225,8 @@ func (i *Imager) outInstaller(ctx context.Context, path string) error {
|
||||
|
||||
config := *configFile.Config.DeepCopy()
|
||||
|
||||
printf("creating empty image")
|
||||
|
||||
newInstallerImg := mutate.MediaType(empty.Image, types.OCIManifestSchema1)
|
||||
newInstallerImg = mutate.ConfigMediaType(newInstallerImg, types.OCIConfigJSON)
|
||||
|
||||
@ -189,6 +247,8 @@ func (i *Imager) outInstaller(ctx context.Context, path string) error {
|
||||
|
||||
var artifacts []filemap.File
|
||||
|
||||
printf("generating artifacts layer")
|
||||
|
||||
if i.prof.SecureBootEnabled() {
|
||||
artifacts = append(artifacts,
|
||||
filemap.File{
|
||||
@ -228,5 +288,13 @@ func (i *Imager) outInstaller(ctx context.Context, path string) error {
|
||||
return fmt.Errorf("failed to parse image reference: %w", err)
|
||||
}
|
||||
|
||||
return tarball.WriteToFile(path, ref, newInstallerImg)
|
||||
printf("writing image tarball")
|
||||
|
||||
if err := tarball.WriteToFile(path, ref, newInstallerImg); err != nil {
|
||||
return fmt.Errorf("failed to write image tarball: %w", err)
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: "installer container image ready", Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -141,18 +141,18 @@ const ovfTpl = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
// CreateOVAFromRAW creates an OVA from a RAW disk.
|
||||
//
|
||||
//nolint:gocyclo
|
||||
func CreateOVAFromRAW(name, path, arch, scratchPath string, diskSize int64) error {
|
||||
func CreateOVAFromRAW(name, path, arch, scratchPath string, diskSize int64, printf func(string, ...any)) error {
|
||||
if err := os.MkdirAll(scratchPath, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
vmdkPath := filepath.Join(scratchPath, name+".vmdk")
|
||||
|
||||
if err := utils.CopyFiles(utils.SourceDestination(path, vmdkPath)); err != nil {
|
||||
if err := utils.CopyFiles(printf, utils.SourceDestination(path, vmdkPath)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := qemuimg.Convert("raw", "vmdk", "compat6,subformat=streamOptimized,adapter_type=lsilogic", vmdkPath); err != nil {
|
||||
if err := qemuimg.Convert("raw", "vmdk", "compat6,subformat=streamOptimized,adapter_type=lsilogic", vmdkPath, printf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -5,13 +5,18 @@
|
||||
package imager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/siderolabs/go-cmd/pkg/cmd"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/reporter"
|
||||
)
|
||||
|
||||
func (i *Imager) postProcessTar(filename string) error {
|
||||
func (i *Imager) postProcessTar(filename string, report *reporter.Reporter) error {
|
||||
report.Report(reporter.Update{Message: "processing .tar.gz", Status: reporter.StatusRunning})
|
||||
|
||||
dir := filepath.Dir(filename)
|
||||
src := "disk.raw"
|
||||
|
||||
@ -25,21 +30,35 @@ func (i *Imager) postProcessTar(filename string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Remove(filepath.Join(dir, src))
|
||||
}
|
||||
|
||||
func (i *Imager) postProcessGz(filename string) error {
|
||||
if _, err := cmd.Run("pigz", "-6", filename); err != nil {
|
||||
if err := os.Remove(filepath.Join(dir, src)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: fmt.Sprintf("archive is ready: %s", outPath), Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) postProcessXz(filename string) error {
|
||||
if _, err := cmd.Run("xz", "-0", "-T", "0", filename); err != nil {
|
||||
func (i *Imager) postProcessGz(filename string, report *reporter.Reporter) error {
|
||||
report.Report(reporter.Update{Message: "compressing .gz", Status: reporter.StatusRunning})
|
||||
|
||||
if _, err := cmd.Run("pigz", "-6", "-f", filename); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s.gz", filename), Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *Imager) postProcessXz(filename string, report *reporter.Reporter) 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
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{Message: fmt.Sprintf("compression done: %s.xz", filename), Status: reporter.StatusSucceeded})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -144,8 +143,8 @@ func fileExists(path string) bool {
|
||||
}
|
||||
|
||||
// Pull the container asset to the path.
|
||||
func (c *ContainerAsset) Pull(ctx context.Context, arch string) (v1.Image, error) {
|
||||
log.Printf("pulling %s...", c.ImageRef)
|
||||
func (c *ContainerAsset) Pull(ctx context.Context, arch string, printf func(string, ...any)) (v1.Image, error) {
|
||||
printf("pulling %s...", c.ImageRef)
|
||||
|
||||
img, err := crane.Pull(c.ImageRef, crane.WithPlatform(&v1.Platform{
|
||||
Architecture: arch,
|
||||
@ -159,8 +158,8 @@ func (c *ContainerAsset) Pull(ctx context.Context, arch string) (v1.Image, error
|
||||
}
|
||||
|
||||
// Extract the container asset to the path.
|
||||
func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string) error {
|
||||
img, err := c.Pull(ctx, arch)
|
||||
func (c *ContainerAsset) Extract(ctx context.Context, destination, arch string, printf func(string, ...any)) error {
|
||||
img, err := c.Pull(ctx, arch, printf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -102,7 +102,7 @@ func (p *Profile) Validate() error {
|
||||
return fmt.Errorf("customization of meta partition is not supported for %s output", p.Output.Kind)
|
||||
}
|
||||
case OutKindUKI:
|
||||
if p.SecureBootEnabled() {
|
||||
if !p.SecureBootEnabled() {
|
||||
return fmt.Errorf("!secureboot is not supported for %s output", p.Output.Kind)
|
||||
}
|
||||
}
|
||||
|
||||
28
pkg/imager/progress.go
Normal file
28
pkg/imager/progress.go
Normal file
@ -0,0 +1,28 @@
|
||||
// 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 imager
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/siderolabs/talos/pkg/reporter"
|
||||
)
|
||||
|
||||
// progressPrintf wraps a reporter.Reporter to report progress via Printf logging.
|
||||
func progressPrintf(report *reporter.Reporter, status reporter.Update) func(format string, args ...any) {
|
||||
return func(format string, args ...any) {
|
||||
msg := status.Message
|
||||
extra := fmt.Sprintf(format, args...)
|
||||
|
||||
if extra != "" {
|
||||
msg += "\n\t" + extra
|
||||
}
|
||||
|
||||
report.Report(reporter.Update{
|
||||
Message: msg,
|
||||
Status: status.Status,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -12,10 +12,12 @@ import (
|
||||
)
|
||||
|
||||
// Convert converts an image from one format to another.
|
||||
func Convert(inputFmt, outputFmt, options, path string) error {
|
||||
func Convert(inputFmt, outputFmt, options, path string, printf func(string, ...any)) error {
|
||||
src := path + ".in"
|
||||
dest := path
|
||||
|
||||
printf("converting %s to %s", inputFmt, outputFmt)
|
||||
|
||||
if err := os.Rename(path, src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -7,7 +7,6 @@ package utils
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
@ -23,7 +22,7 @@ func SourceDestination(src, dest string) CopyInstruction {
|
||||
}
|
||||
|
||||
// CopyFiles copies files according to the given instructions.
|
||||
func CopyFiles(instructions ...CopyInstruction) error {
|
||||
func CopyFiles(printf func(string, ...any), instructions ...CopyInstruction) error {
|
||||
for _, instruction := range instructions {
|
||||
if err := func(instruction CopyInstruction) error {
|
||||
src, dest := instruction.F1, instruction.F2
|
||||
@ -32,7 +31,7 @@ func CopyFiles(instructions ...CopyInstruction) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("copying %s to %s", src, dest)
|
||||
printf("copying %s to %s", src, dest)
|
||||
|
||||
from, err := os.Open(src)
|
||||
if err != nil {
|
||||
@ -52,7 +51,7 @@ func CopyFiles(instructions ...CopyInstruction) error {
|
||||
|
||||
return err
|
||||
}(instruction); err != nil {
|
||||
return fmt.Errorf("error copying %s -> %s", instruction.F1, instruction.F2)
|
||||
return fmt.Errorf("error copying %s -> %s: %w", instruction.F1, instruction.F2, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"syscall"
|
||||
|
||||
@ -14,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
// CreateRawDisk creates a raw disk image of the specified size.
|
||||
func CreateRawDisk(path string, diskSize int64) error {
|
||||
log.Printf("creating raw disk of size %s", humanize.Bytes(uint64(diskSize)))
|
||||
func CreateRawDisk(printf func(string, ...any), path string, diskSize int64) error {
|
||||
printf("creating raw disk of size %s", humanize.Bytes(uint64(diskSize)))
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
|
||||
@ -6,14 +6,13 @@ package utils
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TouchFiles updates mtime for all the files under root if SOURCE_DATE_EPOCH is set.
|
||||
func TouchFiles(root string) error {
|
||||
func TouchFiles(printf func(string, ...any), root string) error {
|
||||
epochInt, ok, err := SourceDateEpoch()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -25,7 +24,7 @@ func TouchFiles(root string) error {
|
||||
|
||||
timestamp := time.Unix(epochInt, 0)
|
||||
|
||||
log.Printf("changing timestamps under %q to %s", root, timestamp)
|
||||
printf("changing timestamps under %q to %s", root, timestamp)
|
||||
|
||||
return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user