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

cli

package
v1.35.9-stratum.0-rc.2 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: 58 Imported by: 0

Documentation

Overview

Package cli builds the stratum command tree as a *cobra.Command. It is stratum's one deliberate public API surface: an importer (e.g. a downstream build) can call NewRootCommand, then AddCommand additional subcommands onto the returned root before calling Execute, without reimplementing any of the base commands. tools/check-cli-api-boundary.sh enforces that this package's exported surface never leaks an internal/* type back out, which is what makes that safe to import from outside this module.

Index

Constants

This section is empty.

Variables

View Source
var ErrCheckFailed = errors.New("the check found failures")

ErrCheckFailed is returned by a check command whose report found a failure, such as an importer's audit command, so main can exit 1 (a bare lint-style nonzero exit — the full report has already been printed, so no additional "stratum: ..." line is wanted) without an os.Exit call inside pkg/cli itself. pkg/cli is a public, importable package, so an os.Exit from inside a RunE would terminate the importing process outright — a host binary couldn't observe the failure, run its own cleanup, or apply its own exit policy, because the process would already be gone.

View Source
var ErrNoCommand = errors.New("no command given")

ErrNoCommand is returned when stratum is invoked with no subcommand, so main can exit 2 (usage error) instead of 1 without printing a redundant "stratum: <err>" line on top of the usage cmd.Help() already printed — a bare `stratum` invocation is a usage error, not a run failure.

Functions

func AddDestructiveNodeTool

func AddDestructiveNodeTool[In any](r *Registrar, t *mcp.Tool, op NodeOperation[In])

AddDestructiveNodeTool registers op at the destructive tier, run like the engine's destructive tools: on a Stratum node only, one at a time with them.

func AddTool

func AddTool[In, Out any](r *Registrar, tier Tier, t *mcp.Tool, h mcp.ToolHandlerFor[In, Out])

AddTool registers a typed tool at the given tier, or does nothing at all when the operator has not enabled that tier. On a server built by the internal MCP package's NewServer, the text of every result and error the tool returns reaches the client fenced as untrusted data.

func DestructiveTool

func DestructiveTool(name, description string) *mcp.Tool

DestructiveTool builds the annotations for a tool that can take a cluster down or lose data.

func EngineAPIServerArgs

func EngineAPIServerArgs() []string

EngineAPIServerArgs returns the apiserver argv entries every control plane renders whatever its profile.

func ExitCode

func ExitCode(err error, stderr io.Writer) int

ExitCode maps err — the error Execute returned on the root command — to the process exit code the caller must end with, writing any error report it owes the operator to stderr. It is the single mapping every binary that embeds pkg/cli shares: an embedding binary imports pkg/cli, so a new sentinel is mapped once where every embedding binary can adopt it, and TestExitCodeCoversEveryExportedSentinel fails until it is.

The mapping is an observable exit-code contract:

  • ErrNoCommand and *ErrUsage exit 2 (usage error). Both have already printed everything the operator needs — help, or the flag error plus usage — so nothing more is written.
  • ErrCheckFailed exits 1 (lint-style failure). The full report has already printed, so nothing more is written.
  • *ErrExitCode propagates its Code exactly. Whoever returned it has already told the operator what it needed to (the passthrough's child inherits stdout/stderr; `upgrade --check` prints its answer), so nothing is written.
  • any other error exits 1, and because no sentinel has reported it, this is the one place a failing command's error prints, as a "stratum: <message>" line on stderr.

A nil err is success: exit 0, nothing written. Callers can therefore hand Execute's return value straight over and end with the returned code:

if code := cli.ExitCode(root.Execute(), os.Stderr); code != 0 {
	os.Exit(code)
}

func NewRootCommand

func NewRootCommand(opts ...Option) *cobra.Command

NewRootCommand builds the full stratum command tree. An importer adds its own commands with root.AddCommand(...) before calling root.Execute().

func NodeAdminClientset

func NodeAdminClientset() (kubernetes.Interface, error)

NodeAdminClientset returns a clientset on this node's internal admin kubeconfig: super-admin.conf, else admin.conf.

func PKIRotationCopies

func PKIRotationCopies(name string) []string

PKIRotationCopies returns the staged and pre-rotation names a CA rotation keeps beside the PKI file name, or nil when it keeps none.

func ProfileAPIServerArgs

func ProfileAPIServerArgs(p Profile) ([]string, error)

ProfileAPIServerArgs returns the apiserver argv entries p contributes, as a control plane renders them.

func ReadTool

func ReadTool(name, description string) *mcp.Tool

ReadTool builds the annotations for a tool that changes nothing.

func TLSCipherSuites

func TLSCipherSuites() []string

TLSCipherSuites returns the strong TLS cipher suites the apiserver and kubelet are rendered with.

func WriteTool

func WriteTool(name, description string) *mcp.Tool

WriteTool builds the annotations for a tool that mutates recoverably.

Types

type AuditResult

type AuditResult int

AuditResult is the outcome of one engine audit.

const (
	// AuditPass means the audit verified the property.
	AuditPass AuditResult = iota + 1
	// AuditFail means the audit found the property violated, or could not read what it must.
	AuditFail
	// AuditException means the audit could not verify the property and reports it unverified.
	AuditException
)

func (AuditResult) String

func (r AuditResult) String() string

type AuditVerdict

type AuditVerdict struct {
	Result AuditResult
	Detail string
}

AuditVerdict is an audit's result and the detail an operator reads.

func AuditEtcdTrust

func AuditEtcdTrust(ctx context.Context) AuditVerdict

AuditEtcdTrust reports whether this node's running etcd refuses a client certificate the cluster root signs.

func AuditKubeletClientTrust

func AuditKubeletClientTrust() AuditVerdict

AuditKubeletClientTrust reports whether this node's kubelet trusts a client CA distinct from the cluster root.

func AuditKubeletsRefuseRoot

func AuditKubeletsRefuseRoot(ctx context.Context, cs kubernetes.Interface) AuditVerdict

AuditKubeletsRefuseRoot reports whether every kubelet cs lists refuses a root-signed client certificate and accepts apiserver-kubelet-client.

type ComponentVersions

type ComponentVersions struct {
	Containerd string
	Runc       string
	KubeVip    string
	Cilium     string
	CoreDNS    string
	// Deprecated: konnectivity is not shipped, so nothing reads this. The
	// field stays only because released importers compile against it.
	Konnectivity string
}

ComponentVersions carries the five component versions `stratum version --components` reports. The stamped values live in internal/version (ContainerdVersion, RuncVersion, ...) and the command reads them as its defaults, so this struct is only the per-field override an importer can still supply through WithComponentVersions.

func StampedVersions

func StampedVersions() ComponentVersions

StampedVersions returns the engine's link-time-stamped component versions (internal/version) as a value: the same stamps `version --components` reports as its defaults. ComponentVersions is a plain struct of strings, so the caller receives a copy and cannot reach the package state behind it — read-only, because the stamps are this binary's build identity, not configuration. It exists because the stamps live in an internal package an importer cannot see: a downstream build reporting its own components would otherwise have to re-parse `version --components` output. Every field reads "unknown" in a build linked without the engine's -X stamp list.

type ErrExitCode

type ErrExitCode struct{ Code int }

ErrExitCode is returned when a command's answer is its exit status: the kubectl/etcdctl passthrough when the child exits non-zero, and `upgrade --check` when the node needs the upgrade. It carries the exact code so main can propagate it precisely, without pkg/cli itself calling os.Exit — for the same importability reason as ErrCheckFailed above. Whoever returns it has already told the operator what it needed to (the passthrough's child inherits stdout/stderr; --check prints its answer), so main prints nothing additional for this error.

func (*ErrExitCode) Error

func (e *ErrExitCode) Error() string

type ErrUsage

type ErrUsage struct {
	// contains filtered or unexported fields
}

ErrUsage wraps a flag-parsing failure so main can exit 2 (usage error) instead of 1, without printing a redundant "stratum: <err>" line on top of the error + usage text root's FlagErrorFunc already printed — a bad flag is a usage error, not a run failure.

func (*ErrUsage) Error

func (e *ErrUsage) Error() string

func (*ErrUsage) Unwrap

func (e *ErrUsage) Unwrap() error

type NodeEnv

type NodeEnv struct {
	Out       io.Writer
	Clientset kubernetes.Interface
}

NodeEnv is what a NodeOperation runs with: Out collects the progress the operator reads, and Clientset is on the node connector.

type NodeLayout

type NodeLayout struct {
	ConfDir                      string
	DataDir                      string
	PKIDir                       string
	KubeconfigDir                string
	K8sConfigDir                 string
	StaticPodDir                 string
	KubeletConfig                string
	AdminConf                    string
	SuperAdminConf               string
	BinDir                       string
	ContainerdConfig             string
	ContainerdConfinedSinceStamp string
	SELinuxLabelStamp            string
	EtcdDataDir                  string
}

NodeLayout is where this node's Stratum files live.

func Layout

func Layout() NodeLayout

Layout returns this node's file layout as the engine resolves it.

type NodeOperation

type NodeOperation[In any] func(ctx context.Context, env NodeEnv, in In) (NodeResult, error)

NodeOperation is a destructive operation on this node, run on the node connector's client.

type NodeResult

type NodeResult struct {
	Next     string
	Warnings []string
}

NodeResult is what a NodeOperation reports; Next and Warnings reach the operator whether or not the operation fails.

type Option

type Option func(*rootConfig)

Option configures the root command returned by NewRootCommand.

func WithComponentVersions deprecated

func WithComponentVersions(v ComponentVersions) Option

WithComponentVersions supplies per-field overrides for the component versions `stratum version --components` reports: a non-empty field replaces the stamped default internal/version carries, an empty field leaves the stamp.

Deprecated: the engine stamps its component versions into internal/version, which `version --components` reads as its defaults, so an importer no longer needs to stamp main-package vars and hand them here. The option is not a no-op — it still overrides — and it goes away only through a deliberate breaking-release adoption.

func WithProfiles

func WithProfiles(ps ...Profile) Option

WithProfiles registers node profiles a node may select by name. A profile that is not declared completely, or a name registered twice, fails the node commands that read profiles.

func WithStacks

func WithStacks(f StackFunc) Option

WithStacks adds an importer's manifest stacks to every control-plane start.

func WithToolSets

func WithToolSets(sets ...ToolSet) Option

WithToolSets contributes tool sets to `stratum mcp`. The engine's own tools are always present; these are added after them.

func WithVersionOverride

func WithVersionOverride(display string) Option

WithVersionOverride replaces the top-line version string `stratum version` reports, letting an importer report its own product version while reusing every base command unchanged. Empty (the default) leaves internal/version.StratumVersion, the OSS ldflags-stamped version.

type Profile

type Profile struct {
	// Name selects the profile: a DNS-1123 label, unique in the binary.
	Name string
	// APIServerFlags are spliced into the apiserver's argv on control planes,
	// in order.
	APIServerFlags []ProfileFlag
	// KubeletSettings are rendered into kubelet.conf on every node, in order.
	KubeletSettings []ProfileSetting
	// ConfigFiles are written under the engine's config directory on control
	// planes before the apiserver renders, backed up across an upgrade and
	// restored first on a rollback.
	ConfigFiles []ProfileFile
	// SharedSecrets are minted once, on a cluster's first control plane,
	// carried to every later control plane in the join bundle, written 0600
	// and never regenerated.
	SharedSecrets []ProfileSecret
	// Sysctls are key=value lines written as a sysctl drop-in on every node.
	// A node that cannot apply them does not start.
	Sysctls []string
	// RequiresOperator refuses a control plane with spec.operator: false.
	RequiresOperator bool
	// RequiresSELinux refuses spec.selinux: false and treats a missing
	// SELinux policy as fatal.
	RequiresSELinux bool
	// KubeletServerTLSBootstrap makes every node, control planes included,
	// bootstrap its kubelet serving certificate through the CSR flow.
	KubeletServerTLSBootstrap bool
	// DisableNodeShell turns the dashboard's privileged node shell off by
	// default.
	DisableNodeShell bool
}

Profile is a named node profile an importer registers with WithProfiles. Registering validates the declaration and makes the profile selectable by name; the engine release's notes say which of its fields the node start reads and renders, as the engine's own profile moves onto this seam. Once selection is in place, a node selects a profile with `stratum up --profile <name>` or `spec.profile`, records the name in its state, and hands it to every worker's join line; a node whose state names a profile the running binary did not register refuses to start, so every node of a cluster must run a binary that carries its profile. An importer must not take a registered profile for an applied one on a release whose notes do not say the start reads it.

type ProfileContext

type ProfileContext struct {
	// KubernetesVersion is the version the binary runs, as vMAJOR.MINOR.PATCH
	// with any pre-release suffix the stamp carries.
	KubernetesVersion string
}

ProfileContext is what a profile may read when it renders a file.

type ProfileFile

type ProfileFile struct {
	// Name is the file name; no directory, and not the name of a file the
	// engine keeps in its config directory, such as kubelet.conf.
	Name string
	Kind ProfileFileKind
	// Render returns the file's bytes.
	Render func(ProfileContext) ([]byte, error)
}

ProfileFile is a config file the engine writes for the profile.

type ProfileFileKind

type ProfileFileKind int

ProfileFileKind says how the engine treats a config file beyond writing it.

const (
	// ProfilePlainFile is written, and may be named by a ProfileConfigFile
	// flag.
	ProfilePlainFile ProfileFileKind = ProfileFileKind(profile.PlainFile)
	// ProfileAdmissionConfiguration is an apiserver AdmissionConfiguration
	// the engine merges its own admission plugins into. The engine names the
	// merged file on the apiserver's argv itself.
	ProfileAdmissionConfiguration ProfileFileKind = ProfileFileKind(profile.AdmissionConfiguration)
)

type ProfileFlag

type ProfileFlag struct {
	// Name is the flag with its leading --; the value goes in Value.
	Name  string
	Value string
	From  ProfileSource
}

ProfileFlag is one apiserver flag.

type ProfileSecret

type ProfileSecret struct {
	// Name is the file name; no directory.
	Name string
	// Mint returns the bytes a new cluster starts with.
	Mint func() ([]byte, error)
}

ProfileSecret is a file the engine mints once and shares between control planes.

type ProfileSetting

type ProfileSetting struct {
	// Key is the kubelet.conf key.
	Key string
	// Value is the setting's value, on one line.
	Value string
	From  ProfileSource
}

ProfileSetting is one kubelet.conf line.

type ProfileSource

type ProfileSource int

ProfileSource says where a flag's or a setting's value comes from.

const (
	// ProfileLiteral uses Value as written.
	ProfileLiteral ProfileSource = ProfileSource(profile.Literal)
	// ProfileConfigFile resolves Value, the name of one of the profile's
	// ConfigFiles or SharedSecrets, to the path the engine wrote it at.
	ProfileConfigFile ProfileSource = ProfileSource(profile.ConfigFile)
	// ProfileLogFile resolves Value, a file name, to a path under the
	// engine's log directory.
	ProfileLogFile ProfileSource = ProfileSource(profile.LogFile)
	// ProfilePKIFile resolves Value, a file name, to a path under the
	// engine's PKI directory.
	ProfilePKIFile ProfileSource = ProfileSource(profile.PKIFile)
)

type Registrar

type Registrar struct {
	// contains filtered or unexported fields
}

Registrar admits tools by tier.

func (*Registrar) Allows

func (r *Registrar) Allows(t Tier) bool

Allows reports whether tools at this tier will be registered.

func (*Registrar) Registered

func (r *Registrar) Registered() []string

Registered returns the names of every admitted tool, in registration order.

type StackContext

type StackContext struct {
	ClusterDomain string
	ServiceCIDR   string
}

StackContext is the cluster an importer's stacks are rendered for.

type StackFunc

type StackFunc func(StackContext) (map[string][]byte, error)

A StackFunc returns manifest stacks keyed "<stack>/<file>.yaml" (or .yml): one directory level, a stack name that is a DNS-1123 label and not a bundled one, and no file name starting with a dot. On every control-plane `stratum up` they are staged beside the bundled add-ons, which the operator applies and prunes; a file no longer returned is removed, and its objects pruned. Only the operator leader's copy is applied, so return the same bytes on every control plane. An error, or a key that breaks these rules, fails the start.

type Tier

type Tier int

Tier is how much damage a tool can do. It decides whether the tool is registered, not whether a call is allowed: see AddTool.

const (
	// TierRead cannot change anything. Always registered.
	TierRead Tier = iota
	// TierWrite mutates recoverably — a snapshot, a token, a config. Needs
	// --allow-write.
	TierWrite
	// TierDestructive can take a cluster down or lose data. Needs
	// --allow-destructive.
	TierDestructive
)

func (Tier) String

func (t Tier) String() string

type ToolSet

type ToolSet interface {
	Name() string
	Register(*Registrar)
}

A ToolSet is a named group of MCP tools. Register is called once, at startup, and should call AddTool for each tool it contributes.

Jump to

Keyboard shortcuts

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