mirror of
https://github.com/helm/helm.git
synced 2026-05-28 04:35:48 -04:00
Merge branch 'helm:main' into fixDepUpPerformance
This commit is contained in:
commit
dc761caf00
344 changed files with 9178 additions and 2442 deletions
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
set -euo pipefail
|
||||
|
||||
curl -sSL https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz | tar xz
|
||||
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
|
||||
|
|
@ -1,43 +1,14 @@
|
|||
---
|
||||
|
||||
# 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:
|
||||
working_directory: ~/helm.sh/helm
|
||||
docker:
|
||||
- image: cimg/go:1.18
|
||||
|
||||
auth:
|
||||
username: $DOCKER_USER
|
||||
password: $DOCKER_PASS
|
||||
|
||||
environment:
|
||||
GOCACHE: "/tmp/go/cache"
|
||||
GOLANGCI_LINT_VERSION: "1.46.2"
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: install test dependencies
|
||||
command: .circleci/bootstrap.sh
|
||||
- run:
|
||||
name: test style
|
||||
command: make test-style
|
||||
- run:
|
||||
name: test
|
||||
command: make test-coverage
|
||||
- run:
|
||||
name: test build
|
||||
command: make
|
||||
- deploy:
|
||||
name: deploy
|
||||
command: .circleci/deploy.sh
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build:
|
||||
jobs:
|
||||
- build:
|
||||
filters:
|
||||
tags:
|
||||
only: /.*/
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# 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.
|
||||
set -euo pipefail
|
||||
|
||||
# Skip on pull request builds
|
||||
if [[ -n "${CIRCLE_PR_NUMBER:-}" ]]; then
|
||||
exit
|
||||
fi
|
||||
|
||||
: ${AZURE_STORAGE_CONNECTION_STRING:?"AZURE_STORAGE_CONNECTION_STRING environment variable is not set"}
|
||||
: ${AZURE_STORAGE_CONTAINER_NAME:?"AZURE_STORAGE_CONTAINER_NAME environment variable is not set"}
|
||||
|
||||
VERSION=
|
||||
if [[ -n "${CIRCLE_TAG:-}" ]]; then
|
||||
VERSION="${CIRCLE_TAG}"
|
||||
elif [[ "${CIRCLE_BRANCH:-}" == "main" ]]; then
|
||||
VERSION="canary"
|
||||
else
|
||||
echo "Skipping deploy step; this is neither a releasable branch or a tag"
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "Installing Azure CLI"
|
||||
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ jammy main" | sudo tee /etc/apt/sources.list.d/azure-cli.list
|
||||
curl -L https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add
|
||||
sudo apt install apt-transport-https
|
||||
sudo apt update
|
||||
sudo apt install azure-cli
|
||||
|
||||
|
||||
echo "Building helm binaries"
|
||||
make build-cross
|
||||
make dist checksum VERSION="${VERSION}"
|
||||
|
||||
echo "Pushing binaries to Azure"
|
||||
if [[ "${VERSION}" == "canary" ]]; then
|
||||
az storage blob upload-batch -s _dist/ -d "$AZURE_STORAGE_CONTAINER_NAME" --pattern 'helm-*' --connection-string "$AZURE_STORAGE_CONNECTION_STRING" --overwrite
|
||||
else
|
||||
az storage blob upload-batch -s _dist/ -d "$AZURE_STORAGE_CONTAINER_NAME" --pattern 'helm-*' --connection-string "$AZURE_STORAGE_CONNECTION_STRING"
|
||||
fi
|
||||
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"
|
||||
|
|
|
|||
29
.github/workflows/build-pr.yml
vendored
29
.github/workflows/build-pr.yml
vendored
|
|
@ -1,29 +0,0 @@
|
|||
name: build-pr
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.18'
|
||||
- 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.46.2'
|
||||
GOLANGCI_LINT_SHA256: '242cd4f2d6ac0556e315192e8555784d13da5d1874e51304711570769c4f2b9b'
|
||||
- name: Test style
|
||||
run: make test-style
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
26
.github/workflows/build-test.yml
vendored
Normal file
26
.github/workflows/build-test.yml
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
name: build-test
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release-**'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # pin@5.0.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
- name: Test source headers are present
|
||||
run: make test-source-headers
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
- name: Test build
|
||||
run: make 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@v2
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # pinv3.24.10
|
||||
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@v1
|
||||
uses: github/codeql-action/autobuild@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # pinv3.24.10
|
||||
|
||||
# ℹ️ 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@v1
|
||||
uses: github/codeql-action/analyze@4355270be187e1b672a7a1c7c7bae5afdc1ab94a # pinv3.24.10
|
||||
|
|
|
|||
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@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # pin@5.0.0
|
||||
with:
|
||||
go-version: "1.21"
|
||||
- name: golangci-lint
|
||||
uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 #pin@4.0.0
|
||||
with:
|
||||
version: v1.55
|
||||
102
.github/workflows/release.yml
vendored
Normal file
102
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
name: release
|
||||
on:
|
||||
create:
|
||||
tags:
|
||||
- v*
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
# Note the only differences between release and canary-release jobs are:
|
||||
# - only canary passes --overwrite flag
|
||||
# - the VERSION make variable passed to 'make dist checksum' is expected to
|
||||
# be "canary" if the job is triggered by a push to "main" branch. If the
|
||||
# job is triggered by a tag push, VERSION should be the tag ref.
|
||||
jobs:
|
||||
release:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # pin@5.0.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
|
||||
- name: Build Helm Binaries
|
||||
run: |
|
||||
set -eu -o pipefail
|
||||
|
||||
make build-cross
|
||||
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:
|
||||
AZURE_STORAGE_CONNECTION_STRING: "${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}"
|
||||
AZURE_STORAGE_CONTAINER_NAME: "${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}"
|
||||
with:
|
||||
source_dir: _dist
|
||||
container_name: ${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}
|
||||
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@b4ffde65f46336ab88eb53be808477a3936bae11 # pin@v4.1.1
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # pin@5.0.0
|
||||
with:
|
||||
go-version: '1.21'
|
||||
|
||||
- name: Run unit tests
|
||||
run: make test-coverage
|
||||
|
||||
- name: Build Helm Binaries
|
||||
run: |
|
||||
make build-cross
|
||||
make dist checksum VERSION="canary"
|
||||
|
||||
- name: Upload Binaries
|
||||
uses: bacongobbler/azure-blob-storage-upload@50f7d898b7697e864130ea04c303ca38b5751c50 # pin@3.0.0
|
||||
with:
|
||||
source_dir: _dist
|
||||
container_name: ${{ secrets.AZURE_STORAGE_CONTAINER_NAME }}
|
||||
connection_string: ${{ secrets.AZURE_STORAGE_CONNECTION_STRING }}
|
||||
extra_args: '--pattern helm-*'
|
||||
# WARNING: this will overwrite existing blobs in your blob storage
|
||||
overwrite: 'true'
|
||||
2
.github/workflows/stale-issue-bot.yaml
vendored
2
.github/workflows/stale-issue-bot.yaml
vendored
|
|
@ -10,7 +10,7 @@ jobs:
|
|||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.'
|
||||
exempt-issue-labels: 'keep open,v4.x'
|
||||
exempt-issue-labels: 'keep open,v4.x,in progress'
|
||||
days-before-stale: 90
|
||||
days-before-close: 30
|
||||
operations-per-run: 100
|
||||
|
|
|
|||
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
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ run:
|
|||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- deadcode
|
||||
- dupl
|
||||
- gofmt
|
||||
- goimports
|
||||
|
|
@ -15,7 +14,6 @@ linters:
|
|||
- nakedret
|
||||
- revive
|
||||
- unused
|
||||
- varcheck
|
||||
- staticcheck
|
||||
|
||||
linters-settings:
|
||||
|
|
|
|||
|
|
@ -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/)):
|
||||
|
|
@ -195,7 +195,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
|
||||
|
|
|
|||
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
|
||||
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
|
||||
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)
|
||||
|
|
|
|||
10
OWNERS
10
OWNERS
|
|
@ -1,19 +1,20 @@
|
|||
maintainers:
|
||||
- adamreese
|
||||
- bacongobbler
|
||||
- hickeyma
|
||||
- joejulian
|
||||
- jdolitsky
|
||||
- marckhouzam
|
||||
- mattfarina
|
||||
- sabre1041
|
||||
- scottrigby
|
||||
- SlickNik
|
||||
- technosophos
|
||||
triage:
|
||||
- joejulian
|
||||
- yxxhero
|
||||
- zonggen
|
||||
- gjenkins8
|
||||
- z4ce
|
||||
emeritus:
|
||||
- adamreese
|
||||
- bacongobbler
|
||||
- fibonacci1729
|
||||
- jascott1
|
||||
- michelleN
|
||||
|
|
@ -22,6 +23,7 @@ emeritus:
|
|||
- prydonius
|
||||
- rimusz
|
||||
- seh
|
||||
- SlickNik
|
||||
- thomastaylor312
|
||||
- vaikas-google
|
||||
- viglesiasce
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Helm
|
||||
|
||||
[](https://circleci.com/gh/helm/helm)
|
||||
[](https://github.com/helm/helm/actions?workflow=release)
|
||||
[](https://goreportcard.com/report/github.com/helm/helm)
|
||||
[](https://pkg.go.dev/helm.sh/helm/v3)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/3131)
|
||||
|
|
@ -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).
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Long: bashCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionBash(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -115,7 +115,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Long: zshCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionZsh(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Long: fishCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionFish(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
|
|||
Long: powershellCompDesc,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
return runCompletionPowershell(out, cmd)
|
||||
},
|
||||
}
|
||||
|
|
@ -210,6 +210,6 @@ func runCompletionPowershell(out io.Writer, cmd *cobra.Command) error {
|
|||
}
|
||||
|
||||
// Function to disable file completion
|
||||
func noCompletions(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func noCompletions(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ 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
|
||||
|
|
@ -73,7 +73,7 @@ func newCreateCmd(out io.Writer) *cobra.Command {
|
|||
// No more completions, so disable file completion
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
|
@ -31,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
|
||||
|
|
@ -62,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)
|
||||
|
|
@ -77,7 +76,7 @@ func TestCreateStarterCmd(t *testing.T) {
|
|||
t.Logf("Created %s", dest)
|
||||
}
|
||||
tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl")
|
||||
if err := ioutil.WriteFile(tplpath, []byte("test"), 0644); err != nil {
|
||||
if err := os.WriteFile(tplpath, []byte("test"), 0644); err != nil {
|
||||
t.Fatalf("Could not write template: %s", err)
|
||||
}
|
||||
|
||||
|
|
@ -128,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.
|
||||
|
|
@ -140,7 +139,7 @@ func TestCreateStarterAbsoluteCmd(t *testing.T) {
|
|||
t.Logf("Created %s", dest)
|
||||
}
|
||||
tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl")
|
||||
if err := ioutil.WriteFile(tplpath, []byte("test"), 0644); err != nil {
|
||||
if err := os.WriteFile(tplpath, []byte("test"), 0644); err != nil {
|
||||
t.Fatalf("Could not write template: %s", err)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -206,6 +206,61 @@ func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ func newDocsCmd(out io.Writer) *cobra.Command {
|
|||
Hidden: true,
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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,7 +36,7 @@ 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
|
||||
|
|
@ -44,7 +44,7 @@ func newEnvCmd(out io.Writer) *cobra.Command {
|
|||
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
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 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,13 +59,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})
|
||||
},
|
||||
}
|
||||
|
||||
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 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 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 nil, cobra.ShellCompDirectiveNoFileComp
|
||||
}
|
||||
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 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 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])
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package main // import "helm.sh/helm/v3/cmd/helm"
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
|
@ -106,10 +106,10 @@ func loadReleasesInMemory(actionConfig *action.Configuration) {
|
|||
return
|
||||
}
|
||||
|
||||
actionConfig.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}
|
||||
actionConfig.KubeClient = &kubefake.PrintingKubeClient{Out: io.Discard}
|
||||
|
||||
for _, path := range filePaths {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatal("Unable to read memory driver data", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
|
@ -92,9 +92,9 @@ func executeActionCommandStdinC(store *storage.Storage, in *os.File, cmd string)
|
|||
|
||||
actionConfig := &action.Configuration{
|
||||
Releases: store,
|
||||
KubeClient: &kubefake.PrintingKubeClient{Out: ioutil.Discard},
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -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,20 +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, 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})
|
||||
},
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
|
|
@ -154,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")
|
||||
|
|
@ -170,10 +197,12 @@ 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.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")
|
||||
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
|
||||
|
|
@ -245,6 +274,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
|
||||
|
|
@ -261,6 +291,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)
|
||||
|
|
@ -301,3 +336,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)
|
||||
|
|
@ -252,6 +252,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,7 @@ 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.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
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
Aliases: []string{"ls"},
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
|
@ -129,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
|
||||
|
|
@ -301,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)
|
||||
|
|
@ -311,7 +311,7 @@ func addPluginCommands(plugin *plugin.Plugin, baseCmd *cobra.Command, cmds *plug
|
|||
// loadFile takes a yaml file at the given path, parses it and returns a pluginCommand object
|
||||
func loadFile(path string) (*pluginCommand, error) {
|
||||
cmds := new(pluginCommand)
|
||||
b, err := ioutil.ReadFile(path)
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return cmds, fmt.Errorf("file (%s) not provided by plugin. No plugin auto-completion possible", path)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
|
|
@ -56,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")
|
||||
}
|
||||
|
|
@ -87,7 +86,7 @@ func newPackageCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
|
||||
if client.DependencyUpdate {
|
||||
downloadManager := &downloader.Manager{
|
||||
Out: ioutil.Discard,
|
||||
Out: io.Discard,
|
||||
ChartPath: path,
|
||||
Keyring: client.Keyring,
|
||||
Getters: p,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command {
|
|||
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
|
||||
|
|
@ -52,10 +52,10 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command {
|
|||
// No more completion once the plugin path has been specified
|
||||
return nil, 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)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
|
|||
Aliases: []string{"ls"},
|
||||
Short: "list installed Helm plugins",
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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,19 +51,26 @@ 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, client.PlainHTTP)
|
||||
if err != nil {
|
||||
return fmt.Errorf("missing registry client: %w", err)
|
||||
}
|
||||
client.SetRegistryClient(registryClient)
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
output, err := client.Run(args[i])
|
||||
if err != nil {
|
||||
|
|
@ -83,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)
|
||||
|
|
|
|||
|
|
@ -34,15 +34,23 @@ If the chart has an associated provenance file,
|
|||
it will also be uploaded.
|
||||
`
|
||||
|
||||
type registryPushOptions struct {
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecureSkipTLSverify bool
|
||||
plainHTTP bool
|
||||
}
|
||||
|
||||
func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
client := action.NewPushWithOpts(action.WithPushConfig(cfg))
|
||||
o := ®istryPushOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "push [chart] [remote]",
|
||||
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
|
||||
|
|
@ -59,9 +67,19 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
}
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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)
|
||||
}
|
||||
cfg.RegistryClient = registryClient
|
||||
chartRef := args[0]
|
||||
remote := args[1]
|
||||
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)
|
||||
if err != nil {
|
||||
|
|
@ -72,5 +90,12 @@ func newPushCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file")
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
|
|
@ -36,9 +35,18 @@ const registryLoginDesc = `
|
|||
Authenticate to a remote registry.
|
||||
`
|
||||
|
||||
type registryLoginOptions struct {
|
||||
username string
|
||||
password string
|
||||
passwordFromStdinOpt bool
|
||||
certFile string
|
||||
keyFile string
|
||||
caFile string
|
||||
insecure bool
|
||||
}
|
||||
|
||||
func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
||||
var usernameOpt, passwordOpt string
|
||||
var passwordFromStdinOpt, insecureOpt bool
|
||||
o := ®istryLoginOptions{}
|
||||
|
||||
cmd := &cobra.Command{
|
||||
Use: "login [host]",
|
||||
|
|
@ -46,23 +54,30 @@ func newRegistryLoginCmd(cfg *action.Configuration, out io.Writer) *cobra.Comman
|
|||
Long: registryLoginDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
hostname := args[0]
|
||||
|
||||
username, password, err := getUsernamePassword(usernameOpt, passwordOpt, passwordFromStdinOpt)
|
||||
username, password, err := getUsernamePassword(o.username, o.password, o.passwordFromStdinOpt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return action.NewRegistryLogin(cfg).Run(out, hostname, username, password, insecureOpt)
|
||||
return action.NewRegistryLogin(cfg).Run(out, hostname, username, password,
|
||||
action.WithCertFile(o.certFile),
|
||||
action.WithKeyFile(o.keyFile),
|
||||
action.WithCAFile(o.caFile),
|
||||
action.WithInsecure(o.insecure))
|
||||
},
|
||||
}
|
||||
|
||||
f := cmd.Flags()
|
||||
f.StringVarP(&usernameOpt, "username", "u", "", "registry username")
|
||||
f.StringVarP(&passwordOpt, "password", "p", "", "registry password or identity token")
|
||||
f.BoolVarP(&passwordFromStdinOpt, "password-stdin", "", false, "read password or identity token from stdin")
|
||||
f.BoolVarP(&insecureOpt, "insecure", "", false, "allow connections to TLS registry without certs")
|
||||
f.StringVarP(&o.username, "username", "u", "", "registry username")
|
||||
f.StringVarP(&o.password, "password", "p", "", "registry password or identity token")
|
||||
f.BoolVarP(&o.passwordFromStdinOpt, "password-stdin", "", false, "read password or identity token from stdin")
|
||||
f.BoolVarP(&o.insecure, "insecure", "", false, "allow connections to TLS registry without certs")
|
||||
f.StringVar(&o.certFile, "cert-file", "", "identify registry client using this SSL certificate file")
|
||||
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")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
|
@ -74,7 +89,7 @@ func getUsernamePassword(usernameOpt string, passwordOpt string, passwordFromStd
|
|||
password := passwordOpt
|
||||
|
||||
if passwordFromStdinOpt {
|
||||
passwordFromStdin, err := ioutil.ReadAll(os.Stdin)
|
||||
passwordFromStdin, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ func newRegistryLogoutCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma
|
|||
Long: registryLogoutDesc,
|
||||
Args: require.MinimumNArgs(1),
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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 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}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -73,7 +72,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
|
|||
Short: "add a chart repository",
|
||||
Args: require.ExactArgs(2),
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
o.name = args[0]
|
||||
o.url = args[1]
|
||||
o.repoFile = settings.RepositoryConfig
|
||||
|
|
@ -134,7 +133,7 @@ func (o *repoAddOptions) run(out io.Writer) error {
|
|||
return err
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadFile(o.repoFile)
|
||||
b, err := os.ReadFile(o.repoFile)
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
|
|
@ -213,7 +212,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)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -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"
|
||||
|
|
@ -102,7 +101,7 @@ func TestRepoAdd(t *testing.T) {
|
|||
}
|
||||
os.Setenv(xdg.CacheHomeEnvVar, rootDir)
|
||||
|
||||
if err := o.run(ioutil.Discard); err != nil {
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
|
|
@ -126,11 +125,11 @@ func TestRepoAdd(t *testing.T) {
|
|||
|
||||
o.forceUpdate = true
|
||||
|
||||
if err := o.run(ioutil.Discard); err != nil {
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
t.Errorf("Repository was not updated: %s", err)
|
||||
}
|
||||
|
||||
if err := o.run(ioutil.Discard); err != nil {
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
t.Errorf("Duplicate repository name was added")
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
@ -159,7 +158,7 @@ func TestRepoAddCheckLegalName(t *testing.T) {
|
|||
|
||||
wantErrorMsg := fmt.Sprintf("repository name (%s) contains '/', please specify a different name without '/'", testRepoName)
|
||||
|
||||
if err := o.run(ioutil.Discard); err != nil {
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
if wantErrorMsg != err.Error() {
|
||||
t.Fatalf("Actual error %s, not equal to expected error %s", err, wantErrorMsg)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
@ -211,14 +210,14 @@ func repoAddConcurrent(t *testing.T, testName, repoFile string) {
|
|||
forceUpdate: false,
|
||||
repoFile: repoFile,
|
||||
}
|
||||
if err := o.run(ioutil.Discard); err != nil {
|
||||
if err := o.run(io.Discard); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}(fmt.Sprintf("%s-%d", testName, i))
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
b, err := ioutil.ReadFile(repoFile)
|
||||
b, err := os.ReadFile(repoFile)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
|
@ -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,7 +54,7 @@ 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
|
||||
|
|
@ -61,7 +62,7 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
|
|||
// No more completions, so disable file completion
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
|
|||
Short: "list chart repositories",
|
||||
Args: require.NoArgs,
|
||||
ValidArgsFunction: noCompletions,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -35,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)
|
||||
}
|
||||
|
|
@ -60,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)
|
||||
}
|
||||
|
|
@ -86,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)
|
||||
}
|
||||
|
|
@ -103,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 {
|
||||
|
|
@ -119,7 +117,7 @@ func TestUpdateCustomCacheCmd(t *testing.T) {
|
|||
repoFile: filepath.Join(ts.Root(), "repositories.yaml"),
|
||||
repoCache: cachePath,
|
||||
}
|
||||
b := ioutil.Discard
|
||||
b := io.Discard
|
||||
if err := o.run(b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -130,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 {
|
||||
|
|
@ -165,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 {
|
||||
|
|
@ -198,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 {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ package require
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -71,7 +71,7 @@ func runTestCases(t *testing.T, testCases []testCase) {
|
|||
Args: tc.validateFunc,
|
||||
}
|
||||
cmd.SetArgs(tc.args)
|
||||
cmd.SetOutput(ioutil.Discard)
|
||||
cmd.SetOutput(io.Discard)
|
||||
|
||||
err := cmd.Execute()
|
||||
if tc.wantError == "" {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
|
||||
return nil, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
107
cmd/helm/root.go
107
cmd/helm/root.go
|
|
@ -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,12 +153,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
|
|||
flags.ParseErrorsWhitelist.UnknownFlags = true
|
||||
flags.Parse(args)
|
||||
|
||||
registryClient, err := registry.NewClient(
|
||||
registry.ClientOptDebug(settings.Debug),
|
||||
registry.ClientOptEnableCache(true),
|
||||
registry.ClientOptWriter(os.Stderr),
|
||||
registry.ClientOptCredentialsFile(settings.RegistryConfig),
|
||||
)
|
||||
registryClient, err := newDefaultRegistryClient(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -261,3 +257,48 @@ func checkForExpiredRepos(repofile string) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
registryClient, err := newDefaultRegistryClient(plainHTTP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
||||
func newRegistryClientWithTLS(certFile, keyFile, caFile string, insecureSkipTLSverify bool) (*registry.Client, error) {
|
||||
// Create a new registry client
|
||||
registryClient, err := registry.NewRegistryClientWithTLS(os.Stderr, certFile, keyFile, caFile, insecureSkipTLSverify,
|
||||
settings.RegistryConfig, settings.Debug,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registryClient, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*Package search provides client-side repository searching.
|
||||
/*
|
||||
Package search provides client-side repository searching.
|
||||
|
||||
This supports building an in-memory search index based on the contents of
|
||||
multiple repositories, and then using string matching or regular expressions
|
||||
|
|
@ -146,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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
|
@ -64,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 {
|
||||
|
|
@ -81,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)
|
||||
|
|
@ -94,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
|
||||
|
|
@ -124,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() {
|
||||
|
|
@ -205,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)
|
||||
|
|
@ -235,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))
|
||||
|
||||
|
|
@ -259,7 +272,7 @@ func compListChartsOfRepo(repoName string, prefix string) []string {
|
|||
var charts []string
|
||||
|
||||
path := filepath.Join(settings.RepositoryCache, helmpath.CacheChartsFile(repoName))
|
||||
content, err := ioutil.ReadFile(path)
|
||||
content, err := os.ReadFile(path)
|
||||
if err == nil {
|
||||
scanner := bufio.NewScanner(bytes.NewReader(content))
|
||||
for scanner.Scan() {
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ func newShowCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
@ -82,8 +82,12 @@ 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 {
|
||||
return err
|
||||
}
|
||||
output, err := runShow(args, client)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -99,8 +103,12 @@ 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 {
|
||||
return err
|
||||
}
|
||||
output, err := runShow(args, client)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -116,8 +124,12 @@ 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 {
|
||||
return err
|
||||
}
|
||||
output, err := runShow(args, client)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -133,8 +145,12 @@ 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 {
|
||||
return err
|
||||
}
|
||||
output, err := runShow(args, client)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -150,8 +166,12 @@ 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 {
|
||||
return err
|
||||
}
|
||||
output, err := runShow(args, client)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -179,7 +199,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
|
||||
}
|
||||
|
|
@ -204,3 +224,13 @@ func runShow(args []string, client *action.Show) (string, error) {
|
|||
}
|
||||
return client.Run(cp)
|
||||
}
|
||||
|
||||
func addRegistryClient(client *action.Show) error {
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,13 +58,20 @@ 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 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
|
||||
// returned. This mirrors the handling in kubectl.
|
||||
if outfmt == output.Table {
|
||||
client.ShowResourcesTable = true
|
||||
}
|
||||
rel, err := client.Run(args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -73,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})
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -81,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])
|
||||
}
|
||||
|
|
@ -105,6 +112,7 @@ type statusPrinter struct {
|
|||
debug bool
|
||||
showDescription bool
|
||||
showResources bool
|
||||
showMetadata bool
|
||||
}
|
||||
|
||||
func (s statusPrinter) WriteJSON(out io.Writer) error {
|
||||
|
|
@ -119,15 +127,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 {
|
||||
|
|
@ -142,31 +155,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)),
|
||||
|
|
@ -176,38 +189,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 {
|
||||
fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(s.release.Info.Notes))
|
||||
_, _ = 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,6 +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, 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
|
||||
|
|
@ -106,11 +119,15 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
|
|||
if client.UseReleaseName {
|
||||
newDir = filepath.Join(client.OutputDir, client.ReleaseName)
|
||||
}
|
||||
_, err := os.Stat(filepath.Join(newDir, m.Path))
|
||||
if err == nil {
|
||||
fileWritten[m.Path] = true
|
||||
}
|
||||
|
||||
err = writeToFile(newDir, m.Path, m.Manifest, fileWritten[m.Path])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileWritten[m.Path] = true
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
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
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue