Skip to content

feat(lifecycle): standalone control plane lifecycle - #7416

Merged
cuisongliu merged 1 commit into
release-v5.1from
cherry-pick-release-v5.1-7344
Oct 28, 2026
Merged

cuisongliu merged 1 commit into
release-v5.1from
cherry-pick-release-v5.1-7344

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

fix(cherry-pick): Automated CherryPick 7344 for release-v5.1

Automated changes by create-pull-request GitHub action

* feat: add standalone control plane lifecycle

* fix: do not block mode switch on route readiness

* feat: add standalone control-plane lifecycle

* chore(standalone): add fixture license headers

* fix(standalone): preserve fixture shebang

* fix(standalone): annotate intentional probes and commands

* fix(standalone): complete security checks and lifecycle regression

* test(e2e): allow K3s pods time to start

* test(e2e): align command option tests with current CLI

* test(e2e): capture host state before failure cleanup

* test(e2e): retain failure diagnostics under sudo

* fix(kubernetes): restart containers when reloading API certificates

* test(e2e): reserve service ports from ephemeral allocation

* test(e2e): capture K3s startup diagnostics before reset

* test(e2e): use K3s release with CoreDNS cache fix

* fix(lifecycle): preserve standalone recovery and reset behavior

* style(lifecycle): format recovery tests with repository formatters

* refactor: simplify standalone lifecycle helpers and documentation

* refactor: simplify lifecycle inventory and worker identity checks

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* refactor(lifecycle): group control-plane switches under dedicated subcommand

* fix(lifecycle): align standalone kubeconfig handling with upstream

* fix(lifecycle): satisfy lint after upstream rebase

* test(lifecycle): preallocate switch command arguments

* fix(lifecycle): remove stale Nodes when deleting standalone masters

* chore(lifecycle): use standard full license headers

* feat(lifecycle): persist custom route controller images

* fix lifecycle review findings

* chore(lifecycle): align standalone gosec annotations

* chore(lifecycle): annotate controlled upgrade command execution

* fix(lifecycle): preserve newer etcd during standalone upgrades

* style(lifecycle): format etcd preservation message

* docs(lifecycle): record sequential upgrade validation and port requirements

* fix(lifecycle): publish standalone worker config without a Node

* docs(lifecycle): shorten standalone usage and testing guides

* fix(lifecycle): sync sealctl before control-plane switch

* style(lifecycle): format sealctl switch synchronization

* fix(lifecycle): preserve best-effort standalone reset behavior

* fix(lifecycle): discard previous reset completion on mode conversion

* feat(lifecycle): provision standalone controller credentials

Generate dedicated read-only controller credentials for installation and mode conversion. Preserve native kubeadm component decoding during bootstrap and upgrades, and remove stale mode state during registered reset.

* refactor(lifecycle): reuse standalone reset file cleanup

* docs(lifecycle): remove hard wrapping from standalone paragraphs

* refactor(lifecycle): simplify standalone file checks and host execution

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* test(lifecycle): handle retained Nodes in membership checks

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* fix(lifecycle): refresh shared certificates before adding masters

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* test(lifecycle): cover both token cache refresh paths

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* fix(lifecycle): terminate temporary registry with sync context

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* docs(lifecycle): clarify regression coverage and network prerequisites

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* fix(lifecycle): preserve cluster signing material after standalone bootstrap

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>

* fix(lifecycle): recover fresh standalone installs from doubled defaults and early RBAC

Merging a Clusterfile that restates default values appended duplicate
enforceNodeAllocatable entries; kubelet rejects the duplicated list and
refuses to start during bootstrap. Deduplicate the list after each merge.

The controller credential readiness probe aborted on Forbidden before a
fresh API server bootstrapped its default RBAC roles, failing the whole
apply. Keep polling instead; the host context still bounds the wait.

* feat(switch): opt-in Node object cleanup for standalone conversion

Add --delete-master-nodes to 'sealos switch control-plane standalone'. Each
master deletes its own registered Node object right after its kubelet runs
standalone, guarded by the recorded registration UID, so the pod garbage
collector can remove orphaned workloads. The flag defaults to off and is
rejected for registered mode; reruns are idempotent.

* fix(switch): inherit and commit route-controller settings

A conversion to standalone only inherited routeController.image from the
committed Clusterfile, while a fresh standalone install honored every
field. Custom kubeconfig, config, table and protocol selections were
silently dropped: a conversion after adding a master fell back to
managed credentials without any warning, and an operator-provided
kubeconfig had to be repeated on every invocation.

Conversions now inherit each committed setting the operator did not
select with a flag, and the mode commit persists the effective settings
next to controlPlaneMode so later conversions and node additions reuse
them. Registered conversions keep leaving the committed settings
untouched.

* feat(switch): admission preflight for managed credentials

The --check-only conversion preflight validated host files but never
exercised admission, so a policy denying the managed route-controller
RBAC only surfaced after a conversion had started and paused mid-flight.

Preflight now server-side dry-runs every object the managed credential
flow creates: the ServiceAccount, ClusterRole, ClusterRoleBinding, Role,
RoleBinding and the token Secret. Validating admission sees dry-run
requests, so denials appear in --check-only output with the offending
object and webhook, while nothing is persisted. Existing objects are
accepted; controllers rendered with a custom kubeconfig keep skipping
credential work entirely.

The admin client construction is shared with the real credential flow,
and the token Secret dry-run carries the service-account name annotation
API validation requires.

* feat(switch): allow disabling the route controller

Clusters on cloud or overlay CNIs never consume the host routing table
the controller programs, and the control plane resolves pod IPs
directly, so deploying it there only costs an image, a static pod, and
a kubeconfig. Add --route-controller-enabled (spec.routeController.
enabled; nil keeps today's behavior) to sealos and sealctl.

Disabling stops the static pod, removes its manifest, and drops the
routes the controller owned; managed credentials are left in place for
a later re-enable. The choice is committed to the Clusterfile and
inherited by later conversions, installs, and joins, and every
bootstrap, readiness, and credential step is skipped while disabled.
Masters lose direct Service ClusterIP access, which is the documented
trade-off.

* feat(switch): clean up credentials when disabling route-controller

Disabling stopped the static pod, its manifest, and its routes, but
left the managed kubeconfig on every host and the shared
ServiceAccount, token Secret, and RBAC objects in the API. A disabled
cluster therefore kept a live credential it no longer used.

A disabling update now records whether the replaced deployment used
managed credentials and drops the generated kubeconfig with the rest
of the host state. Once every host of a switch finishes disabled, the
operator-side sealos deletes the shared identity; deleting it per host
would revoke the token controllers on the remaining hosts still use.
Objects without the managed label are refused, missing ones count as
removed, and re-enabling recreates everything from scratch.

* feat(switch): persist route-controller CIDR selection

The controller scopes its routes with --pod-cidr, --service-cidr and
--router-cidr, but sealos only offered a config file path: the file had
to be distributed to every host by hand, a later switch regenerated
the manifest around it, and a joining master failed its preflight
until the file appeared. Operator policy like the worker CIDR allowed
to serve as a next hop deserves first-class management.

Add --route-controller-{pod,service,router}-cidr to sealos and sealctl.
Values are validated at the CLI, rendered as controller arguments,
inherited from the committed Clusterfile by later conversions, installs
and joins, and written back as spec.routeController.{podCIDR,
serviceCIDR,routerCIDR}. Passing an explicit empty flag clears a
selection and removes the key, restoring automatic discovery; an unset
flag keeps the saved value. --interface stays per-host and is not
persisted.

* fix(switch): keep the disabled baseline consistent on bootstrap

Installing a standalone cluster with the controller disabled wrote a
baseline state whose ControllerDisabled stayed false while its manifest
was empty: controller-config then failed with an invalid baseline, and
a joining master consumed that broken seed. The join path also tried
to pre-pull the controller image before bootstrap even when the plan
deployed none, failing on the empty image name.

Record ControllerDisabled from the bootstrap plan next to the manifest
it explains, and pull the controller image only when the plan enables
it. Verified by reinstalling the cluster from scratch with the
controller disabled and joining two masters: no manifest, no
credentials, no readiness wait, and a usable controller-config.

* test(e2e): pull benchmark images from mirror.gcr.io

The public ECR gallery caps anonymous data transfer and shared GitHub
runner addresses have exhausted the quota, so every run fails the
remote pull specs with 429 toomanyrequests. mirror.gcr.io serves the
same Docker Hub images without an anonymous quota; all four benchmark
tags are available there.

---------

Signed-off-by: zijiren233 <pyh1670605849@gmail.com>
@pull-request-size

Copy link
Copy Markdown

Whoa! Easy there, Partner!

This PR is too big. Please break it up into smaller PRs.

@cuisongliu
cuisongliu marked this pull request as ready for review October 28, 2026 11:21
@cuisongliu
cuisongliu requested a review from a team as a code owner October 28, 2026 11:21
@cuisongliu cuisongliu added this to the v5.1 milestone Oct 28, 2026
@cuisongliu
cuisongliu merged commit 08380a2 into release-v5.1 Oct 28, 2026
13 checks passed
@cuisongliu
cuisongliu deleted the cherry-pick-release-v5.1-7344 branch October 28, 2026 11:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants