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 c0dc2f3
Show file tree
Hide file tree
Showing 14 changed files with 312 additions and 115 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
123 changes: 74 additions & 49 deletions cmd/installer/pkg/install/install.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,49 @@
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 +75,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,35 +94,36 @@ 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
}

if opts.Board != constants.BoardNone {
// board 'rpi_4' was removed in Talos 1.5 in favor of `rpi_generic`
if opts.Board == "rpi_4" {
opts.Board = constants.BoardRPiGeneric
}

var b runtime.Board
if err := cmdline.AppendAll(
opts.ExtraKernelArgs,
procfs.WithOverwriteArgs("console"),
procfs.WithOverwriteArgs(constants.KernelParamPlatform),
procfs.WithDeleteNegatedArgs(),
); err != nil {
return err
}

b, err = board.NewBoard(opts.Board)
if overlayPresent {
extraOptionsBytes, err := os.ReadFile(constants.ImagerOverlayExtraOptionsPath)
if err != nil {
return err
}

cmdline.Append(constants.KernelParamBoard, b.Name())
var extraOptions overlay.ExtraOptions

cmdline.SetAll(b.KernelArgs().Strings())
}
decoder := yaml.NewDecoder(bytes.NewReader(extraOptionsBytes))
decoder.KnownFields(true)

if err = cmdline.AppendAll(
opts.ExtraKernelArgs,
procfs.WithOverwriteArgs("console"),
procfs.WithOverwriteArgs(constants.KernelParamPlatform),
procfs.WithDeleteNegatedArgs(),
); err != nil {
return err
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)
Expand Down Expand Up @@ -280,23 +296,12 @@ func (i *Installer) Install(ctx context.Context, mode Mode) (err error) {
return err
}

if i.options.Board != constants.BoardNone {
var b runtime.Board

b, err = board.NewBoard(i.options.Board)
if err != nil {
return err
}

i.options.Printf("installing U-Boot for %q", b.Name())

if err = b.Install(runtime.BoardInstallOptions{
InstallDisk: i.options.Disk,
MountPrefix: i.options.MountPrefix,
UBootPath: i.options.BootAssets.UBootPath,
DTBPath: i.options.BootAssets.DTBPath,
RPiFirmwarePath: i.options.BootAssets.RPiFirmwarePath,
Printf: i.options.Printf,
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
}
Expand Down Expand Up @@ -394,3 +399,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
Loading

0 comments on commit c0dc2f3

Please sign in to comment.