Skip to content

Commit

Permalink
feat: implement Install for imager overlays
Browse files Browse the repository at this point in the history
Implement `Install` for imager overlays.
Also add support for generating installers.

Depends on: siderolabs#8377

Fixes: siderolabs#8350
Fixes: siderolabs#8351
Fixes: siderolabs#8350

Signed-off-by: Noel Georgi <git@frezbo.dev>
  • Loading branch information
frezbo committed Mar 12, 2024
1 parent e3c2a63 commit 747b063
Show file tree
Hide file tree
Showing 14 changed files with 319 additions and 81 deletions.
41 changes: 41 additions & 0 deletions cmd/installer/cmd/imager/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
package imager

import (
"bytes"
"context"
"fmt"
"os"
"runtime"
"strings"

"github.com/dustin/go-humanize"
"github.com/siderolabs/gen/xslices"
Expand All @@ -22,6 +24,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/machinery/overlay"
"github.com/siderolabs/talos/pkg/reporter"
)

Expand All @@ -39,6 +42,7 @@ var cmdFlags struct {
TarToStdout bool
OverlayName string
OverlayImage string
OverlayOptions []string
}

// rootCmd represents the base command when called without any subcommands.
Expand Down Expand Up @@ -76,12 +80,47 @@ var rootCmd = &cobra.Command{
},
}

extraOverlayOptions := overlay.ExtraOptions{}

for _, option := range cmdFlags.OverlayOptions {
if strings.HasPrefix(option, "@") {
data, err := os.ReadFile(option[1:])
if err != nil {
return err
}

decoder := yaml.NewDecoder(bytes.NewReader(data))
decoder.KnownFields(true)

if err := decoder.Decode(&extraOverlayOptions); err != nil {
return err
}

continue

}

k, v, _ := strings.Cut(option, "=")

if strings.HasPrefix(v, "@") {
data, err := os.ReadFile(v[1:])
if err != nil {
return err
}

v = string(data)
}

extraOverlayOptions[k] = v
}

if cmdFlags.OverlayName != "" || cmdFlags.OverlayImage != "" {
prof.Overlay = &profile.OverlayOptions{
Name: cmdFlags.OverlayName,
Image: profile.ContainerAsset{
ImageRef: cmdFlags.OverlayImage,
},
ExtraOptions: extraOverlayOptions,
}
}

Expand Down Expand Up @@ -176,6 +215,8 @@ func init() {
rootCmd.PersistentFlags().BoolVar(&cmdFlags.TarToStdout, "tar-to-stdout", false, "Tar output and send to stdout")
rootCmd.PersistentFlags().StringVar(&cmdFlags.OverlayName, "overlay-name", "", "The name of the overlay to use")
rootCmd.PersistentFlags().StringVar(&cmdFlags.OverlayImage, "overlay-image", "", "The image reference to the overlay")
rootCmd.PersistentFlags().StringArrayVar(&cmdFlags.OverlayOptions, "overlay-option", []string{}, "Extra options to pass to the overlay")
rootCmd.MarkFlagsMutuallyExclusive("board", "overlay-name")
rootCmd.MarkFlagsMutuallyExclusive("board", "overlay-image")
rootCmd.MarkFlagsMutuallyExclusive("board", "overlay-option")
}
3 changes: 1 addition & 2 deletions cmd/installer/cmd/installer/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ var options = &install.Options{}

var bootloader bool

