Help us shape the future of Go! Take the 2026 Go Developer Survey

certs

package
v1.37.1-stratum.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2026 License: Apache-2.0 Imports: 70 Imported by: 0

Documentation

Overview

Cross-node orchestration for `stratum certs rotate-ca --cluster`. It drives the three CA-rotation phases across every node PHASE-MAJOR — every node completes phase N before any node starts phase N+1 — which is what keeps etcd peers and kubelet clients from ever facing a cert signed by a CA a peer doesn't yet trust.

Reuses the same mechanism as `stratum upgrade --cluster`: a privileged, node-pinned Job that nsenters PID 1 and runs the on-host `stratum` binary. It does NOT drain or cordon (CA rotation is not workload-disruptive). New CA material is generated once on the initiator and distributed to the other control planes via the existing encrypted stratum-certs Secret (keyed by a transient stratum-carotate Secret).

CA rotation (`stratum certs rotate-ca`) — the on-demand, cluster-wide rotation of the trust anchors the leaf rotation deliberately leaves alone: the root CA, the front-proxy CA, and the ServiceAccount signing keypair. It runs in three phases that must each be applied to EVERY node before the next (RotateCACluster, or a careful operator, enforces the ordering):

trust-new  every node trusts old‖new (bundles); signer still old
sign-new   control planes promote the new signer + reissue all leaves
finalize   every node drops the old CA; only new remains

This file is the single-node executor for one phase; the cross-node orchestrator lives in cacluster.go.

Package certs orchestrates control-plane certificate rotation on a node: inspect expiry (check), and force-reissue the leaf certs from the existing CA + rewrite the kubeconfigs + restart the control-plane units with health gates (rotate). Leaf-only — it never touches the root/front-proxy CAs or the SA signing keypair (those are the heavier cluster-wide "CA rotation" flow).

The per-node half of `stratum certs migrate-etcd-ca`: one phase, on this node, followed by the restart the phase requires.

internal/pki owns the file surgery and every safety guard; this file owns the two things a primitive deliberately does not — deciding whether the phase is already applied, and restarting the component whose in-memory state the write does not reach. pki.MigrateEtcdTrustBoth's doc comment states the second obligation outright: "Nothing in-tree owns that restart yet ... So the restart is the caller's responsibility." This is that caller.

The cluster driver (etcdcacluster.go) invokes this same code path on every control plane, through a Job, so a `--cluster` run and three hand-run `--phase` invocations do exactly the same thing on each node.

Cross-node orchestration for `stratum certs migrate-etcd-ca --cluster` walks trust-both → issue-new → drop-root across every control plane PHASE-MAJOR — every node completes phase N before any node starts phase N+1 — which is what stops a member presenting a leaf its peers do not yet trust and losing quorum. internal/pki/etcdca.go states the requirement.

Same mechanism as `stratum certs rotate-ca --cluster` and `stratum upgrade --cluster`: a privileged, node-pinned Job that nsenters PID 1 and runs the on-host `stratum` binary. It does NOT drain or cordon — no workload is disrupted — and it touches control planes only, because a worker holds no etcd leaf and no etcd CA. The etcd CA is minted once on the initiator and distributed to the other control planes over the existing encrypted stratum-certs Secret, keyed by the transient stratum-carotate Secret.

A cluster-scoped custom resource carrying a command would grant root on every node, so the per-node Job is the house pattern; this reuses it rather than introducing a CR.

Where the migration state lives

Nowhere. There is no journal, no CR, no ConfigMap and no phase counter. State is re-derived at the start of every run, and again before the gate, by asking each node's RUNNING etcd what it trusts (pki.ProbeEtcdTrust). A second store could disagree with what etcd actually enforces, and the disagreement that matters — a bundle written whose restart never took — is precisely the one a counter would record as success.

Mounted ServiceAccount tokens and the old signing key. Finalize drops the old ServiceAccount key from the apiserver's verification set, so a token still signed by it gets 401 from that moment. After sign-new every token the apiserver issues carries the new key's id, but one that was issued before (a kubelet restarted while another control plane still signed with the old key) stays mounted until the kubelet re-issues it. This file finds those tokens on a node, which is what the finalize gate runs on every node.

In-cluster API clients and CA rotation. A client that loads the cluster CA once, at start, fails with x509 errors from sign-new until it restarts. It can restart safely only after kube-controller-manager has republished kube-root-ca.crt as old‖new, which it does once trust-new is on every node, so the restart goes between trust-new and sign-new. This file finds those clients, waits for the republish, rolls what Stratum owns, and gates on what it does not.

Index

Constants

View Source
const (
	PhaseTrustNew = "trust-new"
	PhaseSignNew  = "sign-new"
	PhaseFinalize = "finalize"
)

Phase names for CA rotation.

View Source
const (
	CARotateClusterWarning = "WARNING: workloads that use in-cluster API config (operators, cert-manager, ingress controllers, Cilium)\n" +
		"         keep the CA they started with, so from sign-new they fail with x509 errors until restarted.\n" +
		"         Stratum restarts its own add-ons before sign-new. Your Deployments, DaemonSets and StatefulSets\n" +
		"         are restarted before sign-new with --restart-workloads; with --skip-workload-check they are\n" +
		"         not, and you restart them once, when this command completes.\n" +
		"         A client that re-reads its service-account token needs no restart after sign-new, if it was\n" +
		"         restarted before it.\n" +
		"         Finalize refuses while any node still mounts a token signed with the old key. That scan reads\n" +
		"         mounted token files, so it cannot see a client that caches its token in memory: that client\n" +
		"         holds an old-key token even after a restart before sign-new, and is rejected with 401 once\n" +
		"         finalize drops the key. Restart it after sign-new has completed on every control plane and\n" +
		"         before finalize. This command runs finalize itself, so use --phase runs for that window;\n" +
		"         otherwise restart it when this command completes.\n" +
		"         Tokens from kubernetes.io/service-account-token Secrets do not rotate: recreate those\n" +
		"         Secrets (and any kubeconfig built from them) after finalize; a restart does not help.\n\n"
	CARotatePhaseWarning = "WARNING: workloads that use in-cluster API config (operators, cert-manager, ingress controllers, Cilium)\n" +
		"         keep the CA they started with, so from sign-new they fail with x509 errors until restarted.\n" +
		"         Restart all of them, Stratum's own add-ons included, after trust-new has completed on every\n" +
		"         node and before sign-new; sign-new refuses until they have, unless you pass --skip-workload-check.\n" +
		"         A client that re-reads its service-account token needs no restart after sign-new, if it was\n" +
		"         restarted before it.\n" +
		"         A client that caches its service-account token in memory holds an old-key token even after\n" +
		"         that restart, and is rejected with 401 once finalize drops the key. Restart it after sign-new\n" +
		"         has completed on every control plane and before finalize.\n" +
		"         --phase=finalize on a control plane refuses while that node mounts a token signed with the\n" +
		"         old key. It checks that control plane only, and finalize on a worker does not check: scan\n" +
		"         every node before the first control plane finalizes. The scan reads mounted token files, so\n" +
		"         it does not see a client that caches its token in memory.\n" +
		"         Tokens from kubernetes.io/service-account-token Secrets do not rotate: recreate those\n" +
		"         Secrets (and any kubeconfig built from them) after finalize; a restart does not help.\n\n"
)

The restart warnings are printed by the CLI and returned in the MCP result before a rotating (not rollback) rotate-ca run. Workloads using in-cluster API config cache the CA they loaded at start, so from sign-new they fail with x509 errors until restarted: the restart belongs after trust-new publishes the new CA and before sign-new. --cluster does it itself for the add-ons Stratum ships, and for the user's Deployments, DaemonSets and StatefulSets with --restart-workloads; without it --cluster refuses to start while there are any, unless --skip-workload-check leaves them to the operator (one restart, once the run completes). A --phase run rolls nothing, so --phase=sign-new refuses until every workload has restarted, Stratum's own included. A pod restarted before sign-new gets an old-key token, which its client re-reads when the kubelet refreshes it, so nothing needs restarting after sign-new for those. The residual is a client that caches its token in memory, which still holds an old-key token after a restart before sign-new and which the finalize gate cannot see (it scans the tokens mounted as files): it needs a restart after sign-new has completed on every control plane and before finalize. --cluster runs finalize itself, so only --phase runs leave that window, and a --cluster operator restarts it after the run. The gate is cluster-wide under --cluster, but a --phase=finalize run checks only its own control plane, and a worker's finalize not at all. The other residual is the tokens of legacy kubernetes.io/service-account-token Secrets, which never rotate and which a restart never fixes. The per-node Jobs of a --cluster run execute --phase, so their logs carry the --phase text.

