Compare commits
3 Commits
d84ddc491a
...
19625ba8c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 19625ba8c0 | |||
| 40bfac268d | |||
| 8fada1ebfe |
@ -0,0 +1,92 @@
|
||||
From a50511de74db08a0b4426be88bb52ce860a008a4 Mon Sep 17 00:00:00 2001
|
||||
From: builder <build@svrnty.io>
|
||||
Date: Fri, 22 May 2026 16:10:42 -0400
|
||||
Subject: [PATCH] grub: EFI-at-/boot fallback for BOOT-less SBC layout in
|
||||
Upgrade path
|
||||
|
||||
---
|
||||
.../v1alpha1/bootloader/grub/upgrade.go | 43 +++++++++++++++----
|
||||
1 file changed, 35 insertions(+), 8 deletions(-)
|
||||
|
||||
diff --git a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go
|
||||
index 9e3f86b84..e99bf2939 100644
|
||||
--- a/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go
|
||||
+++ b/internal/app/machined/pkg/runtime/v1alpha1/bootloader/grub/upgrade.go
|
||||
@@ -6,6 +6,7 @@ package grub
|
||||
|
||||
import (
|
||||
"context"
|
||||
+ "fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/siderolabs/go-blockdevice/v2/blkid"
|
||||
@@ -18,12 +19,12 @@ import (
|
||||
|
||||
// Upgrade copies new boot assets and updates grub configuration on an existing installation.
|
||||
func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, error) {
|
||||
- mountSpecs := []mount.Spec{
|
||||
- {
|
||||
- PartitionLabel: constants.BootPartitionLabel,
|
||||
- FilesystemType: partition.FilesystemTypeXFS,
|
||||
- MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint),
|
||||
- },
|
||||
+ var mountSpecs []mount.Spec
|
||||
+
|
||||
+ bootMountSpec := mount.Spec{
|
||||
+ PartitionLabel: constants.BootPartitionLabel,
|
||||
+ FilesystemType: partition.FilesystemTypeXFS,
|
||||
+ MountTarget: filepath.Join(opts.MountPrefix, constants.BootMountPoint),
|
||||
}
|
||||
|
||||
efiMountSpec := mount.Spec{
|
||||
@@ -32,6 +33,23 @@ func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, e
|
||||
MountTarget: filepath.Join(opts.MountPrefix, constants.EFIMountPoint),
|
||||
}
|
||||
|
||||
+ // check if the BOOT partition is present (absent on SBC layouts like RPi5/CM5)
|
||||
+ if err := mount.PartitionOp(
|
||||
+ opts.BootDisk,
|
||||
+ []mount.Spec{bootMountSpec},
|
||||
+ func() error {
|
||||
+ return nil
|
||||
+ },
|
||||
+ []blkid.ProbeOption{
|
||||
+ blkid.WithSkipLocking(true),
|
||||
+ },
|
||||
+ nil,
|
||||
+ nil,
|
||||
+ opts.BlkidInfo,
|
||||
+ ); err == nil {
|
||||
+ mountSpecs = append(mountSpecs, bootMountSpec)
|
||||
+ }
|
||||
+
|
||||
var efiFound bool
|
||||
|
||||
// check if the EFI partition is present
|
||||
@@ -49,12 +67,21 @@ func (c *Config) Upgrade(opts options.InstallOptions) (*options.InstallResult, e
|
||||
opts.BlkidInfo,
|
||||
); err == nil {
|
||||
efiFound = true
|
||||
- }
|
||||
|
||||
- if efiFound {
|
||||
+ if len(mountSpecs) == 0 {
|
||||
+ // No BOOT partition (SBC layout): mount EFI at /boot so GRUB
|
||||
+ // can find kernel/initramfs/grub.cfg in the expected place.
|
||||
+ efiMountSpec.MountTarget = filepath.Join(opts.MountPrefix, constants.BootMountPoint)
|
||||
+ c.bootFromEFI = true
|
||||
+ }
|
||||
+
|
||||
mountSpecs = append(mountSpecs, efiMountSpec)
|
||||
}
|
||||
|
||||
+ if len(mountSpecs) == 0 {
|
||||
+ return nil, fmt.Errorf("neither BOOT nor EFI partition found on disk %s", opts.BootDisk)
|
||||
+ }
|
||||
+
|
||||
err := mount.PartitionOp(
|
||||
opts.BootDisk,
|
||||
mountSpecs,
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,114 @@
|
||||
From 27501076fcb1e183e68313ef0e14092d7f403063 Mon Sep 17 00:00:00 2001
|
||||
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
|
||||
Date: Mon, 25 May 2026 17:10:13 -0400
|
||||
Subject: [PATCH] Wait for STATE volume on slow-init disks in config acquire
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
On slow-init storage (notably RPi CM5 eMMC, which takes ~2 minutes 13
|
||||
seconds from kernel boot to mmc0/CQE-enabled), the config acquire state
|
||||
machine's stateDisk step sees STATE in phase=missing during the early
|
||||
boot window and immediately transitions to stateEmbedded. With no
|
||||
embedded config either, the node falls through to maintenance mode and
|
||||
stays there even after the STATE volume later reaches phase=ready —
|
||||
the state machine is one-way and never re-enters stateDisk.
|
||||
|
||||
Result: in-place upgrades to v1.13.2 on CM5 hardware leave the node in
|
||||
maintenance with its on-disk config.yaml intact but unread, until a
|
||||
human runs `talosctl apply-config --insecure` to re-feed the same file
|
||||
that's already on the STATE filesystem.
|
||||
|
||||
This patch makes stateDisk tolerate transient phase=missing for up to
|
||||
5 minutes (stateMissingDiskTimeout) before falling through. The outer
|
||||
Run loop gets a 5-second ticker so the timeout can fire even when no
|
||||
further volume-status events arrive (e.g. truly missing STATE on a
|
||||
fresh install). Fast-init hardware sees no change — STATE reaches ready
|
||||
within seconds and the existing path runs.
|
||||
|
||||
Tested on RPi CM5 (eMMC, 6.12.47 RPi-downstream kernel) — boot path
|
||||
that previously dropped to maintenance now waits ~2m15s, sees STATE
|
||||
reach ready, and continues to stateDone with the persisted config.
|
||||
---
|
||||
.../pkg/controllers/config/acquire.go | 37 ++++++++++++++++++-
|
||||
1 file changed, 36 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/internal/app/machined/pkg/controllers/config/acquire.go b/internal/app/machined/pkg/controllers/config/acquire.go
|
||||
index a70fddb..7a3758f 100644
|
||||
--- a/internal/app/machined/pkg/controllers/config/acquire.go
|
||||
+++ b/internal/app/machined/pkg/controllers/config/acquire.go
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
+ "time"
|
||||
|
||||
"github.com/cosi-project/runtime/pkg/controller"
|
||||
"github.com/cosi-project/runtime/pkg/safe"
|
||||
@@ -86,8 +87,16 @@ type AcquireController struct {
|
||||
diskConfig config.Provider
|
||||
storedEmbeddedConfig []byte
|
||||
skipMaskingEmbeddedConfig bool
|
||||
+ firstSeenStateMissing time.Time
|
||||
}
|
||||
|
||||
+// stateMissingDiskTimeout is how long stateDisk will wait for the STATE volume
|
||||
+// to leave the "missing" phase before falling through to embedded/maintenance.
|
||||
+// On slow-init storage (e.g. RPi CM5 eMMC) the volume can take ~2-3 minutes to
|
||||
+// appear after kernel boot; transitioning to embedded too eagerly leaves the
|
||||
+// node permanently in maintenance even though the on-disk config is intact.
|
||||
+const stateMissingDiskTimeout = 5 * time.Minute
|
||||
+
|
||||
// Name implements controller.Controller interface.
|
||||
func (ctrl *AcquireController) Name() string {
|
||||
return "config.AcquireController"
|
||||
@@ -177,11 +186,17 @@ func (ctrl *AcquireController) Run(ctx context.Context, r controller.Runtime, lo
|
||||
// initialize with empty sources
|
||||
ctrl.configSourcesUsed = []string{}
|
||||
|
||||
+ // periodic wake-up so stateDisk can re-check slow-init volumes and trip
|
||||
+ // its timeout even when no resource events fire.
|
||||
+ tick := time.NewTicker(5 * time.Second)
|
||||
+ defer tick.Stop()
|
||||
+
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-r.EventCh():
|
||||
+ case <-tick.C:
|
||||
}
|
||||
|
||||
// check the spec first
|
||||
@@ -262,7 +277,27 @@ func (ctrl *AcquireController) stateDisk(ctx context.Context, r controller.Runti
|
||||
// wait for the status to be available
|
||||
return nil, nil, nil
|
||||
case stateVolumeStatus.TypedSpec().Phase == block.VolumePhaseMissing:
|
||||
- // STATE is missing, proceed to stateEmbedded
|
||||
+ // STATE is reported missing. On slow-init storage (e.g. RPi CM5 eMMC)
|
||||
+ // the volume can take a couple of minutes to appear after kernel boot,
|
||||
+ // so wait up to stateMissingDiskTimeout for it to reach ready before
|
||||
+ // falling through to embedded/maintenance.
|
||||
+ if ctrl.firstSeenStateMissing.IsZero() {
|
||||
+ ctrl.firstSeenStateMissing = time.Now()
|
||||
+ }
|
||||
+
|
||||
+ if time.Since(ctrl.firstSeenStateMissing) < stateMissingDiskTimeout {
|
||||
+ logger.Info("STATE volume not yet available, waiting",
|
||||
+ zap.Duration("elapsed", time.Since(ctrl.firstSeenStateMissing)),
|
||||
+ zap.Duration("timeout", stateMissingDiskTimeout),
|
||||
+ )
|
||||
+
|
||||
+ return nil, nil, nil
|
||||
+ }
|
||||
+
|
||||
+ logger.Warn("STATE volume still missing after timeout, proceeding to embedded",
|
||||
+ zap.Duration("waited", time.Since(ctrl.firstSeenStateMissing)),
|
||||
+ )
|
||||
+
|
||||
return ctrl.stateEmbedded, nil, nil
|
||||
case stateVolumeStatus.TypedSpec().Phase == block.VolumePhaseReady:
|
||||
// STATE is ready, proceed to to the action
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
From e2bacf8336c63a4d11a54f3978fc15e8bf4362ce Mon Sep 17 00:00:00 2001
|
||||
From: Mathias Beaulieu-Duncan <mathias@svrnty.io>
|
||||
Date: Mon, 25 May 2026 17:20:32 -0400
|
||||
Subject: [PATCH] imager: respect --insecure for Overlay and OverlayInstaller
|
||||
assets
|
||||
|
||||
The --insecure flag on talosctl imager (and the imager docker image) only
|
||||
applied to BaseInstaller, ImageCache, and SystemExtensions assets.
|
||||
Overlay.Image and Input.OverlayInstaller never received ForceInsecure,
|
||||
so pulling overlay images from an insecure (HTTP) registry failed with
|
||||
'http: server gave HTTP response to HTTPS client' even when --insecure
|
||||
was passed.
|
||||
|
||||
Set ForceInsecure on both overlay-related ContainerAsset references
|
||||
when the flag is present, matching how the other inputs are handled.
|
||||
---
|
||||
cmd/installer/cmd/imager/root.go | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/cmd/installer/cmd/imager/root.go b/cmd/installer/cmd/imager/root.go
|
||||
index d65b56089..ea16a45b3 100644
|
||||
--- a/cmd/installer/cmd/imager/root.go
|
||||
+++ b/cmd/installer/cmd/imager/root.go
|
||||
@@ -120,12 +120,14 @@ var rootCmd = &cobra.Command{
|
||||
prof.Overlay = &profile.OverlayOptions{
|
||||
Name: cmdFlags.OverlayName,
|
||||
Image: profile.ContainerAsset{
|
||||
- ImageRef: cmdFlags.OverlayImage,
|
||||
+ ImageRef: cmdFlags.OverlayImage,
|
||||
+ ForceInsecure: cmdFlags.Insecure,
|
||||
},
|
||||
ExtraOptions: extraOverlayOptions,
|
||||
}
|
||||
|
||||
prof.Input.OverlayInstaller.ImageRef = cmdFlags.OverlayImage
|
||||
+ prof.Input.OverlayInstaller.ForceInsecure = cmdFlags.Insecure
|
||||
}
|
||||
|
||||
prof.Input.SystemExtensions = xslices.Map(
|
||||
--
|
||||
2.50.1 (Apple Git-155)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user