//nolint:goconst
func init() {
rootCmd.PersistentFlags().StringVar(&options.ConfigSource, "config", "", "The value of "+constants.KernelParamConfig)
rootCmd.PersistentFlags().StringVar(&options.Disk, "disk", "", "The path to the disk to install to")
rootCmd.PersistentFlags().StringVar(&options.Platform, "platform", "", "The value of "+constants.KernelParamPlatform)
rootCmd.PersistentFlags().StringVar(&options.Arch, "arch", runtime.GOARCH, "The target architecture")
rootCmd.PersistentFlags().StringVar(&options.Board, "board", constants.BoardNone, "The value of "+constants.KernelParamBoard)
rootCmd.PersistentFlags().StringVar(&options.Board, "board", constants.BoardNone, "Deprecated: no op")
rootCmd.PersistentFlags().StringArrayVar(&options.ExtraKernelArgs, "extra-kernel-arg", []string{}, "Extra argument to pass to the kernel")
rootCmd.PersistentFlags().BoolVar(&bootloader, "bootloader", true, "Deprecated: no op")
rootCmd.PersistentFlags().BoolVar(&options.Upgrade, "upgrade", false, "Indicates that the install is being performed by an upgrade")
Expand Down
96 changes: 81 additions & 15 deletions cmd/installer/pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,50 @@
package install

import (
"bytes"
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"syscall"
"time"

"github.com/siderolabs/go-blockdevice/blockdevice"
"github.com/siderolabs/go-procfs/procfs"
"github.com/siderolabs/go-retry/retry"
"gopkg.in/yaml.v3"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/board"
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader"
bootloaderoptions "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/bootloader/options"
"github.com/siderolabs/talos/internal/pkg/meta"
"github.com/siderolabs/talos/internal/pkg/mount"
"github.com/siderolabs/talos/pkg/imager/overlay/executor"
"github.com/siderolabs/talos/pkg/machinery/constants"
"github.com/siderolabs/talos/pkg/machinery/kernel"
"github.com/siderolabs/talos/pkg/machinery/overlay"
"github.com/siderolabs/talos/pkg/machinery/version"
)

// Options represents the set of options available for an install.
type Options struct {
ConfigSource string
Disk string
Platform string
Arch string
Board string
ExtraKernelArgs []string
Upgrade bool
Force bool
Zero bool
LegacyBIOSSupport bool
MetaValues MetaValues
ConfigSource string
Disk string
Platform string
Arch string
Board string
ExtraKernelArgs []string
Upgrade bool
Force bool
Zero bool
LegacyBIOSSupport bool
MetaValues MetaValues
OverlayInstaller overlay.Installer[overlay.ExtraOptions]
OverlayExtractedDir string
ExtraOptions overlay.ExtraOptions

// Options specific for the image creation mode.
ImageSecureboot bool
Expand Down Expand Up @@ -68,7 +76,15 @@ func (m Mode) IsImage() bool {
}

// Install installs Talos.
func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options) (err error) {
//
//nolint:gocyclo
func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options) error {
overlayPresent := overlayPresent()

if b := getBoard(); b != constants.BoardNone && !overlayPresent {
return fmt.Errorf("using standard installer image is not supported for board: %s, use an installer with overlay", b)
}

cmdline := procfs.NewCmdline("")
cmdline.Append(constants.KernelParamPlatform, p.Name())

Expand All @@ -79,7 +95,7 @@ func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options)
cmdline.SetAll(p.KernelArgs().Strings())

// first defaults, then extra kernel args to allow extra kernel args to override defaults
if err = cmdline.AppendAll(kernel.DefaultArgs); err != nil {
if err := cmdline.AppendAll(kernel.DefaultArgs); err != nil {
return err
}

Expand All @@ -91,7 +107,7 @@ func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options)

var b runtime.Board

b, err = board.NewBoard(opts.Board)
b, err := board.NewBoard(opts.Board)
if err != nil {
return err
}
Expand All @@ -101,7 +117,7 @@ func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options)
cmdline.SetAll(b.KernelArgs().Strings())
}

if err = cmdline.AppendAll(
if err := cmdline.AppendAll(
opts.ExtraKernelArgs,
procfs.WithOverwriteArgs("console"),
procfs.WithOverwriteArgs(constants.KernelParamPlatform),
Expand All @@ -110,6 +126,25 @@ func Install(ctx context.Context, p runtime.Platform, mode Mode, opts *Options)
return err
}

if overlayPresent {
extraOptionsBytes, err := os.ReadFile(constants.ImagerOverlayExtraOptionsPath)
if err != nil {
return err
}

var extraOptions overlay.ExtraOptions

decoder := yaml.NewDecoder(bytes.NewReader(extraOptionsBytes))
decoder.KnownFields(true)

if err := decoder.Decode(&extraOptions); err != nil {
return fmt.Errorf("failed to decode extra options: %w", err)
}

opts.OverlayInstaller = executor.New(constants.ImagerOverlayInstallerDefaultPath)
opts.ExtraOptions = extraOptions
}

i, err := NewInstaller(ctx, cmdline, mode, opts)
if err != nil {
return err
Expand Down Expand Up @@ -302,6 +337,17 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) {
}
}

if i.options.OverlayInstaller != nil {
if err = i.options.OverlayInstaller.Install(overlay.InstallOptions[overlay.ExtraOptions]{
InstallDisk: i.options.Disk,
MountPrefix: i.options.MountPrefix,
ArtifactsPath: filepath.Join(i.options.OverlayExtractedDir, constants.ImagerOverlayArtifactsPath),
ExtraOptions: i.options.ExtraOptions,
}); err != nil {
return err
}
}

