Worker node upgrade chain

This commit is contained in:
2026-04-29 19:28:18 +08:00
parent e1959bee6d
commit 2a1a5a8f08
9 changed files with 170 additions and 52 deletions

View File

@@ -272,11 +272,17 @@ func listTargetNodeNames(
})
if osu.Spec.NodeSelector != nil {
sel, err := metav1.LabelSelectorAsSelector(osu.Spec.NodeSelector)
userSelector, err := metav1.LabelSelectorAsSelector(osu.Spec.NodeSelector)
if err != nil {
return nil, fmt.Errorf("invalid nodeSelector: %w", err)
}
selector = sel
reqs, selectable := userSelector.Requirements()
if !selectable {
selector = labels.Nothing()
} else {
selector = selector.Add(reqs...)
}
}
list, err := clients.Kubernetes.CoreV1().

View File

@@ -0,0 +1,108 @@
package node
import (
"context"
"errors"
"fmt"
"os"
"strings"
"k8s.io/klog/v2"
"example.com/monok8s/pkg/system"
)
const kubeadmUpgradeNodeHostnameBugFixedIn = "v1.35.0"
// COMPAT(kubeadm-upgrade-node-hostname)
// Affects: Kubernetes/kubeadm < v1.35.0
// Upstream: kubernetes/kubeadm#3244, kubernetes/kubernetes#134319
// RemoveWhen: minimum supported Kubernetes version >= v1.35.0
//
// Affected kubeadm versions can derive the target Node name for
// `kubeadm upgrade node` from the local OS hostname instead of the existing
// kubeadm NodeRegistration / kubelet --hostname-override state.
func needsKubeadmUpgradeNodeHostnameWorkaround(kubeadmVersion string) bool {
lt, err := versionLt(kubeadmVersion, kubeadmUpgradeNodeHostnameBugFixedIn)
if err != nil {
klog.Warningf(
"could not parse kubeadm version %q; enabling kubeadm upgrade node hostname workaround: %v",
kubeadmVersion,
err,
)
return true
}
return lt
}
// runWithTemporaryHostname works around kubernetes/kubeadm#3244, fixed by
// kubernetes/kubernetes#134319 in Kubernetes v1.35.0.
//
// Affected kubeadm versions can derive the target Node name for
// `kubeadm upgrade node` from the local OS hostname instead of the existing
// kubeadm NodeRegistration / kubelet --hostname-override state. That breaks
// valid setups where the machine hostname differs from the Kubernetes Node
// name: kubeadm may authenticate as one node but try to get/patch another Node,
// and the Node authorizer correctly rejects it.
//
// Keep this workaround scoped to affected kubeadm versions only. Set the
// temporary hostname to the Kubernetes Node name, run kubeadm, then restore the
// configured machine hostname immediately afterward.
func runWithTemporaryHostname(ctx context.Context, nctx *NodeContext, fn func(context.Context) error) error {
if nctx == nil {
return errors.New("node context is nil")
}
temporaryHostname := strings.TrimSpace(nctx.Config.Spec.NodeName)
if temporaryHostname == "" {
return errors.New("temporary hostname is required")
}
originalHostname, err := os.Hostname()
if err != nil {
return fmt.Errorf("get current hostname: %w", err)
}
if originalHostname == temporaryHostname {
return fn(ctx)
}
restoreHostname := strings.TrimSpace(nctx.Config.Spec.Network.Hostname)
if restoreHostname == "" {
restoreHostname = originalHostname
}
klog.Warningf(
"temporarily changing hostname for kubeadm upgrade node: current=%q temporary=%q restore=%q",
originalHostname,
temporaryHostname,
restoreHostname,
)
if err := system.SetHostname(temporaryHostname); err != nil {
return fmt.Errorf("set temporary hostname to %q: %w", temporaryHostname, err)
}
defer func() {
if err := system.SetHostname(restoreHostname); err != nil {
klog.Errorf("failed to restore hostname to %q: %v", restoreHostname, err)
}
}()
return fn(ctx)
}
// COMPAT(kubeadm-upgrade-node-hostname)
// RemoveWhen: minimum supported Kubernetes version >= v1.35.0
func runKubeadmUpgradeNodeWithCompat(
ctx context.Context,
nctx *NodeContext,
kubeadmVersion string,
fn func(context.Context) error,
) error {
if needsKubeadmUpgradeNodeHostnameWorkaround(kubeadmVersion) {
return runWithTemporaryHostname(ctx, nctx, fn)
}
return fn(ctx)
}

View File

@@ -329,21 +329,28 @@ func RunKubeadmUpgradeNode(ctx context.Context, nctx *NodeContext) error {
kubeconfigPath,
}
_, err := nctx.SystemRunner.RunWithOptions(
ctx,
"kubeadm",
args,
system.RunOptions{
Timeout: 10 * time.Minute,
OnStdoutLine: func(line string) {
klog.Infof("[kubeadm] %s", line)
runKubeadm := func(ctx context.Context) error {
_, err := nctx.SystemRunner.RunWithOptions(
ctx,
"kubeadm",
args,
system.RunOptions{
Timeout: 10 * time.Minute,
OnStdoutLine: func(line string) {
klog.Infof("[kubeadm] %s", line)
},
OnStderrLine: func(line string) {
klog.Infof("[kubeadm] %s", line)
},
},
OnStderrLine: func(line string) {
klog.Infof("[kubeadm] %s", line)
},
},
)
if err != nil {
)
return err
}
// COMPAT(kubeadm-upgrade-node-hostname)
// RemoveWhen: minimum supported Kubernetes version >= v1.35.0
// Replace this wrapper with direct runKubeadm(ctx).
if err := runKubeadmUpgradeNodeWithCompat(ctx, nctx, wantVersion, runKubeadm); err != nil {
return fmt.Errorf("run kubeadm upgrade node: %w", err)
}

View File

@@ -203,6 +203,26 @@ func versionEq(a, b string) bool {
return normalizeKubeVersion(a) == normalizeKubeVersion(b)
}
func versionLt(a, b string) (bool, error) {
av, err := parseKubeVersion(a)
if err != nil {
return false, err
}
bv, err := parseKubeVersion(b)
if err != nil {
return false, err
}
if av.Major != bv.Major {
return av.Major < bv.Major, nil
}
if av.Minor != bv.Minor {
return av.Minor < bv.Minor, nil
}
return av.Patch < bv.Patch, nil
}
func normalizeKubeVersion(v string) string {
v = strings.TrimSpace(v)
if v == "" {