Kubernetes tools
rules_helm can fetch kubectl the same way it fetches helm, so a release
driven by helm_install or
helm_upgrade can be inspected from the same
workspace without installing anything on the host. Everyone on a project gets
the same kubectl version, on every platform Bazel runs on.
Setup
rules_helm registers the kubectl toolchains itself. To get the @kubectl
repository for bazel run, add the extension to your MODULE.bazel:
k8s = use_extension("@rules_helm//k8s:extensions.bzl", "k8s")
k8s.kubectl()
use_repo(k8s, "kubectl")
Debugging a helm deploy
bazel run passes your environment through, so KUBECONFIG, ~/.kube/config
and the usual --context / --namespace flags behave exactly as they do for a
locally installed kubectl.
# Roll out the release
bazel run //deploy:my_chart.install
# What did helm do?
bazel run @helm//:helm -- status my-release
bazel run @helm//:helm -- history my-release
bazel run @helm//:helm -- get manifest my-release
# What is the cluster doing with it?
bazel run @kubectl//:kubectl -- get pods --namespace my-namespace
bazel run @kubectl//:kubectl -- describe deployment my-release
bazel run @kubectl//:kubectl -- logs deployment/my-release --tail 100
bazel run @kubectl//:kubectl -- get events --sort-by=.lastTimestamp
Use in a genrule
The resolved toolchain exposes the binary as the KUBECTL_BIN make variable:
genrule(
name = "kubectl_version",
outs = ["kubectl_version.json"],
cmd = "$(KUBECTL_BIN) version --client --output json > $@",
toolchains = ["@rules_helm//k8s:current_kubectl_toolchain"],
)
Pinning a version
rules_helm ships checksums for a single default kubectl version. Clusters
that are more than one minor version away from it should pin a matching
release, which the root module can do with the version attribute:
k8s.kubectl(version = "1.36.5")
Checksums for versions rules_helm does not know about are looked up from the
upstream .sha256 files while the extension is evaluated. On Bazel 8.5 and newer
the digests are then stored as facts in MODULE.bazel.lock, so the lookup
happens once per version and airgapped builds keep working from the lockfile.
Older Bazel versions repeat the lookup whenever the extension is re-evaluated.
Several versions can live side by side by giving each tag its own repository
name. Only the first kubectl tag in the root module decides which version the
registered toolchains serve:
k8s.kubectl()
k8s.kubectl(name = "kubectl_legacy", version = "1.35.6")
use_repo(k8s, "kubectl", "kubectl_legacy")
bazel run @kubectl_legacy//:kubectl -- get nodes
For rule authors
Rules that need kubectl in an action depend on the
@rules_helm//k8s:kubectl_toolchain_type toolchain type and read the binary
from ctx.toolchains[...].kubectl. See kubectl_toolchain
for the provider fields and the k8s extension for the
MODULE.bazel API.