if mode == ModeUpgrade || len(i.options.MetaValues.values) > 0 {
var (
metaState *meta.Meta
Expand Down Expand Up @@ -394,3 +440,23 @@ func retryBlockdeviceOpen(device string) (*blockdevice.BlockDevice, error) {

return bd, err
}

func overlayPresent() bool {
_, err := os.Stat(constants.ImagerOverlayInstallerDefaultPath)

return err == nil
}

func getBoard() string {
cmdline := procfs.ProcCmdline()
if cmdline == nil {
return constants.BoardNone
}

board := cmdline.Get(constants.KernelParamBoard)
if board == nil {
return constants.BoardNone
}

return *board.First()
}
23 changes: 0 additions & 23 deletions internal/app/machined/pkg/runtime/v1alpha1/board/board.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
package board

import (
"errors"
"fmt"
"os"

"github.com/siderolabs/go-procfs/procfs"

"github.com/siderolabs/talos/internal/app/machined/pkg/runtime"
bananapim64 "github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/board/bananapi_m64"
Expand All @@ -25,25 +21,6 @@ import (
"github.com/siderolabs/talos/pkg/machinery/constants"
)

// CurrentBoard is a helper func for discovering the current board.
func CurrentBoard() (b runtime.Board, err error) {
var board string

if p := procfs.ProcCmdline().Get(constants.KernelParamBoard).First(); p != nil {
board = *p
}

if p, ok := os.LookupEnv("BOARD"); ok {
board = p
}

if board == "" {
return nil, errors.New("failed to determine board")
}

return newBoard(board)
}

// NewBoard initializes and returns a runtime.Board.
func NewBoard(board string) (b runtime.Board, err error) {
return newBoard(board)
Expand Down
4 changes: 0 additions & 4 deletions internal/pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,6 @@ func RunInstallerContainer(disk, platform, ref string, cfg configcore.Config, cf
"--zero=" + zero,
}

if c := procfs.ProcCmdline().Get(constants.KernelParamBoard).First(); c != nil {
args = append(args, "--board="+*c)
}

for _, arg := range options.ExtraKernelArgs {
args = append(args, "--extra-kernel-arg", arg)
}
Expand Down
23 changes: 10 additions & 13 deletions pkg/imager/imager.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/siderolabs/talos/internal/app/machined/pkg/runtime/v1alpha1/platform"
"github.com/siderolabs/talos/internal/pkg/secureboot/uki"
"github.com/siderolabs/talos/pkg/imager/extensions"
"github.com/siderolabs/talos/pkg/imager/internal/overlay/executor"
"github.com/siderolabs/talos/pkg/imager/overlay/executor"
"github.com/siderolabs/talos/pkg/imager/profile"
"github.com/siderolabs/talos/pkg/imager/quirks"
"github.com/siderolabs/talos/pkg/imager/utils"
Expand All @@ -38,7 +38,7 @@ import (
type Imager struct {
prof profile.Profile

overlayInstaller overlay.Installer
overlayInstaller overlay.Installer[overlay.ExtraOptions]

tempDir string

Expand Down Expand Up @@ -170,7 +170,7 @@ func (i *Imager) handleOverlay(ctx context.Context, report *reporter.Reporter) e
return nil
}

tempOverlayPath := filepath.Join(i.tempDir, "overlay")
tempOverlayPath := filepath.Join(i.tempDir, constants.ImagerOverlayBasePath)

if err := os.MkdirAll(tempOverlayPath, 0o755); err != nil {
return fmt.Errorf("failed to create overlay directory: %w", err)
Expand All @@ -180,19 +180,17 @@ func (i *Imager) handleOverlay(ctx context.Context, report *reporter.Reporter) e
return err
}

// find all *.yaml files in the tempOverlayPath/profiles/ directory
profileYAMLs, err := filepath.Glob(filepath.Join(tempOverlayPath, "profiles", "*.yaml"))
// find all *.yaml files in the overlay/profiles/ directory
profileYAMLs, err := filepath.Glob(filepath.Join(i.tempDir, constants.ImagerOverlayProfilesPath, "*.yaml"))
if err != nil {
return fmt.Errorf("failed to find profiles: %w", err)
}

installerName := i.prof.Overlay.Name

if installerName == "" {
installerName = "default"
if i.prof.Overlay.Name == "" {
i.prof.Overlay.Name = constants.ImagerOverlayInstallerDefault
}

i.overlayInstaller = executor.New(filepath.Join(tempOverlayPath, "installers", installerName))
i.overlayInstaller = executor.New(filepath.Join(i.tempDir, constants.ImagerOverlayInstallersPath, i.prof.Overlay.Name))

for _, profilePath := range profileYAMLs {
profileName := strings.TrimSuffix(filepath.Base(profilePath), ".yaml")
Expand Down Expand Up @@ -327,8 +325,7 @@ func (i *Imager) buildCmdline() error {
cmdline.SetAll(p.KernelArgs().Strings())

// board kernel args
// TODO: check if supports overlay quirk
if i.prof.Board != "" {
if i.prof.Board != "" && !quirks.New(i.prof.Version).SupportsOverlay() {
var b talosruntime.Board

b, err = board.NewBoard(i.prof.Board)
Expand All @@ -342,7 +339,7 @@ func (i *Imager) buildCmdline() error {

// overlay kernel args
if i.overlayInstaller != nil {
options, optsErr := i.overlayInstaller.GetOptions(i.prof.Overlay.Options)
options, optsErr := i.overlayInstaller.GetOptions(i.prof.Overlay.ExtraOptions)
if optsErr != nil {
return optsErr
}
Expand Down
Loading

0 comments on commit 747b063

Please sign in to comment.