diff --git a/Makefile b/Makefile index 2fba5a869..cfab41b5f 100644 --- a/Makefile +++ b/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). diff --git a/cmd/installer/cmd/imager/root.go b/cmd/installer/cmd/imager/root.go index 44cb2db60..dc6cb117a 100644 --- a/cmd/installer/cmd/imager/root.go +++ b/cmd/installer/cmd/imager/root.go @@ -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 |-", - Short: "Generate various boot assets and images.", - Long: ``, - Args: cobra.ExactArgs(1), + Use: "imager |-", + 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) } } diff --git a/cmd/installer/pkg/install/install.go b/cmd/installer/pkg/install/install.go index 6a158dafc..580fb892a 100644 --- a/cmd/installer/pkg/install/install.go +++ b/cmd/installer/pkg/install/install.go @@ -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 diff --git a/cmd/installer/pkg/install/manifest.go b/cmd/installer/pkg/install/manifest.go index 27a00f0e9..fd226ad4a 100644 --- a/cmd/installer/pkg/install/manifest.go +++ b/cmd/installer/pkg/install/manifest.go @@ -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) } } diff --git a/cmd/installer/pkg/install/manifest_test.go b/cmd/installer/pkg/install/manifest_test.go index 9338046eb..4a2d53fd1 100644 --- a/cmd/installer/pkg/install/manifest_test.go +++ b/cmd/installer/pkg/install/manifest_test.go @@ -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) diff --git a/cmd/installer/pkg/install/target.go b/cmd/installer/pkg/install/target.go index a3a05b695..63aefeea2 100644 --- a/cmd/installer/pkg/install/target.go +++ b/cmd/installer/pkg/install/target.go @@ -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. diff --git a/internal/app/machined/pkg/runtime/sequencer.go b/internal/app/machined/pkg/runtime/sequencer.go index 4d5170a9e..9288fb06e 100644 --- a/internal/app/machined/pkg/runtime/sequencer.go +++ b/internal/app/machined/pkg/runtime/sequencer.go @@ -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 } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/encode.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/encode.go index f8f62abd3..73af2a442 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/encode.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/encode.go @@ -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) } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub_test.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub_test.go index 89f9c2dc8..8c7dd1caa 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub_test.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/grub_test.go @@ -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()) diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go index d9343592a..7b04eca62 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/install.go @@ -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) } } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go index 0a84b7cbd..0fceacb67 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/revert.go @@ -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) } diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go index 8026d7c0d..d327e7d03 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options/options.go @@ -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. diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go index 30a147b37..5f9cb2fe5 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/sdboot/sdboot.go @@ -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 diff --git a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go index 6b4c8700a..fb15b1877 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/v1alpha1_sequencer_tasks.go @@ -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) } } diff --git a/internal/pkg/mount/system.go b/internal/pkg/mount/system.go index 36fdce249..3c498f5f5 100644 --- a/internal/pkg/mount/system.go +++ b/internal/pkg/mount/system.go @@ -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 diff --git a/internal/pkg/partition/format.go b/internal/pkg/partition/format.go index a94e3f2bb..d3e763618 100644 --- a/internal/pkg/partition/format.go +++ b/internal/pkg/partition/format.go @@ -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 { diff --git a/internal/pkg/partition/format_test.go b/internal/pkg/partition/format_test.go index d17ba1b1c..79ffe22f1 100644 --- a/internal/pkg/partition/format_test.go +++ b/internal/pkg/partition/format_test.go @@ -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 diff --git a/internal/pkg/partition/partition.go b/internal/pkg/partition/partition.go index 6b0ccd903..fcafa687a 100644 --- a/internal/pkg/partition/partition.go +++ b/internal/pkg/partition/partition.go @@ -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 } diff --git a/internal/pkg/secureboot/uki/sbat.go b/internal/pkg/secureboot/uki/sbat.go index e526f8b2c..d4331ad3d 100644 --- a/internal/pkg/secureboot/uki/sbat.go +++ b/internal/pkg/secureboot/uki/sbat.go @@ -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 diff --git a/internal/pkg/secureboot/uki/uki.go b/internal/pkg/secureboot/uki/uki.go index 6643085af..855e7e7ca 100644 --- a/internal/pkg/secureboot/uki/uki.go +++ b/internal/pkg/secureboot/uki/uki.go @@ -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) } diff --git a/pkg/imager/extensions/rebuild.go b/pkg/imager/extensions/rebuild.go index 5b8c2c585..8ded71a29 100644 --- a/pkg/imager/extensions/rebuild.go +++ b/pkg/imager/extensions/rebuild.go @@ -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 diff --git a/pkg/imager/imager.go b/pkg/imager/imager.go index 01c00b74d..47a187d2d 100644 --- a/pkg/imager/imager.go +++ b/pkg/imager/imager.go @@ -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 } diff --git a/pkg/imager/iso/grub.go b/pkg/imager/iso/grub.go index 2215dd1ad..3818811a1 100644 --- a/pkg/imager/iso/grub.go +++ b/pkg/imager/iso/grub.go @@ -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) } diff --git a/pkg/imager/iso/uefi.go b/pkg/imager/iso/uefi.go index c2a7de9fb..23c30621d 100644 --- a/pkg/imager/iso/uefi.go +++ b/pkg/imager/iso/uefi.go @@ -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", diff --git a/pkg/imager/out.go b/pkg/imager/out.go index e22ea5dbc..dbce75fad 100644 --- a/pkg/imager/out.go +++ b/pkg/imager/out.go @@ -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 } diff --git a/pkg/imager/ova/ova.go b/pkg/imager/ova/ova.go index f2e4e21c2..a619671fd 100644 --- a/pkg/imager/ova/ova.go +++ b/pkg/imager/ova/ova.go @@ -141,18 +141,18 @@ const ovfTpl = ` // 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 } diff --git a/pkg/imager/post.go b/pkg/imager/post.go index 28a71bab5..58214a19b 100644 --- a/pkg/imager/post.go +++ b/pkg/imager/post.go @@ -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 } diff --git a/pkg/imager/profile/input.go b/pkg/imager/profile/input.go index cefca1398..d189ca952 100644 --- a/pkg/imager/profile/input.go +++ b/pkg/imager/profile/input.go @@ -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 } diff --git a/pkg/imager/profile/profile.go b/pkg/imager/profile/profile.go index dbcc4bf2d..9817f52dd 100644 --- a/pkg/imager/profile/profile.go +++ b/pkg/imager/profile/profile.go @@ -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) } } diff --git a/pkg/imager/progress.go b/pkg/imager/progress.go new file mode 100644 index 000000000..a67aac442 --- /dev/null +++ b/pkg/imager/progress.go @@ -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, + }) + } +} diff --git a/pkg/imager/qemuimg/qemuimg.go b/pkg/imager/qemuimg/qemuimg.go index 1f2adcb0c..1de7f8859 100644 --- a/pkg/imager/qemuimg/qemuimg.go +++ b/pkg/imager/qemuimg/qemuimg.go @@ -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 } diff --git a/pkg/imager/utils/copy.go b/pkg/imager/utils/copy.go index a354d93b0..aacf4704c 100644 --- a/pkg/imager/utils/copy.go +++ b/pkg/imager/utils/copy.go @@ -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) } } diff --git a/pkg/imager/utils/raw.go b/pkg/imager/utils/raw.go index 7c876e40d..f0351d475 100644 --- a/pkg/imager/utils/raw.go +++ b/pkg/imager/utils/raw.go @@ -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 { diff --git a/pkg/imager/utils/touch.go b/pkg/imager/utils/touch.go index a19855c80..4fcf23133 100644 --- a/pkg/imager/utils/touch.go +++ b/pkg/imager/utils/touch.go @@ -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 {