View Source
const EtcdPhaseReAddRoot = "re-add-root"

EtcdPhaseReAddRoot is the first pass of a forced un-migration: widen trust cluster-wide before any leaf moves. It is a certs-level phase rather than a pki one because it exists purely to give the ORCHESTRATOR an ordering it can make safe; a single node has no use for it.

View Source
const ExitRollbackUnsafe = 11

ExitRollbackUnsafe is the exit status of `rotate-ca --rollback --check` on a node that cannot roll back, so the cluster-wide check tells a refusal from a Job that failed to run.

View Source
const ExitStaleTokens = 10

ExitStaleTokens is the exit status of `rotate-ca --scan-tokens` on a node that mounts a token signed by the old key. The scan Job's answer is its exit status, as the upgrade --check Job's is (internal/upgrade.ExitUpgradeNeeded).

View Source
const KubeletClientName = "kubelet-client"

KubeletClientName is the `certs check` row for a worker's CSR-issued kubelet client cert.

Variables

CARotatePhases are the CA-rotation phases, in the only order they may run. Both front ends name the phases from this list, so a phase that is added, dropped or renamed here changes what every front end accepts and prints.

View Source
var ErrStaleTokens = errors.New("this node mounts a token signed by the old service-account key")

ErrStaleTokens is ScanTokens' error when it found such a token.

EtcdCAAllPhases is every phase a single-node migrate-etcd-ca accepts: the forward phases plus rollback, the way back. The cluster orchestrator runs only EtcdCAPhases, in EtcdCAPhases' order; rollback is never a scheduled step, and re-add-root exists only inside a forced --cluster un-migration.

EtcdCAPhases are the forward phases, in the only order they may be run.

KubeletCAPhases are the forward phases of `certs migrate-kubelet-client-ca`, in order.

KubeletCARollbackPhases undo them, in order.

Functions

func CARotateKeyStaged

func CARotateKeyStaged() (bool, error)

