Helm¶
Real helm/tar invocations against real (small, public) chart
repositories and a real cluster — nothing here is mocked (the tar
calls genuinely list/extract files from local chart archives; see
Resources: File). See BDD
conventions for the "define, then act"
pattern and table shapes these steps build on.
Directory¶
Steps scoped to a local filesystem path: index, lint, package, and manage dependencies.
Define a Directory¶
Given Directory "<ChartsDirectory>" at "./charts"
Given Directory known as "<ChartsDirectory>":
| PROPERTY | VALUE |
| path | ./charts |
See Resources: Directory for the construction shape, validation behavior, negative-path forms, and how to purge one. The base object every other Helm step in this section either wraps directly (a Chart) or operates on.
Index a Directory¶
When I index Directory known as "<Resource>"
When I index Directory known as "<Resource>" with:
When I index Directory "<Resource>" with {flags}
Runs helm repo index <path> against the directory.
Given Directory "<ChartsDirectory>" at "./charts"
When I index Directory "<ChartsDirectory>" with --json
Then the command exited with 0
Given Directory known as "<ChartsDirectory>":
| PROPERTY | VALUE |
| path | ./charts |
When I index Directory known as "<ChartsDirectory>" with:
| OPTION | VALUE |
| --json | True |
Then the command exited with 0
No flags to pass at all is the bare, table-less form — identical in
both -short.feature and -full.feature (nothing to shorten):
When I index Directory known as "<ChartsDirectory>"
Then the command exited with 0
Lint a Directory¶
When I lint Directory known as "<Resource>"
When I lint Directory known as "<Resource>" with:
When I lint Directory "<Resource>" with {flags}
Runs helm lint <path>. Only accepts a local path — a Directory,
never a Chart (which can also be a URL/OCI/reference that lint can't
operate on).
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
When I lint Directory "<NginxChartDirectory>" with --strict
Then the command exited with 0 STDOUT contains "1 chart(s) linted, 0 chart(s) failed"
Given Directory known as "<NginxChartDirectory>":
| PROPERTY | VALUE |
| path | ./charts/test-nginx |
When I lint Directory known as "<NginxChartDirectory>" with:
| OPTION | VALUE |
| --strict | True |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | 1 chart(s) linted, 0 chart(s) failed |
Package a Directory¶
When I package Directory known as "<Resource>" with:
When I package Directory "<Resource>" with {flags}
Runs helm package <path>, same local-path-only restriction as lint.
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
When I package Directory "<NginxChartDirectory>" with --destination .cache
Then the command exited with 0 STDOUT contains "Successfully packaged chart"
Given Directory known as "<NginxChartDirectory>":
| PROPERTY | VALUE |
| path | ./charts/test-nginx |
When I package Directory known as "<NginxChartDirectory>" with:
| OPTION | VALUE |
| --destination | .cache |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | Successfully packaged chart |
Manage Directory Dependencies¶
When I {build|list|update} dependencies for Directory known as "<Resource>"
When I {build|list|update} dependencies for Directory known as "<Resource>" with:
When I {build|list|update} dependencies for Directory "<Resource>" with {flags}
Runs helm dependency build|list|update <path> against a chart
directory declaring real dependencies.
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
When I list dependencies for Directory "<NginxChartDirectory>" with --max-col-width 200
Then the command exited with 0 STDOUT contains "no dependencies at"
Given Directory known as "<NginxChartDirectory>":
| PROPERTY | VALUE |
| path | ./charts/test-nginx |
When I list dependencies for Directory known as "<NginxChartDirectory>" with:
| OPTION | VALUE |
| --max-col-width | 200 |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | no dependencies at |
Exercised against charts/test-dependency/ (a fixture that exists
solely to declare a real dependency) for the build/update forms:
When I list dependencies for Directory known as "<DependencyChartDirectory>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | missing |
When I update dependencies for Directory known as "<DependencyChartDirectory>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | Saving 1 charts |
Exercised against charts/test-dependency/, a fixture that exists
solely to declare a real dependency.
Chart¶
Steps scoped to a chart reference: define it from any of five real
sources, assert on its Chart.yaml, template it, or inspect it with
helm show.
Define a Chart¶
Given Helm Chart "<RepoReferenceNginxHelmChart>" in "nginx" repo "<BitnamiRepoUrl>" version "25.1.10"
Given Helm Chart known as "<RepoReferenceNginxHelmChart>":
| PROPERTY | VALUE |
| chart | nginx |
| repo | <BitnamiRepoUrl> |
| version | 25.1.10 |
Registers a chart reference — a local directory, a local archive, a
URL, an OCI artifact, or a repo/name reference (optionally built from
a bare name plus a separate repo field). The source/repo fields
accept either a literal value or a <Directory>/<File>/<URL>/<OCI
Artifact> resource — see Resources for how each of
those four is itself constructed. All five source kinds, each pulled
from a real scenario:
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
And Helm Chart "<LocalNginxHelmChart>" in "<NginxChartDirectory>"
Given File "<NginxChartFile>" at "./charts/test-nginx-0.1.0.tgz"
And Helm Chart "<LocalArchiveNginxHelmChart>" in "<NginxChartFile>"
Given URL "<NginxChartUrl>" at "https://charts.bitnami.com/bitnami/nginx-18.2.5.tgz"
And Helm Chart "<UrlNginxHelmChart>" in "<NginxChartUrl>"
Given URL "<BitnamiRepoUrl>" at "https://charts.bitnami.com/bitnami"
And Helm Chart "<RepoReferenceNginxHelmChart>" in "nginx" repo "<BitnamiRepoUrl>" version "25.1.10"
version is optional and only valid for this "reference" source kind
(chart+repo, or a bare repo/name) — a local path/URL/OCI ref is
already pinned by what it points at, and setting version on one of
those fails loudly rather than being silently ignored.
Given OCI Artifact "<NginxOciArtifact>" at "oci://registry-1.docker.io/bitnamicharts/nginx"
And Helm Chart "<OciNginxHelmChart>" in "<NginxOciArtifact>"
Assert on a Chart's Chart.yaml¶
Given Helm Chart known as "<Resource>" has:
Given Helm Chart "<Resource>" has {string} {condition} {word} # oneline sibling, one condition at a time
Asserts against the chart's real, parsed Chart.yaml — KEY|CONDITION|VALUE.
The oneline form takes a quoted value instead of {word} for one
containing a space, and drops the value entirely for exists/
undefined (the only two conditions that ignore it):
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
And Helm Chart "<LocalNginxHelmChart>" in "<NginxChartDirectory>"
And Helm Chart "<LocalNginxHelmChart>" has "apiVersion" is v2
And Helm Chart "<LocalNginxHelmChart>" has "name" is test-nginx
And Helm Chart "<LocalNginxHelmChart>" has "type" undefined
Given Directory known as "<NginxChartDirectory>":
| PROPERTY | VALUE |
| path | ./charts/test-nginx |
And Helm Chart known as "<LocalNginxHelmChart>":
| PROPERTY | VALUE |
| chart | <NginxChartDirectory> |
And Helm Chart known as "<LocalNginxHelmChart>" has:
| KEY | CONDITION | VALUE |
| apiVersion | equals | v2 |
| name | equals | test-nginx |
| type | undefined | |
Template a Chart¶
When I template Helm Chart known as "<Resource>" with:
When I template Helm Chart "<Resource>" with {flags}
Runs helm template [NAME] [CHART]. Any table-supplied positional arg
(a blank-OPTION row, or a bare token in the {flags} form) must come
before the chart args — the wrong order gets the chart's own path
parsed as a stray extra arg.
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
And Helm Chart "<LocalNginxHelmChart>" in "<NginxChartDirectory>"
When I template Helm Chart "<LocalNginxHelmChart>" with test-nginx
Then the command exited with 0 STDOUT contains "# Source: test-nginx/templates/service.yaml"
Given Directory known as "<NginxChartDirectory>":
| PROPERTY | VALUE |
| path | ./charts/test-nginx |
And Helm Chart known as "<LocalNginxHelmChart>":
| PROPERTY | VALUE |
| chart | <NginxChartDirectory> |
When I template Helm Chart known as "<LocalNginxHelmChart>" with:
| OPTION | VALUE |
| | test-nginx |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | # Source: test-nginx/templates/service.yaml |
Show Chart Info¶
When I show {chart|values|readme|crds|all} for Helm Chart known as "<Resource>"
When I show {chart|values|readme|crds|all} for Helm Chart known as "<Resource>" with:
When I show {chart|values|readme|crds|all} for Helm Chart "<Resource>" with {flags}
Runs helm show <sub> [CHART]. chart/values/crds output YAML
(assert with the command result data has:); readme is plain
markdown and all has no -o flag at all — both use the raw-text the
command exited with {int}: form instead.
Given Directory "<NginxChartDirectory>" at "./charts/test-nginx"
And Helm Chart "<LocalNginxHelmChart>" in "<NginxChartDirectory>"
When I show crds for Helm Chart "<LocalNginxHelmChart>" with --devel
Then the command exited with 0
Given Directory known as "<NginxChartDirectory>":
| PROPERTY | VALUE |
| path | ./charts/test-nginx |
And Helm Chart known as "<LocalNginxHelmChart>":
| PROPERTY | VALUE |
| chart | <NginxChartDirectory> |
When I show crds for Helm Chart known as "<LocalNginxHelmChart>" with:
| OPTION | VALUE |
| --devel | True |
Then the command exited with 0
The table-less form (nothing to shorten) covers the common values/
chart case:
When I show values for Helm Chart known as "<LocalNginxHelmChart>"
Then the command result data has:
| KEY | CONDITION | VALUE |
| replicaCount | equals | 1 |
Repository¶
Steps scoped to a chart repository: define it, then add/remove/update/list
it via helm repo.
Define a Repository¶
Given Helm Repo "<BitnamiHelmRepo>" named "bitnami" at "<BitnamiRepoUrl>"
Given Helm Repo known as "<BitnamiHelmRepo>":
| PROPERTY | VALUE |
| name | bitnami |
| url | <BitnamiRepoUrl> |
Registers a repository's real name/url — pure construction, no
helm repo add yet.
Given URL "<BitnamiRepoUrl>" at "https://charts.bitnami.com/bitnami"
And Helm Repo "<BitnamiHelmRepo>" named "bitnami" at "<BitnamiRepoUrl>"
When I add Helm Repo known as "<BitnamiHelmRepo>"
And Helm Chart "<ReferenceNginxHelmChart>" in "bitnami/nginx"
Registering the repo for real, then referencing it by its registered
name/chart string from a Chart — the alternative to the
bare-name-plus-repo-field form shown in the "Reference" tab above.
Add a Repository¶
When I add Helm Repo known as "<Resource>"
When I add Helm Repo known as "<Resource>" with:
When I add Helm Repo "<Resource>" with {flags}
Runs helm repo add <name> <url>.
Given URL "<MetricsServerRepoUrl>" at "https://kubernetes-sigs.github.io/metrics-server/"
And Helm Repo "<ThomasAddExampleRepo>" named "thomas-add-example" at "<MetricsServerRepoUrl>"
When I add Helm Repo "<ThomasAddExampleRepo>" with --insecure-skip-tls-verify
Then the command exited with 0 STDOUT contains "has been added to your repositories"
Given URL known as "<MetricsServerRepoUrl>":
| PROPERTY | VALUE |
| value | https://kubernetes-sigs.github.io/metrics-server/ |
And Helm Repo known as "<ThomasAddExampleRepo>":
| PROPERTY | VALUE |
| name | thomas-add-example |
| url | <MetricsServerRepoUrl> |
When I add Helm Repo known as "<ThomasAddExampleRepo>" with:
| OPTION | VALUE |
| --insecure-skip-tls-verify | True |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | has been added to your repositories |
Remove a Repository¶
When I remove Helm Repo known as "<Resource>"
When I remove Helm Repo known as "<Resource>" with:
When I remove Helm Repo "<Resource>" with {flags}
Runs helm repo remove <name>.
When I remove Helm Repo "<ThomasRemoveExampleRepo>" with --debug
Then the command exited with 0 STDOUT contains "has been removed from your repositories"
When I remove Helm Repo known as "<ThomasRemoveExampleRepo>" with:
| OPTION | VALUE |
| --debug | True |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | has been removed from your repositories |
Update a Repository¶
When I update Helm Repo known as "<Resource>"
When I update Helm Repo known as "<Resource>" with:
When I update Helm Repo "<Resource>" with {flags}
Runs helm repo update <name>.
When I update Helm Repo "<MetricsServerHelmRepo>" with --timeout 5m
Then the command exited with 0
When I update Helm Repo known as "<MetricsServerHelmRepo>" with:
| OPTION | VALUE |
| --timeout | 5m |
Then the command exited with 0
List Repositories¶
When I list Helm Repo
When I list Helm Repo with:
When I list Helm Repo with {flags}
Runs helm repo list (no resource — lists every registered repo).
When I list Helm Repo with --output yaml
Then the command result has "[*].name" is bitnami
When I list Helm Repo with:
| OPTION | VALUE |
| --output | yaml |
Then the command result data has:
| KEY | CONDITION | VALUE |
| [*].name | equals | bitnami |
Release¶
Steps scoped to a Helm release: define it, then install/upgrade/roll back/inspect/list it.
Define a Release¶
Given Helm Release "<NginxRelease>" of "<NginxHelmChart>" named "thomas-nginx-release" in "thomas-helm-test"
Given Helm Release known as "<NginxRelease>":
| PROPERTY | VALUE |
| chart | <NginxHelmChart> |
| name | thomas-nginx-release |
| namespace | thomas-helm-test |
Registers a release's chart (resolved to the real Chart object),
name, and namespace — pure construction, no helm install yet.
See Kubernetes: Deployment for discovering the
real Deployment/Service/Pod a Release creates once installed.
Manage a Release¶
When I {install|upgrade|uninstall|rollback|status|history|test} Helm Release known as "<Resource>"
When I {install|upgrade|uninstall|rollback|status|history|test} Helm Release known as "<Resource>" with:
When I {install|upgrade|uninstall|rollback|status|history|test} Helm Release "<Resource>" with <flags>
When I get status of Helm Release "<Resource>" as JSON|YAML
Only install/upgrade take a chart ref; the rest only ever take
RELEASE_NAME -n NAMESPACE. The last form is a shorthand specifically
for --output json/--output yaml — by far the most common real flags
payload — alongside the general with <flags> short form for anything
else.
helm install is genuinely not idempotent
A second install of the same release name errors
(cannot reuse a name that is still in use — Helm v3 spelled it
re-use, so the scenarios match only the shared tail) — that's real,
correct Helm behavior, not a framework limitation. The rerun-safe idiom used
throughout this suite is upgrade with --install/--atomic set
(see the "upgrade" tab below).
--atomic blocks until the rollout is healthy (or rolls back on
failure) — this is why Kubernetes discovery right after an
--atomic upgrade never needs to poll. the command succeeds (short
form only) is a plain-English alias for the command exited with 0
specifically — the command exited with {int} is still the form for a
real nonzero-exit assertion. Then Helm Release "<X>" is "<status>" is
a fixed-key shorthand for info.status equals <status> against the
helm status result right above it — still just reading the captured
command output, plus a real check that "<X>" is a genuinely
registered Release first.
When I upgrade Helm Release "<NginxRelease>" with --install --atomic --create-namespace
Then the command succeeds
When I get status of Helm Release "<NginxRelease>" as YAML
Then Helm Release "<NginxRelease>" is "deployed"
When I get history of Helm Release "<NginxRelease>" as YAML
Then the command result has "[*].status" is deployed
When I test Helm Release known as "<NginxRelease>"
Then the command exited with 0 STDOUT contains Succeeded
When I uninstall Helm Release known as "<NginxRelease>"
Then the command exited with 0 STDOUT contains uninstalled
Rollback, and the one deliberate non-idempotent install (a second
install of the same release name), each in their own scenario:
When I upgrade Helm Release "<RollbackRelease>" with --set replicaCount=2
Then the command exited with 0
When I rollback Helm Release "<RollbackRelease>" with 1
Then the command exited with 0 STDOUT contains "Rollback was a success"
When I install Helm Release "<DuplicateRelease>" with --create-namespace
Then the command exited with 0
When I install Helm Release known as "<DuplicateRelease>"
Then the command exited with 1 STDERR contains "name that is still in use"
When I upgrade Helm Release known as "<NginxRelease>" with:
| OPTION | VALUE |
| --install | True |
| --atomic | True |
| --create-namespace | True |
Then the command exited with 0
When I status Helm Release known as "<NginxRelease>" with:
| OPTION | VALUE |
| --output | yaml |
Then the command result data has:
| KEY | CONDITION | VALUE |
| info.status | equals | deployed |
When I history Helm Release known as "<NginxRelease>" with:
| OPTION | VALUE |
| --output | yaml |
Then the command result data has:
| KEY | CONDITION | VALUE |
| [*].status | equals | deployed |
When I test Helm Release known as "<NginxRelease>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | Succeeded |
When I uninstall Helm Release known as "<NginxRelease>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | uninstalled |
Rollback, and the one deliberate non-idempotent install, each in
their own scenario:
When I upgrade Helm Release known as "<RollbackRelease>" with:
| OPTION | VALUE |
| --set | replicaCount=2 |
Then the command exited with 0
When I rollback Helm Release known as "<RollbackRelease>" with:
| OPTION | VALUE |
| | 1 |
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | Rollback was a success |
When I install Helm Release known as "<DuplicateRelease>" with:
| OPTION | VALUE |
| --create-namespace | True |
Then the command exited with 0
When I install Helm Release known as "<DuplicateRelease>"
Then the command exited with 1:
| SOURCE | CONDITION | VALUE |
| STDERR | contains | cannot re-use a name |
Get Release Info¶
When I get {all|hooks|manifest|metadata|notes|values} for Helm Release known as "<Resource>"
When I get {all|hooks|manifest|metadata|notes|values} for Helm Release known as "<Resource>" with:
Runs helm get <sub> <name> -n <namespace>. values/metadata output
YAML (assert with the command result data has:); the rest use the
raw-text the command exited with {int}: form. hooks/manifest/
notes/all take no real flags in this suite, so their table-less
form is identical in both syntaxes (nothing to shorten).
When I get values for Helm Release "<NginxRelease>" with --all --output yaml
Then the command result has "replicaCount" is 1
When I get metadata for Helm Release "<NginxRelease>" as YAML
Then the command result has "name" is thomas-nginx-release
Then the command result has "chart" is test-nginx
When I get hooks for Helm Release known as "<NginxRelease>"
Then the command exited with 0 STDOUT contains "helm.sh/hook\": test"
When I get manifest for Helm Release known as "<NginxRelease>"
Then the command exited with 0 STDOUT contains "# Source: test-nginx/templates/service.yaml"
When I get notes for Helm Release known as "<NginxRelease>"
Then the command exited with 0 STDOUT contains "Access it:"
When I get all for Helm Release known as "<NginxRelease>"
Then the command exited with 0 STDOUT contains NOTES:
Then the command exited with 0 STDOUT contains "# Source: test-nginx/templates/service.yaml"
When I get values for Helm Release known as "<NginxRelease>" with:
| OPTION | VALUE |
| --all | True |
| --output | yaml |
Then the command result data has:
| KEY | CONDITION | VALUE |
| replicaCount | equals | 1 |
When I get metadata for Helm Release known as "<NginxRelease>" with:
| OPTION | VALUE |
| --output | yaml |
Then the command result data has:
| KEY | CONDITION | VALUE |
| name | equals | thomas-nginx-release |
| chart | equals | test-nginx |
When I get hooks for Helm Release known as "<NginxRelease>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | helm.sh/hook": test |
When I get manifest for Helm Release known as "<NginxRelease>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | # Source: test-nginx/templates/service.yaml |
When I get notes for Helm Release known as "<NginxRelease>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | Access it: |
When I get all for Helm Release known as "<NginxRelease>"
Then the command exited with 0:
| SOURCE | CONDITION | VALUE |
| STDOUT | contains | NOTES: |
| STDOUT | contains | # Source: test-nginx/templates/service.yaml |
List Releases¶
When I list Helm Release
When I list Helm Release with:
When I list Helm Release with {flags}
Runs helm list (no resource — lists releases matching the given options).
When I list Helm Release with --namespace thomas-helm-test --output yaml
Then the command result has "[*].name" is thomas-nginx-release
When I list Helm Release with:
| OPTION | VALUE |
| --namespace | thomas-helm-test |
| --output | yaml |
Then the command result data has:
| KEY | CONDITION | VALUE |
| [*].name | equals | thomas-nginx-release |