Repository navigation
feat(lifecycle): standalone control plane lifecycle - #7416
Merged
Merged
Conversation
* 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>
Whoa! Easy there, Partner!This PR is too big. Please break it up into smaller PRs. |
cuisongliu
marked this pull request as ready for review
October 28, 2026 11:21
cuisongliu
approved these changes
Oct 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
fix(cherry-pick): Automated CherryPick 7344 for release-v5.1
Automated changes by create-pull-request GitHub action