CARotateKeyStaged reports whether the transient stratum-carotate Secret is present in the cluster. `certs rotate-ca --cluster` and the etcd-CA migration create it to hand follower control planes the key that decrypts stratum-certs; while it exists, re-encrypting stratum-certs under a new key would strand those followers. Unlike RotationPhase() (this node's local PKI files, which read "idle" on a follower the initiator has not reached yet), this is a cluster-wide signal visible from any control plane — what `certs check` reads to flag a key that outlived its run. The re-key guard reads the Secret and the shared rotation record itself (GuardRekey).

func CheckRollback

func CheckRollback(w io.Writer) error

CheckRollback reports whether THIS node can roll back, and changes nothing. A control plane answers with cpRollbackCheck, the whole of RollbackCA's refusal logic; a worker holds no CA and refreshes its trust from the control planes, so it can unless workerRollbackCheck refuses. It first prints the node's rollbackState.

func CheckRollbackCluster

func CheckRollbackCluster(ctx context.Context, w io.Writer, buildClient ...clientBuilder) error

CheckRollbackCluster asks every node, control planes first, whether it can roll back, and refuses if one cannot. It takes no lease and changes nothing on any node: the point is to find a node that lost an old key before any node rolls back, because the healthy nodes would return to the old root while that one can only go forward, and the cluster would be split across two roots. Run it from a control plane, before `--rollback --yes` on any node.

func FinalizePreflight

func FinalizePreflight(ctx context.Context, w io.Writer, cs kubernetes.Interface, nodes []cnode) error

FinalizePreflight refuses to drop the old CA unless the cluster is provably healthy on the new signer: every node Ready, no pending CSRs (a worker awaiting its new-CA client cert), every CP's apiserver serving, and every kubelet serving a cert the new CA signed (checkKubeletServingTrusted). Because the orchestrator halts on the first node failure, reaching here already implies every node completed sign-new; this is the explicit safety re-check. The mounted-token arm of the preflight (finalizeTokenGateForKey) runs after it.

func GuardRekey

func GuardRekey() error

GuardRekey is the one re-key entry point for every path that mints a new certificate-key and re-encrypts stratum-certs — `certs rotate --rekey-shared`, `token create --control-plane` and the MCP token_create tool. A re-key while a cluster CA rotation or etcd-CA migration is in flight strands the key staged in stratum-carotate that the flow's follower control planes decrypt stratum-certs with, so the mint must refuse.

The guard answers from two signals, the cheap one first: this node's rotation phase (local PKI files, which catch a rotation staged on THIS node), and — because RotationPhase() reads "idle" on a follower the initiator has not reached yet — the cluster-wide stratum-carotate Secret or an unfinished stratum-ca-rotation record, visible from any control plane: the record also covers a halted run whose key is gone, which no one is waiting out, so that refusal names the resume and the record to clear after a completed rollback. An unreadable staged-key check refuses the mint rather than wave it through: an apiserver failure is not evidence that nothing is staged.

The orchestrators' own re-uploads (rotate-ca --cluster, migrate-etcd-ca) do not consult this guard: they run under the shared maintenance lease, stage the key themselves and re-encrypt deliberately.

func GuardRekeyWithClient

func GuardRekeyWithClient(cs kubernetes.Interface) error

GuardRekeyWithClient uses the caller's credentials for cluster-wide checks.

func IncompleteRotation

func IncompleteRotation() (present, expired bool)

IncompleteRotation reports whether a leaf rotation reissued leaves but has not yet restarted every component onto them, and whether that rotation began from expired leaves. A marker that cannot be read still counts as present.

func IsRollbackUnsafe

func IsRollbackUnsafe(err error) bool

IsRollbackUnsafe reports whether err is a refusal to roll back, from this node's check or from the cluster-wide one, so a front end can exit with ExitRollbackUnsafe.

func MigrateEtcdCA

func MigrateEtcdCA(w io.Writer, o EtcdCAOptions) error

MigrateEtcdCA runs one phase on this node.

The node maintenance lock comes before anything is decided about the phase — before the node identity, the applied-phase check, the shared-CA fetch or the first write — so an invocation beside a live upgrade or other maintenance operation refuses with the operation-naming lock message, and the defer releases the lock on every exit. The lockable entry is the exported one: the cluster driver's in-process initiator and every node Job (which run the same CLI command) reach it, while the lock-free migrateEtcdCAWith stays the seam the cluster tests drive.

func MigrateEtcdCACluster

func MigrateEtcdCACluster(o EtcdCAOptions) error

MigrateEtcdCACluster drives the etcd-CA migration across every control plane from one of them.

func MigrateEtcdCAClusterContext

func MigrateEtcdCAClusterContext(ctx context.Context, w io.Writer, o EtcdCAOptions, buildClient ...clientBuilder) error

MigrateEtcdCAClusterContext runs the migration with the caller's context, without installing a signal handler or detaching from parent cancellation.

buildClient is the client builder the run acquires its one API client through (see clientBuilder); callers that pass none get the package's admin.conf builder. The front ends inject their own, so the credential decision stays with the caller.

func MigrateKubeletClientCA

func MigrateKubeletClientCA(w io.Writer, o KubeletCAOptions) error

MigrateKubeletClientCA runs one phase on this node. Its progress goes through the writer the caller hands it — os.Stdout for the CLI, the MCP handler's result buffer for the tool.

func MigrateKubeletClientCACluster

func MigrateKubeletClientCACluster(w io.Writer, o KubeletCAOptions) error

MigrateKubeletClientCACluster runs every phase on every node from this control plane: to KubeletClientCAName, or with Rollback back to the root.

func MigrateKubeletClientCAClusterContext

func MigrateKubeletClientCAClusterContext(ctx context.Context, w io.Writer, o KubeletCAOptions, buildClient ...clientBuilder) error

MigrateKubeletClientCAClusterContext is MigrateKubeletClientCACluster with the caller's context. Its progress goes through w, and like the other cluster orchestrators it acquires its API client through an injectable builder: the front ends hand it the node credential's builder, the default stays the package's admin seam.

func RenewalRecordPath

func RenewalRecordPath() string

RenewalRecordPath is where the renewal record persists, beside node.yaml in the node's state area.

func RequireNodeProfile

func RequireNodeProfile(op string) error

RequireNodeProfile refuses a certificate operation on a node whose saved profile this binary does not register, before the operation writes anything. The renderers an operation reaches later — the static-pod restarts, the finalize preflight — stop on the same profile, but mid-run, with leaves reissued and kubeconfigs rewritten. A node with no state yet runs no profile. op names the operation the way its command is spelled.

func RollbackCA

func RollbackCA(w io.Writer, o CARotateOptions) error

RollbackCA reverts a partial rotation on THIS node. It restores the pre-rotation CA/SA + bundles (pki.RollbackCA); if the signer had already been promoted (sign-new reached, detected via the .pre-carotate backup), it also reissues the leaves under the restored old CA so the node is internally consistent, then restarts. Workers hold no CA — they just refresh trust from cluster-info.

func Rotate

func Rotate(w io.Writer, o Options) (bool, error)

Rotate reissues the leaf certs from the existing CA, rewrites the kubeconfigs preserving the current endpoint, and restarts the control-plane units. It reports whether the run reissued anything: (false, nil) when the window check found nothing to rotate, (true, nil) after a completed rotation, and false beside the error on every refusal or failure.

The cluster fence and host lock come before any change, so a run beside a live upgrade or other maintenance operation refuses having reissued nothing, and the defer releases it on every exit.

func RotateCA

func RotateCA(w io.Writer, o CARotateOptions) error

RotateCA runs one CA-rotation phase on THIS node. Control-plane nodes rotate the CA/SA material and reissue leaves; workers refresh their trust bundle (trust-new, finalize) and renew the kubelet client cert under the new CA (sign-new).

The node maintenance lock comes first, before the phase is even decided — ahead of the staged-rotation guard, the first pre-change refusal on the control-plane path — so an invocation beside a live upgrade or other maintenance operation refuses with the operation-naming lock message and leaves a staged rotation untouched. The defer releases the lock on every exit, including a phase that fails after the lock.

func RotateCACluster

func RotateCACluster(o CARotateOptions) error

RotateCACluster orchestrates a full CA rotation across the cluster from a control plane. It runs trust-new → sign-new → finalize, each phase completed on the initiator, then every other control plane (serial, quorum-safe), then every worker — before advancing. Halts on the first node failure, leaving the cluster in the stable, still-working state reached so far.

func RotateCAClusterContext

func RotateCAClusterContext(ctx context.Context, w io.Writer, o CARotateOptions, buildClient ...clientBuilder) error

RotateCAClusterContext runs the cluster rotation with the caller's context. The CLI supplies its parent-derived signal context; this entry point does not install another signal handler or detach from the caller's cancellation.

buildClient is the client builder the run acquires every API client through (see clientBuilder): the client at entry, and the two mid-run rebuilds after trust-new and after sign-new, where the admin certificate has been reissued under the new CA. Callers that pass none get the package's admin.conf builder; the front ends inject their own, so the credential decision stays with the caller and a rebuild cannot quietly reload admin.conf.

func RotateContext

func RotateContext(ctx context.Context, w io.Writer, o Options) (bool, error)

RotateContext reissues the leaf certs from the existing CA, rewrites the kubeconfigs preserving the current endpoint, and restarts the control-plane units, bounded by ctx: a rotation whose context ends mid-run stops at the next step boundary — the step in flight (usually the RSA reissue) finishes, and no later step starts. The run group joins the renewal loop at shutdown, so this is what keeps that join from waiting out a whole rotation and what keeps systemd's KillMode=process from cutting one down mid-step instead. The cluster fence and host lock come before any change, so a run beside a live upgrade or other maintenance operation refuses having reissued nothing, and the defers release them on every exit. Like Rotate, it reports whether the run reissued anything: false when the window check found nothing to rotate or on any error, true only after the restart sequence completed.

func RotationDetail

func RotationDetail() []string

RotationDetail is what the halt messages send an operator to `certs check` for: whether the rotation was forced, which forbids a rollback, whether a staged pair signs in place of a lost key, for each of the root CA, the service-account key and the front-proxy CA, and whether this node can roll back. Empty when no rotation is open.

func RotationPhase

func RotationPhase() string

RotationPhase describes this node's CA-rotation state for `stratum certs check`.

func SaveRenewalRecord

func SaveRenewalRecord(r RenewalRecord) error

SaveRenewalRecord persists the renewal record atomically through the shared atomic writer (internal/atomicfile): a failed or crashed save leaves either the old record or the new one, never a truncated one and never a stray temp copy beside it.

func ScanTokens

func ScanTokens(w io.Writer, oldKid string, restart bool) error

ScanTokens is `certs rotate-ca --scan-tokens`, the per-node scan Job: it prints one line per mounted token signed by the key with id oldKid and returns ErrStaleTokens if there were any. With restart it restarts the kubelet first when there are, and reports what is left.

func SubjectDrift

func SubjectDrift() ([]string, error)

SubjectDrift is pki.SubjectDrift for this node: the leaves on disk whose subject this release no longer issues, as leaves an earlier release issued in system:masters.

Types

type CARotateFlags

type CARotateFlags struct {
	Cluster     bool
	Phase       string
	Force       bool
	Rollback    bool
	FetchShared bool
	// Check, with Rollback, reports whether this node (with Cluster, every
	// node) can roll back, and changes nothing.
	Check bool

	RestartWorkloads  bool
	SkipWorkloadCheck bool
	SkipTokenCheck    bool
}

CARotateFlags is the raw flag set `stratum certs rotate-ca` was given: what the CLI's cobra flags and the MCP tool's input struct both carry.

func (CARotateFlags) Options

func (f CARotateFlags) Options() CARotateOptions

Options turns validated flags into the options the entry points take, so a front end cannot assemble CARotateOptions by hand and drift from what the validator just accepted.

func (CARotateFlags) Validate

func (f CARotateFlags) Validate() error

Validate refuses contradictory or incomplete argument combinations rather than silently picking one, on a command that restarts the control plane on every node.

It lives here, not in pkg/cli, because both front ends must refuse the same requests with the same words: the MCP tool returns this error verbatim, so it spells the CLI's flag names (--cluster, --phase) and an AI client can map its own arguments onto what the refusal asks for.

type CARotateOptions

type CARotateOptions struct {
	Force       bool   // rotate even though an old CA or SA key is gone (compromise recovery); ca.crt is still required
	Phase       string // trust-new | sign-new | finalize
	Cluster     bool   // orchestrate across all nodes (see RotateCACluster)
	FetchShared bool   // (follower CP, trust-new) pull the initiator's staged material first, so every CP promotes the SAME new CA
	Quiet       bool

	// RestartWorkloads (--cluster) has Stratum restart the user Deployments,
	// DaemonSets and StatefulSets that use in-cluster API credentials, between
	// trust-new and sign-new. SkipWorkloadCheck turns off the checks on those
	// workloads (before trust-new, and before sign-new on a node).
	RestartWorkloads  bool
	SkipWorkloadCheck bool

	// SkipTokenCheck finalizes without checking that no mounted ServiceAccount
	// token is still signed by the old key (see satoken.go). The operator's
	// --skip-token-check, for compromise recovery, needs Force (CheckWorkloadFlags);
	// the orchestrator sets it directly, having checked every node itself.
	SkipTokenCheck bool

	// RunID carries the cluster rotation identity to each phase, including
	// remote node Jobs. Manual phases leave it empty and consult the shared
	// record only when finalized material needs to be scoped to an active run.
	RunID string
	// contains filtered or unexported fields
}

CARotateOptions configures a CA rotation.

func (CARotateOptions) CheckWorkloadFlags

func (o CARotateOptions) CheckWorkloadFlags() error

CheckWorkloadFlags refuses the workload flag combinations that make no sense. One check, for the CLI and the MCP tool both.

type CertStatus

type CertStatus struct {
	Name           string
	NotAfter       time.Time
	DaysLeft       int
	IsCA           bool
	Expiring       bool // within the warn window
	Rotatable      bool // a leaf we can rotate (false for CAs)
	Kubelet        bool // the kubelet's CSR-issued client cert, which the kubelet renews itself
	RenewalOverdue bool // the kubelet client cert is past its latest expected renewal point
}

CertStatus is one row of `stratum certs check`.

func Check

func Check(o Options) ([]CertStatus, error)

Check reports expiry for managed certificates and the kubelet client cert. Any present managed or kubelet cert that cannot be read or parsed is an error for operators to address; absent ones are skipped.

type EtcdCAFlags

type EtcdCAFlags struct {
	Cluster     bool
	Phase       string
	Resume      bool
	Rollback    bool
	Force       bool
	FetchShared bool
}

EtcdCAFlags is the raw flag set `stratum certs migrate-etcd-ca` was given.

func (EtcdCAFlags) Options

func (f EtcdCAFlags) Options() EtcdCAOptions

Options turns validated flags into the options the two entry points take.

func (EtcdCAFlags) Validate

func (f EtcdCAFlags) Validate() error

Validate rejects contradictory, incomplete or misspelled arguments rather than silently picking one, on a command that restarts etcd on every control plane. Both front ends run it before anything touches the host: the CLI's RunE and the MCP tool's handler return its error verbatim, so a refusal spells the CLI's flag names (--cluster, --phase) and no front end can rebuild the rules, or their wording, by hand.

It lives here, not in pkg/cli, because the cluster driver builds the per-node Job argv for THIS command (etcdNodeArgv) and the Job runs it through the same CLI — so the rules and the argv have to agree, and a rule that lives only in pkg/cli cannot be checked against the argv that must satisfy it. An earlier version had them disagree: the argv carried `--phase rollback --force` while the CLI accepted --force only alongside the separate --rollback bool, so every follower Job failed flag validation. The initiator does not go through cobra (the driver runs it in-process), so it succeeded — splitting the cluster's trust rather than failing cleanly.

type EtcdCAOptions

type EtcdCAOptions struct {
	Phase       string // trust-both | issue-new | drop-root | rollback
	Resume      bool   // --cluster: proceed on a cluster whose nodes are at different phases
	Force       bool   // --phase=rollback: un-migrate a node that already completed drop-root
	FetchShared bool   // follower CP: pull the initiator's etcd CA first — set by --cluster
	Quiet       bool
}

EtcdCAOptions configures one per-node invocation.

type KubeletCAFlags

type KubeletCAFlags struct {
	Cluster     bool
	Phase       string
	Rollback    bool
	Force       bool
	FetchShared bool
}

KubeletCAFlags is the flag set `certs migrate-kubelet-client-ca` was given.

func (KubeletCAFlags) Options

func (f KubeletCAFlags) Options() KubeletCAOptions

Options turns validated flags into options.

func (KubeletCAFlags) Validate

func (f KubeletCAFlags) Validate() error

Validate refuses contradictory flags.

type KubeletCAOptions

type KubeletCAOptions struct {
	Phase       string
	Rollback    bool
	Force       bool
	FetchShared bool
	Quiet       bool
}

KubeletCAOptions configures one invocation.

type Options

type Options struct {
	IfExpiringDays int  // >0: no-op unless a leaf is within this many days of expiry (timer path). 0 = force.
	WarnDays       int  // window for the "expiring" flag in Check (default version.CertRenewWindowDays)
	Quiet          bool // suppress the per-step step logs (timer path)

	// NodeLockHeld is the held-lock capability: the caller already holds the
	// node maintenance lock (internal/nodelock) for the whole call and Rotate
	// must not reacquire it — a second Acquire from the same process refuses
	// against its own fence, because flock conflicts between open file
	// descriptions. The caller releases the lock on its own exits and must
	// also hold the cluster fence when a renewal is needed.
	NodeLockHeld bool

	// KubeVipRequired fails the rotation when kube-vip cannot be restarted,
	// or when kubelet has not replaced the process that ran before the
	// restart within kubeVipReplaceWait, where otherwise that restart is
	// best-effort: a caller moving kube-vip off a credential it must no
	// longer hold cannot count a rotation that left the running kube-vip on
	// it as done.
	KubeVipRequired bool
}

Options for RotateNode.

type RenewalRecord

type RenewalRecord struct {
	// AttemptedAt is when the loop started its last rotation attempt.
	AttemptedAt time.Time `json:"attemptedAt"`
	// Result is that attempt's rotation error, empty when it succeeded.
	Result string `json:"result,omitempty"`
}

RenewalRecord is the scheduled renewal loop's LAST attempt on this node, persisted under the node's state area so `stratum certs check` can show a dead or failing loop before the leaves expire — nothing else on the node says when renewal last ran or how it went.

func LoadRenewalRecord

func LoadRenewalRecord() (RenewalRecord, error)

LoadRenewalRecord reads the persisted renewal record. A record that does not exist is reported as an error wrapping fs.ErrNotExist, which callers read as no attempt recorded yet.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL