mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
Merge remote-tracking branch 'upstream/main' into feat/toml-parsing
Signed-off-by: Joe Julian <me@joejulian.name>
This commit is contained in:
commit
1c73ea44ad
325 changed files with 8255 additions and 2218 deletions
|
|
@ -1,14 +0,0 @@
|
|||
---
|
||||
|
||||
# This file can be removed when Helm no longer uses CircleCI on any release
|
||||
# branches. Once CircleCI is turned off this file can be removed.
|
||||
version: 2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: cimg/go:1.18
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
16
.github/dependabot.yml
vendored
16
.github/dependabot.yml
vendored
|
|
@ -4,4 +4,18 @@ updates:
|
|||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
interval: "daily"
|
||||
groups:
|
||||
k8s.io:
|
||||
patterns:
|
||||
- "k8s.io/api"
|
||||
- "k8s.io/apiextensions-apiserver"
|
||||
- "k8s.io/apimachinery"
|
||||
- "k8s.io/apiserver"
|
||||
- "k8s.io/cli-runtime"
|
||||
- "k8s.io/client-go"
|
||||
- "k8s.io/kubectl"
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
|
|
|||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
|
|
@ -7,6 +7,6 @@
|
|||
**Special notes for your reviewer**:
|
||||
|
||||
**If applicable**:
|
||||
- [ ] this PR contains documentation
|
||||
- [ ] this PR contains user facing changes (the `docs needed` label should be applied if so)
|
||||
- [ ] this PR contains unit tests
|
||||
- [ ] this PR has been tested for backwards compatibility
|
||||
|
|
|
|||
24
.github/workflows/build-test.yml
vendored
24
.github/workflows/build-test.yml
vendored
|
|
@ -2,8 +2,8 @@ name: build-test
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release-**'
|
||||
- "main"
|
||||
- "release-**"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
|
@ -13,23 +13,13 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # pin@v3.2.0
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@3.5.0
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
|
||||
with:
|
||||
go-version: '1.20'
|
||||
- name: Install golangci-lint
|
||||
run: |
|
||||
curl -sSLO https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz
|
||||
shasum -a 256 golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz | grep "^$GOLANGCI_LINT_SHA256 " > /dev/null
|
||||
tar -xf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz
|
||||
sudo mv golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64/golangci-lint /usr/local/bin/golangci-lint
|
||||
rm -rf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64*
|
||||
env:
|
||||
GOLANGCI_LINT_VERSION: '1.51.2'
|
||||
GOLANGCI_LINT_SHA256: '4de479eb9d9bc29da51aec1834e7c255b333723d38dbd56781c68e5dddc6a90b'
|
||||
- name: Test style
|
||||
run: make test-style
|
||||
go-version: '1.22.7'
|
||||
- name: Test source headers are present
|
||||
run: make test-source-headers
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
- name: Test build
|
||||
|
|
|
|||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
|
|
@ -35,11 +35,11 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # pin@v3.2.0
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # pinv2.1.37
|
||||
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 # pinv3.26.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
|
|
@ -50,7 +50,7 @@ jobs:
|
|||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # pinv2.1.37
|
||||
uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 # pinv3.26.6
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
|
@ -64,4 +64,4 @@ jobs:
|
|||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@959cbb7472c4d4ad70cdfe6f4976053fe48ab394 # pinv2.1.37
|
||||
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 # pinv3.26.6
|
||||
|
|
|
|||
22
.github/workflows/golangci-lint.yml
vendored
Normal file
22
.github/workflows/golangci-lint.yml
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
name: golangci-lint
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
golangci:
|
||||
name: golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
|
||||
with:
|
||||
go-version: "1.22.7"
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 #pin@6.1.0
|
||||
with:
|
||||
version: v1.58
|
||||
21
.github/workflows/govulncheck.yml
vendored
Normal file
21
.github/workflows/govulncheck.yml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
name: govulncheck
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- go.sum
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
govulncheck:
|
||||
name: govulncheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
|
||||
with:
|
||||
go-version: '1.22.7'
|
||||
- name: govulncheck
|
||||
uses: golang/govulncheck-action@dd0578b371c987f96d1185abb54344b44352bd58 # pin@1.0.3
|
||||
with:
|
||||
go-package: ./...
|
||||
43
.github/workflows/release.yml
vendored
43
.github/workflows/release.yml
vendored
|
|
@ -18,21 +18,37 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # pin@v3.2.0
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@3.5.0
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
|
||||
with:
|
||||
go-version: '1.20'
|
||||
|
||||
go-version: '1.22.7'
|
||||
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
|
||||
- name: Build Helm Binaries
|
||||
run: |
|
||||
make build-cross
|
||||
set -eu -o pipefail
|
||||
|
||||
make build-cross VERSION="${{ github.ref_name }}"
|
||||
make dist checksum VERSION="${{ github.ref_name }}"
|
||||
|
||||
- name: Set latest version
|
||||
run: |
|
||||
set -eu -o pipefail
|
||||
|
||||
mkdir -p _dist_versions
|
||||
|
||||
# Push the latest semver tag, excluding prerelease tags
|
||||
LATEST_VERSION="$(git tag | sort -r --version-sort | grep '^v[0-9]' | grep -v '-' | head -n1)"
|
||||
echo "LATEST_VERSION=${LATEST_VERSION}"
|
||||
echo "${LATEST_VERSION}" > _dist_versions/helm-latest-version
|
||||
echo "${LATEST_VERSION}" > _dist_versions/helm3-latest-version
|
||||
|
||||
- name: Upload Binaries
|
||||
uses: bacongobbler/azure-blob-storage-upload@50f7d898b7697e864130ea04c303ca38b5751c50 # pin@3.0.0
|
||||
env:
|
||||
|
|
@ -44,17 +60,28 @@ jobs:
|
|||
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
|
||||
extra_args: '--pattern helm-*'
|
||||
|
||||
- name: Upload Version tag files
|
||||
uses: bacongobbler/azure-blob-storage-upload@50f7d898b7697e864130ea04c303ca38b5751c50 # pin@3.0.0
|
||||
env:
|
||||
AZURE_STORAGE_CONNECTION_STRING: "${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}"
|
||||
AZURE_STORAGE_CONTAINER_NAME: "${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}"
|
||||
with:
|
||||
overwrite: 'true'
|
||||
source_dir: _dist_versions
|
||||
container_name: ${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}
|
||||
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
|
||||
|
||||
canary-release:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main'
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@755da8c3cf115ac066823e79a1e1788f8940201b # pin@v3.2.0
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # pin@v4.1.7
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # pin@3.5.0
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # pin@5.0.2
|
||||
with:
|
||||
go-version: '1.20'
|
||||
go-version: '1.22.7'
|
||||
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
|
|
|
|||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -5,7 +5,9 @@
|
|||
.idea/
|
||||
.vimrc
|
||||
.vscode/
|
||||
.devcontainer/
|
||||
_dist/
|
||||
_dist_versions/
|
||||
bin/
|
||||
vendor/
|
||||
# Ignores charts pulled for dependency build tests
|
||||
|
|
|
|||
|
|
@ -5,12 +5,15 @@
|
|||
|
||||
# Organizations Using Helm
|
||||
|
||||
- [Blood Orange](https://bloodorange.io)
|
||||
- [IBM](https://www.ibm.com)
|
||||
- [Microsoft](https://microsoft.com)
|
||||
- [Octopus Deploy](https://octopus.com/)
|
||||
- [New Relic](https://www.newrelic.com)
|
||||
- [Qovery](https://www.qovery.com/)
|
||||
- [Samsung SDS](https://www.samsungsds.com/)
|
||||
- [Softonic](https://hello.softonic.com/)
|
||||
- [Syself](https://syself.com)
|
||||
- [Ville de Montreal](https://montreal.ca)
|
||||
- [Intercept](https://Intercept.cloud)
|
||||
|
||||
_This file is part of the CNCF official documentation for projects._
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ chance to try to fix the issue before it is exploited in the wild.
|
|||
|
||||
## Sign Your Work
|
||||
|
||||
The sign-off is a simple line at the end of the explanation for a commit. All commits needs to be
|
||||
The sign-off is a simple line at the end of the explanation for a commit. All commits need to be
|
||||
signed. Your signature certifies that you wrote the patch or otherwise have the right to contribute
|
||||
the material. The rules are pretty simple, if you can certify the below (from
|
||||
[developercertificate.org](https://developercertificate.org/)):
|
||||
|
|
@ -66,6 +66,18 @@ Use your real name (sorry, no pseudonyms or anonymous contributions.)
|
|||
If you set your `user.name` and `user.email` git configs, you can sign your commit automatically
|
||||
with `git commit -s`.
|
||||
|
||||
The following command will update your git config with `user.email`:
|
||||
|
||||
``` bash
|
||||
git config --global user.email joe.smith@example.com
|
||||
```
|
||||
|
||||
This command will update your git config with `user.name`:
|
||||
|
||||
``` bash
|
||||
git config --global user.name "Joe Smith"
|
||||
```
|
||||
|
||||
Note: If your git config information is set properly then viewing the `git log` information for your
|
||||
commit will look something like this:
|
||||
|
||||
|
|
@ -115,8 +127,9 @@ Helm maintains a strong commitment to backward compatibility. All of our changes
|
|||
formats are backward compatible from one major release to the next. No features, flags, or commands
|
||||
are removed or substantially modified (unless we need to fix a security issue).
|
||||
|
||||
We also try very hard to not change publicly accessible Go library definitions inside of the `pkg/`
|
||||
directory of our source code.
|
||||
We also remain committed to not changing publicly accessible Go library definitions inside of the `pkg/` directory of our source code in a non-backwards-compatible way.
|
||||
|
||||
For more details on Helm’s minor and patch release backwards-compatibility rules, please read [HIP-0004](https://github.com/helm/community/blob/main/hips/hip-0004.md)
|
||||
|
||||
For a quick summary of our backward compatibility guidelines for releases between 3.0 and 4.0:
|
||||
|
||||
|
|
@ -126,30 +139,9 @@ For a quick summary of our backward compatibility guidelines for releases betwee
|
|||
(barring the cases where (a) Kubernetes itself changed, and (b) the chart worked because it
|
||||
exploited a bug)
|
||||
- Chart repository functionality MUST be backward compatible
|
||||
- Go libraries inside of `pkg/` SHOULD remain backward compatible, though code inside of `cmd/` and
|
||||
- Go libraries inside of `pkg/` MUST remain backward compatible, though code inside of `cmd/` and
|
||||
`internal/` may be changed from release to release without notice.
|
||||
|
||||
## Support Contract for Helm 2
|
||||
|
||||
With Helm 2's current release schedule, we want to take into account any migration issues for users
|
||||
due to the upcoming holiday shopping season and tax season. We also want to clarify what actions may
|
||||
occur after the support contract ends for Helm 2, so that users will not be surprised or caught off
|
||||
guard.
|
||||
|
||||
After Helm 2.15.0 is released, Helm 2 will go into "maintenance mode". We will continue to accept
|
||||
bug fixes and fix any security issues that arise, but no new features will be accepted for Helm 2.
|
||||
All feature development will be moved over to Helm 3.
|
||||
|
||||
6 months after Helm 3.0.0's public release, Helm 2 will stop accepting bug fixes. Only security
|
||||
issues will be accepted.
|
||||
|
||||
12 months after Helm 3.0.0's public release, support for Helm 2 will formally end. Download links
|
||||
for the Helm 2 client through Google Cloud Storage, the Docker image for Tiller stored in Google
|
||||
Container Registry, and the Google Cloud buckets for the stable and incubator chart repositories may
|
||||
no longer work at any point. Client downloads through `get.helm.sh` will continue to work, and we
|
||||
will distribute a Tiller image that will be made available at an alternative location which can be
|
||||
updated with `helm init --tiller-image`.
|
||||
|
||||
## Issues
|
||||
|
||||
Issues are used as the primary method for tracking anything to do with the Helm project.
|
||||
|
|
@ -195,7 +187,7 @@ below.
|
|||
See [Proposing an Idea](#proposing-an-idea). Smaller quality-of-life enhancements are exempt.
|
||||
- Issues that are labeled as `feature` or `bug` should be connected to the PR that resolves it.
|
||||
- Whoever is working on a `feature` or `bug` issue (whether a maintainer or someone from the
|
||||
community), should either assign the issue to themself or make a comment in the issue saying
|
||||
community), should either assign the issue to themselves or make a comment in the issue saying
|
||||
that they are taking it.
|
||||
- `proposal` and `support/question` issues should stay open until resolved or if they have not
|
||||
been active for more than 30 days. This will help keep the issue queue to a manageable size
|
||||
|
|
@ -282,9 +274,9 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
|
|||
|
||||
#### Documentation PRs
|
||||
|
||||
Documentation PRs will follow the same lifecycle as other PRs. They will also be labeled with the
|
||||
`docs` label. For documentation, special attention will be paid to spelling, grammar, and clarity
|
||||
(whereas those things don't matter *as* much for comments in code).
|
||||
Documentation PRs should be made on the docs repo: <https://github.com/helm/helm-www>. Keeping Helm's documentation up to date is highly desirable, and it is recommend all user facing changes. Accurate and helpful documentation is critical for effectively communicating Helm's behavior to a wide audience.
|
||||
|
||||
Small, ad-hoc changes/PRs to Helm which introduce user facing changes, which would benefit from documentation changes, should apply the `docs needed` label. Larger changes associated with a HIP should track docs via that HIP. The `docs needed` label doesn't block PRs, and maintainers/PR reviewers should apply discretion judging in whether the `docs needed` label should be applied.
|
||||
|
||||
## The Triager
|
||||
|
||||
|
|
@ -327,6 +319,7 @@ The following tables define all label types used for Helm. It is split up by cat
|
|||
| `needs rebase` | Indicates a PR needs to be rebased before it can be merged |
|
||||
| `needs pick` | Indicates a PR needs to be cherry-picked into a feature branch (generally bugfix branches). Once it has been, the `picked` label should be applied and this one removed |
|
||||
| `picked` | This PR has been cherry-picked into a feature branch |
|
||||
| `docs needed` | Tracks PRs that introduces a feature/change for which documentation update would be desirable (non-blocking). Once a suitable documentation PR has been created, then this label should be removed |
|
||||
|
||||
#### Size labels
|
||||
|
||||
|
|
|
|||
97
KEYS
97
KEYS
|
|
@ -940,3 +940,100 @@ AirPev6SluPhLJ2mswaK3THlhOZulKO/VIEJ6g50m5Vj3hdYf6sR603yK9rP+3iu
|
|||
IagTQt2SGfW3Ap0RO3Yt+w29BpZ1CZ5Ml4gAYkXz0hiiMnVRhlcLIOHoFw==
|
||||
=h3+3
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
pub rsa4096 2018-12-08 [SC]
|
||||
208DD36ED5BB3745A16743A4C7C6FBB5B91C1155
|
||||
uid [ultimate] Scott Rigby <scott@r6by.com>
|
||||
sig 3 C7C6FBB5B91C1155 2018-12-08 [self-signature]
|
||||
sig 134FC1555856DA4F 2018-12-13 [User ID not found]
|
||||
sig 62F49E747D911B60 2018-12-17 [User ID not found]
|
||||
sig F54982D216088EE1 2019-01-05 [User ID not found]
|
||||
sub rsa4096 2018-12-08 [E]
|
||||
sig C7C6FBB5B91C1155 2018-12-08 [self-signature]
|
||||
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mQINBFwMUAcBEADplQ+msULZ4kt01bXDvZ66MSVe5Fi1cPqAa/5/ZtaHZWSKrcN6
|
||||
K0cadpozJp74HSZzORLYV/50EGwXU+OG1dFe73FbsTCgQyLCbh/OjT+Exq553g2D
|
||||
/IB4/6/vCs9XXiYdKot3P2NsHI6RqeGqgW2IkFVsMXO2Lq1XKFTWQniO7PhHW8nG
|
||||
Trub7HrxR6i9KHtVtxLs+XoXY7Jj0gB7WyRkYjHLXti4VtvcBq0WK3pSgEIy5MwR
|
||||
WDepmle8n8EJZrh3T323YM41MXKGT00wCSKMbSHJO7QssiOda9XluC175HDfihm3
|
||||
q5OKV2ZYIbChsQxuJz1Y97hwZ5KkLn//W2pxTdOElOcynFpQNx7D4b6UTP2DCCRc
|
||||
n41SiDIyHg25cUXXAkJWlYRD1koGfLBipJA0DcKqlh3W+8zNfngZ0PSxwFtJwSre
|
||||
Zx5I5uHAgKO5nS4hLxGYUMv+MsSKHMYR6qkqFg1Eal6tTa68bPFTbzypDmMUKXZT
|
||||
sZtZ79WoIUU9D3O+F+Z9rxwaQ3Dv7J49FdbLPB3zqENqX7OWHZ38m5dsweTFhQi+
|
||||
4AaDLEMiqMi27SfPkF1/+JDc1SOoLVo9QgukqhFlz6qEIbud7LUfpeKBRNJsfdr6
|
||||
HE3cH8MWHInnlJ49De1oLl5bwAwScQig5jmv5DZxN5qdTg64vgoreBLgsQARAQAB
|
||||
tBxTY290dCBSaWdieSA8c2NvdHRAcjZieS5jb20+iQJOBBMBCAA4FiEEII3TbtW7
|
||||
N0WhZ0Okx8b7tbkcEVUFAlwMUAcCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AA
|
||||
CgkQx8b7tbkcEVUIPg//cK8zaAOUIFClQ31TeQnwHsJwcBMeRRegUjt4cIYSjv3F
|
||||
oNe5EQKxgpwT/1iegvRURjxHbhSE0paQ9wsi8Tr5Ygs0DJLDp4PJVZtVHJ5qB3XT
|
||||
VuCiqyb90kIG+ok1m8FHBwpeU7o2a05InYwJLWowBQTulhrS4VKzrPs2kMu4z5Am
|
||||
5sbs70f0hRLUXGmEAFUnZm+lpF48/PCMNSPrxgZ6rtNqtXq8oCyPNvyO8Ou130tv
|
||||
TSoHx5Tobz3RnSeDXpidC/Z1rQGq4Spi+a0WwC9BCArDvtOrQBoeQFgdpy5OsE/t
|
||||
QfQNEffyLRLlAZgXewOC+PeF+xrz+ku/rMBlt/h0h5UcMS3joUHTRZyQoA5dzWZh
|
||||
K8pMXYppHXJtxl0fNSG1rH1vsTu+Mjxmd9eBwaDxnBFwzBOukiwSUgW1r2DyZ7S+
|
||||
25ZW62lcz/E9+sgWV/PGR+YKGbsDdnraPdQEc95k8NQnYsX/7YO/WXJfP7Cdqd8X
|
||||
dAAVW6/dGUp2i4QogEYk+GeJz3rJJK24Wzgf2FJ32FuSH8uOQUH93h5QZbJAD+pD
|
||||
R2qZAZjCHYAgvSuqnmDlefvG95TwIVzy3OFWbLwp6YyBdyZrdpTafER4zk17f0HF
|
||||
xS3z3LG0LsEWHShxccjzjoAUeppAU0Qojag3kKLoCwveambIdjXIxliBr/S2VSWJ
|
||||
AjMEEAEKAB0WIQTlYbNM52c/3RO87D71SYLSFgiO4QUCXC/89gAKCRD1SYLSFgiO
|
||||
4YfMEACS7/90VvyhbcQYB/2N6dYYuVd4tMRpM7jgWO6LqDDh3C9S2NI2bzwxGFBR
|
||||
nqV9N6fD+yLug/dtAPq5D4i7AXzRqPA8XQ2ky/1EJOv5EmOl6NYnUZafEBMDMai6
|
||||
F6XOji2JR7b6xlRC7GwzUdMR1rn8eyCxuJobgB+wMzfcSAnaDsH1qU7a5ohEewtQ
|
||||
IgEcLLmiLapCqNXm0l5oIYQQypbRrogw4ePw8KcLDreRtPCpPdLIThqdfkXLQ69Z
|
||||
ZwGd1+Pg6xgu7MJggnNq2RYortW/Jx9WUFs0Jj9c+Yz5pPeQmc1jjF4uHvxfi0gt
|
||||
wFqL+bu+HfxI/Hiudkzzp3v+Llmjk6RypJowLLk5cxMxv7XMfK3cLLDO5uw0pPiq
|
||||
l1f4u4P3YeRLv5YFh8SRrk/PadEqFr10mIOmreASq9LivJkhGK9eqQ0X7zQfHmDq
|
||||
S3nw62ousqlmEld39MIAMn02Ak9i9CD2M3G2O5gCAcvnFlWblx5CN9Pjc6kOb52W
|
||||
eDMYisUmKnIkChzvAlfh8PhvQfLUpKN1AmzUOcXJokpu1Yx7OGaoDnfpXSHS8fe8
|
||||
pu0jMsEhlqsNxNS6y5N0tWjxjYg7D1Qpeq3O4oft4HiO3ZfMPI4V1tatfeohnjic
|
||||
UJkpVsS4RZybu8aqNGc8i/ggagiWc50oydK8Lp906XfOcEert4kCMwQQAQoAHRYh
|
||||
BKuiUpWY9mJsQg0zW2L0nnR9kRtgBQJcF85IAAoJEGL0nnR9kRtg0OMQAKqpxGtA
|
||||
uaMknrZxnxu+y4FXXrX/W2TLlF7Ns71upXhitwSWk0pVJi+OZUvIGj+8yCj2MEg6
|
||||
o5qBJPyo8TuwIh1YfxBYigY5Hmt/uVVbKBM/VXyKDxzGrSts+r73cxf3BpPfyANB
|
||||
a90LjKHvFv0czu5sfiRMHU9GCOehnBukdZ9PhOOcRuUlHoHlSf21x9kxa1tUFPVJ
|
||||
eeoVOOnONDK6Dzmi6GoGRTq3X/HZ2JjhcdYSn37z/KxmZ/SNiwat60gw5zJHTh0a
|
||||
dM54hwsdsp48/avpF8BlTgXsoH5dVdbaOTyNGXBbQaoL09FY2x2eXaFCP3RbMWK8
|
||||
TpWh0Ijs/3JLFJ20jrvZqsmxmwIX25TmBb4UoR3HSEHFasYoIxv/me6V4oB7D1SJ
|
||||
F3q5scPnRzV3I5wCRljKJcNHQbNb/c9Xnt5UVnLHRtkZW/NUw93H5Bq15dkq4U6i
|
||||
prC0EgjfiWy2Esd9TiGb5kRuN0/duUY/d7dewp1tJGDRZACwWwHyYkPiPA6gzQfN
|
||||
83yk29evZeX1rSBslnXSLzuwhVJc28KNZCeEcC2o1JninqfqoYnypgSFOS5BK6ZG
|
||||
5YJD0gkKFX+ImC8OSKsIJ6QyrzyOBb7UwRcK5qlvYZGYgrt+B+mFDpxWPkzgpfMe
|
||||
CvE0nUCgKDNg0Yvhr5w9JhK+49Qn6TuTADMIiQIzBBABCAAdFiEE2o9xo8issez5
|
||||
FC4eE0/BVVhW2k8FAlwSzMYACgkQE0/BVVhW2k8jEQ//cV4+ke8eHhwwxCPZd+lA
|
||||
mvgzalwSiZZ8H0EgAB2cK0LXEFe07XGKxe2tDkf5FDIQcNm7sIk0OcLhJzYX0p9P
|
||||
A7ZzO2tZu8QuZlUqt4VnDL7B3xeW2Sh3STEmw80wubkgauRRysflAHIw3edchnIX
|
||||
9Hq55MLBBAplQFkpA/Y7arg3Bn8v/8YQlULc30xRO8EoyxD+zyl+Ic+xFtFUxNc/
|
||||
2dkqkdjq0Ohq89wTGTy0jaSI8INhZTGqR0cEYQPKZD+PXUUym/TaPKJKXagqxmu2
|
||||
XXBv6QPp46a58viBxMj6+fl1JJH3DxNF9YY++7Xp8CckA3TKDA9hxOJK6wbrTzDB
|
||||
DB+tjcwR4ff8QLv/CV3psyk2fX9CGCBdr0k0SCMQSFcHM8pKagkySjG+EJ1Tcflf
|
||||
UDY2LD33n2BBIdCaQTu6u+Zeqq2e6R3UXm/raXuGrxUxzvOQBIhb7XaC6nhfDu8k
|
||||
07yN/Tjwp+rgHt9ouH4pfFbGpvaIomBJq6pkTOk9ywDtHlSatqoVkbrbKpNzmwf8
|
||||
z7pt+ICtKqAAWQTPFPD83h6elP26GKlsyXyhT7HNmKUHaXInEbaD+IoCJ+wY/O3i
|
||||
gHV2Dn4QllSBSBhYlhl6utmP1zqwJJ0rI39mPS+nMXOhGB+bJ8EzAF/3N5J6y3TG
|
||||
FnMEJD8qgdpDEgztjHUSAy25Ag0EXAxQBwEQALqYikkk0Ur4gn9PjxtjW4OxS5J4
|
||||
e/u0UyOsv8znFM7CG9Ndha9rQs/7c7NEf3e+K6a7XqhzDKtyGAFVFlZArxbe6X8e
|
||||
UV1OidOaH46z2vmtWOJYHIupXHlXS9LeXNO+pJjCNEAzmHbGjpkjGtNz6Opl4Uae
|
||||
LoMFubRViXhvD8pBF72dGUlp8m+U4yeXJ03/q0sR94AdTA+1OzGd2+1s7PvL5XAx
|
||||
BwXqx9gccMYhrNRPyBo/yRA+Wf4ewwluIVBMi9cpR1sNF4ITIYCH4i3mf4NJvg7P
|
||||
0sPBY0s9k2jvHGLpINbFk6PkMtaRpqmgw695szTz16Gp0j41hRnEh7KnGneEp+SU
|
||||
6A8A9UGnjM9upR/d1591I5gT2U+6Q05B8RtJQUmd3HBeHEBgftjgBR0tstH8qeac
|
||||
Xc4V81OGlf3tdYP/LVggIlv8V5cdSZ4Bd3BXYWj2TIc2RmwWA2LWf4SA6JYvhEfp
|
||||
OxOzzphlgPtZF0kneEgV/b/D/KQqEk7MyZl3gN+LNk+zX7VJ2RDeUiUnoxZDFJGi
|
||||
jsbZd7yoDLkYvGiFkcQXORs2zbucweVXXK1Gyskj0c3Ih4syYYmKS0WJHMEozJUl
|
||||
b81oa7kSc2XFArcAnPz2c1yErfzcCAlg/HImkZmAgVqAfuRyxZ426F7CYucHAOcE
|
||||
N7bpIrOkqFp3uUb3ABEBAAGJAjYEGAEIACAWIQQgjdNu1bs3RaFnQ6THxvu1uRwR
|
||||
VQUCXAxQBwIbDAAKCRDHxvu1uRwRVbFaD/0e57rP3H+1rUoGhRO0oeIveQqIdd9V
|
||||
LKXUYuwzoK3HLg3BYUDEN03RS0KyNMYlHpnjyFl5L2JuXqGiJd/eu2iRXCwUMRb7
|
||||
SPvH7gypa1NUK5te85+Y8JhXOMjwZkly3zS2nRTyvHxMn9EV7NPlT/oEVu/woPrM
|
||||
o7XzmPChuvnk8pLWBW04wg5G5atDbu5+QVZlecNCrtRYJg/Cd8alKpJSeZX7y3cy
|
||||
fe2P20Gv0UOipKWaAFL55zFLbmu7HWVumYAKs6T+X/pZqmcfMaVwodIBeRJxRIvl
|
||||
PkrBxljahaFGOdgJ6FVnmO34uoYcpd019NEr9gbPoaFWmw37h3Tnc6U5sLAouaV4
|
||||
AERWmwBPIVTizYt1h8Qj4qyBhJ+QgZMjPlRqHWPZogHfMXDQV4gw3jgvVWTMVp1Z
|
||||
gDQgrFNbw02CqPwgtFn15VNwAv/4vbyToRhc3pG54e3xwdAFM8R2uM9lHJKuHafW
|
||||
7aFUk7aA20k8SG2BsZalb6tZLGxgcZOwMdO3lnLMPu1I5oOLl4cVoUIRZxtgmrbQ
|
||||
ROaGdXGIgO7fJBXXogMxjUGhMola+v6ioFQpbOnJRAr2AUVBCrrEgHoodAufGTDu
|
||||
nk38BkgHg3LHjCbCNEVkSK2TMT69A58iwpY9WUQlphsiz4WBpafSPbv/jSlsm7uK
|
||||
TNWtbFGBRpJyEg==
|
||||
=w141
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
|
|
|||
14
Makefile
14
Makefile
|
|
@ -1,8 +1,8 @@
|
|||
BINDIR := $(CURDIR)/bin
|
||||
INSTALL_PATH ?= /usr/local/bin
|
||||
DIST_DIRS := find * -type d -exec
|
||||
TARGETS := darwin/amd64 darwin/arm64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
|
||||
TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum darwin-arm64.tar.gz darwin-arm64.tar.gz.sha256 darwin-arm64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum
|
||||
TARGETS := darwin/amd64 darwin/arm64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x linux/riscv64 windows/amd64 windows/arm64
|
||||
TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 darwin-amd64.tar.gz.sha256sum darwin-arm64.tar.gz darwin-arm64.tar.gz.sha256 darwin-arm64.tar.gz.sha256sum linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-amd64.tar.gz.sha256sum linux-386.tar.gz linux-386.tar.gz.sha256 linux-386.tar.gz.sha256sum linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm.tar.gz.sha256sum linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-arm64.tar.gz.sha256sum linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-ppc64le.tar.gz.sha256sum linux-s390x.tar.gz linux-s390x.tar.gz.sha256 linux-s390x.tar.gz.sha256sum linux-riscv64.tar.gz linux-riscv64.tar.gz.sha256 linux-riscv64.tar.gz.sha256sum windows-amd64.zip windows-amd64.zip.sha256 windows-amd64.zip.sha256sum windows-arm64.zip windows-arm64.zip.sha256 windows-arm64.zip.sha256sum
|
||||
BINNAME ?= helm
|
||||
|
||||
GOBIN = $(shell go env GOBIN)
|
||||
|
|
@ -11,7 +11,7 @@ GOBIN = $(shell go env GOPATH)/bin
|
|||
endif
|
||||
GOX = $(GOBIN)/gox
|
||||
GOIMPORTS = $(GOBIN)/goimports
|
||||
ARCH = $(shell uname -p)
|
||||
ARCH = $(shell go env GOARCH)
|
||||
|
||||
ACCEPTANCE_DIR:=../acceptance-testing
|
||||
# To specify the subset of acceptance tests to run. '.' means all tests
|
||||
|
|
@ -114,7 +114,11 @@ test-coverage:
|
|||
|
||||
.PHONY: test-style
|
||||
test-style:
|
||||
GO111MODULE=on golangci-lint run
|
||||
golangci-lint run ./...
|
||||
@scripts/validate-license.sh
|
||||
|
||||
.PHONY: test-source-headers
|
||||
test-source-headers:
|
||||
@scripts/validate-license.sh
|
||||
|
||||
.PHONY: test-acceptance
|
||||
|
|
@ -155,7 +159,7 @@ gen-test-golden: test-unit
|
|||
# without a go.mod file when downloading the following dependencies
|
||||
|
||||
$(GOX):
|
||||
(cd /; GO111MODULE=on go install github.com/mitchellh/gox@latest)
|
||||
(cd /; GO111MODULE=on go install github.com/mitchellh/gox@v1.0.2-0.20220701044238-9f712387e2d2)
|
||||
|
||||
$(GOIMPORTS):
|
||||
(cd /; GO111MODULE=on go install golang.org/x/tools/cmd/goimports@latest)
|
||||
|
|
|
|||
12
OWNERS
12
OWNERS
|
|
@ -1,20 +1,21 @@
|
|||
maintainers:
|
||||
- adamreese
|
||||
- bacongobbler
|
||||
- hickeyma
|
||||
- joejulian
|
||||
- jdolitsky
|
||||
- marckhouzam
|
||||
- mattfarina
|
||||
- sabre1041
|
||||
- scottrigby
|
||||
- SlickNik
|
||||
- technosophos
|
||||
triage:
|
||||
- joejulian
|
||||
- yxxhero
|
||||
- zonggen
|
||||
- gjenkins8
|
||||
- z4ce
|
||||
emeritus:
|
||||
- adamreese
|
||||
- bacongobbler
|
||||
- fibonacci1729
|
||||
- hickeyma
|
||||
- jascott1
|
||||
- michelleN
|
||||
- migmartri
|
||||
|
|
@ -22,6 +23,7 @@ emeritus:
|
|||
- prydonius
|
||||
- rimusz
|
||||
- seh
|
||||
- SlickNik
|
||||
- thomastaylor312
|
||||
- vaikas-google
|
||||
- viglesiasce
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ Think of it like apt/yum/homebrew for Kubernetes.
|
|||
|
||||
## Install
|
||||
|
||||
|
||||
Binary downloads of the Helm client can be found on [the Releases page](https://github.com/helm/helm/releases/latest).
|
||||
|
||||
Unpack the `helm` binary and add it to your PATH and you are good to go!
|
||||
|
|
@ -40,7 +39,6 @@ If you want to use a package manager:
|
|||
- [Homebrew](https://brew.sh/) users can use `brew install helm`.
|
||||
- [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`.
|
||||
- [Scoop](https://scoop.sh/) users can use `scoop install helm`.
|
||||
- [GoFish](https://gofi.sh/) users can use `gofish install helm`.
|
||||
- [Snapcraft](https://snapcraft.io/) users can use `snap install helm --classic`
|
||||
|
||||
To rapidly get Helm up and running, start with the [Quick Start Guide](https://helm.sh/docs/intro/quickstart/).
|
||||
|
|
@ -68,6 +66,10 @@ You can reach the Helm community and developers via the following channels:
|
|||
- [Helm Mailing List](https://lists.cncf.io/g/cncf-helm)
|
||||
- Developer Call: Thursdays at 9:30-10:00 Pacific ([meeting details](https://github.com/helm/community/blob/master/communication.md#meetings))
|
||||
|
||||
### Contribution
|
||||
|
||||
If you're interested in contributing, please refer to the [Contributing Guide](CONTRIBUTING.md) **before submitting a pull request**.
|
||||
|
||||
### Code of conduct
|
||||
|
||||
Participation in the Helm community is governed by the [Code of Conduct](code-of-conduct.md).
|
||||
|
|
|
|||
|
|
@ -102,8 +102,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Short: "generate autocompletion script for bash",
|
||||
Long: bashCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionBash(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -114,8 +114,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Short: "generate autocompletion script for zsh",
|
||||
Long: zshCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionZsh(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -126,8 +126,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Short: "generate autocompletion script for fish",
|
||||
Long: fishCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionFish(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -138,8 +138,8 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Short: "generate autocompletion script for powershell",
|
||||
Long: powershellCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionPowershell(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -209,7 +209,15 @@ func runCompletionPowershell(out io.Writer, cmd *cobra.Command) error {
|
|||
return cmd.Root().GenPowerShellCompletionWithDesc(out)
|
||||
}
|
||||
|
||||
// Function to disable file completion
|
||||
func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
// noMoreArgsCompFunc deactivates file completion when doing argument shell completion.
|
||||
// It also provides some ActiveHelp to indicate no more arguments are accepted.
|
||||
func noMoreArgsCompFunc(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
|
||||
// noMoreArgsComp deactivates file completion when doing argument shell completion.
|
||||
// It also provides some ActiveHelp to indicate no more arguments are accepted.
|
||||
func noMoreArgsComp() ([]string, cobra.ShellCompDirective) {
|
||||
activeHelpMsg := "This command does not take any more arguments (but may accept flags)."
|
||||
return cobra.AppendActiveHelp(nil, activeHelpMsg), cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,16 +64,16 @@ func newCreateCmd(out io.Writer) *cobra.Command {
|
|||
Short: "create a new chart with the given name",
|
||||
Long: createDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
// Allow file completion when completing the argument for the name
|
||||
// which could be a path
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
// No more completions, so disable file completion
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.name = args[0]
|
||||
o.starterDir = helmpath.DataPath("starters")
|
||||
return o.run(out)
|
||||
|
|
|
|||
|
|
@ -30,9 +30,9 @@ import (
|
|||
)
|
||||
|
||||
func TestCreateCmd(t *testing.T) {
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
cname := "testchart"
|
||||
dir := ensure.TempDir(t)
|
||||
dir := t.TempDir()
|
||||
defer testChdir(t, dir)()
|
||||
|
||||
// Run a create
|
||||
|
|
@ -61,7 +61,7 @@ func TestCreateCmd(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateStarterCmd(t *testing.T) {
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
cname := "testchart"
|
||||
defer resetEnv()()
|
||||
os.MkdirAll(helmpath.CachePath(), 0755)
|
||||
|
|
@ -127,7 +127,7 @@ func TestCreateStarterCmd(t *testing.T) {
|
|||
|
||||
func TestCreateStarterAbsoluteCmd(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
cname := "testchart"
|
||||
|
||||
// Create a starter.
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
|
|||
Short: "list the dependencies for the given chart",
|
||||
Long: dependencyListDesc,
|
||||
Args: require.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
chartpath := "."
|
||||
if len(args) > 0 {
|
||||
chartpath = filepath.Clean(args[0])
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ func newDependencyBuildCmd(cfg *action.Configuration, out io.Writer) *cobra.Comm
|
|||
Short: "rebuild the charts/ directory based on the Chart.lock file",
|
||||
Long: dependencyBuildDesc,
|
||||
Args: require.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
chartpath := "."
|
||||
if len(args) > 0 {
|
||||
chartpath = filepath.Clean(args[0])
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func newDependencyUpdateCmd(cfg *action.Configuration, out io.Writer) *cobra.Com
|
|||
Short: "update charts/ based on the contents of Chart.yaml",
|
||||
Long: dependencyUpDesc,
|
||||
Args: require.MaximumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
chartpath := "."
|
||||
if len(args) > 0 {
|
||||
chartpath = filepath.Clean(args[0])
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
|
|||
|
||||
func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
|
||||
srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz")
|
||||
if err != nil {
|
||||
|
|
@ -200,12 +200,68 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Make sure tmpcharts is deleted
|
||||
if _, err := os.Stat(filepath.Join(dir(chartname), "tmpcharts")); !os.IsNotExist(err) {
|
||||
// Make sure tmpcharts-x is deleted
|
||||
tmpPath := filepath.Join(dir(chartname), fmt.Sprintf("tmpcharts-%d", os.Getpid()))
|
||||
if _, err := os.Stat(tmpPath); !os.IsNotExist(err) {
|
||||
t.Fatalf("tmpcharts dir still exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDependencyUpdateCmd_WithRepoThatWasNotAdded(t *testing.T) {
|
||||
srv := setupMockRepoServer(t)
|
||||
srvForUnmanagedRepo := setupMockRepoServer(t)
|
||||
defer srv.Stop()
|
||||
defer srvForUnmanagedRepo.Stop()
|
||||
|
||||
dir := func(p ...string) string {
|
||||
return filepath.Join(append([]string{srv.Root()}, p...)...)
|
||||
}
|
||||
|
||||
chartname := "depup"
|
||||
ch := createTestingMetadata(chartname, srv.URL())
|
||||
chartDependency := &chart.Dependency{
|
||||
Name: "signtest",
|
||||
Version: "0.1.0",
|
||||
Repository: srvForUnmanagedRepo.URL(),
|
||||
}
|
||||
ch.Metadata.Dependencies = append(ch.Metadata.Dependencies, chartDependency)
|
||||
|
||||
if err := chartutil.SaveDir(ch, dir()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
_, out, err := executeActionCommand(
|
||||
fmt.Sprintf("dependency update '%s' --repository-config %s --repository-cache %s", dir(chartname),
|
||||
dir("repositories.yaml"), dir()),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
t.Logf("Output: %s", out)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// This is written directly to stdout, so we have to capture as is
|
||||
if !strings.Contains(out, `Getting updates for unmanaged Helm repositories...`) {
|
||||
t.Errorf("No ‘unmanaged’ Helm repo used in test chartdependency or it doesn’t cause the creation "+
|
||||
"of an ‘ad hoc’ repo index cache file\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
func setupMockRepoServer(t *testing.T) *repotest.Server {
|
||||
srv, err := repotest.NewTempServerWithCleanup(t, "testdata/testcharts/*.tgz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Logf("Listening on directory %s", srv.Root())
|
||||
|
||||
if err := srv.LinkIndices(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return srv
|
||||
}
|
||||
|
||||
// createTestingMetadata creates a basic chart that depends on reqtest-0.1.0
|
||||
//
|
||||
// The baseURL can be used to point to a particular repository server.
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ func newDocsCmd(out io.Writer) *cobra.Command {
|
|||
Long: docsDesc,
|
||||
Hidden: true,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
o.topCmd = cmd.Root()
|
||||
return o.run(out)
|
||||
},
|
||||
|
|
@ -70,14 +70,14 @@ func newDocsCmd(out io.Writer) *cobra.Command {
|
|||
f.StringVar(&o.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)")
|
||||
f.BoolVar(&o.generateHeaders, "generate-headers", false, "generate standard headers for markdown files")
|
||||
|
||||
cmd.RegisterFlagCompletionFunc("type", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
cmd.RegisterFlagCompletionFunc("type", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{"bash", "man", "markdown"}, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (o *docsOptions) run(out io.Writer) error {
|
||||
func (o *docsOptions) run(_ io.Writer) error {
|
||||
switch o.docTypeString {
|
||||
case "markdown", "mdown", "md":
|
||||
if o.generateHeaders {
|
||||
|
|
|
|||
|
|
@ -36,15 +36,15 @@ func newEnvCmd(out io.Writer) *cobra.Command {
|
|||
Short: "helm client environment information",
|
||||
Long: envHelp,
|
||||
Args: require.MaximumNArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
keys := getSortedEnvVarKeys()
|
||||
return keys, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
Run: func(_ *cobra.Command, args []string) {
|
||||
envVars := settings.EnvVars()
|
||||
|
||||
if len(args) == 0 {
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
|
|||
f.StringArrayVar(&v.StringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
|
||||
f.StringArrayVar(&v.FileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
|
||||
f.StringArrayVar(&v.JSONValues, "set-json", []string{}, "set JSON values on the command line (can specify multiple or separate values with commas: key1=jsonval1,key2=jsonval2)")
|
||||
f.StringArrayVar(&v.LiteralValues, "set-literal", []string{}, "set a literal STRING value on the command line")
|
||||
}
|
||||
|
||||
func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
|
||||
|
|
@ -60,6 +61,7 @@ func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
|
|||
f.StringVar(&c.CertFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
|
||||
f.StringVar(&c.KeyFile, "key-file", "", "identify HTTPS client using this SSL key file")
|
||||
f.BoolVar(&c.InsecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart download")
|
||||
f.BoolVar(&c.PlainHTTP, "plain-http", false, "use insecure HTTP connections for the chart download")
|
||||
f.StringVar(&c.CaFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
f.BoolVar(&c.PassCredentialsAll, "pass-credentials", false, "pass credentials to all domains")
|
||||
}
|
||||
|
|
@ -70,7 +72,7 @@ func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) {
|
|||
cmd.Flags().VarP(newOutputValue(output.Table, varRef), outputFlag, "o",
|
||||
fmt.Sprintf("prints the output in the specified format. Allowed values: %s", strings.Join(output.Formats(), ", ")))
|
||||
|
||||
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
var formatNames []string
|
||||
for format, desc := range output.FormatsWithDesc() {
|
||||
formatNames = append(formatNames, fmt.Sprintf("%s\t%s", format, desc))
|
||||
|
|
@ -193,7 +195,7 @@ func (p *postRendererArgsSlice) GetSlice() []string {
|
|||
return p.options.args
|
||||
}
|
||||
|
||||
func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func compVersionFlag(chartRef string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
chartInfo := strings.Split(chartRef, "/")
|
||||
if len(chartInfo) != 2 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ get extended information about the release, including:
|
|||
- The generated manifest file
|
||||
- The notes provided by the chart of the release
|
||||
- The hooks associated with the release
|
||||
- The metadata of the release
|
||||
`
|
||||
|
||||
func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
|
|
@ -48,6 +49,7 @@ func newGetCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
cmd.AddCommand(newGetManifestCmd(cfg, out))
|
||||
cmd.AddCommand(newGetHooksCmd(cfg, out))
|
||||
cmd.AddCommand(newGetNotesCmd(cfg, out))
|
||||
cmd.AddCommand(newGetMetadataCmd(cfg, out))
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,13 +41,13 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "download all information for a named release",
|
||||
Long: getAllHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -58,14 +58,13 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
}
|
||||
return tpl(template, data, out)
|
||||
}
|
||||
|
||||
return output.Table.Write(out, &statusPrinter{res, true, false, false})
|
||||
return output.Table.Write(out, &statusPrinter{res, true, false, false, true, false})
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,13 +41,13 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "download all hooks for a named release",
|
||||
Long: getHooksHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -60,7 +60,7 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
}
|
||||
|
||||
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,13 +43,13 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
|
|||
Short: "download the manifest for a named release",
|
||||
Long: getManifestHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -60,7 +60,7 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
|
|||
}
|
||||
|
||||
cmd.Flags().IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
|
|
|||
94
cmd/helm/get_metadata.go
Normal file
94
cmd/helm/get_metadata.go
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"helm.sh/helm/v3/cmd/helm/require"
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/cli/output"
|
||||
)
|
||||
|
||||
type metadataWriter struct {
|
||||
metadata *action.Metadata
|
||||
}
|
||||
|
||||
func newGetMetadataCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
var outfmt output.Format
|
||||
client := action.NewGetMetadata(cfg)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "metadata RELEASE_NAME",
|
||||
Short: "This command fetches metadata for a given release",
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
releaseMetadata, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return outfmt.Write(out, &metadataWriter{releaseMetadata})
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.IntVar(&client.Version, "revision", 0, "specify release revision")
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
bindOutputFlag(cmd, &outfmt)
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (w metadataWriter) WriteTable(out io.Writer) error {
|
||||
_, _ = fmt.Fprintf(out, "NAME: %v\n", w.metadata.Name)
|
||||
_, _ = fmt.Fprintf(out, "CHART: %v\n", w.metadata.Chart)
|
||||
_, _ = fmt.Fprintf(out, "VERSION: %v\n", w.metadata.Version)
|
||||
_, _ = fmt.Fprintf(out, "APP_VERSION: %v\n", w.metadata.AppVersion)
|
||||
_, _ = fmt.Fprintf(out, "NAMESPACE: %v\n", w.metadata.Namespace)
|
||||
_, _ = fmt.Fprintf(out, "REVISION: %v\n", w.metadata.Revision)
|
||||
_, _ = fmt.Fprintf(out, "STATUS: %v\n", w.metadata.Status)
|
||||
_, _ = fmt.Fprintf(out, "DEPLOYED_AT: %v\n", w.metadata.DeployedAt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w metadataWriter) WriteJSON(out io.Writer) error {
|
||||
return output.EncodeJSON(out, w.metadata)
|
||||
}
|
||||
|
||||
func (w metadataWriter) WriteYAML(out io.Writer) error {
|
||||
return output.EncodeYAML(out, w.metadata)
|
||||
}
|
||||
66
cmd/helm/get_metadata_test.go
Normal file
66
cmd/helm/get_metadata_test.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright The Helm Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/release"
|
||||
)
|
||||
|
||||
func TestGetMetadataCmd(t *testing.T) {
|
||||
tests := []cmdTestCase{{
|
||||
name: "get metadata with a release",
|
||||
cmd: "get metadata thomas-guide",
|
||||
golden: "output/get-metadata.txt",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
}, {
|
||||
name: "get metadata requires release name arg",
|
||||
cmd: "get metadata",
|
||||
golden: "output/get-metadata-args.txt",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
wantError: true,
|
||||
}, {
|
||||
name: "get metadata to json",
|
||||
cmd: "get metadata thomas-guide --output json",
|
||||
golden: "output/get-metadata.json",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
}, {
|
||||
name: "get metadata to yaml",
|
||||
cmd: "get metadata thomas-guide --output yaml",
|
||||
golden: "output/get-metadata.yaml",
|
||||
rels: []*release.Release{release.Mock(&release.MockReleaseOptions{Name: "thomas-guide"})},
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestGetMetadataCompletion(t *testing.T) {
|
||||
checkReleaseCompletion(t, "get metadata", false)
|
||||
}
|
||||
|
||||
func TestGetMetadataRevisionCompletion(t *testing.T) {
|
||||
revisionFlagCompletionTest(t, "get metadata")
|
||||
}
|
||||
|
||||
func TestGetMetadataOutputCompletion(t *testing.T) {
|
||||
outputFlagCompletionTest(t, "get metadata")
|
||||
}
|
||||
|
||||
func TestGetMetadataFileCompletion(t *testing.T) {
|
||||
checkFileCompletion(t, "get metadata", false)
|
||||
checkFileCompletion(t, "get metadata myrelease", false)
|
||||
}
|
||||
|
|
@ -39,13 +39,13 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "download the notes for a named release",
|
||||
Long: getNotesHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
res, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -59,7 +59,7 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
|
||||
f := cmd.Flags()
|
||||
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "download the values file for a named release",
|
||||
Long: getValuesHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
vals, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -63,7 +63,7 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
|
||||
f := cmd.Flags()
|
||||
f.IntVar(&client.Version, "revision", 0, "get the named release with revision")
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"sigs.k8s.io/yaml"
|
||||
|
|
@ -45,7 +46,8 @@ func init() {
|
|||
|
||||
func debug(format string, v ...interface{}) {
|
||||
if settings.Debug {
|
||||
format = fmt.Sprintf("[debug] %s\n", format)
|
||||
timeNow := time.Now().String()
|
||||
format = fmt.Sprintf("%s [debug] %s\n", timeNow, format)
|
||||
log.Output(2, fmt.Sprintf(format, v...))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string)
|
|||
Releases: store,
|
||||
KubeClient: &kubefake.PrintingKubeClient{Out: io.Discard},
|
||||
Capabilities: chartutil.DefaultCapabilities,
|
||||
Log: func(format string, v ...interface{}) {},
|
||||
Log: func(_ string, _ ...interface{}) {},
|
||||
}
|
||||
|
||||
root, err := newRootCmd(actionConfig, buf, args)
|
||||
|
|
|
|||
|
|
@ -60,13 +60,13 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "fetch release history",
|
||||
Aliases: []string{"hist"},
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
history, err := getHistory(client, args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -184,7 +184,7 @@ func min(x, y int) int {
|
|||
return y
|
||||
}
|
||||
|
||||
func compListRevisions(toComplete string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) {
|
||||
func compListRevisions(_ string, cfg *action.Configuration, releaseName string) ([]string, cobra.ShellCompDirective) {
|
||||
client := action.NewHistory(cfg)
|
||||
|
||||
var revisions []string
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ set for a key called 'foo', the 'newbar' value would take precedence:
|
|||
|
||||
$ helm install --set foo=bar --set foo=newbar myredis ./redis
|
||||
|
||||
Similarly, in the following example 'foo' is set to '["four"]':
|
||||
Similarly, in the following example 'foo' is set to '["four"]':
|
||||
|
||||
$ helm install --set-json='foo=["one", "two", "three"]' --set-json='foo=["four"]' myredis ./redis
|
||||
|
||||
|
|
@ -94,7 +94,11 @@ And in the following example, 'foo' is set to '{"key1":"value1","key2":"bar"}':
|
|||
$ helm install --set-json='foo={"key1":"value1","key2":"value2"}' --set-json='foo.key2="bar"' myredis ./redis
|
||||
|
||||
To check the generated manifests of a release without installing the chart,
|
||||
the '--debug' and '--dry-run' flags can be combined.
|
||||
the --debug and --dry-run flags can be combined.
|
||||
|
||||
The --dry-run flag will output all generated chart manifests, including Secrets
|
||||
which can contain sensitive values. To hide Kubernetes Secrets use the
|
||||
--hide-secret flag. Please carefully consider how and when these flags are used.
|
||||
|
||||
If --verify is set, the chart MUST have a provenance file, and the provenance
|
||||
file MUST pass all verification steps.
|
||||
|
|
@ -132,26 +136,37 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "install a chart",
|
||||
Long: installDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstall(args, toComplete, client)
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
|
||||
client.InsecureSkipTLSverify, client.PlainHTTP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing registry client: %w", err)
|
||||
}
|
||||
client.SetRegistryClient(registryClient)
|
||||
|
||||
// This is for the case where "" is specifically passed in as a
|
||||
// value. When there is no value passed in NoOptDefVal will be used
|
||||
// and it is set to client. See addInstallFlags.
|
||||
if client.DryRunOption == "" {
|
||||
client.DryRunOption = "none"
|
||||
}
|
||||
rel, err := runInstall(args, client, valueOpts, out)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "INSTALLATION FAILED")
|
||||
}
|
||||
|
||||
return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false})
|
||||
return outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, client.HideNotes})
|
||||
},
|
||||
}
|
||||
|
||||
addInstallFlags(cmd, cmd.Flags(), client, valueOpts)
|
||||
// hide-secret is not available in all places the install flags are used so
|
||||
// it is added separately
|
||||
f := cmd.Flags()
|
||||
f.BoolVar(&client.HideSecret, "hide-secret", false, "hide Kubernetes Secrets when also using the --dry-run flag")
|
||||
bindOutputFlag(cmd, &outfmt)
|
||||
bindPostRenderFlag(cmd, &client.PostRenderer)
|
||||
|
||||
|
|
@ -160,7 +175,13 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
|
||||
func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
|
||||
f.BoolVar(&client.CreateNamespace, "create-namespace", false, "create the release namespace if not present")
|
||||
f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
|
||||
// --dry-run options with expected outcome:
|
||||
// - Not set means no dry run and server is contacted.
|
||||
// - Set with no value, a value of client, or a value of true and the server is not contacted
|
||||
// - Set with a value of false, none, or false and the server is contacted
|
||||
// The true/false part is meant to reflect some legacy behavior while none is equal to "".
|
||||
f.StringVar(&client.DryRunOption, "dry-run", "", "simulate an install. If --dry-run is set with no option being specified or as '--dry-run=client', it will not attempt cluster connections. Setting '--dry-run=server' allows attempting cluster connections.")
|
||||
f.Lookup("dry-run").NoOptDefVal = "client"
|
||||
f.BoolVar(&client.Force, "force", false, "force resource updates through a replacement strategy")
|
||||
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
|
||||
f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
|
||||
|
|
@ -176,11 +197,14 @@ func addInstallFlags(cmd *cobra.Command, f *pflag.FlagSet, client *action.Instal
|
|||
f.BoolVar(&client.Atomic, "atomic", false, "if set, the installation process deletes the installation on failure. The --wait flag will be set automatically if --atomic is used")
|
||||
f.BoolVar(&client.SkipCRDs, "skip-crds", false, "if set, no CRDs will be installed. By default, CRDs are installed if not already present")
|
||||
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
|
||||
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
|
||||
f.StringToStringVarP(&client.Labels, "labels", "l", nil, "Labels that would be added to release metadata. Should be divided by comma.")
|
||||
f.BoolVar(&client.EnableDNS, "enable-dns", false, "enable DNS lookups when rendering templates")
|
||||
f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in install output. Does not affect presence in chart metadata")
|
||||
addValueOptionsFlags(f, valueOpts)
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
|
||||
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
requiredArgs := 2
|
||||
if client.GenerateName {
|
||||
requiredArgs = 1
|
||||
|
|
@ -252,6 +276,7 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
|
|||
RepositoryConfig: settings.RepositoryConfig,
|
||||
RepositoryCache: settings.RepositoryCache,
|
||||
Debug: settings.Debug,
|
||||
RegistryClient: client.GetRegistryClient(),
|
||||
}
|
||||
if err := man.Update(); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -268,6 +293,11 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
|
|||
|
||||
client.Namespace = settings.Namespace()
|
||||
|
||||
// Validate DryRunOption member is one of the allowed values
|
||||
if err := validateDryRunOptionFlag(client.DryRunOption); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create context and prepare the handle of SIGTERM
|
||||
ctx := context.Background()
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
|
|
@ -308,3 +338,19 @@ func compInstall(args []string, toComplete string, client *action.Install) ([]st
|
|||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
||||
func validateDryRunOptionFlag(dryRunOptionFlagValue string) error {
|
||||
// Validate dry-run flag value with a set of allowed value
|
||||
allowedDryRunValues := []string{"false", "true", "none", "client", "server"}
|
||||
isAllowed := false
|
||||
for _, v := range allowedDryRunValues {
|
||||
if dryRunOptionFlagValue == v {
|
||||
isAllowed = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !isAllowed {
|
||||
return errors.New("Invalid dry-run flag. Flag must one of the following: false, true, none, client, server")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ func TestInstall(t *testing.T) {
|
|||
}
|
||||
defer srv.Stop()
|
||||
|
||||
srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username != "username" || password != "password" {
|
||||
t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)
|
||||
|
|
@ -225,6 +225,12 @@ func TestInstall(t *testing.T) {
|
|||
wantError: true,
|
||||
golden: "output/subchart-schema-cli-negative.txt",
|
||||
},
|
||||
// Install, values from yaml, schematized with errors but skip schema validation, expect success
|
||||
{
|
||||
name: "install with schema file and schematized subchart, extra values from cli, skip schema validation",
|
||||
cmd: "install schema testdata/testcharts/chart-with-schema-and-subchart --set lastname=doe --set subchart-with-schema.age=-25 --skip-schema-validation",
|
||||
golden: "output/schema.txt",
|
||||
},
|
||||
// Install deprecated chart
|
||||
{
|
||||
name: "install with warning about deprecated chart",
|
||||
|
|
@ -252,6 +258,22 @@ func TestInstall(t *testing.T) {
|
|||
cmd: fmt.Sprintf("install aeneas test/reqtest --username username --password password --repository-config %s --repository-cache %s", repoFile, srv.Root()),
|
||||
golden: "output/install.txt",
|
||||
},
|
||||
{
|
||||
name: "dry-run displaying secret",
|
||||
cmd: "install secrets testdata/testcharts/chart-with-secret --dry-run",
|
||||
golden: "output/install-dry-run-with-secret.txt",
|
||||
},
|
||||
{
|
||||
name: "dry-run hiding secret",
|
||||
cmd: "install secrets testdata/testcharts/chart-with-secret --dry-run --hide-secret",
|
||||
golden: "output/install-dry-run-with-secret-hidden.txt",
|
||||
},
|
||||
{
|
||||
name: "hide-secret error without dry-run",
|
||||
cmd: "install secrets testdata/testcharts/chart-with-secret --hide-secret",
|
||||
wantError: true,
|
||||
golden: "output/install-hide-secret.txt",
|
||||
},
|
||||
}
|
||||
|
||||
runTestCmd(t, tests)
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/spf13/cobra"
|
||||
|
||||
"helm.sh/helm/v3/pkg/action"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
"helm.sh/helm/v3/pkg/cli/values"
|
||||
"helm.sh/helm/v3/pkg/getter"
|
||||
"helm.sh/helm/v3/pkg/lint/support"
|
||||
|
|
@ -44,19 +45,29 @@ or recommendation, it will emit [WARNING] messages.
|
|||
func newLintCmd(out io.Writer) *cobra.Command {
|
||||
client := action.NewLint()
|
||||
valueOpts := &values.Options{}
|
||||
var kubeVersion string
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "lint PATH",
|
||||
Short: "examine a chart for possible issues",
|
||||
Long: longLintHelp,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
paths := []string{"."}
|
||||
if len(args) > 0 {
|
||||
paths = args
|
||||
}
|
||||
|
||||
if kubeVersion != "" {
|
||||
parsedKubeVersion, err := chartutil.ParseKubeVersion(kubeVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid kube version '%s': %s", kubeVersion, err)
|
||||
}
|
||||
client.KubeVersion = parsedKubeVersion
|
||||
}
|
||||
|
||||
if client.WithSubcharts {
|
||||
for _, p := range paths {
|
||||
filepath.Walk(filepath.Join(p, "charts"), func(path string, info os.FileInfo, err error) error {
|
||||
filepath.Walk(filepath.Join(p, "charts"), func(path string, info os.FileInfo, _ error) error {
|
||||
if info != nil {
|
||||
if info.Name() == "Chart.yaml" {
|
||||
paths = append(paths, filepath.Dir(path))
|
||||
|
|
@ -137,6 +148,8 @@ func newLintCmd(out io.Writer) *cobra.Command {
|
|||
f.BoolVar(&client.Strict, "strict", false, "fail on lint warnings")
|
||||
f.BoolVar(&client.WithSubcharts, "with-subcharts", false, "lint dependent charts")
|
||||
f.BoolVar(&client.Quiet, "quiet", false, "print only warnings and errors")
|
||||
f.BoolVar(&client.SkipSchemaValidation, "skip-schema-validation", false, "if set, disables JSON schema validation")
|
||||
f.StringVar(&kubeVersion, "kube-version", "", "Kubernetes version used for capabilities and deprecation checks")
|
||||
addValueOptionsFlags(f, valueOpts)
|
||||
|
||||
return cmd
|
||||
|
|
|
|||
|
|
@ -53,11 +53,44 @@ func TestLintCmdWithQuietFlag(t *testing.T) {
|
|||
name: "lint chart with warning using --quiet flag",
|
||||
cmd: "lint --quiet testdata/testcharts/chart-with-only-crds",
|
||||
golden: "output/lint-quiet-with-warning.txt",
|
||||
}, {
|
||||
name: "lint non-existent chart using --quiet flag",
|
||||
cmd: "lint --quiet thischartdoesntexist/",
|
||||
golden: "",
|
||||
wantError: true,
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
|
||||
}
|
||||
|
||||
func TestLintCmdWithKubeVersionFlag(t *testing.T) {
|
||||
testChart := "testdata/testcharts/chart-with-deprecated-api"
|
||||
tests := []cmdTestCase{{
|
||||
name: "lint chart with deprecated api version using kube version flag",
|
||||
cmd: fmt.Sprintf("lint --kube-version 1.22.0 %s", testChart),
|
||||
golden: "output/lint-chart-with-deprecated-api.txt",
|
||||
wantError: false,
|
||||
}, {
|
||||
name: "lint chart with deprecated api version using kube version and strict flag",
|
||||
cmd: fmt.Sprintf("lint --kube-version 1.22.0 --strict %s", testChart),
|
||||
golden: "output/lint-chart-with-deprecated-api-strict.txt",
|
||||
wantError: true,
|
||||
}, {
|
||||
// the test builds will use the default k8sVersionMinor const in deprecations.go and capabilities.go
|
||||
// which is "20"
|
||||
name: "lint chart with deprecated api version without kube version",
|
||||
cmd: fmt.Sprintf("lint %s", testChart),
|
||||
golden: "output/lint-chart-with-deprecated-api-old-k8s.txt",
|
||||
wantError: false,
|
||||
}, {
|
||||
name: "lint chart with deprecated api version with older kube version",
|
||||
cmd: fmt.Sprintf("lint --kube-version 1.21.0 --strict %s", testChart),
|
||||
golden: "output/lint-chart-with-deprecated-api-old-k8s.txt",
|
||||
wantError: false,
|
||||
}}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
||||
func TestLintFileCompletion(t *testing.T) {
|
||||
checkFileCompletion(t, "lint", true)
|
||||
checkFileCompletion(t, "lint mypath", true) // Multiple paths can be given
|
||||
|
|
|
|||
|
|
@ -68,8 +68,8 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Long: listHelp,
|
||||
Aliases: []string{"ls"},
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if client.AllNamespaces {
|
||||
if err := cfg.Init(settings.RESTClientGetter(), "", os.Getenv("HELM_DRIVER"), debug); err != nil {
|
||||
return err
|
||||
|
|
|
|||
|
|
@ -128,7 +128,8 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io.
|
|||
env = append(env, fmt.Sprintf("%s=%s", k, v))
|
||||
}
|
||||
|
||||
prog := exec.Command(main, argv...)
|
||||
mainCmdExp := os.ExpandEnv(main)
|
||||
prog := exec.Command(mainCmdExp, argv...)
|
||||
prog.Env = env
|
||||
prog.Stdin = os.Stdin
|
||||
prog.Stdout = out
|
||||
|
|
@ -153,7 +154,7 @@ func callPluginExecutable(pluginName string, main string, argv []string, out io.
|
|||
func manuallyProcessArgs(args []string) ([]string, []string) {
|
||||
known := []string{}
|
||||
unknown := []string{}
|
||||
kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--kube-ca-file", "--registry-config", "--repository-cache", "--repository-config", "--insecure-skip-tls-verify", "--tls-server-name"}
|
||||
kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--kube-apiserver", "--kube-token", "--kube-as-user", "--kube-as-group", "--kube-ca-file", "--registry-config", "--repository-cache", "--repository-config", "--kube-insecure-skip-tls-verify", "--kube-tls-server-name"}
|
||||
knownArg := func(a string) bool {
|
||||
for _, pre := range kvargs {
|
||||
if strings.HasPrefix(a, pre+"=") {
|
||||
|
|
@ -300,7 +301,7 @@ func addPluginCommands(plugin *plugin.Plugin, baseCmd *cobra.Command, cmds *plug
|
|||
// to the dynamic completion script of the plugin.
|
||||
DisableFlagParsing: true,
|
||||
// A Run is required for it to be a valid command without subcommands
|
||||
Run: func(cmd *cobra.Command, args []string) {},
|
||||
Run: func(_ *cobra.Command, _ []string) {},
|
||||
}
|
||||
baseCmd.AddCommand(subCmd)
|
||||
addPluginCommands(plugin, subCmd, &cmd)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Use: "package [CHART_PATH] [...]",
|
||||
Short: "package a chart directory into a chart archive",
|
||||
Long: packageDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
if len(args) == 0 {
|
||||
return errors.Errorf("need at least one argument, the path to the chart")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/internal/test/ensure"
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chart/loader"
|
||||
)
|
||||
|
|
@ -111,7 +110,7 @@ func TestPackage(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cachePath := ensure.TempDir(t)
|
||||
cachePath := t.TempDir()
|
||||
defer testChdir(t, cachePath)()
|
||||
|
||||
if err := os.MkdirAll("toot", 0777); err != nil {
|
||||
|
|
@ -170,7 +169,7 @@ func TestSetAppVersion(t *testing.T) {
|
|||
var ch *chart.Chart
|
||||
expectedAppVersion := "app-version-foo"
|
||||
chartToPackage := "testdata/testcharts/alpine"
|
||||
dir := ensure.TempDir(t)
|
||||
dir := t.TempDir()
|
||||
cmd := fmt.Sprintf("package %s --destination=%s --app-version=%s", chartToPackage, dir, expectedAppVersion)
|
||||
_, output, err := executeActionCommand(cmd)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -39,23 +39,23 @@ This command allows you to install a plugin from a url to a VCS repo or a local
|
|||
func newPluginInstallCmd(out io.Writer) *cobra.Command {
|
||||
o := &pluginInstallOptions{}
|
||||
cmd := &cobra.Command{
|
||||
Use: "install [options] <path|url>...",
|
||||
Short: "install one or more Helm plugins",
|
||||
Use: "install [options] <path|url>",
|
||||
Short: "install a Helm plugin",
|
||||
Long: pluginInstallDesc,
|
||||
Aliases: []string{"add"},
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
// We do file completion, in case the plugin is local
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
// No more completion once the plugin path has been specified
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(_ *cobra.Command, args []string) error {
|
||||
return o.complete(args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return o.run(out)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,8 +30,8 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
|
|||
Use: "list",
|
||||
Aliases: []string{"ls"},
|
||||
Short: "list installed Helm plugins",
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
debug("pluginDirs: %s", settings.PluginsDirectory)
|
||||
plugins, err := plugin.FindPlugins(settings.PluginsDirectory)
|
||||
if err != nil {
|
||||
|
|
@ -75,7 +75,7 @@ func filterPlugins(plugins []*plugin.Plugin, ignoredPluginNames []string) []*plu
|
|||
}
|
||||
|
||||
// Provide dynamic auto-completion for plugin names
|
||||
func compListPlugins(toComplete string, ignoredPluginNames []string) []string {
|
||||
func compListPlugins(_ string, ignoredPluginNames []string) []string {
|
||||
var pNames []string
|
||||
plugins, err := plugin.FindPlugins(settings.PluginsDirectory)
|
||||
if err == nil && len(plugins) > 0 {
|
||||
|
|
|
|||
|
|
@ -161,6 +161,81 @@ func TestLoadPlugins(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadPluginsWithSpace(t *testing.T) {
|
||||
settings.PluginsDirectory = "testdata/helm home with space/helm/plugins"
|
||||
settings.RepositoryConfig = "testdata/helm home with space/helm/repositories.yaml"
|
||||
settings.RepositoryCache = "testdata/helm home with space/helm/repository"
|
||||
|
||||
var (
|
||||
out bytes.Buffer
|
||||
cmd cobra.Command
|
||||
)
|
||||
loadPlugins(&cmd, &out)
|
||||
|
||||
envs := strings.Join([]string{
|
||||
"fullenv",
|
||||
"testdata/helm home with space/helm/plugins/fullenv",
|
||||
"testdata/helm home with space/helm/plugins",
|
||||
"testdata/helm home with space/helm/repositories.yaml",
|
||||
"testdata/helm home with space/helm/repository",
|
||||
os.Args[0],
|
||||
}, "\n")
|
||||
|
||||
// Test that the YAML file was correctly converted to a command.
|
||||
tests := []struct {
|
||||
use string
|
||||
short string
|
||||
long string
|
||||
expect string
|
||||
args []string
|
||||
code int
|
||||
}{
|
||||
{"fullenv", "show env vars", "show all env vars", envs + "\n", []string{}, 0},
|
||||
}
|
||||
|
||||
plugins := cmd.Commands()
|
||||
|
||||
if len(plugins) != len(tests) {
|
||||
t.Fatalf("Expected %d plugins, got %d", len(tests), len(plugins))
|
||||
}
|
||||
|
||||
for i := 0; i < len(plugins); i++ {
|
||||
out.Reset()
|
||||
tt := tests[i]
|
||||
pp := plugins[i]
|
||||
if pp.Use != tt.use {
|
||||
t.Errorf("%d: Expected Use=%q, got %q", i, tt.use, pp.Use)
|
||||
}
|
||||
if pp.Short != tt.short {
|
||||
t.Errorf("%d: Expected Use=%q, got %q", i, tt.short, pp.Short)
|
||||
}
|
||||
if pp.Long != tt.long {
|
||||
t.Errorf("%d: Expected Use=%q, got %q", i, tt.long, pp.Long)
|
||||
}
|
||||
|
||||
// Currently, plugins assume a Linux subsystem. Skip the execution
|
||||
// tests until this is fixed
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := pp.RunE(pp, tt.args); err != nil {
|
||||
if tt.code > 0 {
|
||||
perr, ok := err.(pluginError)
|
||||
if !ok {
|
||||
t.Errorf("Expected %s to return pluginError: got %v(%T)", tt.use, err, err)
|
||||
}
|
||||
if perr.code != tt.code {
|
||||
t.Errorf("Expected %s to return %d: got %d", tt.use, tt.code, perr.code)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("Error running %s: %+v", tt.use, err)
|
||||
}
|
||||
}
|
||||
if out.String() != tt.expect {
|
||||
t.Errorf("Expected %s to output:\n%s\ngot\n%s", tt.use, tt.expect, out.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type staticCompletionDetails struct {
|
||||
use string
|
||||
validArgs []string
|
||||
|
|
|
|||
|
|
@ -38,13 +38,13 @@ func newPluginUninstallCmd(out io.Writer) *cobra.Command {
|
|||
Use: "uninstall <plugin>...",
|
||||
Aliases: []string{"rm", "remove"},
|
||||
Short: "uninstall one or more Helm plugins",
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compListPlugins(toComplete, args), cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(_ *cobra.Command, args []string) error {
|
||||
return o.complete(args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return o.run(out)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,13 +39,13 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command {
|
|||
Use: "update <plugin>...",
|
||||
Aliases: []string{"up"},
|
||||
Short: "update one or more Helm plugins",
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compListPlugins(toComplete, args), cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
PreRunE: func(_ *cobra.Command, args []string) error {
|
||||
return o.complete(args)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return o.run(out)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,20 +51,21 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Aliases: []string{"fetch"},
|
||||
Long: pullDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
return compListCharts(toComplete, false)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.Settings = settings
|
||||
if client.Version == "" && client.Devel {
|
||||
debug("setting version to >0.0.0-0")
|
||||
client.Version = ">0.0.0-0"
|
||||
}
|
||||
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
|
||||
client.InsecureSkipTLSverify, client.PlainHTTP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing registry client: %w", err)
|
||||
}
|
||||
|
|
@ -89,7 +90,7 @@ func newPullCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
f.StringVarP(&client.DestDir, "destination", "d", ".", "location to write the chart. If this and untardir are specified, untardir is appended to this")
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
|
||||
err := cmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 1 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ func TestPullWithCredentialsCmd(t *testing.T) {
|
|||
}
|
||||
defer srv.Stop()
|
||||
|
||||
srv.WithMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
srv.WithMiddleware(http.HandlerFunc(func(_ http.ResponseWriter, r *http.Request) {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username != "username" || password != "password" {
|
||||
t.Errorf("Expected request to use basic auth and for username == 'username' and password == 'password', got '%v', '%s', '%s'", ok, username, password)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ type registryPushOptions struct {
|
|||
keyFile string
|
||||
caFile string
|
||||
insecureSkipTLSverify bool
|
||||
plainHTTP bool
|
||||
}
|
||||
|
||||
func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
|
|
@ -49,7 +50,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "push a chart to remote",
|
||||
Long: pushDesc,
|
||||
Args: require.MinimumNArgs(2),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
// Do file completion for the chart file to push
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
|
|
@ -64,10 +65,10 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
}
|
||||
return comps, cobra.ShellCompDirectiveNoFileComp | cobra.ShellCompDirectiveNoSpace
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
registryClient, err := newRegistryClient(o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify)
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
registryClient, err := newRegistryClient(o.certFile, o.keyFile, o.caFile, o.insecureSkipTLSverify, o.plainHTTP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing registry client: %w", err)
|
||||
}
|
||||
|
|
@ -77,6 +78,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
client := action.NewPushWithOpts(action.WithPushConfig(cfg),
|
||||
action.WithTLSClientConfig(o.certFile, o.keyFile, o.caFile),
|
||||
action.WithInsecureSkipTLSVerify(o.insecureSkipTLSverify),
|
||||
action.WithPlainHTTP(o.plainHTTP),
|
||||
action.WithPushOptWriter(out))
|
||||
client.Settings = settings
|
||||
output, err := client.Run(chartRef, remote)
|
||||
|
|
@ -93,6 +95,7 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
f.StringVar(&o.keyFile, "key-file", "", "identify registry client using this SSL key file")
|
||||
f.StringVar(&o.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
|
||||
f.BoolVar(&o.insecureSkipTLSverify, "insecure-skip-tls-verify", false, "skip tls certificate checks for the chart upload")
|
||||
f.BoolVar(&o.plainHTTP, "plain-http", false, "use insecure HTTP connections for the chart upload")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
|
|||
Short: "login to a registry",
|
||||
Long: registryLoginDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
hostname := args[0]
|
||||
|
||||
username, password, err := getUsernamePassword(o.username, o.password, o.passwordFromStdinOpt)
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma
|
|||
Short: "logout from a registry",
|
||||
Long: registryLogoutDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: cobra.NoFileCompletions,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
hostname := args[0]
|
||||
return action.NewRegistryLogout(cfg).Run(out, hostname)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -48,20 +48,20 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
|
|||
Short: "run tests for a release",
|
||||
Long: releaseTestHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.Namespace = settings.Namespace()
|
||||
notName := regexp.MustCompile(`^!\s?name=`)
|
||||
for _, f := range filter {
|
||||
if strings.HasPrefix(f, "name=") {
|
||||
client.Filters["name"] = append(client.Filters["name"], strings.TrimPrefix(f, "name="))
|
||||
client.Filters[action.IncludeNameFilter] = append(client.Filters[action.IncludeNameFilter], strings.TrimPrefix(f, "name="))
|
||||
} else if notName.MatchString(f) {
|
||||
client.Filters["!name"] = append(client.Filters["!name"], notName.ReplaceAllLiteralString(f, ""))
|
||||
client.Filters[action.ExcludeNameFilter] = append(client.Filters[action.ExcludeNameFilter], notName.ReplaceAllLiteralString(f, ""))
|
||||
}
|
||||
}
|
||||
rel, runErr := client.Run(args[0])
|
||||
|
|
@ -72,7 +72,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
|
|||
return runErr
|
||||
}
|
||||
|
||||
if err := outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false}); err != nil {
|
||||
if err := outfmt.Write(out, &statusPrinter{rel, settings.Debug, false, false, false, client.HideNotes}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +92,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
|
|||
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
|
||||
f.BoolVar(&outputLogs, "logs", false, "dump the logs from test pods (this runs after all tests are complete, but before any cleanup)")
|
||||
f.StringSliceVar(&filter, "filter", []string{}, "specify tests by attribute (currently \"name\") using attribute=value syntax or '!attribute=value' to exclude a test (can specify multiple or separate values with commas: name=test1,name=test2)")
|
||||
f.BoolVar(&client.HideNotes, "hide-notes", false, "if set, do not show notes in test output. Does not affect presence in chart metadata")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,11 +68,16 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
|
|||
o := &repoAddOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "add [NAME] [URL]",
|
||||
Short: "add a chart repository",
|
||||
Args: require.ExactArgs(2),
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
Use: "add [NAME] [URL]",
|
||||
Short: "add a chart repository",
|
||||
Args: require.ExactArgs(2),
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) > 1 {
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.name = args[0]
|
||||
o.url = args[1]
|
||||
o.repoFile = settings.RepositoryConfig
|
||||
|
|
@ -212,7 +217,7 @@ func (o *repoAddOptions) run(out io.Writer) error {
|
|||
|
||||
f.Update(&c)
|
||||
|
||||
if err := f.WriteFile(o.repoFile, 0644); err != nil {
|
||||
if err := f.WriteFile(o.repoFile, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(out, "%q has been added to your repositories\n", o.name)
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import (
|
|||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"helm.sh/helm/v3/internal/test/ensure"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/helmpath/xdg"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
|
|
@ -48,7 +47,7 @@ func TestRepoAddCmd(t *testing.T) {
|
|||
}
|
||||
defer srv2.Stop()
|
||||
|
||||
tmpdir := filepath.Join(ensure.TempDir(t), "path-component.yaml/data")
|
||||
tmpdir := filepath.Join(t.TempDir(), "path-component.yaml/data")
|
||||
err = os.MkdirAll(tmpdir, 0777)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
|
@ -88,7 +87,7 @@ func TestRepoAdd(t *testing.T) {
|
|||
}
|
||||
defer ts.Stop()
|
||||
|
||||
rootDir := ensure.TempDir(t)
|
||||
rootDir := t.TempDir()
|
||||
repoFile := filepath.Join(rootDir, "repositories.yaml")
|
||||
|
||||
const testRepoName = "test-name"
|
||||
|
|
@ -145,8 +144,8 @@ func TestRepoAddCheckLegalName(t *testing.T) {
|
|||
|
||||
const testRepoName = "test-hub/test-name"
|
||||
|
||||
rootDir := ensure.TempDir(t)
|
||||
repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
|
||||
rootDir := t.TempDir()
|
||||
repoFile := filepath.Join(t.TempDir(), "repositories.yaml")
|
||||
|
||||
o := &repoAddOptions{
|
||||
name: testRepoName,
|
||||
|
|
@ -170,25 +169,25 @@ func TestRepoAddCheckLegalName(t *testing.T) {
|
|||
|
||||
func TestRepoAddConcurrentGoRoutines(t *testing.T) {
|
||||
const testName = "test-name"
|
||||
repoFile := filepath.Join(ensure.TempDir(t), "repositories.yaml")
|
||||
repoFile := filepath.Join(t.TempDir(), "repositories.yaml")
|
||||
repoAddConcurrent(t, testName, repoFile)
|
||||
}
|
||||
|
||||
func TestRepoAddConcurrentDirNotExist(t *testing.T) {
|
||||
const testName = "test-name-2"
|
||||
repoFile := filepath.Join(ensure.TempDir(t), "foo", "repositories.yaml")
|
||||
repoFile := filepath.Join(t.TempDir(), "foo", "repositories.yaml")
|
||||
repoAddConcurrent(t, testName, repoFile)
|
||||
}
|
||||
|
||||
func TestRepoAddConcurrentNoFileExtension(t *testing.T) {
|
||||
const testName = "test-name-3"
|
||||
repoFile := filepath.Join(ensure.TempDir(t), "repositories")
|
||||
repoFile := filepath.Join(t.TempDir(), "repositories")
|
||||
repoAddConcurrent(t, testName, repoFile)
|
||||
}
|
||||
|
||||
func TestRepoAddConcurrentHiddenFile(t *testing.T) {
|
||||
const testName = "test-name-4"
|
||||
repoFile := filepath.Join(ensure.TempDir(t), ".repositories")
|
||||
repoFile := filepath.Join(t.TempDir(), ".repositories")
|
||||
repoAddConcurrent(t, testName, repoFile)
|
||||
}
|
||||
|
||||
|
|
@ -254,7 +253,7 @@ func TestRepoAddWithPasswordFromStdin(t *testing.T) {
|
|||
t.Errorf("unexpected error, got '%v'", err)
|
||||
}
|
||||
|
||||
tmpdir := ensure.TempDir(t)
|
||||
tmpdir := t.TempDir()
|
||||
repoFile := filepath.Join(tmpdir, "repositories.yaml")
|
||||
|
||||
store := storageFixture()
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ type repoIndexOptions struct {
|
|||
dir string
|
||||
url string
|
||||
merge string
|
||||
json bool
|
||||
}
|
||||
|
||||
func newRepoIndexCmd(out io.Writer) *cobra.Command {
|
||||
|
|
@ -53,15 +54,15 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
|
|||
Short: "generate an index file given a directory containing packaged charts",
|
||||
Long: repoIndexDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
// Allow file completion when completing the argument for the directory
|
||||
return nil, cobra.ShellCompDirectiveDefault
|
||||
}
|
||||
// No more completions, so disable file completion
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.dir = args[0]
|
||||
return o.run(out)
|
||||
},
|
||||
|
|
@ -70,20 +71,21 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
|
|||
f := cmd.Flags()
|
||||
f.StringVar(&o.url, "url", "", "url of chart repository")
|
||||
f.StringVar(&o.merge, "merge", "", "merge the generated index into the given index")
|
||||
f.BoolVar(&o.json, "json", false, "output in JSON format")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
func (i *repoIndexOptions) run(out io.Writer) error {
|
||||
func (i *repoIndexOptions) run(_ io.Writer) error {
|
||||
path, err := filepath.Abs(i.dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return index(path, i.url, i.merge)
|
||||
return index(path, i.url, i.merge, i.json)
|
||||
}
|
||||
|
||||
func index(dir, url, mergeTo string) error {
|
||||
func index(dir, url, mergeTo string, json bool) error {
|
||||
out := filepath.Join(dir, "index.yaml")
|
||||
|
||||
i, err := repo.IndexDirectory(dir, url)
|
||||
|
|
@ -95,7 +97,7 @@ func index(dir, url, mergeTo string) error {
|
|||
var i2 *repo.IndexFile
|
||||
if _, err := os.Stat(mergeTo); os.IsNotExist(err) {
|
||||
i2 = repo.NewIndexFile()
|
||||
i2.WriteFile(mergeTo, 0644)
|
||||
writeIndexFile(i2, mergeTo, json)
|
||||
} else {
|
||||
i2, err = repo.LoadIndexFile(mergeTo)
|
||||
if err != nil {
|
||||
|
|
@ -105,5 +107,12 @@ func index(dir, url, mergeTo string) error {
|
|||
i.Merge(i2)
|
||||
}
|
||||
i.SortEntries()
|
||||
return writeIndexFile(i, out, json)
|
||||
}
|
||||
|
||||
func writeIndexFile(i *repo.IndexFile, out string, json bool) error {
|
||||
if json {
|
||||
return i.WriteJSONFile(out, 0644)
|
||||
}
|
||||
return i.WriteFile(out, 0644)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,18 +18,18 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/internal/test/ensure"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
)
|
||||
|
||||
func TestRepoIndexCmd(t *testing.T) {
|
||||
|
||||
dir := ensure.TempDir(t)
|
||||
dir := t.TempDir()
|
||||
|
||||
comp := filepath.Join(dir, "compressedchart-0.1.0.tgz")
|
||||
if err := linkOrCopy("testdata/testcharts/compressedchart-0.1.0.tgz", comp); err != nil {
|
||||
|
|
@ -68,6 +68,28 @@ func TestRepoIndexCmd(t *testing.T) {
|
|||
t.Errorf("expected %q, got %q", expectedVersion, vs[0].Version)
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(destIndex)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if json.Valid(b) {
|
||||
t.Error("did not expect index file to be valid json")
|
||||
}
|
||||
|
||||
// Test with `--json`
|
||||
|
||||
c.ParseFlags([]string{"--json", "true"})
|
||||
if err := c.RunE(c, []string{dir}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if b, err = os.ReadFile(destIndex); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !json.Valid(b) {
|
||||
t.Error("index file is not valid json")
|
||||
}
|
||||
|
||||
// Test with `--merge`
|
||||
|
||||
// Remove first two charts.
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
|
|||
Aliases: []string{"ls"},
|
||||
Short: "list chart repositories",
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
ValidArgsFunction: noMoreArgsCompFunc,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
f, _ := repo.LoadFile(settings.RepositoryConfig)
|
||||
if len(f.Repositories) == 0 && !(outfmt == output.JSON || outfmt == output.YAML) {
|
||||
return errors.New("no repositories to show")
|
||||
|
|
@ -123,7 +123,7 @@ func filterRepos(repos []*repo.Entry, ignoredRepoNames []string) []*repo.Entry {
|
|||
}
|
||||
|
||||
// Provide dynamic auto-completion for repo names
|
||||
func compListRepos(prefix string, ignoredRepoNames []string) []string {
|
||||
func compListRepos(_ string, ignoredRepoNames []string) []string {
|
||||
var rNames []string
|
||||
|
||||
f, err := repo.LoadFile(settings.RepositoryConfig)
|
||||
|
|
|
|||
|
|
@ -44,10 +44,10 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command {
|
|||
Aliases: []string{"rm"},
|
||||
Short: "remove one or more chart repositories",
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compListRepos(toComplete, args), cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.repoFile = settings.RepositoryConfig
|
||||
o.repoCache = settings.RepositoryCache
|
||||
o.names = args
|
||||
|
|
@ -67,7 +67,7 @@ func (o *repoRemoveOptions) run(out io.Writer) error {
|
|||
if !r.Remove(name) {
|
||||
return errors.Errorf("no repo named %q found", name)
|
||||
}
|
||||
if err := r.WriteFile(o.repoFile, 0644); err != nil {
|
||||
if err := r.WriteFile(o.repoFile, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,6 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/internal/test/ensure"
|
||||
"helm.sh/helm/v3/pkg/helmpath"
|
||||
"helm.sh/helm/v3/pkg/repo"
|
||||
"helm.sh/helm/v3/pkg/repo/repotest"
|
||||
|
|
@ -37,7 +36,7 @@ func TestRepoRemove(t *testing.T) {
|
|||
}
|
||||
defer ts.Stop()
|
||||
|
||||
rootDir := ensure.TempDir(t)
|
||||
rootDir := t.TempDir()
|
||||
repoFile := filepath.Join(rootDir, "repositories.yaml")
|
||||
|
||||
const testRepoName = "test-name"
|
||||
|
|
@ -169,7 +168,7 @@ func TestRepoRemoveCompletion(t *testing.T) {
|
|||
}
|
||||
defer ts.Stop()
|
||||
|
||||
rootDir := ensure.TempDir(t)
|
||||
rootDir := t.TempDir()
|
||||
repoFile := filepath.Join(rootDir, "repositories.yaml")
|
||||
repoCache := filepath.Join(rootDir, "cache/")
|
||||
|
||||
|
|
|
|||
|
|
@ -57,10 +57,10 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
|
|||
Short: "update information of available charts locally from chart repositories",
|
||||
Long: updateDesc,
|
||||
Args: require.MinimumNArgs(0),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compListRepos(toComplete, args), cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.repoFile = settings.RepositoryConfig
|
||||
o.repoCache = settings.RepositoryCache
|
||||
o.names = args
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func TestUpdateCmd(t *testing.T) {
|
|||
var out bytes.Buffer
|
||||
// Instead of using the HTTP updater, we provide our own for this test.
|
||||
// The TestUpdateCharts test verifies the HTTP behavior independently.
|
||||
updater := func(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
|
||||
updater := func(repos []*repo.ChartRepository, out io.Writer, _ bool) error {
|
||||
for _, re := range repos {
|
||||
fmt.Fprintln(out, re.Config.Name)
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ func TestUpdateCmdMultiple(t *testing.T) {
|
|||
var out bytes.Buffer
|
||||
// Instead of using the HTTP updater, we provide our own for this test.
|
||||
// The TestUpdateCharts test verifies the HTTP behavior independently.
|
||||
updater := func(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
|
||||
updater := func(repos []*repo.ChartRepository, out io.Writer, _ bool) error {
|
||||
for _, re := range repos {
|
||||
fmt.Fprintln(out, re.Config.Name)
|
||||
}
|
||||
|
|
@ -85,7 +85,7 @@ func TestUpdateCmdInvalid(t *testing.T) {
|
|||
var out bytes.Buffer
|
||||
// Instead of using the HTTP updater, we provide our own for this test.
|
||||
// The TestUpdateCharts test verifies the HTTP behavior independently.
|
||||
updater := func(repos []*repo.ChartRepository, out io.Writer, failOnRepoUpdateFail bool) error {
|
||||
updater := func(repos []*repo.ChartRepository, out io.Writer, _ bool) error {
|
||||
for _, re := range repos {
|
||||
fmt.Fprintln(out, re.Config.Name)
|
||||
}
|
||||
|
|
@ -102,10 +102,9 @@ func TestUpdateCmdInvalid(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUpdateCustomCacheCmd(t *testing.T) {
|
||||
rootDir := ensure.TempDir(t)
|
||||
rootDir := t.TempDir()
|
||||
cachePath := filepath.Join(rootDir, "updcustomcache")
|
||||
os.Mkdir(cachePath, os.ModePerm)
|
||||
defer os.RemoveAll(cachePath)
|
||||
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
|
|
@ -129,7 +128,7 @@ func TestUpdateCustomCacheCmd(t *testing.T) {
|
|||
|
||||
func TestUpdateCharts(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
|
|
@ -164,7 +163,7 @@ func TestRepoUpdateFileCompletion(t *testing.T) {
|
|||
|
||||
func TestUpdateChartsFail(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
|
|
@ -197,7 +196,7 @@ func TestUpdateChartsFail(t *testing.T) {
|
|||
|
||||
func TestUpdateChartsFailWithError(t *testing.T) {
|
||||
defer resetEnv()()
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
|
||||
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ const rollbackDesc = `
|
|||
This command rolls back a release to a previous revision.
|
||||
|
||||
The first argument of the rollback command is the name of a release, and the
|
||||
second is a revision (version) number. If this argument is omitted, it will
|
||||
roll back to the previous release.
|
||||
second is a revision (version) number. If this argument is omitted or set to
|
||||
0, it will roll back to the previous release.
|
||||
|
||||
To see revision numbers, run 'helm history RELEASE'.
|
||||
`
|
||||
|
|
@ -46,7 +46,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "roll back a release to a previous revision",
|
||||
Long: rollbackDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 0 {
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
}
|
||||
|
|
@ -55,9 +55,9 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
if len(args) > 1 {
|
||||
ver, err := strconv.Atoi(args[1])
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ limitations under the License.
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
|
|
@ -64,6 +66,12 @@ func TestRollbackCmd(t *testing.T) {
|
|||
cmd: "rollback funny-honey",
|
||||
golden: "output/rollback-no-revision.txt",
|
||||
rels: rels,
|
||||
}, {
|
||||
name: "rollback a release with non-existent version",
|
||||
cmd: "rollback funny-honey 3",
|
||||
golden: "output/rollback-non-existent-version.txt",
|
||||
rels: rels,
|
||||
wantError: true,
|
||||
}, {
|
||||
name: "rollback a release without release name",
|
||||
cmd: "rollback",
|
||||
|
|
@ -115,3 +123,44 @@ func TestRollbackFileCompletion(t *testing.T) {
|
|||
checkFileCompletion(t, "rollback myrelease", false)
|
||||
checkFileCompletion(t, "rollback myrelease 1", false)
|
||||
}
|
||||
|
||||
func TestRollbackWithLabels(t *testing.T) {
|
||||
labels1 := map[string]string{"operation": "install", "firstLabel": "firstValue"}
|
||||
labels2 := map[string]string{"operation": "upgrade", "secondLabel": "secondValue"}
|
||||
|
||||
releaseName := "funny-bunny-labels"
|
||||
rels := []*release.Release{
|
||||
{
|
||||
Name: releaseName,
|
||||
Info: &release.Info{Status: release.StatusSuperseded},
|
||||
Chart: &chart.Chart{},
|
||||
Version: 1,
|
||||
Labels: labels1,
|
||||
},
|
||||
{
|
||||
Name: releaseName,
|
||||
Info: &release.Info{Status: release.StatusDeployed},
|
||||
Chart: &chart.Chart{},
|
||||
Version: 2,
|
||||
Labels: labels2,
|
||||
},
|
||||
}
|
||||
storage := storageFixture()
|
||||
for _, rel := range rels {
|
||||
if err := storage.Create(rel); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
_, _, err := executeActionCommandC(storage, fmt.Sprintf("rollback %s 1", releaseName))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error, got '%v'", err)
|
||||
}
|
||||
updatedRel, err := storage.Get(releaseName, 3)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error, got '%v'", err)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(updatedRel.Labels, labels1) {
|
||||
t.Errorf("Expected {%v}, got {%v}", labels1, updatedRel.Labels)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,31 +45,32 @@ Common actions for Helm:
|
|||
|
||||
Environment variables:
|
||||
|
||||
| Name | Description |
|
||||
|------------------------------------|---------------------------------------------------------------------------------------------------|
|
||||
| $HELM_CACHE_HOME | set an alternative location for storing cached files. |
|
||||
| $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. |
|
||||
| $HELM_DATA_HOME | set an alternative location for storing Helm data. |
|
||||
| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode |
|
||||
| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, sql. |
|
||||
| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. |
|
||||
| $HELM_MAX_HISTORY | set the maximum number of helm release history. |
|
||||
| $HELM_NAMESPACE | set the namespace used for the helm operations. |
|
||||
| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. |
|
||||
| $HELM_PLUGINS | set the path to the plugins directory |
|
||||
| $HELM_REGISTRY_CONFIG | set the path to the registry config file. |
|
||||
| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory |
|
||||
| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. |
|
||||
| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") |
|
||||
| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication |
|
||||
| $HELM_KUBECAFILE | set the Kubernetes certificate authority file. |
|
||||
| $HELM_KUBEASGROUPS | set the Groups to use for impersonation using a comma-separated list. |
|
||||
| $HELM_KUBEASUSER | set the Username to impersonate for the operation. |
|
||||
| $HELM_KUBECONTEXT | set the name of the kubeconfig context. |
|
||||
| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. |
|
||||
| $HELM_KUBEINSECURE_SKIP_TLS_VERIFY | indicate if the Kubernetes API server's certificate validation should be skipped (insecure) |
|
||||
| $HELM_KUBETLS_SERVER_NAME | set the server name used to validate the Kubernetes API server certificate |
|
||||
| $HELM_BURST_LIMIT | set the default burst limit in the case the server contains many CRDs (default 100, -1 to disable)|
|
||||
| Name | Description |
|
||||
|------------------------------------|------------------------------------------------------------------------------------------------------------|
|
||||
| $HELM_CACHE_HOME | set an alternative location for storing cached files. |
|
||||
| $HELM_CONFIG_HOME | set an alternative location for storing Helm configuration. |
|
||||
| $HELM_DATA_HOME | set an alternative location for storing Helm data. |
|
||||
| $HELM_DEBUG | indicate whether or not Helm is running in Debug mode |
|
||||
| $HELM_DRIVER | set the backend storage driver. Values are: configmap, secret, memory, sql. |
|
||||
| $HELM_DRIVER_SQL_CONNECTION_STRING | set the connection string the SQL storage driver should use. |
|
||||
| $HELM_MAX_HISTORY | set the maximum number of helm release history. |
|
||||
| $HELM_NAMESPACE | set the namespace used for the helm operations. |
|
||||
| $HELM_NO_PLUGINS | disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. |
|
||||
| $HELM_PLUGINS | set the path to the plugins directory |
|
||||
| $HELM_REGISTRY_CONFIG | set the path to the registry config file. |
|
||||
| $HELM_REPOSITORY_CACHE | set the path to the repository cache directory |
|
||||
| $HELM_REPOSITORY_CONFIG | set the path to the repositories file. |
|
||||
| $KUBECONFIG | set an alternative Kubernetes configuration file (default "~/.kube/config") |
|
||||
| $HELM_KUBEAPISERVER | set the Kubernetes API Server Endpoint for authentication |
|
||||
| $HELM_KUBECAFILE | set the Kubernetes certificate authority file. |
|
||||
| $HELM_KUBEASGROUPS | set the Groups to use for impersonation using a comma-separated list. |
|
||||
| $HELM_KUBEASUSER | set the Username to impersonate for the operation. |
|
||||
| $HELM_KUBECONTEXT | set the name of the kubeconfig context. |
|
||||
| $HELM_KUBETOKEN | set the Bearer KubeToken used for authentication. |
|
||||
| $HELM_KUBEINSECURE_SKIP_TLS_VERIFY | indicate if the Kubernetes API server's certificate validation should be skipped (insecure) |
|
||||
| $HELM_KUBETLS_SERVER_NAME | set the server name used to validate the Kubernetes API server certificate |
|
||||
| $HELM_BURST_LIMIT | set the default burst limit in the case the server contains many CRDs (default 100, -1 to disable) |
|
||||
| $HELM_QPS | set the Queries Per Second in cases where a high number of calls exceed the option for higher burst values |
|
||||
|
||||
Helm stores cache, configuration, and data based on the following configuration order:
|
||||
|
||||
|
|
@ -99,7 +100,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
|
|||
addKlogFlags(flags)
|
||||
|
||||
// Setup shell completion for the namespace flag
|
||||
err := cmd.RegisterFlagCompletionFunc("namespace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("namespace", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
if client, err := actionConfig.KubernetesClientSet(); err == nil {
|
||||
// Choose a long enough timeout that the user notices something is not working
|
||||
// but short enough that the user is not made to wait very long
|
||||
|
|
@ -122,7 +123,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
|
|||
}
|
||||
|
||||
// Setup shell completion for the kube-context flag
|
||||
err = cmd.RegisterFlagCompletionFunc("kube-context", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err = cmd.RegisterFlagCompletionFunc("kube-context", func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
cobra.CompDebugln("About to get the different kube-contexts", settings.Debug)
|
||||
|
||||
loadingRules := clientcmd.NewDefaultClientConfigLoadingRules()
|
||||
|
|
@ -152,7 +153,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
|
|||
flags.ParseErrorsWhitelist.UnknownFlags = true
|
||||
flags.Parse(args)
|
||||
|
||||
registryClient, err := newDefaultRegistryClient()
|
||||
registryClient, err := newDefaultRegistryClient(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -257,7 +258,7 @@ func checkForExpiredRepos(repofile string) {
|
|||
|
||||
}
|
||||
|
||||
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
|
||||
func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify, plainHTTP bool) (*registry.Client, error) {
|
||||
if certFile != "" && keyFile != "" || caFile != "" || insecureSkipTLSverify {
|
||||
registryClient, err := newRegistryClientWithTLS(certFile, keyFile, caFile, insecureSkipTLSverify)
|
||||
if err != nil {
|
||||
|
|
@ -265,21 +266,26 @@ func newRegistryClient(certFile, keyFile, caFile string, insecureSkipTLSverify b
|
|||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
registryClient, err := newDefaultRegistryClient()
|
||||
registryClient, err := newDefaultRegistryClient(plainHTTP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
||||
func newDefaultRegistryClient() (*registry.Client, error) {
|
||||
// Create a new registry client
|
||||
registryClient, err := registry.NewClient(
|
||||
func newDefaultRegistryClient(plainHTTP bool) (*registry.Client, error) {
|
||||
opts := []registry.ClientOption{
|
||||
registry.ClientOptDebug(settings.Debug),
|
||||
registry.ClientOptEnableCache(true),
|
||||
registry.ClientOptWriter(os.Stderr),
|
||||
registry.ClientOptCredentialsFile(settings.RegistryConfig),
|
||||
)
|
||||
}
|
||||
if plainHTTP {
|
||||
opts = append(opts, registry.ClientOptPlainHTTP())
|
||||
}
|
||||
|
||||
// Create a new registry client
|
||||
registryClient, err := registry.NewClient(opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ func TestRootCmd(t *testing.T) {
|
|||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer ensure.HelmHome(t)()
|
||||
ensure.HelmHome(t)
|
||||
|
||||
for k, v := range tt.envvars {
|
||||
os.Setenv(k, v)
|
||||
|
|
|
|||
|
|
@ -147,11 +147,10 @@ func (i *Index) SearchLiteral(term string, threshold int) []*Result {
|
|||
term = strings.ToLower(term)
|
||||
buf := []*Result{}
|
||||
for k, v := range i.lines {
|
||||
lk := strings.ToLower(k)
|
||||
lv := strings.ToLower(v)
|
||||
res := strings.Index(lv, term)
|
||||
if score := i.calcScore(res, lv); res != -1 && score < threshold {
|
||||
parts := strings.Split(lk, verSep) // Remove version, if it is there.
|
||||
parts := strings.Split(k, verSep) // Remove version, if it is there.
|
||||
buf = append(buf, &Result{Name: parts[0], Score: score, Chart: i.charts[k]})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,15 +101,15 @@ var indexfileEntries = map[string]repo.ChartVersions{
|
|||
},
|
||||
}
|
||||
|
||||
func loadTestIndex(t *testing.T, all bool) *Index {
|
||||
func loadTestIndex(_ *testing.T, all bool) *Index {
|
||||
i := NewIndex()
|
||||
i.AddRepo("testing", &repo.IndexFile{Entries: indexfileEntries}, all)
|
||||
i.AddRepo("ztesting", &repo.IndexFile{Entries: map[string]repo.ChartVersions{
|
||||
"pinta": {
|
||||
"Pinta": {
|
||||
{
|
||||
URLs: []string{"http://example.com/charts/pinta-2.0.0.tgz"},
|
||||
Metadata: &chart.Metadata{
|
||||
Name: "pinta",
|
||||
Name: "Pinta",
|
||||
Version: "2.0.0",
|
||||
Description: "Two ship, version two",
|
||||
},
|
||||
|
|
@ -170,14 +170,14 @@ func TestSearchByName(t *testing.T) {
|
|||
query: "pinta",
|
||||
expect: []*Result{
|
||||
{Name: "testing/pinta"},
|
||||
{Name: "ztesting/pinta"},
|
||||
{Name: "ztesting/Pinta"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "repo-specific search for one result",
|
||||
query: "ztesting/pinta",
|
||||
expect: []*Result{
|
||||
{Name: "ztesting/pinta"},
|
||||
{Name: "ztesting/Pinta"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -199,7 +199,15 @@ func TestSearchByName(t *testing.T) {
|
|||
query: "two",
|
||||
expect: []*Result{
|
||||
{Name: "testing/pinta"},
|
||||
{Name: "ztesting/pinta"},
|
||||
{Name: "ztesting/Pinta"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "search mixedCase and result should be mixedCase too",
|
||||
query: "pinta",
|
||||
expect: []*Result{
|
||||
{Name: "testing/pinta"},
|
||||
{Name: "ztesting/Pinta"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -207,7 +215,7 @@ func TestSearchByName(t *testing.T) {
|
|||
query: "TWO",
|
||||
expect: []*Result{
|
||||
{Name: "testing/pinta"},
|
||||
{Name: "ztesting/pinta"},
|
||||
{Name: "ztesting/Pinta"},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -54,6 +54,7 @@ type searchHubOptions struct {
|
|||
maxColWidth uint
|
||||
outputFormat output.Format
|
||||
listRepoURL bool
|
||||
failOnNoResult bool
|
||||
}
|
||||
|
||||
func newSearchHubCmd(out io.Writer) *cobra.Command {
|
||||
|
|
@ -63,7 +64,7 @@ func newSearchHubCmd(out io.Writer) *cobra.Command {
|
|||
Use: "hub [KEYWORD]",
|
||||
Short: "search for charts in the Artifact Hub or your own hub instance",
|
||||
Long: searchHubDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return o.run(out, args)
|
||||
},
|
||||
}
|
||||
|
|
@ -72,6 +73,7 @@ func newSearchHubCmd(out io.Writer) *cobra.Command {
|
|||
f.StringVar(&o.searchEndpoint, "endpoint", "https://hub.helm.sh", "Hub instance to query for charts")
|
||||
f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table")
|
||||
f.BoolVar(&o.listRepoURL, "list-repo-url", false, "print charts repository URL")
|
||||
f.BoolVar(&o.failOnNoResult, "fail-on-no-result", false, "search fails if no results are found")
|
||||
|
||||
bindOutputFlag(cmd, &o.outputFormat)
|
||||
|
||||
|
|
@ -91,7 +93,7 @@ func (o *searchHubOptions) run(out io.Writer, args []string) error {
|
|||
return fmt.Errorf("unable to perform search against %q", o.searchEndpoint)
|
||||
}
|
||||
|
||||
return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth, o.listRepoURL))
|
||||
return o.outputFormat.Write(out, newHubSearchWriter(results, o.searchEndpoint, o.maxColWidth, o.listRepoURL, o.failOnNoResult))
|
||||
}
|
||||
|
||||
type hubChartRepo struct {
|
||||
|
|
@ -108,12 +110,13 @@ type hubChartElement struct {
|
|||
}
|
||||
|
||||
type hubSearchWriter struct {
|
||||
elements []hubChartElement
|
||||
columnWidth uint
|
||||
listRepoURL bool
|
||||
elements []hubChartElement
|
||||
columnWidth uint
|
||||
listRepoURL bool
|
||||
failOnNoResult bool
|
||||
}
|
||||
|
||||
func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint, listRepoURL bool) *hubSearchWriter {
|
||||
func newHubSearchWriter(results []monocular.SearchResult, endpoint string, columnWidth uint, listRepoURL, failOnNoResult bool) *hubSearchWriter {
|
||||
var elements []hubChartElement
|
||||
for _, r := range results {
|
||||
// Backwards compatibility for Monocular
|
||||
|
|
@ -126,11 +129,16 @@ func newHubSearchWriter(results []monocular.SearchResult, endpoint string, colum
|
|||
|
||||
elements = append(elements, hubChartElement{url, r.Relationships.LatestChartVersion.Data.Version, r.Relationships.LatestChartVersion.Data.AppVersion, r.Attributes.Description, hubChartRepo{URL: r.Attributes.Repo.URL, Name: r.Attributes.Repo.Name}})
|
||||
}
|
||||
return &hubSearchWriter{elements, columnWidth, listRepoURL}
|
||||
return &hubSearchWriter{elements, columnWidth, listRepoURL, failOnNoResult}
|
||||
}
|
||||
|
||||
func (h *hubSearchWriter) WriteTable(out io.Writer) error {
|
||||
if len(h.elements) == 0 {
|
||||
// Fail if no results found and --fail-on-no-result is enabled
|
||||
if h.failOnNoResult {
|
||||
return fmt.Errorf("no results found")
|
||||
}
|
||||
|
||||
_, err := out.Write([]byte("No results found\n"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write results: %s", err)
|
||||
|
|
@ -165,6 +173,11 @@ func (h *hubSearchWriter) WriteYAML(out io.Writer) error {
|
|||
}
|
||||
|
||||
func (h *hubSearchWriter) encodeByFormat(out io.Writer, format output.Format) error {
|
||||
// Fail if no results found and --fail-on-no-result is enabled
|
||||
if len(h.elements) == 0 && h.failOnNoResult {
|
||||
return fmt.Errorf("no results found")
|
||||
}
|
||||
|
||||
// Initialize the array so no results returns an empty array instead of null
|
||||
chartList := make([]hubChartElement, 0, len(h.elements))
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func TestSearchHubCmd(t *testing.T) {
|
|||
|
||||
// Setup a mock search service
|
||||
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
fmt.Fprintln(w, searchResult)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
|
@ -57,7 +57,7 @@ func TestSearchHubListRepoCmd(t *testing.T) {
|
|||
|
||||
// Setup a mock search service
|
||||
var searchResult = `{"data":[{"id":"stable/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"stable","url":"https://charts.helm.sh/stable"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/stable/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T17:57:31.38Z","digest":"119c499251bffd4b06ff0cd5ac98c2ce32231f84899fb4825be6c2d90971c742","urls":["https://charts.helm.sh/stable/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/stable/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/stable/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/stable/phpmyadmin/versions/3.0.0"}}}},{"id":"bitnami/phpmyadmin","type":"chart","attributes":{"name":"phpmyadmin","repo":{"name":"bitnami","url":"https://charts.bitnami.com"},"description":"phpMyAdmin is an mysql administration frontend","home":"https://www.phpmyadmin.net/","keywords":["mariadb","mysql","phpmyadmin"],"maintainers":[{"name":"Bitnami","email":"containers@bitnami.com"}],"sources":["https://github.com/bitnami/bitnami-docker-phpmyadmin"],"icon":""},"links":{"self":"/v1/charts/bitnami/phpmyadmin"},"relationships":{"latestChartVersion":{"data":{"version":"3.0.0","app_version":"4.9.0-1","created":"2019-08-08T18:34:13.341Z","digest":"66d77cf6d8c2b52c488d0a294cd4996bd5bad8dc41d3829c394498fb401c008a","urls":["https://charts.bitnami.com/bitnami/phpmyadmin-3.0.0.tgz"],"readme":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/README.md","values":"/v1/assets/bitnami/phpmyadmin/versions/3.0.0/values.yaml"},"links":{"self":"/v1/charts/bitnami/phpmyadmin/versions/3.0.0"}}}}]}`
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
fmt.Fprintln(w, searchResult)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
|
@ -90,3 +90,98 @@ func TestSearchHubOutputCompletion(t *testing.T) {
|
|||
func TestSearchHubFileCompletion(t *testing.T) {
|
||||
checkFileCompletion(t, "search hub", true) // File completion may be useful when inputting a keyword
|
||||
}
|
||||
|
||||
func TestSearchHubCmd_FailOnNoResponseTests(t *testing.T) {
|
||||
var (
|
||||
searchResult = `{"data":[]}`
|
||||
noResultFoundErr = "Error: no results found\n"
|
||||
noResultFoundWarn = "No results found\n"
|
||||
noResultFoundWarnInList = "[]\n"
|
||||
)
|
||||
|
||||
type testCase struct {
|
||||
name string
|
||||
cmd string
|
||||
response string
|
||||
expected string
|
||||
wantErr bool
|
||||
}
|
||||
|
||||
var tests = []testCase{
|
||||
{
|
||||
name: "Search hub with no results in response",
|
||||
cmd: `search hub maria`,
|
||||
response: searchResult,
|
||||
expected: noResultFoundWarn,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Search hub with no results in response and output JSON",
|
||||
cmd: `search hub maria --output json`,
|
||||
response: searchResult,
|
||||
expected: noResultFoundWarnInList,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Search hub with no results in response and output YAML",
|
||||
cmd: `search hub maria --output yaml`,
|
||||
response: searchResult,
|
||||
expected: noResultFoundWarnInList,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Search hub with no results in response and --fail-on-no-result enabled, expected failure",
|
||||
cmd: `search hub maria --fail-on-no-result`,
|
||||
response: searchResult,
|
||||
expected: noResultFoundErr,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Search hub with no results in response, output JSON and --fail-on-no-result enabled, expected failure",
|
||||
cmd: `search hub maria --fail-on-no-result --output json`,
|
||||
response: searchResult,
|
||||
expected: noResultFoundErr,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Search hub with no results in response, output YAML and --fail-on-no-result enabled, expected failure",
|
||||
cmd: `search hub maria --fail-on-no-result --output yaml`,
|
||||
response: searchResult,
|
||||
expected: noResultFoundErr,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Setup a mock search service
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
fmt.Fprintln(w, tt.response)
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
// Add mock server URL to command
|
||||
tt.cmd += " --endpoint " + ts.URL
|
||||
|
||||
storage := storageFixture()
|
||||
|
||||
_, out, err := executeActionCommandC(storage, tt.cmd)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Errorf("expected error due to no record in response, got nil")
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error, got %q", err)
|
||||
}
|
||||
}
|
||||
|
||||
if out != tt.expected {
|
||||
t.Errorf("expected and actual output did not match\n"+
|
||||
"expected: %q\n"+
|
||||
"actual : %q",
|
||||
tt.expected, out)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,14 +63,15 @@ Repositories are managed with 'helm repo' commands.
|
|||
const searchMaxScore = 25
|
||||
|
||||
type searchRepoOptions struct {
|
||||
versions bool
|
||||
regexp bool
|
||||
devel bool
|
||||
version string
|
||||
maxColWidth uint
|
||||
repoFile string
|
||||
repoCacheDir string
|
||||
outputFormat output.Format
|
||||
versions bool
|
||||
regexp bool
|
||||
devel bool
|
||||
version string
|
||||
maxColWidth uint
|
||||
repoFile string
|
||||
repoCacheDir string
|
||||
outputFormat output.Format
|
||||
failOnNoResult bool
|
||||
}
|
||||
|
||||
func newSearchRepoCmd(out io.Writer) *cobra.Command {
|
||||
|
|
@ -80,7 +81,7 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command {
|
|||
Use: "repo [keyword]",
|
||||
Short: "search repositories for a keyword in charts",
|
||||
Long: searchRepoDesc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.repoFile = settings.RepositoryConfig
|
||||
o.repoCacheDir = settings.RepositoryCache
|
||||
return o.run(out, args)
|
||||
|
|
@ -93,6 +94,8 @@ func newSearchRepoCmd(out io.Writer) *cobra.Command {
|
|||
f.BoolVar(&o.devel, "devel", false, "use development versions (alpha, beta, and release candidate releases), too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
|
||||
f.StringVar(&o.version, "version", "", "search using semantic versioning constraints on repositories you have added")
|
||||
f.UintVar(&o.maxColWidth, "max-col-width", 50, "maximum column width for output table")
|
||||
f.BoolVar(&o.failOnNoResult, "fail-on-no-result", false, "search fails if no results are found")
|
||||
|
||||
bindOutputFlag(cmd, &o.outputFormat)
|
||||
|
||||
return cmd
|
||||
|
|
@ -123,7 +126,7 @@ func (o *searchRepoOptions) run(out io.Writer, args []string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
return o.outputFormat.Write(out, &repoSearchWriter{data, o.maxColWidth})
|
||||
return o.outputFormat.Write(out, &repoSearchWriter{data, o.maxColWidth, o.failOnNoResult})
|
||||
}
|
||||
|
||||
func (o *searchRepoOptions) setupSearchedVersion() {
|
||||
|
|
@ -204,12 +207,18 @@ type repoChartElement struct {
|
|||
}
|
||||
|
||||
type repoSearchWriter struct {
|
||||
results []*search.Result
|
||||
columnWidth uint
|
||||
results []*search.Result
|
||||
columnWidth uint
|
||||
failOnNoResult bool
|
||||
}
|
||||
|
||||
func (r *repoSearchWriter) WriteTable(out io.Writer) error {
|
||||
if len(r.results) == 0 {
|
||||
// Fail if no results found and --fail-on-no-result is enabled
|
||||
if r.failOnNoResult {
|
||||
return fmt.Errorf("no results found")
|
||||
}
|
||||
|
||||
_, err := out.Write([]byte("No results found\n"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to write results: %s", err)
|
||||
|
|
@ -234,6 +243,11 @@ func (r *repoSearchWriter) WriteYAML(out io.Writer) error {
|
|||
}
|
||||
|
||||
func (r *repoSearchWriter) encodeByFormat(out io.Writer, format output.Format) error {
|
||||
// Fail if no results found and --fail-on-no-result is enabled
|
||||
if len(r.results) == 0 && r.failOnNoResult {
|
||||
return fmt.Errorf("no results found")
|
||||
}
|
||||
|
||||
// Initialize the array so no results returns an empty array instead of null
|
||||
chartList := make([]repoChartElement, 0, len(r.results))
|
||||
|
||||
|
|
|
|||
|
|
@ -56,6 +56,20 @@ func TestSearchRepositoriesCmd(t *testing.T) {
|
|||
name: "search for 'syzygy', expect no matches",
|
||||
cmd: "search repo syzygy",
|
||||
golden: "output/search-not-found.txt",
|
||||
}, {
|
||||
name: "search for 'syzygy' with --fail-on-no-result, expect failure for no results",
|
||||
cmd: "search repo syzygy --fail-on-no-result",
|
||||
golden: "output/search-not-found-error.txt",
|
||||
wantError: true,
|
||||
}, {name: "search for 'syzygy' with json output and --fail-on-no-result, expect failure for no results",
|
||||
cmd: "search repo syzygy --output json --fail-on-no-result",
|
||||
golden: "output/search-not-found-error.txt",
|
||||
wantError: true,
|
||||
}, {
|
||||
name: "search for 'syzygy' with yaml output --fail-on-no-result, expect failure for no results",
|
||||
cmd: "search repo syzygy --output yaml --fail-on-no-result",
|
||||
golden: "output/search-not-found-error.txt",
|
||||
wantError: true,
|
||||
}, {
|
||||
name: "search for 'alp[a-z]+', expect two matches",
|
||||
cmd: "search repo alp[a-z]+ --regexp",
|
||||
|
|
|
|||
|
|
@ -60,18 +60,17 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
client := action.NewShowWithConfig(action.ShowAll, cfg)
|
||||
|
||||
showCommand := &cobra.Command{
|
||||
Use: "show",
|
||||
Short: "show information of a chart",
|
||||
Aliases: []string{"inspect"},
|
||||
Long: showDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions, // Disable file completion
|
||||
Use: "show",
|
||||
Short: "show information of a chart",
|
||||
Aliases: []string{"inspect"},
|
||||
Long: showDesc,
|
||||
Args: require.NoArgs,
|
||||
}
|
||||
|
||||
// Function providing dynamic auto-completion
|
||||
validArgsFunc := func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
validArgsFunc := func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListCharts(toComplete, true)
|
||||
}
|
||||
|
|
@ -82,7 +81,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Long: showAllDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: validArgsFunc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.OutputFormat = action.ShowAll
|
||||
err := addRegistryClient(client)
|
||||
if err != nil {
|
||||
|
|
@ -103,7 +102,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Long: showValuesDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: validArgsFunc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.OutputFormat = action.ShowValues
|
||||
err := addRegistryClient(client)
|
||||
if err != nil {
|
||||
|
|
@ -124,7 +123,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Long: showChartDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: validArgsFunc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.OutputFormat = action.ShowChart
|
||||
err := addRegistryClient(client)
|
||||
if err != nil {
|
||||
|
|
@ -145,7 +144,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Long: readmeChartDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: validArgsFunc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.OutputFormat = action.ShowReadme
|
||||
err := addRegistryClient(client)
|
||||
if err != nil {
|
||||
|
|
@ -166,7 +165,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Long: showCRDsDesc,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: validArgsFunc,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
client.OutputFormat = action.ShowCRDs
|
||||
err := addRegistryClient(client)
|
||||
if err != nil {
|
||||
|
|
@ -199,7 +198,7 @@ func addShowFlags(subCmd *cobra.Command, client *action.Show) {
|
|||
}
|
||||
addChartPathOptionsFlags(f, &client.ChartPathOptions)
|
||||
|
||||
err := subCmd.RegisterFlagCompletionFunc("version", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := subCmd.RegisterFlagCompletionFunc("version", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 1 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
@ -226,7 +225,8 @@ func runShow(args []string, client *action.Show) (string, error) {
|
|||
}
|
||||
|
||||
func addRegistryClient(client *action.Show) error {
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
|
||||
client.InsecureSkipTLSverify, client.PlainHTTP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing registry client: %w", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,13 +58,13 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "display the status of the named release",
|
||||
Long: statusHelp,
|
||||
Args: require.ExactArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) != 0 {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
return noMoreArgsComp()
|
||||
}
|
||||
return compListReleases(toComplete, args, cfg)
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
|
||||
// When the output format is a table the resources should be fetched
|
||||
// and displayed as a table. When YAML or JSON the resources will be
|
||||
|
|
@ -80,7 +80,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
// strip chart metadata from the output
|
||||
rel.Chart = nil
|
||||
|
||||
return outfmt.Write(out, &statusPrinter{rel, false, client.ShowDescription, client.ShowResources})
|
||||
return outfmt.Write(out, &statusPrinter{rel, false, client.ShowDescription, client.ShowResources, false, false})
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +88,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
|
||||
f.IntVar(&client.Version, "revision", 0, "if set, display the status of the named release with revision")
|
||||
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
err := cmd.RegisterFlagCompletionFunc("revision", func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
if len(args) == 1 {
|
||||
return compListRevisions(toComplete, cfg, args[0])
|
||||
}
|
||||
|
|
@ -112,6 +112,8 @@ type statusPrinter struct {
|
|||
debug bool
|
||||
showDescription bool
|
||||
showResources bool
|
||||
showMetadata bool
|
||||
hideNotes bool
|
||||
}
|
||||
|
||||
func (s statusPrinter) WriteJSON(out io.Writer) error {
|
||||
|
|
@ -126,15 +128,20 @@ func (s statusPrinter) WriteTable(out io.Writer) error {
|
|||
if s.release == nil {
|
||||
return nil
|
||||
}
|
||||
fmt.Fprintf(out, "NAME: %s\n", s.release.Name)
|
||||
_, _ = fmt.Fprintf(out, "NAME: %s\n", s.release.Name)
|
||||
if !s.release.Info.LastDeployed.IsZero() {
|
||||
fmt.Fprintf(out, "LAST DEPLOYED: %s\n", s.release.Info.LastDeployed.Format(time.ANSIC))
|
||||
_, _ = fmt.Fprintf(out, "LAST DEPLOYED: %s\n", s.release.Info.LastDeployed.Format(time.ANSIC))
|
||||
}
|
||||
_, _ = fmt.Fprintf(out, "NAMESPACE: %s\n", s.release.Namespace)
|
||||
_, _ = fmt.Fprintf(out, "STATUS: %s\n", s.release.Info.Status.String())
|
||||
_, _ = fmt.Fprintf(out, "REVISION: %d\n", s.release.Version)
|
||||
if s.showMetadata {
|
||||
_, _ = fmt.Fprintf(out, "CHART: %s\n", s.release.Chart.Metadata.Name)
|
||||
_, _ = fmt.Fprintf(out, "VERSION: %s\n", s.release.Chart.Metadata.Version)
|
||||
_, _ = fmt.Fprintf(out, "APP_VERSION: %s\n", s.release.Chart.Metadata.AppVersion)
|
||||
}
|
||||
fmt.Fprintf(out, "NAMESPACE: %s\n", s.release.Namespace)
|
||||
fmt.Fprintf(out, "STATUS: %s\n", s.release.Info.Status.String())
|
||||
fmt.Fprintf(out, "REVISION: %d\n", s.release.Version)
|
||||
if s.showDescription {
|
||||
fmt.Fprintf(out, "DESCRIPTION: %s\n", s.release.Info.Description)
|
||||
_, _ = fmt.Fprintf(out, "DESCRIPTION: %s\n", s.release.Info.Description)
|
||||
}
|
||||
|
||||
if s.showResources && s.release.Info.Resources != nil && len(s.release.Info.Resources) > 0 {
|
||||
|
|
@ -149,31 +156,31 @@ func (s statusPrinter) WriteTable(out io.Writer) error {
|
|||
}
|
||||
|
||||
for _, t := range keys {
|
||||
fmt.Fprintf(buf, "==> %s\n", t)
|
||||
_, _ = fmt.Fprintf(buf, "==> %s\n", t)
|
||||
|
||||
vk := s.release.Info.Resources[t]
|
||||
for _, resource := range vk {
|
||||
if err := printer.PrintObj(resource, buf); err != nil {
|
||||
fmt.Fprintf(buf, "failed to print object type %s: %v\n", t, err)
|
||||
_, _ = fmt.Fprintf(buf, "failed to print object type %s: %v\n", t, err)
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
|
||||
fmt.Fprintf(out, "RESOURCES:\n%s\n", buf.String())
|
||||
_, _ = fmt.Fprintf(out, "RESOURCES:\n%s\n", buf.String())
|
||||
}
|
||||
|
||||
executions := executionsByHookEvent(s.release)
|
||||
if tests, ok := executions[release.HookTest]; !ok || len(tests) == 0 {
|
||||
fmt.Fprintln(out, "TEST SUITE: None")
|
||||
_, _ = fmt.Fprintln(out, "TEST SUITE: None")
|
||||
} else {
|
||||
for _, h := range tests {
|
||||
// Don't print anything if hook has not been initiated
|
||||
if h.LastRun.StartedAt.IsZero() {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(out, "TEST SUITE: %s\n%s\n%s\n%s\n",
|
||||
_, _ = fmt.Fprintf(out, "TEST SUITE: %s\n%s\n%s\n%s\n",
|
||||
h.Name,
|
||||
fmt.Sprintf("Last Started: %s", h.LastRun.StartedAt.Format(time.ANSIC)),
|
||||
fmt.Sprintf("Last Completed: %s", h.LastRun.CompletedAt.Format(time.ANSIC)),
|
||||
|
|
@ -183,37 +190,38 @@ func (s statusPrinter) WriteTable(out io.Writer) error {
|
|||
}
|
||||
|
||||
if s.debug {
|
||||
fmt.Fprintln(out, "USER-SUPPLIED VALUES:")
|
||||
_, _ = fmt.Fprintln(out, "USER-SUPPLIED VALUES:")
|
||||
err := output.EncodeYAML(out, s.release.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Print an extra newline
|
||||
fmt.Fprintln(out)
|
||||
_, _ = fmt.Fprintln(out)
|
||||
|
||||
cfg, err := chartutil.CoalesceValues(s.release.Chart, s.release.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintln(out, "COMPUTED VALUES:")
|
||||
_, _ = fmt.Fprintln(out, "COMPUTED VALUES:")
|
||||
err = output.EncodeYAML(out, cfg.AsMap())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Print an extra newline
|
||||
fmt.Fprintln(out)
|
||||
_, _ = fmt.Fprintln(out)
|
||||
}
|
||||
|
||||
if strings.EqualFold(s.release.Info.Description, "Dry run complete") || s.debug {
|
||||
fmt.Fprintln(out, "HOOKS:")
|
||||
_, _ = fmt.Fprintln(out, "HOOKS:")
|
||||
for _, h := range s.release.Hooks {
|
||||
fmt.Fprintf(out, "---\n# Source: %s\n%s\n", h.Path, h.Manifest)
|
||||
_, _ = fmt.Fprintf(out, "---\n# Source: %s\n%s\n", h.Path, h.Manifest)
|
||||
}
|
||||
fmt.Fprintf(out, "MANIFEST:\n%s\n", s.release.Manifest)
|
||||
_, _ = fmt.Fprintf(out, "MANIFEST:\n%s\n", s.release.Manifest)
|
||||
}
|
||||
|
||||
if len(s.release.Info.Notes) > 0 {
|
||||
// Hide notes from output - option in install and upgrades
|
||||
if !s.hideNotes && len(s.release.Info.Notes) > 0 {
|
||||
fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(s.release.Info.Notes))
|
||||
}
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ func TestStatusCmd(t *testing.T) {
|
|||
Name: "flummoxed-chickadee",
|
||||
Namespace: "default",
|
||||
Info: info,
|
||||
Chart: &chart.Chart{},
|
||||
Chart: &chart.Chart{Metadata: &chart.Metadata{Name: "name", Version: "1.2.3", AppVersion: "3.2.1"}},
|
||||
Hooks: hooks,
|
||||
}}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Short: "locally render templates",
|
||||
Long: templateDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
ValidArgsFunction: func(_ *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
return compInstall(args, toComplete, client)
|
||||
},
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
|
|
@ -73,12 +73,19 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
client.KubeVersion = parsedKubeVersion
|
||||
}
|
||||
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile, client.InsecureSkipTLSverify)
|
||||
registryClient, err := newRegistryClient(client.CertFile, client.KeyFile, client.CaFile,
|
||||
client.InsecureSkipTLSverify, client.PlainHTTP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing registry client: %w", err)
|
||||
}
|
||||
client.SetRegistryClient(registryClient)
|
||||
|
||||
// This is for the case where "" is specifically passed in as a
|
||||
// value. When there is no value passed in NoOptDefVal will be used
|
||||
// and it is set to client. See addInstallFlags.
|
||||
if client.DryRunOption == "" {
|
||||
client.DryRunOption = "true"
|
||||
}
|
||||
client.DryRun = true
|
||||
client.ReleaseName = "release-name"
|
||||
client.Replace = true // Skip the name check
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ import (
|
|||
var chartPath = "testdata/testcharts/subchart"
|
||||
|
||||
func TestTemplateCmd(t *testing.T) {
|
||||
deletevalchart := "testdata/testcharts/issue-9027"
|
||||
|
||||
tests := []cmdTestCase{
|
||||
{
|
||||
name: "check name",
|
||||
|
|
@ -131,6 +133,34 @@ func TestTemplateCmd(t *testing.T) {
|
|||
cmd: fmt.Sprintf(`template '%s' --skip-tests`, chartPath),
|
||||
golden: "output/template-skip-tests.txt",
|
||||
},
|
||||
{
|
||||
// This test case is to ensure the case where specified dependencies
|
||||
// in the Chart.yaml and those where the Chart.yaml don't have them
|
||||
// specified are the same.
|
||||
name: "ensure nil/null values pass to subcharts delete values",
|
||||
cmd: fmt.Sprintf("template '%s'", deletevalchart),
|
||||
golden: "output/issue-9027.txt",
|
||||
},
|
||||
{
|
||||
// Ensure that parent chart values take precedence over imported values
|
||||
name: "template with imported subchart values ensuring import",
|
||||
cmd: fmt.Sprintf("template '%s' --set configmap.enabled=true --set subchartb.enabled=true", chartPath),
|
||||
golden: "output/template-subchart-cm.txt",
|
||||
},
|
||||
{
|
||||
// Ensure that user input values take precedence over imported
|
||||
// values from sub-charts.
|
||||
name: "template with imported subchart values set with --set",
|
||||
cmd: fmt.Sprintf("template '%s' --set configmap.enabled=true --set subchartb.enabled=true --set configmap.value=baz", chartPath),
|
||||
golden: "output/template-subchart-cm-set.txt",
|
||||
},
|
||||
{
|
||||
// Ensure that user input values take precedence over imported
|
||||
// values from sub-charts when passed by file
|
||||
name: "template with imported subchart values set with --set",
|
||||
cmd: fmt.Sprintf("template '%s' -f %s/extra_values.yaml", chartPath, chartPath),
|
||||
golden: "output/template-subchart-cm-set-file.txt",
|
||||
},
|
||||
}
|
||||
runTestCmd(t, tests)
|
||||
}
|
||||
|
|
|
|||
19
cmd/helm/testdata/helm home with space/helm/plugins/fullenv/completion.yaml
vendored
Normal file
19
cmd/helm/testdata/helm home with space/helm/plugins/fullenv/completion.yaml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
name: wrongname
|
||||
commands:
|
||||
- name: empty
|
||||
- name: full
|
||||
commands:
|
||||
- name: more
|
||||
validArgs:
|
||||
- one
|
||||
- two
|
||||
flags:
|
||||
- b
|
||||
- ball
|
||||
- name: less
|
||||
flags:
|
||||
- a
|
||||
- all
|
||||
flags:
|
||||
- z
|
||||
- q
|
||||
7
cmd/helm/testdata/helm home with space/helm/plugins/fullenv/fullenv.sh
vendored
Executable file
7
cmd/helm/testdata/helm home with space/helm/plugins/fullenv/fullenv.sh
vendored
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
echo $HELM_PLUGIN_NAME
|
||||
echo $HELM_PLUGIN_DIR
|
||||
echo $HELM_PLUGINS
|
||||
echo $HELM_REPOSITORY_CONFIG
|
||||
echo $HELM_REPOSITORY_CACHE
|
||||
echo $HELM_BIN
|
||||
4
cmd/helm/testdata/helm home with space/helm/plugins/fullenv/plugin.yaml
vendored
Normal file
4
cmd/helm/testdata/helm home with space/helm/plugins/fullenv/plugin.yaml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
name: fullenv
|
||||
usage: "show env vars"
|
||||
description: "show all env vars"
|
||||
command: "$HELM_PLUGIN_DIR/fullenv.sh"
|
||||
6
cmd/helm/testdata/helm home with space/helm/repositories.yaml
vendored
Normal file
6
cmd/helm/testdata/helm home with space/helm/repositories.yaml
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
apiVersion: v1
|
||||
generated: 2016-10-03T16:03:10.640376913-06:00
|
||||
repositories:
|
||||
- cache: testing-index.yaml
|
||||
name: testing
|
||||
url: http://example.com/charts
|
||||
3
cmd/helm/testdata/helm home with space/helm/repository/test-name-index.yaml
vendored
Normal file
3
cmd/helm/testdata/helm home with space/helm/repository/test-name-index.yaml
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
apiVersion: v1
|
||||
entries: {}
|
||||
generated: "2020-09-09T19:50:50.198347916-04:00"
|
||||
66
cmd/helm/testdata/helm home with space/helm/repository/testing-index.yaml
vendored
Normal file
66
cmd/helm/testdata/helm home with space/helm/repository/testing-index.yaml
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
apiVersion: v1
|
||||
entries:
|
||||
alpine:
|
||||
- name: alpine
|
||||
url: https://charts.helm.sh/stable/alpine-0.1.0.tgz
|
||||
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
|
||||
created: "2018-06-27T10:00:18.230700509Z"
|
||||
deprecated: true
|
||||
home: https://helm.sh/helm
|
||||
sources:
|
||||
- https://github.com/helm/helm
|
||||
version: 0.1.0
|
||||
appVersion: 1.2.3
|
||||
description: Deploy a basic Alpine Linux pod
|
||||
keywords: []
|
||||
maintainers: []
|
||||
icon: ""
|
||||
apiVersion: v2
|
||||
- name: alpine
|
||||
url: https://charts.helm.sh/stable/alpine-0.2.0.tgz
|
||||
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
|
||||
created: "2018-07-09T11:34:37.797864902Z"
|
||||
home: https://helm.sh/helm
|
||||
sources:
|
||||
- https://github.com/helm/helm
|
||||
version: 0.2.0
|
||||
appVersion: 2.3.4
|
||||
description: Deploy a basic Alpine Linux pod
|
||||
keywords: []
|
||||
maintainers: []
|
||||
icon: ""
|
||||
apiVersion: v2
|
||||
- name: alpine
|
||||
url: https://charts.helm.sh/stable/alpine-0.3.0-rc.1.tgz
|
||||
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
|
||||
created: "2020-11-12T08:44:58.872726222Z"
|
||||
home: https://helm.sh/helm
|
||||
sources:
|
||||
- https://github.com/helm/helm
|
||||
version: 0.3.0-rc.1
|
||||
appVersion: 3.0.0
|
||||
description: Deploy a basic Alpine Linux pod
|
||||
keywords: []
|
||||
maintainers: []
|
||||
icon: ""
|
||||
apiVersion: v2
|
||||
mariadb:
|
||||
- name: mariadb
|
||||
url: https://charts.helm.sh/stable/mariadb-0.3.0.tgz
|
||||
checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56
|
||||
created: "2018-04-23T08:20:27.160959131Z"
|
||||
home: https://mariadb.org
|
||||
sources:
|
||||
- https://github.com/bitnami/bitnami-docker-mariadb
|
||||
version: 0.3.0
|
||||
description: Chart for MariaDB
|
||||
keywords:
|
||||
- mariadb
|
||||
- mysql
|
||||
- database
|
||||
- sql
|
||||
maintainers:
|
||||
- name: Bitnami
|
||||
email: containers@bitnami.com
|
||||
icon: ""
|
||||
apiVersion: v2
|
||||
|
|
@ -1,2 +1,3 @@
|
|||
_activeHelp_ This command does not take any more arguments (but may accept flags).
|
||||
:4
|
||||
Completion ended with directive: ShellCompDirectiveNoFileComp
|
||||
|
|
|
|||
1
cmd/helm/testdata/output/env-comp.txt
vendored
1
cmd/helm/testdata/output/env-comp.txt
vendored
|
|
@ -15,6 +15,7 @@ HELM_KUBETOKEN
|
|||
HELM_MAX_HISTORY
|
||||
HELM_NAMESPACE
|
||||
HELM_PLUGINS
|
||||
HELM_QPS
|
||||
HELM_REGISTRY_CONFIG
|
||||
HELM_REPOSITORY_CACHE
|
||||
HELM_REPOSITORY_CONFIG
|
||||
|
|
|
|||
3
cmd/helm/testdata/output/get-metadata-args.txt
vendored
Normal file
3
cmd/helm/testdata/output/get-metadata-args.txt
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Error: "helm get metadata" requires 1 argument
|
||||
|
||||
Usage: helm get metadata RELEASE_NAME [flags]
|
||||
1
cmd/helm/testdata/output/get-metadata.json
vendored
Normal file
1
cmd/helm/testdata/output/get-metadata.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"name":"thomas-guide","chart":"foo","version":"0.1.0-beta.1","appVersion":"1.0","namespace":"default","revision":1,"status":"deployed","deployedAt":"1977-09-02T22:04:05Z"}
|
||||
8
cmd/helm/testdata/output/get-metadata.txt
vendored
Normal file
8
cmd/helm/testdata/output/get-metadata.txt
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
NAME: thomas-guide
|
||||
CHART: foo
|
||||
VERSION: 0.1.0-beta.1
|
||||
APP_VERSION: 1.0
|
||||
NAMESPACE: default
|
||||
REVISION: 1
|
||||
STATUS: deployed
|
||||
DEPLOYED_AT: 1977-09-02T22:04:05Z
|
||||
8
cmd/helm/testdata/output/get-metadata.yaml
vendored
Normal file
8
cmd/helm/testdata/output/get-metadata.yaml
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
appVersion: "1.0"
|
||||
chart: foo
|
||||
deployedAt: "1977-09-02T22:04:05Z"
|
||||
name: thomas-guide
|
||||
namespace: default
|
||||
revision: 1
|
||||
status: deployed
|
||||
version: 0.1.0-beta.1
|
||||
3
cmd/helm/testdata/output/get-release.txt
vendored
3
cmd/helm/testdata/output/get-release.txt
vendored
|
|
@ -3,6 +3,9 @@ LAST DEPLOYED: Fri Sep 2 22:04:05 1977
|
|||
NAMESPACE: default
|
||||
STATUS: deployed
|
||||
REVISION: 1
|
||||
CHART: foo
|
||||
VERSION: 0.1.0-beta.1
|
||||
APP_VERSION: 1.0
|
||||
TEST SUITE: None
|
||||
USER-SUPPLIED VALUES:
|
||||
name: value
|
||||
|
|
|
|||
20
cmd/helm/testdata/output/install-dry-run-with-secret-hidden.txt
vendored
Normal file
20
cmd/helm/testdata/output/install-dry-run-with-secret-hidden.txt
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
NAME: secrets
|
||||
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
|
||||
NAMESPACE: default
|
||||
STATUS: pending-install
|
||||
REVISION: 1
|
||||
TEST SUITE: None
|
||||
HOOKS:
|
||||
MANIFEST:
|
||||
---
|
||||
# Source: chart-with-secret/templates/secret.yaml
|
||||
# HIDDEN: The Secret output has been suppressed
|
||||
---
|
||||
# Source: chart-with-secret/templates/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-configmap
|
||||
data:
|
||||
foo: bar
|
||||
|
||||
25
cmd/helm/testdata/output/install-dry-run-with-secret.txt
vendored
Normal file
25
cmd/helm/testdata/output/install-dry-run-with-secret.txt
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
NAME: secrets
|
||||
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
|
||||
NAMESPACE: default
|
||||
STATUS: pending-install
|
||||
REVISION: 1
|
||||
TEST SUITE: None
|
||||
HOOKS:
|
||||
MANIFEST:
|
||||
---
|
||||
# Source: chart-with-secret/templates/secret.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: test-secret
|
||||
stringData:
|
||||
foo: bar
|
||||
---
|
||||
# Source: chart-with-secret/templates/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: test-configmap
|
||||
data:
|
||||
foo: bar
|
||||
|
||||
1
cmd/helm/testdata/output/install-hide-secret.txt
vendored
Normal file
1
cmd/helm/testdata/output/install-hide-secret.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
Error: INSTALLATION FAILED: Hiding Kubernetes secrets requires a dry-run mode
|
||||
32
cmd/helm/testdata/output/issue-9027.txt
vendored
Normal file
32
cmd/helm/testdata/output/issue-9027.txt
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# Source: issue-9027/charts/subchart/templates/values.yaml
|
||||
global:
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
---
|
||||
# Source: issue-9027/templates/values.yaml
|
||||
global:
|
||||
hash:
|
||||
key1: null
|
||||
key2: null
|
||||
key3: 13
|
||||
subchart:
|
||||
global:
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
hash:
|
||||
key3: 13
|
||||
key4: 4
|
||||
key5: 5
|
||||
key6: 6
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
==> Linting testdata/testcharts/chart-with-bad-subcharts
|
||||
[INFO] Chart.yaml: icon is recommended
|
||||
[WARNING] templates/: directory not found
|
||||
[ERROR] templates/: error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
|
||||
[ERROR] : unable to load chart
|
||||
error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
|
||||
|
||||
|
|
@ -9,12 +9,11 @@
|
|||
[ERROR] Chart.yaml: apiVersion is required. The value must be either "v1" or "v2"
|
||||
[ERROR] Chart.yaml: version is required
|
||||
[INFO] Chart.yaml: icon is recommended
|
||||
[WARNING] templates/: directory not found
|
||||
[ERROR] templates/: validation: chart.metadata.name is required
|
||||
[ERROR] : unable to load chart
|
||||
validation: chart.metadata.name is required
|
||||
|
||||
==> Linting testdata/testcharts/chart-with-bad-subcharts/charts/good-subchart
|
||||
[INFO] Chart.yaml: icon is recommended
|
||||
[WARNING] templates/: directory not found
|
||||
|
||||
Error: 3 chart(s) linted, 2 chart(s) failed
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
==> Linting testdata/testcharts/chart-with-bad-subcharts
|
||||
[INFO] Chart.yaml: icon is recommended
|
||||
[WARNING] templates/: directory not found
|
||||
[ERROR] templates/: error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
|
||||
[ERROR] : unable to load chart
|
||||
error unpacking bad-subchart in chart-with-bad-subcharts: validation: chart.metadata.name is required
|
||||
|
||||
|
|
|
|||
4
cmd/helm/testdata/output/lint-chart-with-deprecated-api-old-k8s.txt
vendored
Normal file
4
cmd/helm/testdata/output/lint-chart-with-deprecated-api-old-k8s.txt
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
==> Linting testdata/testcharts/chart-with-deprecated-api
|
||||
[INFO] Chart.yaml: icon is recommended
|
||||
|
||||
1 chart(s) linted, 0 chart(s) failed
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue