CLI in two minutes¶
Install¶
Any of these gives you the same binary; Install covers verification and the other channels.
go install github.com/abd-ulbasit/upgradescope/cmd/upgradescope@latest
upgradescope version
upgradescope version prints the build and the knowledge base it carries,
including its horizon: the newest Kubernetes minor the knowledge base
describes.
Scan rendered manifests¶
No cluster needed. Render what you deploy, then scan it:
helm template my-release ./chart --output-dir rendered # or kustomize build > rendered/all.yaml
upgradescope scan --files rendered --target 1.37
--files takes a directory (every *.yaml, *.yml and *.json under it)
or one file, and decodes each document the way kubectl apply -f does.
In files mode API usage and add-ons are assessed. Add-ons are found in the
container and init-container images and the labels of Pod, Deployment,
DaemonSet, StatefulSet, ReplicaSet, Job and CronJob pod templates (in
kind: List items too), and in an Ingress NGINX IngressClass, matched as
a live scan matches them. Images injected at admission time, such as a mesh
sidecar, are not in the manifests, so they are not seen. Version skew,
deprecated-API requests and Helm releases need a cluster: they are listed
under NOT ASSESSED with the reason files mode, and do not count against
the verdict.
What you scan is what the renderer produced. Without a cluster,
helm template fills .Capabilities with Helm's built-in defaults (its
own Kubernetes version and API list), not your cluster's or the target's.
A chart that picks an API version from .Capabilities.KubeVersion or
.Capabilities.APIVersions.Has can then render a different version from
the one deployed: a false blocker, or a missed one. Pin them to the cluster
you are judging with --kube-version (for example 1.36.0) and one
--api-versions per API the chart tests (for example
policy/v1beta1/PodDisruptionBudget). For what a release has actually
installed, scan helm get manifest <release> output, or scan the cluster.
Do not point --files at the chart directory itself. Its templates hold
{{ }} and are not YAML, so they cannot be read. A directory with a
Chart.yaml that holds such files gets one hint instead of a warning per
file, and the scan cannot say ready: the report is unknown, and
NOT ASSESSED counts the templates that were not read, because a template
can carry any API version. Render the chart first, as above:
helm template my-release ./chart --output-dir rendered
upgradescope scan --files rendered --target 1.37
Scan a live cluster¶
upgradescope scan --target 1.37 # current kubeconfig context
upgradescope scan --context prod --target 1.37 # another context
A live scan only reads: discovery, /version, nodes, namespaces, pods, the
apiserver's /metrics, Helm release Secrets and ConfigMaps (owner=helm),
and metadata-only lists of the resources the knowledge base flags. Each part
that your credentials cannot read is reported as not assessed with the
reason, and the rest of the scan still runs. A scan that could read nothing
at all (an unreachable cluster) is an error, exit 1.
A slow or stalled API server cannot hang the scan: each request is given
up after --request-timeout (default 30s), and each collector step has its
own share of the scan's 5 minutes, so a stalled step leaves only its own
check not assessed. The report's header names the kube context and the API
server it read (Context: prod (API server https://10.0.0.1:6443)), so a
saved report says which cluster it is about.
Read the result¶
SCORE 50/100
READY no
READYis the verdict:yes(ready),no(blocked: at least one blocker) orunknown(no blocker found, but a required check could not run, so one may have been missed). Verdict and score has the rules.- Findings come in three severities. Blockers break the upgrade,
warnings break the one after it or need attention soon, info findings are
listed but never scored. Each one has a
fix:and asee:citation. NOT ASSESSEDlists what the scan could not see, and why.UNRECOGNIZED IMAGESlists container images that no add-on in the add-on registry claims, such asdocker.io/library/busybox. It is information about add-on detection, never a finding: it does not change the score or the verdict. It matters only if one of those images is an add-on you expected the scan to judge; the registry page says how to teach the scan about it.
Pick an output¶
-o is short for --output, as in kubectl and helm: it names the
format, never a file.
upgradescope scan --target 1.37 --output json # the machine-readable report
upgradescope scan --target 1.37 --output sarif # SARIF 2.1.0 for code scanning
upgradescope scan --target 1.37 --output markdown # a summary for a pull-request comment
The JSON report is versioned (schemaVersion) and described in the
JSON report reference.
Gate on it¶
| Exit code | Meaning |
|---|---|
| 0 | The gate passed. |
| 1 | An operational error: an unreachable cluster, an invalid flag, config file or baseline. |
| 2 | The gate failed: a finding at or above --fail-on (default blocker), or a verdict of unknown. |
--fail-on warning is stricter; --fail-on never always exits 0.
A verdict of unknown fails the gate on purpose, since a blocker may be
behind the gap. The common case is a target newer than the knowledge base's
horizon:
$ upgradescope scan --files rendered --target 1.38
...
READY unknown (required checks were not assessed)
kb-coverage (required): knowledge base covers Kubernetes up to 1.37; target 1.38 cannot be assessed (API removals after 1.37 are projected from k8s.io/api lifecycle markers, not shipped releases)
...
$ echo $?
2
$ upgradescope scan --files rendered --target 1.38 --allow-incomplete; echo $?
0
--allow-incomplete gates on the findings alone. Use it knowingly: the
report still says what was not assessed.
Next¶
- Gate pull requests: CI gate.
- Accept known findings, or fail only on new ones: Suppressions and baselines.
- Keep the answer current in the cluster: In-cluster agent.
- Every flag:
upgradescope scan.