Compare commits
No commits in common. "19625ba8c058997df07c3df8dbcaf5199fd8f948" and "d84ddc491afccc696595b001970a2dfe1cd899ac" have entirely different histories.
19625ba8c0
...
d84ddc491a
@ -1,92 +0,0 @@
|
|||||||
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)
|
|
||||||
|
|
||||||
@ -1,114 +0,0 @@
|
|||||||
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)
|
|
||||||
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
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