scan
upgradescope scan¶
Scan a cluster (or rendered manifests) for upgrade readiness
Synopsis¶
Scan a cluster (or rendered manifests) for upgrade readiness.
Exit codes: 0 when the gate passes; 1 on an operational error, including an invalid config file or baseline and a report that could not be written; 2 when the gate fails, which includes an unknown verdict.
The gate (--fail-on) fails when a finding at or above the threshold remains, or (unless --allow-incomplete) when a required check was not assessed, so a blocker may have been missed. A --target that is not an upgrade of the cluster (at or below the minor its kube-apiserver runs) always fails it, --allow-incomplete notwithstanding; only --fail-on never, which always exits 0, passes it. A --target below the oldest minor the knowledge base covers (1.16) is an error (exit 1), not a verdict: quote it in YAML and workflow files, where an unquoted 1.30 is the number 1.3.
CI report formats: the exit code is the gate's in every --output format.
--output junit writes JUnit XML, one test suite per finding category and one
test case per finding, whose outcomes follow --fail-on and --allow-incomplete:
a finding the gate fails on is a failure, one below it passes, suppressed and
baseline-unchanged findings are skipped, and a required check that was not
assessed is an error. --output gitlab-codequality writes a GitLab Code Quality
report: an entry per finding and file location (blocker critical, warning
minor, info info) with a fingerprint that survives line moves; a finding
without a file is placed on the virtual path upgradescope/
Upgrade plan (--plan): the control plane is upgraded one minor at a time, so --plan also judges the cluster at each minor between the one it runs (a live scan's oldest kube-apiserver; --from with --files) and --target, and lists each finding at the first upgrade it affects, with the upgrade where its severity changes. Table and markdown show the plan before the findings; JSON adds hops. Ignore rules apply to every upgrade. The rest of the report, and the gate, judge --target alone.
Files mode (--files): every .yaml, .yml and *.json file under the directory, or the one file named, is decoded as kubectl apply -f decodes it: each document of a YAML stream and each object of a JSON stream (NDJSON, pretty-printed or adjacent), List items expanded, a duplicate key taking its last value. Every document is also decoded by kubectl's own decoder: where it finds an object the scan's line-tracking YAML reading did not, kubectl's objects are counted (located at the document's first line), with a warning. Documents that are not Kubernetes objects are skipped. A document that cannot be decoded is skipped with a warning; when its text names an API the knowledge base lists as removed, api-usage is not assessed, so the verdict is at least unknown and the gate fails unless --allow-incomplete. VCS metadata, node_modules and Go vendor/ directories (with modules.txt) are not walked, nor are symlinked directories (kubectl apply -R does not follow them either); each of these but VCS metadata is a warning. Files mode assesses API usage and add-ons: the container and init-container images and labels of Pod, Deployment, DaemonSet, StatefulSet, ReplicaSet, Job and CronJob pod templates, and IngressClass controllers, matched as a live scan matches them. Images injected at admission (a mesh sidecar) are not in the manifests. Version skew, Helm releases and deprecated API callers need a cluster and are not assessed, and so does team attribution: --files reads no Namespace objects, so every finding is unattributed and --team-label is refused. A manifest at an API version the target does not serve yet (introduced after it) is a blocker like a removed one, since applying it fails the same way; a live scan never reports that.
Suppression: ignore rules in .upgradescope.yaml (found in the scan root, i.e. the --files directory or else the working directory, then at the git repository root; or named with --config) accept findings by key or category, optionally only for objects matching namespace/name/file globs, with a required reason and an optional expires date. Objects can opt out with the upgradescope.basit.engineer/ignore and upgradescope.basit.engineer/ignore-reason annotations. Suppressed findings do not count toward score, verdict or gate, and every output lists them with their reason. An expired rule stops applying and prints a warning.
Baseline: --baseline takes the JSON report of an earlier scan (--output json, or --write-baseline). Findings whose key and listed objects it already had are marked unchanged and do not fail the gate; anything else is new. Score and verdict still count unchanged findings, and an unassessed required check still fails the gate.
upgradescope scan [flags]
Examples¶
# The current kubeconfig context's cluster against Kubernetes 1.37
upgradescope scan --target 1.37
# Another context, as JSON
upgradescope scan --context prod --target 1.37 --output json
# What each control-plane upgrade on the way to 1.37 needs fixed first
upgradescope scan --target 1.37 --plan
# Rendered manifests in CI: SARIF for code scanning, exit 2 on a blocker
upgradescope scan --files rendered/ --target 1.37 --output sarif > upgradescope.sarif
# GitLab, Jenkins or Azure Pipelines: a Code Quality or JUnit report
upgradescope scan --files rendered/ --target 1.37 --output gitlab-codequality > gl-code-quality-report.json
upgradescope scan --files rendered/ --target 1.37 --output junit > upgradescope-junit.xml
# Fail only on findings that are new since an accepted scan
upgradescope scan --files rendered/ --target 1.37 --write-baseline baseline.json
upgradescope scan --files rendered/ --target 1.37 --baseline baseline.json
Options¶
--allow-incomplete with --fail-on blocker|warning, do not fail when the verdict is unknown (required checks not assessed); a --target that is not an upgrade still fails
--baseline string JSON report of an earlier scan (--output json or --write-baseline): the gate fails only on findings that are new since
--config string config file with ignore rules (default: .upgradescope.yaml in the scan root, else at the git repository root)
--context string kubeconfig context to use
--fail-on string exit 2 if findings at/above this severity, or the verdict is unknown: blocker|warning|never (never always exits 0, even for a --target that is not an upgrade) (default "blocker")
--files string scan rendered manifests in this file or directory (*.yaml, *.yml, *.json) instead of a live cluster
--from string with --plan and --files: the minor the cluster runs now, where the plan starts (a live scan reads it from the cluster)
-h, --help help for scan
--kubeconfig string path to kubeconfig (default: standard loading rules)
-o, --output string output format: table|json|sarif|markdown|junit|gitlab-codequality (default "table")
--plan also judge each control-plane upgrade on the way to --target, one minor at a time, listing each finding at the first upgrade it affects (table, markdown, json)
--registry-dir string extra add-on registry entries: one <id>.yaml file or a directory of them, in the schema of registry/CONTRIBUTING.md and validated like the embedded entries; an entry with an embedded id replaces it
--request-timeout duration give up on a single API request after this long (0 = no per-request limit) (default 30s)
--target string target Kubernetes minor version, e.g. 1.37 (required)
--team-label string namespace label used for team attribution (live scans only: --files mode reads no Namespace objects, so all its findings are unattributed) (default "team")
--write-baseline string also write this scan's JSON report to this path, for a later --baseline
SEE ALSO¶
- upgradescope - Continuous Kubernetes upgrade-readiness scanner