Merge branch 'master' into feat-add-hide-secrets-flag

pull/9130/head
Szymon Gibała 5 years ago
commit f64e968403

@ -5,7 +5,7 @@ jobs:
build: build:
working_directory: ~/helm.sh/helm working_directory: ~/helm.sh/helm
docker: docker:
- image: circleci/golang:1.15 - image: circleci/golang:1.16
auth: auth:
username: $DOCKER_USER username: $DOCKER_USER
@ -13,7 +13,7 @@ jobs:
environment: environment:
GOCACHE: "/tmp/go/cache" GOCACHE: "/tmp/go/cache"
GOLANGCI_LINT_VERSION: "1.27.0" GOLANGCI_LINT_VERSION: "1.36.0"
steps: steps:
- checkout - checkout

1
.gitignore vendored

@ -1,4 +1,5 @@
*.exe *.exe
*.swp
.DS_Store .DS_Store
.coverage/ .coverage/
.idea/ .idea/

@ -191,7 +191,9 @@ below.
issue to a milestone until the questions are answered. issue to a milestone until the questions are answered.
- We attempt to do this process at least once per work day. - We attempt to do this process at least once per work day.
3. Discussion 3. Discussion
- issues that are labeled as `feature` or `bug` should be connected to the PR that resolves it. - Issues that are labeled `feature` or `proposal` must write a Helm Improvement Proposal (HIP).
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 - 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 themself or make a comment in the issue saying
that they are taking it. that they are taking it.
@ -200,9 +202,30 @@ below.
and reduce noise. Should the issue need to stay open, the `keep open` label can be added. and reduce noise. Should the issue need to stay open, the `keep open` label can be added.
4. Issue closure 4. Issue closure
## Proposing an Idea
Before proposing a new idea to the Helm project, please make sure to write up a [Helm Improvement
Proposal](https://github.com/helm/community/tree/master/hips). A Helm Improvement Proposal is a
design document that describes a new feature for the Helm project. The proposal should provide a
concise technical specification and rationale for the feature.
It is also worth considering vetting your idea with the community via the
[cncf-helm](mailto:cncf-helm@lists.cncf.io) mailing list. Vetting an idea publicly before going as
far as writing a proposal is meant to save the potential author time. Many ideas have been proposed;
it's quite likely there are others in the community who may be working on a similar proposal, or a
similar proposal may have already been written.
HIPs are submitted to the [helm/community repository](https://github.com/helm/community). [HIP
1](https://github.com/helm/community/blob/master/hips/hip-0001.md) describes the process to write a
HIP as well as the review process.
After your proposal has been approved, follow the [developer's
guide](https://helm.sh/docs/community/developers/) to get started.
## How to Contribute a Patch ## How to Contribute a Patch
1. Identify or create the related issue. 1. Identify or create the related issue. If you're proposing a larger change to
Helm, see [Proposing an Idea](#proposing-an-idea).
2. Fork the desired repo; develop and test your code changes. 2. Fork the desired repo; develop and test your code changes.
3. Submit a pull request, making sure to sign your work and link the related issue. 3. Submit a pull request, making sure to sign your work and link the related issue.
@ -236,7 +259,7 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
maintainers before it can be merged. Those with `size/XS` are per the judgement of the maintainers before it can be merged. Those with `size/XS` are per the judgement of the
maintainers. For more detail see the [Size Labels](#size-labels) section. maintainers. For more detail see the [Size Labels](#size-labels) section.
4. Reviewing/Discussion 4. Reviewing/Discussion
- All reviews will be completed using Github review tool. - All reviews will be completed using GitHub review tool.
- A "Comment" review should be used when there are questions about the code that should be - A "Comment" review should be used when there are questions about the code that should be
answered, but that don't involve code changes. This type of review does not count as approval. answered, but that don't involve code changes. This type of review does not count as approval.
- A "Changes Requested" review indicates that changes to the code need to be made before they - A "Changes Requested" review indicates that changes to the code need to be made before they

88
KEYS

@ -852,3 +852,91 @@ ANQIfZg7P8oNxVDAX+jIsTDxjh8r+S1wsUQcTNop6JMicDbxrBRB13vYIY0Jg4+Z
hS0eN8yqaR533ire0Ur5Vif6+z4A0ifVTZ2hY96B hS0eN8yqaR533ire0Ur5Vif6+z4A0ifVTZ2hY96B
=nEJu =nEJu
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2021-03-12 [SC] [expires: 2037-03-08]
4AB45F1CB0D292975C6371436E2A23D806B6E6DD
uid [ unknown] Matt Butcher <technosophos@gmail.com>
sig 3 6E2A23D806B6E6DD 2021-03-12 Matt Butcher <technosophos@gmail.com>
uid [ unknown] Matt Butcher <matt.butcher@microsoft.com>
sig 3 6E2A23D806B6E6DD 2021-03-12 Matt Butcher <technosophos@gmail.com>
uid [ unknown] Matt Butcher <mabutch@microsoft.com>
sig 3 6E2A23D806B6E6DD 2021-03-12 Matt Butcher <technosophos@gmail.com>
sub rsa4096 2021-03-12 [E] [expires: 2037-03-08]
sig 6E2A23D806B6E6DD 2021-03-12 Matt Butcher <technosophos@gmail.com>
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
mQINBGBLvGsBEADHfZXD7feUfyNQoCwmDYCmygvIGKJxGkgiyxecbGieggOGVbNy
1N0F2w/HHHW7uanlCsrB/wKnSmkNxkp5m1vfcmg+AorjshBJZCjvNZAX78yOGOZk
7UQivwPhRWvJ8fnzwTd7ls7bz7mggPT0wVuBsrHtr6mfioxxmVq5ChTHKER7uFRL
23bd11x6hurfURgDuYPrCaLyrvHmQs7CCe2pxJVLFH4kXyzNoea4jZEbOPGNLXB/
war4QJaXtk9rLqEQ6fp0iM/s7N61eEcrj18HDLj9CTUB66UMTlDKUZUV+36502Ae
I6lrrFSx8KUvK9fcpdcxXKYoaY5t6BIBUS2JK8fCrTgyBdTPQ1J7z5N4GvwYonf6
FBsQpC2aY7wBAqFEbZ8xhdB/A6gY17542OSDhcto3ovdrbLkPaPKHUDz9WRDdR1U
VKAkNeqaf6h00cyEjM/IN8+Ni+Bwz1hUrwN/9qcKkhsaJK+D2z/f+Fq08+8wHm7A
rf/azwtiTT21S/Qwmg+ISkmHJiUueuL9IIIJv0tsgxZ6MsYF9tP2NxjBcmtketTE
h/oygKhFDiK8ybSRftCatEzJuf53cfe4fNIJpacUbD/QM8tGgwrXOpAz26Flm8Ki
drw6re2mvxnDKOua7dyukq+JHR5SBEzKv8WmaNEgzEDxPdaMa6+7mLcVOQARAQAB
tCVNYXR0IEJ1dGNoZXIgPHRlY2hub3NvcGhvc0BnbWFpbC5jb20+iQI4BBMBCAAs
BQJgS7xrCRBuKiPYBrbm3QIbAwUJHhM4AAIZAQQLBwkDBRUICgIDBBYAAQIAAMZ7
D/42lpQArXi7unDfG1K5dksGWv50S8dPy93APKZkxSqmO/LxMxOSUUq6N5NSh5FO
WV3o9Za0u0IfKN+cje4ldkRGaxAEmoPLRaB26lztv9AzkaBUh6c4q/MsUiuExJMN
l9P7los6B8kCtxddq3TjTXf1FVPxT3U6Orprmh9BNsIdw/N9K0teUJjEBl5ui7i9
WqVvbbTy3I34ae2tCdN98iwHVpkfm/VYuvqtKcgzv99FcasvAWLPr+z9fG5iOx54
WthG2UCXf4k75W8Ddd5TD8n/3JaVZX8UUq7EiURRD2fFtqMce4PCDYia2MZybjio
qJOvxMGOr981JMI5uN+2gVKe+A2p9s9ittvHtnHQxVWd1O+CGFQg87+js+0BB4hi
WcYGdDPh6GhpYx38In3tBHxzIfCitvKMOvovFpV1j1kYaMCENrlaO2C2DWHALCX7
unpvrSb3gNnCFzB86+PJkwOSRcWxERdGY8soZacTDoTqUrwCraR4/KgZk6JK8jKH
t3w/a9igvwmzZuUrolAiv41zywDupl/wYOA6uUmvi8GxWCGZ5sHRuLGxm+Tk2QyA
QA6seNaun7OE4gvrTtuA/2AYAy/NVqdVdjHN4oOIFPnsoRfW+ltvWsQ2fBsyG0mW
A0JT7aicKCa8aZZ6ZQtP4zbKMYxJW4n042hiYcgrdCdumLQpTWF0dCBCdXRjaGVy
IDxtYXR0LmJ1dGNoZXJAbWljcm9zb2Z0LmNvbT6JAjUEEwEIACkFAmBLvGsJEG4q
I9gGtubdAhsDBQkeEzgABAsHCQMFFQgKAgMEFgABAgAAWkAP/0KjQDI1HyFIT5GG
j0yufkcmRZrsXSy57eUpfL1RY1OGqTnB/dS4DL6OJX1GaXOlfj3lwjiDl2Y1pHAk
oncv6n5AAXWfvWxkDJzxqyo8A6FhS+fOgoXaKBPAH5/1CgilNzABNIlRmHwJ4uAw
TFP8v20Ug6gqaW9lSH2PXtZKKf+gH6lBB4YwNnzehnIteX30PWhhZ1SUib0jJCoc
6H156wo7G6INzZepg+hqI1ly/XYg/XzL7qRvIREtALOs/7qU04+x1ny4Ys6G1ZAP
hI0sxfcy+qbSqzb5+7oYg/UwrbwIhs81HaTyQLa4FOYKGPyg1GkeJpzo9EENRgoy
u1Dmd/7S/Zbszj4kakF7INMByolvbHvl3FMLAILj6DwFxakI5kd1V9XemYPSRoLA
wzeUlzYHrK5tD1Q+EdmTGBpmVghFuN0ov/jja9tInF/ZXra4GdeCdksatbkUHP5p
xb8BCGmJQtJJ0ncxdn3zwJSl+5qFtdaTmMrc9p20QYiwKuMupHL6+hkdhwncbRux
S8x0dUm4Fn5EnEcejRiLu6Xs6cmUURZyWXEkcUW2i3+cvj+1dkp/HPkStWrBceyb
VarypHX5BhBGThdWiDT/Gl6W7uycFGm8kEUF9bGgSvly1clwRskj0cc6IZnSXmNq
/+efhKkDyQC3krStcwT2/HzvtLgDtCRNYXR0IEJ1dGNoZXIgPG1hYnV0Y2hAbWlj
cm9zb2Z0LmNvbT6JAjUEEwEIACkFAmBLvGsJEG4qI9gGtubdAhsDBQkeEzgABAsH
CQMFFQgKAgMEFgABAgAA3TIP+wSoWwwicctBVV0Mu3zX+9TOC/QT3pf95la5PgIV
fu6S97h7ePphk0ORRFe4qW5f7IM0iXWTN455h1ngnZGXn5tG3JtkUY616AnmK1fJ
MHRZRCJmeD8u5SzCCZGBlL+n3Hp6gOR7q14hhgkeg4oPiFKSF75LJos4JYEeCIYN
WyUa2yjz/glnzrA/zMeRQ+acRXj/Aa1MlwiDukxpIaHzB8U0xm+V6AgWdNzP7T8P
Daxidjgkjk3GGAK741z37avP9MFYUTd/Pq6Z2uB5xFuaB2xD5gJcvVYMBJQtYmtt
AmbzEZwYsROmkfCmS9jmlUFaMbKdAl2do/0feX7Hw29fhVT23tYD2d9Zm39CFXOm
tIb4SDcteyqeIOhQkLZgKLwJiwXkaLsHPVZlQljzvkQlW4qRGvzxyCWWr4PZovQG
ZSyFcO3XJk2hswijbhM3rQOxtOL9GJ9U+khnghLfmet5otSl0Gm1yW+ub7AynXi9
JT+kMv2QZfPP+jZjIeBLC3yItI6K/+0qI53JMswKDvQ8qnmeVj++dquSSnSozXpa
npqxrjxAhZ905UrPKqzxd9lJUegfB4khUBC/IuE7HTkFnZz/I+r6IfJ031YZK/lr
eeCQm6DMvoehR+4vgo+APdvclMmmCWd4TBTFBhtOZvLX5HfMU++YZC13AeDUmzOp
edRWuQINBGBLvGsBEADtGQcj2nLThgu9QBKN7Q4TCwywd/RTyJCZm2aq6NVs2iYP
NGd49RmHdzYbiSgOaSSIYODevDB0KFK0/D3YMjEE5oBpf94MxGDOfq/tVEVOjiOR
rwW7YaKGpxoD0q9QB+CI4+w3Dhu5Yiaiun+carXPfhxaOvoYq26heLipZ/cztgRK
16bqoAn/Kl2/yY3kfN2YRBgHFaLwkKFAKD39QxbxrCTB6YuGLhGOI+BLv47WlECi
TnSM//k80jQVEjuvoXZaFQO0/A8O7vIXF2TarVKO2I2HPlCt4q09ub6rmmqn2MGj
2gwYR1lv1vQZMVevJOe+4gwGKPCicIbp+JX2CN8n9lorS/PlYkUSNZehNhEaBKUK
yl5WFY00oGtjYKwRwStN9m3JwNPAQES9EYipGi4YGdsrTa+MtsIZQdnbaMVA9wlU
sNMyoTBjaGr79Gu4cPLISy3mNy6LRivlEeE3pxcziUj3k/6dLEUFgTfgmH3dGJ2o
c1fqF7RPJ0hvzqh6pG9lx5nkUtpG+s8FC7hDDnuqVXCS+4rPe13sEFRlM6l1YAiC
hXeApBhvpqB71ydiVR/yHua1H9b49+1eVeWzfF6XPtUSSCkwH7W1ZWx+8yUBi6zz
GUgmGNJ4m0GglCDPXsP3w7WNJoPAU15LNsi5z59bjGou3OkI5czPTKF7Q73znwAR
AQABiQI1BBgBCAApBQJgS7xrCRBuKiPYBrbm3QIbDAUJHhM4AAQLBwkDBRUICgID
BBYAAQIAACVcEACY7aIw03LMedYRsWogFn6IkpdbqRVEYP5Zjglky8MFIOQv81j7
Zg99BB4V0lyvSMSlFmom4BE+Sq6EO3uuqC7WR+7GL3p92AyIF9EJIOAg9FFH8eRn
jk1jA12Zdx40V6okWpy3C/OY6D6du17G6AJ1NExfSWtbxXknFAbsv2azQpJ0ATdK
xEPun0PGlOhsg+Bu33k7tQ2P6/4dJT8c2e8QBy/kedj3mGhrb9Ymy0VdOn12P7kA
oVl9TvvQV64f9YSToQzDjHTSP8dxiEV7a8SMD4cm/7sTLF1a7LW8lD405jxqll8a
dtj4+yY/rfSN/rDVoTDBkc6habYL0G97j70o02nZYJtukkIQvSYdYARE0OUdwb+y
SZWuTxT340LDJHUwmDpFyk6L6MTaCwlFPoi4+0FDpjdOngEMjMHe92vWT1gGhk6B
uOKbA/wFozjv87y8T6bCJ+dA1/TqhUT7UJBKJozXpOpcYapI59ZmTVu5V7WwFJvK
JlWm8DSDpOI75JRRy3DTX4UmYg/nRX5pfLPsxq2JQW/QnjPLPJ/y+5Y++b92wWrP
AirPev6SluPhLJ2mswaK3THlhOZulKO/VIEJ6g50m5Vj3hdYf6sR603yK9rP+3iu
IagTQt2SGfW3Ap0RO3Yt+w29BpZ1CZ5Ml4gAYkXz0hiiMnVRhlcLIOHoFw==
=h3+3
-----END PGP PUBLIC KEY BLOCK-----

@ -1,8 +1,8 @@
BINDIR := $(CURDIR)/bin BINDIR := $(CURDIR)/bin
INSTALL_PATH ?= /usr/local/bin INSTALL_PATH ?= /usr/local/bin
DIST_DIRS := find * -type d -exec DIST_DIRS := find * -type d -exec
TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64 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 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 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
BINNAME ?= helm BINNAME ?= helm
GOBIN = $(shell go env GOBIN) GOBIN = $(shell go env GOBIN)
@ -25,7 +25,7 @@ TESTFLAGS :=
LDFLAGS := -w -s LDFLAGS := -w -s
GOFLAGS := GOFLAGS :=
# Rebuild the buinary if any of these files change # Rebuild the binary if any of these files change
SRC := $(shell find . -type f -name '*.go' -print) go.mod go.sum SRC := $(shell find . -type f -name '*.go' -print) go.mod go.sum
# Required for globs to work correctly # Required for globs to work correctly
@ -77,7 +77,7 @@ all: build
build: $(BINDIR)/$(BINNAME) build: $(BINDIR)/$(BINNAME)
$(BINDIR)/$(BINNAME): $(SRC) $(BINDIR)/$(BINNAME): $(SRC)
GO111MODULE=on go build $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm GO111MODULE=on go build $(GOFLAGS) -trimpath -tags '$(TAGS)' -ldflags '$(LDFLAGS)' -o '$(BINDIR)'/$(BINNAME) ./cmd/helm
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# install # install
@ -113,7 +113,7 @@ test-coverage:
.PHONY: test-style .PHONY: test-style
test-style: test-style:
GO111MODULE=on golangci-lint run GO111MODULE=on golangci-lint run --timeout 5m0s
@scripts/validate-license.sh @scripts/validate-license.sh
.PHONY: test-acceptance .PHONY: test-acceptance
@ -165,7 +165,7 @@ $(GOIMPORTS):
.PHONY: build-cross .PHONY: build-cross
build-cross: LDFLAGS += -extldflags "-static" build-cross: LDFLAGS += -extldflags "-static"
build-cross: $(GOX) build-cross: $(GOX)
GO111MODULE=on CGO_ENABLED=0 $(GOX) -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/$(BINNAME)" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/helm GOFLAGS="-trimpath" GO111MODULE=on CGO_ENABLED=0 $(GOX) -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/$(BINNAME)" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' ./cmd/helm
.PHONY: dist .PHONY: dist
dist: dist:

@ -54,7 +54,7 @@ Get started with the [Quick Start guide](https://helm.sh/docs/intro/quickstart/)
## Roadmap ## Roadmap
The [Helm roadmap uses Github milestones](https://github.com/helm/helm/milestones) to track the progress of the project. The [Helm roadmap uses GitHub milestones](https://github.com/helm/helm/milestones) to track the progress of the project.
## Community, discussion, contribution, and support ## Community, discussion, contribution, and support

@ -92,12 +92,11 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
} }
zsh := &cobra.Command{ zsh := &cobra.Command{
Use: "zsh", Use: "zsh",
Short: "generate autocompletion script for zsh", Short: "generate autocompletion script for zsh",
Long: zshCompDesc, Long: zshCompDesc,
Args: require.NoArgs, Args: require.NoArgs,
DisableFlagsInUseLine: true, ValidArgsFunction: noCompletions,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCompletionZsh(out, cmd) return runCompletionZsh(out, cmd)
}, },
@ -105,12 +104,11 @@ func newCompletionCmd(out io.Writer) *cobra.Command {
zsh.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText) zsh.Flags().BoolVar(&disableCompDescriptions, noDescFlagName, false, noDescFlagText)
fish := &cobra.Command{ fish := &cobra.Command{
Use: "fish", Use: "fish",
Short: "generate autocompletion script for fish", Short: "generate autocompletion script for fish",
Long: fishCompDesc, Long: fishCompDesc,
Args: require.NoArgs, Args: require.NoArgs,
DisableFlagsInUseLine: true, ValidArgsFunction: noCompletions,
ValidArgsFunction: noCompletions,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
return runCompletionFish(out, cmd) return runCompletionFish(out, cmd)
}, },

@ -29,9 +29,14 @@ import (
func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) { func checkFileCompletion(t *testing.T, cmdName string, shouldBePerformed bool) {
storage := storageFixture() storage := storageFixture()
storage.Create(&release.Release{ storage.Create(&release.Release{
Name: "myrelease", Name: "myrelease",
Info: &release.Info{Status: release.StatusDeployed}, Info: &release.Info{Status: release.StatusDeployed},
Chart: &chart.Chart{}, Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "Myrelease-Chart",
Version: "1.2.3",
},
},
Version: 1, Version: 1,
}) })
@ -57,3 +62,32 @@ func TestCompletionFileCompletion(t *testing.T) {
checkFileCompletion(t, "completion zsh", false) checkFileCompletion(t, "completion zsh", false)
checkFileCompletion(t, "completion fish", false) checkFileCompletion(t, "completion fish", false)
} }
func checkReleaseCompletion(t *testing.T, cmdName string, multiReleasesAllowed bool) {
multiReleaseTestGolden := "output/empty_nofile_comp.txt"
if multiReleasesAllowed {
multiReleaseTestGolden = "output/release_list_repeat_comp.txt"
}
tests := []cmdTestCase{{
name: "completion for uninstall",
cmd: fmt.Sprintf("__complete %s ''", cmdName),
golden: "output/release_list_comp.txt",
rels: []*release.Release{
release.Mock(&release.MockReleaseOptions{Name: "athos"}),
release.Mock(&release.MockReleaseOptions{Name: "porthos"}),
release.Mock(&release.MockReleaseOptions{Name: "aramis"}),
},
}, {
name: "completion for uninstall repetition",
cmd: fmt.Sprintf("__complete %s porthos ''", cmdName),
golden: multiReleaseTestGolden,
rels: []*release.Release{
release.Mock(&release.MockReleaseOptions{Name: "athos"}),
release.Mock(&release.MockReleaseOptions{Name: "porthos"}),
release.Mock(&release.MockReleaseOptions{Name: "aramis"}),
},
}}
for _, test := range tests {
runTestCmd(t, []cmdTestCase{test})
}
}

@ -150,7 +150,7 @@ func TestDependencyUpdateCmd(t *testing.T) {
} }
} }
func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) { func TestDependencyUpdateCmd_DoNotDeleteOldChartsOnError(t *testing.T) {
defer resetEnv()() defer resetEnv()()
defer ensure.HelmHome(t)() defer ensure.HelmHome(t)()

@ -68,6 +68,17 @@ func newDocsCmd(out io.Writer) *cobra.Command {
f.StringVar(&o.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)") 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") 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) {
types := []string{"bash", "man", "markdown"}
var comps []string
for _, t := range types {
if strings.HasPrefix(t, toComplete) {
comps = append(comps, t)
}
}
return comps, cobra.ShellCompDirectiveNoFileComp
})
return cmd return cmd
} }

@ -20,6 +20,19 @@ import (
"testing" "testing"
) )
func TestDocsTypeFlagCompletion(t *testing.T) {
tests := []cmdTestCase{{
name: "completion for docs --type",
cmd: "__complete docs --type ''",
golden: "output/docs-type-comp.txt",
}, {
name: "completion for docs --type",
cmd: "__complete docs --type mar",
golden: "output/docs-type-filtered-comp.txt",
}}
runTestCmd(t, tests)
}
func TestDocsFileCompletion(t *testing.T) { func TestDocsFileCompletion(t *testing.T) {
checkFileCompletion(t, "docs", false) checkFileCompletion(t, "docs", false)
} }

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"log" "log"
"path/filepath" "path/filepath"
"sort"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -46,7 +47,7 @@ func addValueOptionsFlags(f *pflag.FlagSet, v *values.Options) {
} }
func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) { func addChartPathOptionsFlags(f *pflag.FlagSet, c *action.ChartPathOptions) {
f.StringVar(&c.Version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") f.StringVar(&c.Version, "version", "", "specify a version constraint for the chart version to use. This constraint can be a specific tag (e.g. 1.1.1) or it may reference a valid range (e.g. ^2.0.0). If this is not specified, the latest version is used")
f.BoolVar(&c.Verify, "verify", false, "verify the package before using it") f.BoolVar(&c.Verify, "verify", false, "verify the package before using it")
f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification") f.StringVar(&c.Keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart") f.StringVar(&c.RepoURL, "repo", "", "chart repository url where to locate the requested chart")
@ -66,11 +67,14 @@ func bindOutputFlag(cmd *cobra.Command, varRef *output.Format) {
err := cmd.RegisterFlagCompletionFunc(outputFlag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { err := cmd.RegisterFlagCompletionFunc(outputFlag, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
var formatNames []string var formatNames []string
for _, format := range output.Formats() { for format, desc := range output.FormatsWithDesc() {
if strings.HasPrefix(format, toComplete) { if strings.HasPrefix(format, toComplete) {
formatNames = append(formatNames, format) formatNames = append(formatNames, fmt.Sprintf("%s\t%s", format, desc))
} }
} }
// Sort the results to get a deterministic order for the tests
sort.Strings(formatNames)
return formatNames, cobra.ShellCompDirectiveNoFileComp return formatNames, cobra.ShellCompDirectiveNoFileComp
}) })
@ -150,7 +154,21 @@ func compVersionFlag(chartRef string, toComplete string) ([]string, cobra.ShellC
for _, details := range indexFile.Entries[chartName] { for _, details := range indexFile.Entries[chartName] {
version := details.Metadata.Version version := details.Metadata.Version
if strings.HasPrefix(version, toComplete) { if strings.HasPrefix(version, toComplete) {
versions = append(versions, version) appVersion := details.Metadata.AppVersion
appVersionDesc := ""
if appVersion != "" {
appVersionDesc = fmt.Sprintf("App: %s, ", appVersion)
}
created := details.Created.Format("January 2, 2006")
createdDesc := ""
if created != "" {
createdDesc = fmt.Sprintf("Created: %s ", created)
}
deprecated := ""
if details.Metadata.Deprecated {
deprecated = "(deprecated)"
}
versions = append(versions, fmt.Sprintf("%s\t%s%s%s", version, appVersionDesc, createdDesc, deprecated))
} }
} }
} }

@ -45,7 +45,7 @@ func newGetAllCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
res, err := client.Run(args[0]) res, err := client.Run(args[0])

@ -42,6 +42,10 @@ func TestGetCmd(t *testing.T) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestGetAllCompletion(t *testing.T) {
checkReleaseCompletion(t, "get all", false)
}
func TestGetAllRevisionCompletion(t *testing.T) { func TestGetAllRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get all") revisionFlagCompletionTest(t, "get all")
} }

@ -45,7 +45,7 @@ func newGetHooksCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
res, err := client.Run(args[0]) res, err := client.Run(args[0])

@ -37,6 +37,10 @@ func TestGetHooks(t *testing.T) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestGetHooksCompletion(t *testing.T) {
checkReleaseCompletion(t, "get hooks", false)
}
func TestGetHooksRevisionCompletion(t *testing.T) { func TestGetHooksRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get hooks") revisionFlagCompletionTest(t, "get hooks")
} }

@ -49,7 +49,7 @@ func newGetManifestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
res, err := client.Run(args[0]) res, err := client.Run(args[0])

@ -37,6 +37,10 @@ func TestGetManifest(t *testing.T) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestGetManifestCompletion(t *testing.T) {
checkReleaseCompletion(t, "get manifest", false)
}
func TestGetManifestRevisionCompletion(t *testing.T) { func TestGetManifestRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get manifest") revisionFlagCompletionTest(t, "get manifest")
} }

@ -43,7 +43,7 @@ func newGetNotesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
res, err := client.Run(args[0]) res, err := client.Run(args[0])

@ -37,6 +37,10 @@ func TestGetNotesCmd(t *testing.T) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestGetNotesCompletion(t *testing.T) {
checkReleaseCompletion(t, "get notes", false)
}
func TestGetNotesRevisionCompletion(t *testing.T) { func TestGetNotesRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get notes") revisionFlagCompletionTest(t, "get notes")
} }

@ -50,7 +50,7 @@ func newGetValuesCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
vals, err := client.Run(args[0]) vals, err := client.Run(args[0])

@ -53,6 +53,10 @@ func TestGetValuesCmd(t *testing.T) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestGetValuesCompletion(t *testing.T) {
checkReleaseCompletion(t, "get values", false)
}
func TestGetValuesRevisionCompletion(t *testing.T) { func TestGetValuesRevisionCompletion(t *testing.T) {
revisionFlagCompletionTest(t, "get values") revisionFlagCompletionTest(t, "get values")
} }

@ -65,7 +65,7 @@ func newHistoryCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
history, err := getHistory(client, args[0]) history, err := getHistory(client, args[0])
@ -193,7 +193,9 @@ func compListRevisions(toComplete string, cfg *action.Configuration, releaseName
for _, release := range hist { for _, release := range hist {
version := strconv.Itoa(release.Version) version := strconv.Itoa(release.Version)
if strings.HasPrefix(version, toComplete) { if strings.HasPrefix(version, toComplete) {
revisions = append(revisions, version) appVersion := fmt.Sprintf("App: %s", release.Chart.Metadata.AppVersion)
chartDesc := fmt.Sprintf("Chart: %s-%s", release.Chart.Metadata.Name, release.Chart.Metadata.Version)
revisions = append(revisions, fmt.Sprintf("%s\t%s, %s", version, appVersion, chartDesc))
} }
} }
return revisions, cobra.ShellCompDirectiveNoFileComp return revisions, cobra.ShellCompDirectiveNoFileComp

@ -109,6 +109,10 @@ func revisionFlagCompletionTest(t *testing.T, cmdName string) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestHistoryCompletion(t *testing.T) {
checkReleaseCompletion(t, "history", false)
}
func TestHistoryFileCompletion(t *testing.T) { func TestHistoryFileCompletion(t *testing.T) {
checkFileCompletion(t, "history", false) checkFileCompletion(t, "history", false)
checkFileCompletion(t, "history myrelease", false) checkFileCompletion(t, "history myrelease", false)

@ -114,7 +114,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f := cmd.Flags() f := cmd.Flags()
f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format") f.BoolVarP(&client.Short, "short", "q", false, "output short (quiet) listing format")
f.StringVar(&client.TimeFormat, "time-format", "", "format time. Example: --time-format 2009-11-17 20:34:10 +0000 UTC") f.StringVar(&client.TimeFormat, "time-format", "", `format time using golang time formatter. Example: --time-format "2006-01-02 15:04:05Z0700"`)
f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date") f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date")
f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order") f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order")
f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied") f.BoolVarP(&client.All, "all", "a", false, "show all releases without any filter applied")
@ -126,7 +126,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.Pending, "pending", false, "show pending releases") f.BoolVar(&client.Pending, "pending", false, "show pending releases")
f.BoolVarP(&client.AllNamespaces, "all-namespaces", "A", false, "list releases across all namespaces") f.BoolVarP(&client.AllNamespaces, "all-namespaces", "A", false, "list releases across all namespaces")
f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch") f.IntVarP(&client.Limit, "max", "m", 256, "maximum number of releases to fetch")
f.IntVar(&client.Offset, "offset", 0, "next release name in the list, used to offset from start value") f.IntVar(&client.Offset, "offset", 0, "next release index in the list, used to offset from start value")
f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results") f.StringVarP(&client.Filter, "filter", "f", "", "a regular expression (Perl compatible). Any releases that match the expression will be included in the results")
f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Works only for secret(default) and configmap storage backends.") f.StringVarP(&client.Selector, "selector", "l", "", "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2). Works only for secret(default) and configmap storage backends.")
bindOutputFlag(cmd, &outfmt) bindOutputFlag(cmd, &outfmt)
@ -193,8 +193,32 @@ func (r *releaseListWriter) WriteYAML(out io.Writer) error {
return output.EncodeYAML(out, r.releases) return output.EncodeYAML(out, r.releases)
} }
// Returns all releases from 'releases', except those with names matching 'ignoredReleases'
func filterReleases(releases []*release.Release, ignoredReleaseNames []string) []*release.Release {
// if ignoredReleaseNames is nil, just return releases
if ignoredReleaseNames == nil {
return releases
}
var filteredReleases []*release.Release
for _, rel := range releases {
found := false
for _, ignoredName := range ignoredReleaseNames {
if rel.Name == ignoredName {
found = true
break
}
}
if !found {
filteredReleases = append(filteredReleases, rel)
}
}
return filteredReleases
}
// Provide dynamic auto-completion for release names // Provide dynamic auto-completion for release names
func compListReleases(toComplete string, cfg *action.Configuration) ([]string, cobra.ShellCompDirective) { func compListReleases(toComplete string, ignoredReleaseNames []string, cfg *action.Configuration) ([]string, cobra.ShellCompDirective) {
cobra.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete), settings.Debug) cobra.CompDebugln(fmt.Sprintf("compListReleases with toComplete %s", toComplete), settings.Debug)
client := action.NewList(cfg) client := action.NewList(cfg)
@ -203,14 +227,16 @@ func compListReleases(toComplete string, cfg *action.Configuration) ([]string, c
client.Filter = fmt.Sprintf("^%s", toComplete) client.Filter = fmt.Sprintf("^%s", toComplete)
client.SetStateMask() client.SetStateMask()
results, err := client.Run() releases, err := client.Run()
if err != nil { if err != nil {
return nil, cobra.ShellCompDirectiveDefault return nil, cobra.ShellCompDirectiveDefault
} }
var choices []string var choices []string
for _, res := range results { filteredReleases := filterReleases(releases, ignoredReleaseNames)
choices = append(choices, res.Name) for _, rel := range filteredReleases {
choices = append(choices,
fmt.Sprintf("%s\t%s-%s -> %s", rel.Name, rel.Chart.Metadata.Name, rel.Chart.Metadata.Version, rel.Info.Status.String()))
} }
return choices, cobra.ShellCompDirectiveNoFileComp return choices, cobra.ShellCompDirectiveNoFileComp

@ -51,14 +51,39 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
return cmd return cmd
} }
// Returns all plugins from plugins, except those with names matching ignoredPluginNames
func filterPlugins(plugins []*plugin.Plugin, ignoredPluginNames []string) []*plugin.Plugin {
// if ignoredPluginNames is nil, just return plugins
if ignoredPluginNames == nil {
return plugins
}
var filteredPlugins []*plugin.Plugin
for _, plugin := range plugins {
found := false
for _, ignoredName := range ignoredPluginNames {
if plugin.Metadata.Name == ignoredName {
found = true
break
}
}
if !found {
filteredPlugins = append(filteredPlugins, plugin)
}
}
return filteredPlugins
}
// Provide dynamic auto-completion for plugin names // Provide dynamic auto-completion for plugin names
func compListPlugins(toComplete string) []string { func compListPlugins(toComplete string, ignoredPluginNames []string) []string {
var pNames []string var pNames []string
plugins, err := plugin.FindPlugins(settings.PluginsDirectory) plugins, err := plugin.FindPlugins(settings.PluginsDirectory)
if err == nil { if err == nil && len(plugins) > 0 {
for _, p := range plugins { filteredPlugins := filterPlugins(plugins, ignoredPluginNames)
for _, p := range filteredPlugins {
if strings.HasPrefix(p.Metadata.Name, toComplete) { if strings.HasPrefix(p.Metadata.Name, toComplete) {
pNames = append(pNames, p.Metadata.Name) pNames = append(pNames, fmt.Sprintf("%s\t%s", p.Metadata.Name, p.Metadata.Usage))
} }
} }
} }

@ -305,6 +305,50 @@ func TestLoadPlugins_HelmNoPlugins(t *testing.T) {
} }
} }
func TestPluginCmdsCompletion(t *testing.T) {
tests := []cmdTestCase{{
name: "completion for plugin update",
cmd: "__complete plugin update ''",
golden: "output/plugin_list_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin update repetition",
cmd: "__complete plugin update args ''",
golden: "output/plugin_repeat_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin uninstall",
cmd: "__complete plugin uninstall ''",
golden: "output/plugin_list_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin uninstall repetition",
cmd: "__complete plugin uninstall args ''",
golden: "output/plugin_repeat_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin list",
cmd: "__complete plugin list ''",
golden: "output/empty_nofile_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin install no args",
cmd: "__complete plugin install ''",
golden: "output/empty_default_comp.txt",
rels: []*release.Release{},
}, {
name: "completion for plugin install one arg",
cmd: "__complete plugin list /tmp ''",
golden: "output/empty_nofile_comp.txt",
rels: []*release.Release{},
}, {}}
for _, test := range tests {
settings.PluginsDirectory = "testdata/helmhome/helm/plugins"
runTestCmd(t, []cmdTestCase{test})
}
}
func TestPluginFileCompletion(t *testing.T) { func TestPluginFileCompletion(t *testing.T) {
checkFileCompletion(t, "plugin", false) checkFileCompletion(t, "plugin", false)
} }

@ -39,10 +39,7 @@ func newPluginUninstallCmd(out io.Writer) *cobra.Command {
Aliases: []string{"rm", "remove"}, Aliases: []string{"rm", "remove"},
Short: "uninstall one or more Helm plugins", Short: "uninstall one or more Helm plugins",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 { return compListPlugins(toComplete, args), cobra.ShellCompDirectiveNoFileComp
return nil, cobra.ShellCompDirectiveNoFileComp
}
return compListPlugins(toComplete), cobra.ShellCompDirectiveNoFileComp
}, },
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
return o.complete(args) return o.complete(args)

@ -40,10 +40,7 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command {
Aliases: []string{"up"}, Aliases: []string{"up"},
Short: "update one or more Helm plugins", Short: "update one or more Helm plugins",
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 { return compListPlugins(toComplete, args), cobra.ShellCompDirectiveNoFileComp
return nil, cobra.ShellCompDirectiveNoFileComp
}
return compListPlugins(toComplete), cobra.ShellCompDirectiveNoFileComp
}, },
PreRunE: func(cmd *cobra.Command, args []string) error { PreRunE: func(cmd *cobra.Command, args []string) error {
return o.complete(args) return o.complete(args)

@ -52,7 +52,7 @@ func newReleaseTestCmd(cfg *action.Configuration, out io.Writer) *cobra.Command
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
client.Namespace = settings.Namespace() client.Namespace = settings.Namespace()

@ -20,6 +20,10 @@ import (
"testing" "testing"
) )
func TestReleaseTestingCompletion(t *testing.T) {
checkReleaseCompletion(t, "test", false)
}
func TestReleaseTestingFileCompletion(t *testing.T) { func TestReleaseTestingFileCompletion(t *testing.T) {
checkFileCompletion(t, "test", false) checkFileCompletion(t, "test", false)
checkFileCompletion(t, "test myrelease", false) checkFileCompletion(t, "test myrelease", false)

@ -63,9 +63,15 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
func (o *repoUpdateOptions) run(out io.Writer) error { func (o *repoUpdateOptions) run(out io.Writer) error {
f, err := repo.LoadFile(o.repoFile) f, err := repo.LoadFile(o.repoFile)
if isNotExist(err) || len(f.Repositories) == 0 { switch {
case isNotExist(err):
return errNoRepositories
case err != nil:
return errors.Wrapf(err, "failed loading file: %s", o.repoFile)
case len(f.Repositories) == 0:
return errNoRepositories return errNoRepositories
} }
var repos []*repo.ChartRepository var repos []*repo.ChartRepository
for _, cfg := range f.Repositories { for _, cfg := range f.Repositories {
r, err := repo.NewChartRepository(cfg, getter.All(settings)) r, err := repo.NewChartRepository(cfg, getter.All(settings))

@ -19,6 +19,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -53,20 +54,27 @@ func TestUpdateCmd(t *testing.T) {
} }
func TestUpdateCustomCacheCmd(t *testing.T) { func TestUpdateCustomCacheCmd(t *testing.T) {
var out bytes.Buffer
rootDir := ensure.TempDir(t) rootDir := ensure.TempDir(t)
cachePath := filepath.Join(rootDir, "updcustomcache") cachePath := filepath.Join(rootDir, "updcustomcache")
_ = os.Mkdir(cachePath, os.ModePerm) os.Mkdir(cachePath, os.ModePerm)
defer os.RemoveAll(cachePath) defer os.RemoveAll(cachePath)
ts, err := repotest.NewTempServerWithCleanup(t, "testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
defer ts.Stop()
o := &repoUpdateOptions{ o := &repoUpdateOptions{
update: updateCharts, update: updateCharts,
repoFile: "testdata/repositories.yaml", repoFile: filepath.Join(ts.Root(), "repositories.yaml"),
repoCache: cachePath, repoCache: cachePath,
} }
if err := o.run(&out); err != nil { b := ioutil.Discard
if err := o.run(b); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if _, err := os.Stat(filepath.Join(cachePath, "charts-index.yaml")); err != nil { if _, err := os.Stat(filepath.Join(cachePath, "test-index.yaml")); err != nil {
t.Fatalf("error finding created index file in custom cache: %v", err) t.Fatalf("error finding created index file in custom cache: %v", err)
} }
} }

@ -48,7 +48,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Args: require.MinimumNArgs(1), Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 { if len(args) == 0 {
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
} }
if len(args) == 1 { if len(args) == 1 {

@ -98,7 +98,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
// Setup shell completion for the namespace flag // 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(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if client, err := actionConfig.KubernetesClientSet(); err == nil { if client, err := actionConfig.KubernetesClientSet(); err == nil {
// Choose a long enough timeout that the user notices somethings is not working // 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 // but short enough that the user is not made to wait very long
to := int64(3) to := int64(3)
cobra.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to), settings.Debug) cobra.CompDebugln(fmt.Sprintf("About to call kube client for namespaces with timeout of: %d", to), settings.Debug)
@ -131,13 +131,13 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
if config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( if config, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
loadingRules, loadingRules,
&clientcmd.ConfigOverrides{}).RawConfig(); err == nil { &clientcmd.ConfigOverrides{}).RawConfig(); err == nil {
ctxs := []string{} comps := []string{}
for name := range config.Contexts { for name, context := range config.Contexts {
if strings.HasPrefix(name, toComplete) { if strings.HasPrefix(name, toComplete) {
ctxs = append(ctxs, name) comps = append(comps, fmt.Sprintf("%s\t%s", name, context.Cluster))
} }
} }
return ctxs, cobra.ShellCompDirectiveNoFileComp return comps, cobra.ShellCompDirectiveNoFileComp
} }
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
}) })

@ -27,7 +27,7 @@ import (
func checkPerms() { func checkPerms() {
// This function MUST NOT FAIL, as it is just a check for a common permissions problem. // This function MUST NOT FAIL, as it is just a check for a common permissions problem.
// If for some reason the function hits a stopping condition, it may panic. But only if // If for some reason the function hits a stopping condition, it may panic. But only if
// we can be sure that it is panicing because Helm cannot proceed. // we can be sure that it is panicking because Helm cannot proceed.
kc := settings.KubeConfig kc := settings.KubeConfig
if kc == "" { if kc == "" {

@ -56,5 +56,5 @@ func TestSearchHubOutputCompletion(t *testing.T) {
} }
func TestSearchHubFileCompletion(t *testing.T) { func TestSearchHubFileCompletion(t *testing.T) {
checkFileCompletion(t, "search hub", true) // File completion may be useful when inputing a keyword checkFileCompletion(t, "search hub", true) // File completion may be useful when inputting a keyword
} }

@ -143,7 +143,7 @@ func (o *searchRepoOptions) setupSearchedVersion() {
} }
func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) { func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Result, error) {
if len(o.version) == 0 { if o.version == "" {
return res, nil return res, nil
} }
@ -154,26 +154,19 @@ func (o *searchRepoOptions) applyConstraint(res []*search.Result) ([]*search.Res
data := res[:0] data := res[:0]
foundNames := map[string]bool{} foundNames := map[string]bool{}
appendSearchResults := func(res *search.Result) {
data = append(data, res)
if !o.versions {
foundNames[res.Name] = true // If user hasn't requested all versions, only show the latest that matches
}
}
for _, r := range res { for _, r := range res {
if _, found := foundNames[r.Name]; found { // if not returning all versions and already have found a result,
// you're done!
if !o.versions && foundNames[r.Name] {
continue continue
} }
v, err := semver.NewVersion(r.Chart.Version) v, err := semver.NewVersion(r.Chart.Version)
if err != nil { if err != nil {
// If the current version number check appears ErrSegmentStartsZero or ErrInvalidPrerelease error and not devel mode, ignore continue
if (err == semver.ErrSegmentStartsZero || err == semver.ErrInvalidPrerelease) && !o.devel { }
continue if constraint.Check(v) {
} data = append(data, r)
appendSearchResults(r) foundNames[r.Name] = true
} else if constraint.Check(v) {
appendSearchResults(r)
} }
} }
@ -194,6 +187,7 @@ func (o *searchRepoOptions) buildIndex() (*search.Index, error) {
ind, err := repo.LoadIndexFile(f) ind, err := repo.LoadIndexFile(f)
if err != nil { if err != nil {
warning("Repo %q is corrupt or missing. Try 'helm repo update'.", n) warning("Repo %q is corrupt or missing. Try 'helm repo update'.", n)
warning("%s", err)
continue continue
} }

@ -68,14 +68,6 @@ func TestSearchRepositoriesCmd(t *testing.T) {
name: "search for 'maria', expect valid json output", name: "search for 'maria', expect valid json output",
cmd: "search repo maria --output json", cmd: "search repo maria --output json",
golden: "output/search-output-json.txt", golden: "output/search-output-json.txt",
}, {
name: "search for 'maria', expect one match with semver begin with zero development version",
cmd: "search repo maria --devel",
golden: "output/search-semver-pre-zero-devel-release.txt",
}, {
name: "search for 'nginx-ingress', expect one match with invalid development pre version",
cmd: "search repo nginx-ingress --devel",
golden: "output/search-semver-pre-invalid-release.txt",
}, { }, {
name: "search for 'alpine', expect valid yaml output", name: "search for 'alpine', expect valid yaml output",
cmd: "search repo alpine --output yaml", cmd: "search repo alpine --output yaml",
@ -97,5 +89,5 @@ func TestSearchRepoOutputCompletion(t *testing.T) {
} }
func TestSearchRepoFileCompletion(t *testing.T) { func TestSearchRepoFileCompletion(t *testing.T) {
checkFileCompletion(t, "search repo", true) // File completion may be useful when inputing a keyword checkFileCompletion(t, "search repo", true) // File completion may be useful when inputting a keyword
} }

@ -60,7 +60,7 @@ func newStatusCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
if len(args) != 0 { if len(args) != 0 {
return nil, cobra.ShellCompDirectiveNoFileComp return nil, cobra.ShellCompDirectiveNoFileComp
} }
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
rel, err := client.Run(args[0]) rel, err := client.Run(args[0])

@ -118,56 +118,72 @@ func mustParseTime(t string) helmtime.Time {
} }
func TestStatusCompletion(t *testing.T) { func TestStatusCompletion(t *testing.T) {
releasesMockWithStatus := func(info *release.Info, hooks ...*release.Hook) []*release.Release { rels := []*release.Release{
info.LastDeployed = helmtime.Unix(1452902400, 0).UTC() {
return []*release.Release{{
Name: "athos", Name: "athos",
Namespace: "default", Namespace: "default",
Info: info, Info: &release.Info{
Chart: &chart.Chart{}, Status: release.StatusDeployed,
Hooks: hooks, },
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "Athos-chart",
Version: "1.2.3",
},
},
}, { }, {
Name: "porthos", Name: "porthos",
Namespace: "default", Namespace: "default",
Info: info, Info: &release.Info{
Chart: &chart.Chart{}, Status: release.StatusFailed,
Hooks: hooks, },
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "Porthos-chart",
Version: "111.222.333",
},
},
}, { }, {
Name: "aramis", Name: "aramis",
Namespace: "default", Namespace: "default",
Info: info, Info: &release.Info{
Chart: &chart.Chart{}, Status: release.StatusUninstalled,
Hooks: hooks, },
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "Aramis-chart",
Version: "0.0.0",
},
},
}, { }, {
Name: "dartagnan", Name: "dartagnan",
Namespace: "gascony", Namespace: "gascony",
Info: info, Info: &release.Info{
Chart: &chart.Chart{}, Status: release.StatusUnknown,
Hooks: hooks, },
Chart: &chart.Chart{
Metadata: &chart.Metadata{
Name: "Dartagnan-chart",
Version: "1.2.3-prerelease",
},
},
}} }}
}
tests := []cmdTestCase{{ tests := []cmdTestCase{{
name: "completion for status", name: "completion for status",
cmd: "__complete status a", cmd: "__complete status a",
golden: "output/status-comp.txt", golden: "output/status-comp.txt",
rels: releasesMockWithStatus(&release.Info{ rels: rels,
Status: release.StatusDeployed,
}),
}, { }, {
name: "completion for status with too many arguments", name: "completion for status with too many arguments",
cmd: "__complete status dartagnan ''", cmd: "__complete status dartagnan ''",
golden: "output/status-wrong-args-comp.txt", golden: "output/status-wrong-args-comp.txt",
rels: releasesMockWithStatus(&release.Info{ rels: rels,
Status: release.StatusDeployed,
}),
}, { }, {
name: "completion for status with too many arguments", name: "completion for status with global flag",
cmd: "__complete status --debug a", cmd: "__complete status --debug a",
golden: "output/status-comp.txt", golden: "output/status-comp.txt",
rels: releasesMockWithStatus(&release.Info{ rels: rels,
Status: release.StatusDeployed,
}),
}} }}
runTestCmd(t, tests) runTestCmd(t, tests)
} }

@ -197,7 +197,7 @@ func isTestHook(h *release.Hook) bool {
} }
// The following functions (writeToFile, createOrOpenFile, and ensureDirectoryForFile) // The following functions (writeToFile, createOrOpenFile, and ensureDirectoryForFile)
// are coppied from the actions package. This is part of a change to correct a // are copied from the actions package. This is part of a change to correct a
// bug introduced by #8156. As part of the todo to refactor renderResources // bug introduced by #8156. As part of the todo to refactor renderResources
// this duplicate code should be removed. It is added here so that the API // this duplicate code should be removed. It is added here so that the API
// surface area is as minimally impacted as possible in fixing the issue. // surface area is as minimally impacted as possible in fixing the issue.

@ -4,6 +4,8 @@ entries:
- name: alpine - name: alpine
url: https://charts.helm.sh/stable/alpine-0.1.0.tgz url: https://charts.helm.sh/stable/alpine-0.1.0.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
created: "2018-06-27T10:00:18.230700509Z"
deprecated: true
home: https://helm.sh/helm home: https://helm.sh/helm
sources: sources:
- https://github.com/helm/helm - https://github.com/helm/helm
@ -13,9 +15,11 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
- name: alpine - name: alpine
url: https://charts.helm.sh/stable/alpine-0.2.0.tgz url: https://charts.helm.sh/stable/alpine-0.2.0.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
created: "2018-07-09T11:34:37.797864902Z"
home: https://helm.sh/helm home: https://helm.sh/helm
sources: sources:
- https://github.com/helm/helm - https://github.com/helm/helm
@ -25,9 +29,11 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
- name: alpine - name: alpine
url: https://charts.helm.sh/stable/alpine-0.3.0-rc.1.tgz url: https://charts.helm.sh/stable/alpine-0.3.0-rc.1.tgz
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
created: "2020-11-12T08:44:58.872726222Z"
home: https://helm.sh/helm home: https://helm.sh/helm
sources: sources:
- https://github.com/helm/helm - https://github.com/helm/helm
@ -37,10 +43,12 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
mariadb: mariadb:
- name: mariadb - name: mariadb
url: https://charts.helm.sh/stable/mariadb-0.3.0.tgz url: https://charts.helm.sh/stable/mariadb-0.3.0.tgz
checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56 checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56
created: "2018-04-23T08:20:27.160959131Z"
home: https://mariadb.org home: https://mariadb.org
sources: sources:
- https://github.com/bitnami/bitnami-docker-mariadb - https://github.com/bitnami/bitnami-docker-mariadb
@ -55,33 +63,4 @@ entries:
- name: Bitnami - name: Bitnami
email: containers@bitnami.com email: containers@bitnami.com
icon: "" icon: ""
- name: mariadb apiVersion: v2
url: https://charts.helm.sh/stable/mariadb-0.3.0-0565674.tgz
checksum: 65229f6de44a2be9f215d11dbff311673fc8ba56
home: https://mariadb.org
sources:
- https://github.com/bitnami/bitnami-docker-mariadb
version: 0.3.0-0565674
description: Chart for MariaDB
keywords:
- mariadb
- mysql
- database
- sql
maintainers:
- name: Bitnami
email: containers@bitnami.com
icon: ""
nginx-ingress:
- name: nginx-ingress
url: https://github.com/kubernetes/ingress-nginx/ingress-a.b.c.sdfsdf.tgz
checksum: 25229f6de44a2be9f215d11dbff31167ddc8ba56
home: https://github.com/kubernetes/ingress-nginx
sources:
- https://github.com/kubernetes/ingress-nginx
version: a.b.c.sdfsdf
description: Chart for nginx-ingress
keywords:
- ingress
- nginx
icon: ""

@ -0,0 +1,5 @@
bash
man
markdown
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -0,0 +1,3 @@
markdown
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -0,0 +1,2 @@
:0
Completion ended with directive: ShellCompDirectiveDefault

@ -0,0 +1,2 @@
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -1,5 +1,5 @@
table json Output result in JSON format
json table Output result in human-readable format
yaml yaml Output result in YAML format
:4 :4
Completion ended with directive: ShellCompDirectiveNoFileComp Completion ended with directive: ShellCompDirectiveNoFileComp

@ -0,0 +1,7 @@
args echo args
echo echo stuff
env env stuff
exitwith exitwith code
fullenv show env vars
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -0,0 +1,6 @@
echo echo stuff
env env stuff
exitwith exitwith code
fullenv show env vars
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -0,0 +1,5 @@
aramis foo-0.1.0-beta.1 -> deployed
athos foo-0.1.0-beta.1 -> deployed
porthos foo-0.1.0-beta.1 -> deployed
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -0,0 +1,4 @@
aramis foo-0.1.0-beta.1 -> deployed
athos foo-0.1.0-beta.1 -> deployed
:4
Completion ended with directive: ShellCompDirectiveNoFileComp

@ -1,6 +1,6 @@
8 8 App: 1.0, Chart: foo-0.1.0-beta.1
9 9 App: 1.0, Chart: foo-0.1.0-beta.1
10 10 App: 1.0, Chart: foo-0.1.0-beta.1
11 11 App: 1.0, Chart: foo-0.1.0-beta.1
:4 :4
Completion ended with directive: ShellCompDirectiveNoFileComp Completion ended with directive: ShellCompDirectiveNoFileComp

@ -1,4 +1,4 @@
carabins carabins foo-0.1.0-beta.1 -> superseded
musketeers musketeers foo-0.1.0-beta.1 -> deployed
:4 :4
Completion ended with directive: ShellCompDirectiveNoFileComp Completion ended with directive: ShellCompDirectiveNoFileComp

@ -1,2 +0,0 @@
NAME CHART VERSION APP VERSION DESCRIPTION
testing/nginx-ingress a.b.c.sdfsdf Chart for nginx-ingress

@ -1,2 +0,0 @@
NAME CHART VERSION APP VERSION DESCRIPTION
testing/mariadb 0.3.0-0565674 Chart for MariaDB

@ -1,4 +1,4 @@
aramis aramis Aramis-chart-0.0.0 -> uninstalled
athos athos Athos-chart-1.2.3 -> deployed
:4 :4
Completion ended with directive: ShellCompDirectiveNoFileComp Completion ended with directive: ShellCompDirectiveNoFileComp

@ -1,5 +1,5 @@
0.3.0-rc.1 0.3.0-rc.1 App: 3.0.0, Created: November 12, 2020
0.2.0 0.2.0 App: 2.3.4, Created: July 9, 2018
0.1.0 0.1.0 App: 1.2.3, Created: June 27, 2018 (deprecated)
:4 :4
Completion ended with directive: ShellCompDirectiveNoFileComp Completion ended with directive: ShellCompDirectiveNoFileComp

@ -4,7 +4,7 @@ kind: Ingress
{{ template "common.metadata" . }} {{ template "common.metadata" . }}
{{- if .Values.ingress.annotations }} {{- if .Values.ingress.annotations }}
annotations: annotations:
{{ include "common.annote" .Values.ingress.annotations | indent 4 }} {{ include "common.annotate" .Values.ingress.annotations | indent 4 }}
{{- end }} {{- end }}
spec: spec:
rules: rules:

@ -11,7 +11,7 @@ Any valid hook may be passed in. Separate multiple hooks with a ",".
"helm.sh/hook": {{printf "%s" . | quote}} "helm.sh/hook": {{printf "%s" . | quote}}
{{- end -}} {{- end -}}
{{- define "common.annote" -}} {{- define "common.annotate" -}}
{{- range $k, $v := . }} {{- range $k, $v := . }}
{{ $k | quote }}: {{ $v | quote }} {{ $k | quote }}: {{ $v | quote }}
{{- end -}} {{- end -}}

@ -4,7 +4,7 @@ kind: Ingress
{{ template "common.metadata" . }} {{ template "common.metadata" . }}
{{- if .Values.ingress.annotations }} {{- if .Values.ingress.annotations }}
annotations: annotations:
{{ include "common.annote" .Values.ingress.annotations | indent 4 }} {{ include "common.annotate" .Values.ingress.annotations | indent 4 }}
{{- end }} {{- end }}
spec: spec:
rules: rules:

@ -11,7 +11,7 @@ Any valid hook may be passed in. Separate multiple hooks with a ",".
"helm.sh/hook": {{printf "%s" . | quote}} "helm.sh/hook": {{printf "%s" . | quote}}
{{- end -}} {{- end -}}
{{- define "common.annote" -}} {{- define "common.annotate" -}}
{{- range $k, $v := . }} {{- range $k, $v := . }}
{{ $k | quote }}: {{ $v | quote }} {{ $k | quote }}: {{ $v | quote }}
{{- end -}} {{- end -}}

@ -48,10 +48,7 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Long: uninstallDesc, Long: uninstallDesc,
Args: require.MinimumNArgs(1), Args: require.MinimumNArgs(1),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) != 0 { return compListReleases(toComplete, args, cfg)
return nil, cobra.ShellCompDirectiveNoFileComp
}
return compListReleases(toComplete, cfg)
}, },
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {

@ -67,6 +67,10 @@ func TestUninstall(t *testing.T) {
runTestCmd(t, tests) runTestCmd(t, tests)
} }
func TestUninstallCompletion(t *testing.T) {
checkReleaseCompletion(t, "uninstall", true)
}
func TestUninstallFileCompletion(t *testing.T) { func TestUninstallFileCompletion(t *testing.T) {
checkFileCompletion(t, "uninstall", false) checkFileCompletion(t, "uninstall", false)
checkFileCompletion(t, "uninstall myrelease", false) checkFileCompletion(t, "uninstall myrelease", false)

@ -74,7 +74,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
Args: require.ExactArgs(2), Args: require.ExactArgs(2),
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
if len(args) == 0 { if len(args) == 0 {
return compListReleases(toComplete, cfg) return compListReleases(toComplete, args, cfg)
} }
if len(args) == 1 { if len(args) == 1 {
return compListCharts(toComplete, true) return compListCharts(toComplete, true)

@ -1,19 +1,19 @@
module helm.sh/helm/v3 module helm.sh/helm/v3
go 1.15 go 1.16
require ( require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/DATA-DOG/go-sqlmock v1.5.0
github.com/Masterminds/goutils v1.1.0 github.com/Masterminds/goutils v1.1.1
github.com/Masterminds/semver/v3 v3.1.1 github.com/Masterminds/semver/v3 v3.1.1
github.com/Masterminds/sprig/v3 v3.2.0 github.com/Masterminds/sprig/v3 v3.2.2
github.com/Masterminds/squirrel v1.5.0 github.com/Masterminds/squirrel v1.5.0
github.com/Masterminds/vcs v1.13.1 github.com/Masterminds/vcs v1.13.1
github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535 github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535
github.com/containerd/containerd v1.4.3 github.com/containerd/containerd v1.4.4
github.com/cyphar/filepath-securejoin v0.2.2 github.com/cyphar/filepath-securejoin v0.2.2
github.com/deislabs/oras v0.9.0 github.com/deislabs/oras v0.10.0
github.com/docker/distribution v2.7.1+incompatible github.com/docker/distribution v2.7.1+incompatible
github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce github.com/docker/docker v1.4.2-0.20200203170920-46ec8731fbce
github.com/docker/go-units v0.4.0 github.com/docker/go-units v0.4.0
@ -21,16 +21,16 @@ require (
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gofrs/flock v0.8.0 github.com/gofrs/flock v0.8.0
github.com/gosuri/uitable v0.0.4 github.com/gosuri/uitable v0.0.4
github.com/jmoiron/sqlx v1.2.0 github.com/jmoiron/sqlx v1.3.1
github.com/lib/pq v1.9.0 github.com/lib/pq v1.10.0
github.com/mattn/go-shellwords v1.0.11 github.com/mattn/go-shellwords v1.0.11
github.com/mitchellh/copystructure v1.0.0 github.com/mitchellh/copystructure v1.1.1
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/image-spec v1.0.1
github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2 github.com/phayes/freeport v0.0.0-20180830031419-95f893ade6f2
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351 github.com/rubenv/sql-migrate v0.0.0-20200616145509-8d140a17f351
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.8.1
github.com/spf13/cobra v1.1.1 github.com/spf13/cobra v1.1.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0
@ -38,14 +38,14 @@ require (
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
k8s.io/api v0.20.2 k8s.io/api v0.20.4
k8s.io/apiextensions-apiserver v0.20.2 k8s.io/apiextensions-apiserver v0.20.4
k8s.io/apimachinery v0.20.2 k8s.io/apimachinery v0.20.4
k8s.io/apiserver v0.20.2 k8s.io/apiserver v0.20.4
k8s.io/cli-runtime v0.20.2 k8s.io/cli-runtime v0.20.4
k8s.io/client-go v0.20.2 k8s.io/client-go v0.20.4
k8s.io/klog/v2 v2.4.0 k8s.io/klog/v2 v2.8.0
k8s.io/kubectl v0.20.2 k8s.io/kubectl v0.20.4
sigs.k8s.io/yaml v1.2.0 sigs.k8s.io/yaml v1.2.0
) )

@ -52,12 +52,12 @@ github.com/DATA-DOG/go-sqlmock v1.5.0/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E= github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig/v3 v3.2.0 h1:P1ekkbuU73Ui/wS0nK1HOM37hh4xdfZo485UPf8rc+Y= github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
github.com/Masterminds/sprig/v3 v3.2.0/go.mod h1:tWhwTbUTndesPNeF0C900vKoq283u6zp4APT9vaF3SI= github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8= github.com/Masterminds/squirrel v1.5.0 h1:JukIZisrUXadA9pl3rMkjhiamxiB0cXiu+HGp/Y8cY8=
github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/Masterminds/squirrel v1.5.0/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ= github.com/Masterminds/vcs v1.13.1 h1:NL3G1X7/7xduQtA2sJLpVpfHTNBALVNSjob6KEjPXNQ=
@ -139,6 +139,8 @@ github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on
github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY= github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY=
github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/containerd v1.4.4 h1:rtRG4N6Ct7GNssATwgpvMGfnjnwfjnu/Zs9W3Ikzq+M=
github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 h1:6ejg6Lkk8dskcM7wQ28gONkukbQkM4qpj4RnYbpFzrI= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7 h1:6ejg6Lkk8dskcM7wQ28gONkukbQkM4qpj4RnYbpFzrI=
github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
@ -170,16 +172,16 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE= github.com/daviddengcn/go-colortext v0.0.0-20160507010035-511bcaf42ccd/go.mod h1:dv4zxwHi5C/8AeI+4gX4dCWOIvNi7I6JCSX0HvlKPgE=
github.com/deislabs/oras v0.9.0 h1:R6PRN3bTruUjHcGKgdteurzbpsCxwf3XJCLsxLFyBuU= github.com/deislabs/oras v0.10.0 h1:Eufbi8zVaULb7vYj5HKM9qv9qw6fJ7P75JSjn//gR0E=
github.com/deislabs/oras v0.9.0/go.mod h1:QXnMi3+eEm/rkgGT6L+Lt0TT2WLA7pOzuk7tZIsUhFM= github.com/deislabs/oras v0.10.0/go.mod h1:N1UzE7rBa9qLyN4l8IlBTxc2PkrRcKgWQ3HTJvRnJRE=
github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/denisenkom/go-mssqldb v0.0.0-20191001013358-cfbb681360f0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/cli v20.10.2+incompatible h1:CR/6BZX5w3TLgAHZTyRpVh3yi+Q8Sj5j1fCsb0J2rCk= github.com/docker/cli v20.10.3+incompatible h1:WVEgoV/GpsTK5hruhHdYi79blQ+nmcm+7Ru/ZuiF+7E=
github.com/docker/cli v20.10.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v20.10.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d h1:jC8tT/S0OGx2cswpeUTn4gOIea8P08lD3VFQT0cOZ50= github.com/docker/distribution v0.0.0-20191216044856-a8371794149d h1:jC8tT/S0OGx2cswpeUTn4gOIea8P08lD3VFQT0cOZ50=
github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= github.com/docker/distribution v0.0.0-20191216044856-a8371794149d/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ= github.com/docker/docker-credential-helpers v0.6.3 h1:zI2p9+1NQYdnG6sMU26EX4aVGlqbInSQxQXLvzJ4RPQ=
@ -240,8 +242,9 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc=
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@ -254,8 +257,9 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8= github.com/gobuffalo/envy v1.7.1 h1:OQl5ys5MBea7OGCdvPbBJWRgnhC/fGona6QKfvFeau8=
@ -396,8 +400,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.3.1 h1:aLN7YINNZ7cYOPK3QC83dbM6KT0NMqVMw961TqrejlE=
github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jmoiron/sqlx v1.3.1/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
@ -428,10 +432,9 @@ github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.10.0 h1:Zx5DJFEYQXio93kgXnQ09fXNiUKsqv4OUEu2UtGcB1E=
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
@ -456,16 +459,17 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-shellwords v1.0.11 h1:vCoR9VPpsk/TZFW2JwK5I9S0xdrtUq2bph6/YjEPnaw= github.com/mattn/go-shellwords v1.0.11 h1:vCoR9VPpsk/TZFW2JwK5I9S0xdrtUq2bph6/YjEPnaw=
github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.12.0 h1:u/x3mp++qUxvYfulZ4HKOvVO0JWhk7HtE8lWhbGz/Do=
github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.1.1 h1:Bp6x9R1Wn16SIz3OfeDr0b7RnCG2OB66Y7PQyC/cvq4=
github.com/mitchellh/copystructure v1.1.1/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
@ -477,8 +481,9 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ=
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE=
github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:NT0cwArZg/wGdvY8pzej4tPr+9WGmDdkF8Suj+mkz2g= github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible h1:NT0cwArZg/wGdvY8pzej4tPr+9WGmDdkF8Suj+mkz2g=
github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/moby v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc=
github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd h1:aY7OQNf2XqY/JQ6qREWamhI/81os/agb2BAGpcx5yWI=
@ -619,6 +624,8 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
@ -834,8 +841,9 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -1010,33 +1018,34 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw= k8s.io/api v0.20.4 h1:xZjKidCirayzX6tHONRQyTNDVIR55TYVqgATqo6ZULY=
k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
k8s.io/apiextensions-apiserver v0.20.2 h1:rfrMWQ87lhd8EzQWRnbQ4gXrniL/yTRBgYH1x1+BLlo= k8s.io/apiextensions-apiserver v0.20.4 h1:VO/Y5PwBdznMIctX/vvgSNhxffikEmcLC/V1bpbhHhU=
k8s.io/apiextensions-apiserver v0.20.2/go.mod h1:F6TXp389Xntt+LUq3vw6HFOLttPa0V8821ogLGwb6Zs= k8s.io/apiextensions-apiserver v0.20.4/go.mod h1:Hzebis/9c6Io5yzHp24Vg4XOkTp1ViMwKP/6gmpsfA4=
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= k8s.io/apimachinery v0.20.4 h1:vhxQ0PPUUU2Ns1b9r4/UFp13UPs8cw2iOoTjnY9faa0=
k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
k8s.io/apiserver v0.20.2 h1:lGno2t3gcZnLtzsKH4oG0xA9/4GTiBzMO1DGp+K+Bak= k8s.io/apiserver v0.20.4 h1:zMMKIgIUDIFiwK3LyY7qOV4Z4wKsHVYExL6vXY9fPX4=
k8s.io/apiserver v0.20.2/go.mod h1:2nKd93WyMhZx4Hp3RfgH2K5PhwyTrprrkWYnI7id7jA= k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
k8s.io/cli-runtime v0.20.2 h1:W0/FHdbApnl9oB7xdG643c/Zaf7TZT+43I+zKxwqvhU= k8s.io/cli-runtime v0.20.4 h1:jVU13lBeebHLtarHeHkoIi3uRONFzccmP7hHLzEoQ4w=
k8s.io/cli-runtime v0.20.2/go.mod h1:FjH6uIZZZP3XmwrXWeeYCbgxcrD6YXxoAykBaWH0VdM= k8s.io/cli-runtime v0.20.4/go.mod h1:dz38e1CM4uuIhy8PMFUZv7qsvIdoE3ByZYlmbHNCkt4=
k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ= k8s.io/client-go v0.20.4 h1:85crgh1IotNkLpKYKZHVNI1JT86nr/iDCvq2iWKsql4=
k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
k8s.io/code-generator v0.20.2/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg= k8s.io/code-generator v0.20.4/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
k8s.io/component-base v0.20.2 h1:LMmu5I0pLtwjpp5009KLuMGFqSc2S2isGw8t1hpYKLE= k8s.io/component-base v0.20.4 h1:gdvPs4G11e99meQnW4zN+oYOjH8qkLz1sURrAzvKWqc=
k8s.io/component-base v0.20.2/go.mod h1:pzFtCiwe/ASD0iV7ySMu8SYVJjCapNM9bjvk7ptpKh0= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
k8s.io/component-helpers v0.20.2/go.mod h1:qeM6iAWGqIr+WE8n2QW2OK9XkpZkPNTxAoEv9jl40/I= k8s.io/component-helpers v0.20.4/go.mod h1:S7jGg8zQp3kwvSzfuGtNaQAMVmvzomXDioTm5vABn9g=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts=
k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
k8s.io/kubectl v0.20.2 h1:mXExF6N4eQUYmlfXJmfWIheCBLF6/n4VnwQKbQki5iE= k8s.io/kubectl v0.20.4 h1:Y1gUiigiZM+ulcrnWeqSHlTd0/7xWcQIXjuMnjtHyoo=
k8s.io/kubectl v0.20.2/go.mod h1:/bchZw5fZWaGZxaRxxfDQKej/aDEtj/Tf9YSS4Jl0es= k8s.io/kubectl v0.20.4/go.mod h1:yCC5lUQyXRmmtwyxfaakryh9ezzp/bT0O14LeoFLbGo=
k8s.io/metrics v0.20.2/go.mod h1:yTck5nl5wt/lIeLcU6g0b8/AKJf2girwe0PQiaM4Mwk= k8s.io/metrics v0.20.4/go.mod h1:DDXS+Ls+2NAxRcVhXKghRPa3csljyJRjDRjPe6EOg/g=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

@ -96,7 +96,7 @@ func TestResolve(t *testing.T) {
{ {
name: "repo from invalid local path", name: "repo from invalid local path",
req: []*chart.Dependency{ req: []*chart.Dependency{
{Name: "notexist", Repository: "file://testdata/notexist", Version: "0.1.0"}, {Name: "nonexistent", Repository: "file://testdata/nonexistent", Version: "0.1.0"},
}, },
err: true, err: true,
}, },
@ -255,7 +255,7 @@ func TestGetLocalPath(t *testing.T) {
}, },
{ {
name: "invalid local path", name: "invalid local path",
repo: "file://testdata/notexist", repo: "file://testdata/nonexistent",
chartpath: "testdata/chartpath", chartpath: "testdata/chartpath",
err: true, err: true,
}, },

@ -13,6 +13,7 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
- name: alpine - name: alpine
urls: urls:
- https://charts.helm.sh/stable/alpine-0.2.0.tgz - https://charts.helm.sh/stable/alpine-0.2.0.tgz
@ -25,6 +26,7 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
mariadb: mariadb:
- name: mariadb - name: mariadb
urls: urls:
@ -44,3 +46,4 @@ entries:
- name: Bitnami - name: Bitnami
email: containers@bitnami.com email: containers@bitnami.com
icon: "" icon: ""
apiVersion: v2

@ -58,7 +58,7 @@ func AssertGoldenString(t TestingT, actual, filename string) {
} }
} }
// AssertGoldenFile assers that the content of the actual file matches the contents of the expected file // AssertGoldenFile asserts that the content of the actual file matches the contents of the expected file
func AssertGoldenFile(t TestingT, actualFileName string, expectedFilename string) { func AssertGoldenFile(t TestingT, actualFileName string, expectedFilename string) {
t.Helper() t.Helper()
@ -77,6 +77,7 @@ func path(filename string) string {
} }
func compare(actual []byte, filename string) error { func compare(actual []byte, filename string) error {
actual = normalize(actual)
if err := update(filename, actual); err != nil { if err := update(filename, actual); err != nil {
return err return err
} }
@ -85,6 +86,7 @@ func compare(actual []byte, filename string) error {
if err != nil { if err != nil {
return errors.Wrapf(err, "unable to read testdata %s", filename) return errors.Wrapf(err, "unable to read testdata %s", filename)
} }
expected = normalize(expected)
if !bytes.Equal(expected, actual) { if !bytes.Equal(expected, actual) {
return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'\n", filename, expected, actual) return errors.Errorf("does not match golden file %s\n\nWANT:\n'%s'\n\nGOT:\n'%s'\n", filename, expected, actual)
} }

@ -46,7 +46,7 @@ func TestClientConfig(t *testing.T) {
t.Fatalf("expecting 1 client certificates, got %d", got) t.Fatalf("expecting 1 client certificates, got %d", got)
} }
if cfg.InsecureSkipVerify { if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false") t.Fatalf("insecure skip verify mismatch, expecting false")
} }
if cfg.RootCAs == nil { if cfg.RootCAs == nil {
t.Fatalf("mismatch tls RootCAs, expecting non-nil") t.Fatalf("mismatch tls RootCAs, expecting non-nil")
@ -75,7 +75,7 @@ func TestNewClientTLS(t *testing.T) {
t.Fatalf("expecting 1 client certificates, got %d", got) t.Fatalf("expecting 1 client certificates, got %d", got)
} }
if cfg.InsecureSkipVerify { if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false") t.Fatalf("insecure skip verify mismatch, expecting false")
} }
if cfg.RootCAs == nil { if cfg.RootCAs == nil {
t.Fatalf("mismatch tls RootCAs, expecting non-nil") t.Fatalf("mismatch tls RootCAs, expecting non-nil")
@ -90,7 +90,7 @@ func TestNewClientTLS(t *testing.T) {
t.Fatalf("expecting 0 client certificates, got %d", got) t.Fatalf("expecting 0 client certificates, got %d", got)
} }
if cfg.InsecureSkipVerify { if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false") t.Fatalf("insecure skip verify mismatch, expecting false")
} }
if cfg.RootCAs == nil { if cfg.RootCAs == nil {
t.Fatalf("mismatch tls RootCAs, expecting non-nil") t.Fatalf("mismatch tls RootCAs, expecting non-nil")
@ -105,7 +105,7 @@ func TestNewClientTLS(t *testing.T) {
t.Fatalf("expecting 1 client certificates, got %d", got) t.Fatalf("expecting 1 client certificates, got %d", got)
} }
if cfg.InsecureSkipVerify { if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false") t.Fatalf("insecure skip verify mismatch, expecting false")
} }
if cfg.RootCAs != nil { if cfg.RootCAs != nil {
t.Fatalf("mismatch tls RootCAs, expecting nil") t.Fatalf("mismatch tls RootCAs, expecting nil")

@ -62,14 +62,14 @@ func (d *Dependency) List(chartpath string, out io.Writer) error {
return nil return nil
} }
// dependecyStatus returns a string describing the status of a dependency viz a viz the parent chart. // dependencyStatus returns a string describing the status of a dependency viz a viz the parent chart.
func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string { func (d *Dependency) dependencyStatus(chartpath string, dep *chart.Dependency, parent *chart.Chart) string {
filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*") filename := fmt.Sprintf("%s-%s.tgz", dep.Name, "*")
// If a chart is unpacked, this will check the unpacked chart's `charts/` directory for tarballs. // If a chart is unpacked, this will check the unpacked chart's `charts/` directory for tarballs.
// Technically, this is COMPLETELY unnecessary, and should be removed in Helm 4. It is here // Technically, this is COMPLETELY unnecessary, and should be removed in Helm 4. It is here
// to preserved backward compatibility. In Helm 2/3, there is a "difference" between // to preserved backward compatibility. In Helm 2/3, there is a "difference" between
// the tgz version (which outputs "ok" if it unpacks) and the loaded version (which outouts // the tgz version (which outputs "ok" if it unpacks) and the loaded version (which outputs
// "unpacked"). Early in Helm 2's history, this would have made a difference. But it no // "unpacked"). Early in Helm 2's history, this would have made a difference. But it no
// longer does. However, since this code shipped with Helm 3, the output must remain stable // longer does. However, since this code shipped with Helm 3, the output must remain stable
// until Helm 4. // until Helm 4.

@ -450,7 +450,7 @@ func TestNameTemplate(t *testing.T) {
{ {
tpl: "foobar-{{", tpl: "foobar-{{",
expected: "", expected: "",
expectedErrorStr: "unexpected unclosed action", expectedErrorStr: "template: name-template:1: unclosed action",
}, },
} }

@ -111,6 +111,10 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
} }
kept, errs := u.deleteRelease(rel) kept, errs := u.deleteRelease(rel)
if kept != "" {
kept = "These resources were kept due to the resource policy:\n" + kept
}
res.Info = kept res.Info = kept
if !u.DisableHooks { if !u.DisableHooks {
@ -189,7 +193,7 @@ func (u *Uninstall) deleteRelease(rel *release.Release) (string, []error) {
filesToKeep, filesToDelete := filterManifestsToKeep(files) filesToKeep, filesToDelete := filterManifestsToKeep(files)
var kept string var kept string
for _, f := range filesToKeep { for _, f := range filesToKeep {
kept += f.Name + "\n" kept += "[" + f.Head.Kind + "] " + f.Head.Metadata.Name + "\n"
} }
var builder strings.Builder var builder strings.Builder

@ -0,0 +1,62 @@
/*
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 action
import (
"testing"
"github.com/stretchr/testify/assert"
)
func uninstallAction(t *testing.T) *Uninstall {
config := actionConfigFixture(t)
unAction := NewUninstall(config)
return unAction
}
func TestUninstallRelease_deleteRelease(t *testing.T) {
is := assert.New(t)
unAction := uninstallAction(t)
unAction.DisableHooks = true
unAction.DryRun = false
unAction.KeepHistory = true
rel := releaseStub()
rel.Name = "keep-secret"
rel.Manifest = `{
"apiVersion": "v1",
"kind": "Secret",
"metadata": {
"name": "secret",
"annotations": {
"helm.sh/resource-policy": "keep"
}
},
"type": "Opaque",
"data": {
"password": "password"
}
}`
unAction.cfg.Releases.Create(rel)
res, err := unAction.Run(rel.Name)
is.NoError(err)
expected := `These resources were kept due to the resource policy:
[Secret] secret
`
is.Contains(res.Info, expected)
}

@ -94,7 +94,7 @@ type Upgrade struct {
// PostRender is an optional post-renderer // PostRender is an optional post-renderer
// //
// If this is non-nil, then after templates are rendered, they will be sent to the // If this is non-nil, then after templates are rendered, they will be sent to the
// post renderer before sending to the Kuberntes API server. // post renderer before sending to the Kubernetes API server.
PostRenderer postrender.PostRenderer PostRenderer postrender.PostRenderer
// DisableOpenAPIValidation controls whether OpenAPI validation is enforced. // DisableOpenAPIValidation controls whether OpenAPI validation is enforced.
DisableOpenAPIValidation bool DisableOpenAPIValidation bool

@ -49,6 +49,23 @@ type Dependency struct {
Alias string `json:"alias,omitempty"` Alias string `json:"alias,omitempty"`
} }
// Validate checks for common problems with the dependency datastructure in
// the chart. This check must be done at load time before the dependency's charts are
// loaded.
func (d *Dependency) Validate() error {
d.Name = sanitizeString(d.Name)
d.Version = sanitizeString(d.Version)
d.Repository = sanitizeString(d.Repository)
d.Condition = sanitizeString(d.Condition)
for i := range d.Tags {
d.Tags[i] = sanitizeString(d.Tags[i])
}
if d.Alias != "" && !aliasNameFormat.MatchString(d.Alias) {
return ValidationErrorf("dependency %q has disallowed characters in the alias", d.Name)
}
return nil
}
// Lock is a lock file for dependencies. // Lock is a lock file for dependencies.
// //
// It represents the state that the dependencies should be in. // It represents the state that the dependencies should be in.

@ -0,0 +1,44 @@
/*
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 chart
import (
"testing"
)
func TestValidateDependency(t *testing.T) {
dep := &Dependency{
Name: "example",
}
for value, shouldFail := range map[string]bool{
"abcdefghijklmenopQRSTUVWXYZ-0123456780_": false,
"-okay": false,
"_okay": false,
"- bad": true,
" bad": true,
"bad\nvalue": true,
"bad ": true,
"bad$": true,
} {
dep.Alias = value
res := dep.Validate()
if res != nil && !shouldFail {
t.Errorf("Failed on case %q", dep.Alias)
} else if res == nil && shouldFail {
t.Errorf("Expected failure for %q", dep.Alias)
}
}
}

@ -15,6 +15,13 @@ limitations under the License.
package chart package chart
import (
"strings"
"unicode"
"github.com/Masterminds/semver/v3"
)
// Maintainer describes a Chart maintainer. // Maintainer describes a Chart maintainer.
type Maintainer struct { type Maintainer struct {
// Name is a user name or organization name // Name is a user name or organization name
@ -25,15 +32,23 @@ type Maintainer struct {
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
} }
// Validate checks valid data and sanitizes string characters.
func (m *Maintainer) Validate() error {
m.Name = sanitizeString(m.Name)
m.Email = sanitizeString(m.Email)
m.URL = sanitizeString(m.URL)
return nil
}
// Metadata for a Chart file. This models the structure of a Chart.yaml file. // Metadata for a Chart file. This models the structure of a Chart.yaml file.
type Metadata struct { type Metadata struct {
// The name of the chart // The name of the chart. Required.
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
// The URL to a relevant project page, git repo, or contact person // The URL to a relevant project page, git repo, or contact person
Home string `json:"home,omitempty"` Home string `json:"home,omitempty"`
// Source is the URL to the source code of this chart // Source is the URL to the source code of this chart
Sources []string `json:"sources,omitempty"` Sources []string `json:"sources,omitempty"`
// A SemVer 2 conformant version string of the chart // A SemVer 2 conformant version string of the chart. Required.
Version string `json:"version,omitempty"` Version string `json:"version,omitempty"`
// A one-sentence description of the chart // A one-sentence description of the chart
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
@ -43,7 +58,7 @@ type Metadata struct {
Maintainers []*Maintainer `json:"maintainers,omitempty"` Maintainers []*Maintainer `json:"maintainers,omitempty"`
// The URL to an icon file. // The URL to an icon file.
Icon string `json:"icon,omitempty"` Icon string `json:"icon,omitempty"`
// The API Version of this chart. // The API Version of this chart. Required.
APIVersion string `json:"apiVersion,omitempty"` APIVersion string `json:"apiVersion,omitempty"`
// The condition to check to enable chart // The condition to check to enable chart
Condition string `json:"condition,omitempty"` Condition string `json:"condition,omitempty"`
@ -64,11 +79,28 @@ type Metadata struct {
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
} }
// Validate checks the metadata for known issues, returning an error if metadata is not correct // Validate checks the metadata for known issues and sanitizes string
// characters.
func (md *Metadata) Validate() error { func (md *Metadata) Validate() error {
if md == nil { if md == nil {
return ValidationError("chart.metadata is required") return ValidationError("chart.metadata is required")
} }
md.Name = sanitizeString(md.Name)
md.Description = sanitizeString(md.Description)
md.Home = sanitizeString(md.Home)
md.Icon = sanitizeString(md.Icon)
md.Condition = sanitizeString(md.Condition)
md.Tags = sanitizeString(md.Tags)
md.AppVersion = sanitizeString(md.AppVersion)
md.KubeVersion = sanitizeString(md.KubeVersion)
for i := range md.Sources {
md.Sources[i] = sanitizeString(md.Sources[i])
}
for i := range md.Keywords {
md.Keywords[i] = sanitizeString(md.Keywords[i])
}
if md.APIVersion == "" { if md.APIVersion == "" {
return ValidationError("chart.metadata.apiVersion is required") return ValidationError("chart.metadata.apiVersion is required")
} }
@ -78,19 +110,26 @@ func (md *Metadata) Validate() error {
if md.Version == "" { if md.Version == "" {
return ValidationError("chart.metadata.version is required") return ValidationError("chart.metadata.version is required")
} }
if !isValidSemver(md.Version) {
return ValidationErrorf("chart.metadata.version %q is invalid", md.Version)
}
if !isValidChartType(md.Type) { if !isValidChartType(md.Type) {
return ValidationError("chart.metadata.type must be application or library") return ValidationError("chart.metadata.type must be application or library")
} }
for _, m := range md.Maintainers {
if err := m.Validate(); err != nil {
return err
}
}
// Aliases need to be validated here to make sure that the alias name does // Aliases need to be validated here to make sure that the alias name does
// not contain any illegal characters. // not contain any illegal characters.
for _, dependency := range md.Dependencies { for _, dependency := range md.Dependencies {
if err := validateDependency(dependency); err != nil { if err := dependency.Validate(); err != nil {
return err return err
} }
} }
// TODO validate valid semver here?
return nil return nil
} }
@ -102,12 +141,20 @@ func isValidChartType(in string) bool {
return false return false
} }
// validateDependency checks for common problems with the dependency datastructure in func isValidSemver(v string) bool {
// the chart. This check must be done at load time before the dependency's charts are _, err := semver.NewVersion(v)
// loaded. return err == nil
func validateDependency(dep *Dependency) error { }
if len(dep.Alias) > 0 && !aliasNameFormat.MatchString(dep.Alias) {
return ValidationErrorf("dependency %q has disallowed characters in the alias", dep.Name) // sanitizeString normalize spaces and removes non-printable characters.
} func sanitizeString(str string) string {
return nil return strings.Map(func(r rune) rune {
if unicode.IsSpace(r) {
return ' '
}
if unicode.IsPrint(r) {
return r
}
return -1
}, str)
} }

@ -72,6 +72,10 @@ func TestValidate(t *testing.T) {
}, },
ValidationError("dependency \"bad\" has disallowed characters in the alias"), ValidationError("dependency \"bad\" has disallowed characters in the alias"),
}, },
{
&Metadata{APIVersion: "v2", Name: "test", Version: "1.2.3.4"},
ValidationError("chart.metadata.version \"1.2.3.4\" is invalid"),
},
} }
for _, tt := range tests { for _, tt := range tests {
@ -82,26 +86,15 @@ func TestValidate(t *testing.T) {
} }
} }
func TestValidateDependency(t *testing.T) { func TestValidate_sanitize(t *testing.T) {
dep := &Dependency{ md := &Metadata{APIVersion: "v2", Name: "test", Version: "1.0", Description: "\adescr\u0081iption\rtest", Maintainers: []*Maintainer{{Name: "\r"}}}
Name: "example", if err := md.Validate(); err != nil {
t.Fatalf("unexpected error: %s", err)
} }
for value, shouldFail := range map[string]bool{ if md.Description != "description test" {
"abcdefghijklmenopQRSTUVWXYZ-0123456780_": false, t.Fatalf("description was not sanitized: %q", md.Description)
"-okay": false, }
"_okay": false, if md.Maintainers[0].Name != " " {
"- bad": true, t.Fatal("maintainer name was not sanitized")
" bad": true,
"bad\nvalue": true,
"bad ": true,
"bad$": true,
} {
dep.Alias = value
res := validateDependency(dep)
if res != nil && !shouldFail {
t.Errorf("Failed on case %q", dep.Alias)
} else if res == nil && shouldFail {
t.Errorf("Expected failure for %q", dep.Alias)
}
} }
} }

@ -190,13 +190,13 @@ func TestProcessDependencyImportValues(t *testing.T) {
e["overridden-chartA.SCAbool"] = "true" e["overridden-chartA.SCAbool"] = "true"
e["overridden-chartA.SCAfloat"] = "41.3" e["overridden-chartA.SCAfloat"] = "41.3"
e["overridden-chartA.SCAint"] = "808" e["overridden-chartA.SCAint"] = "808"
e["overridden-chartA.SCAstring"] = "jaberwocky" e["overridden-chartA.SCAstring"] = "jabberwocky"
e["overridden-chartA.SPextra4"] = "true" e["overridden-chartA.SPextra4"] = "true"
e["overridden-chartA-B.SCAbool"] = "true" e["overridden-chartA-B.SCAbool"] = "true"
e["overridden-chartA-B.SCAfloat"] = "41.3" e["overridden-chartA-B.SCAfloat"] = "41.3"
e["overridden-chartA-B.SCAint"] = "808" e["overridden-chartA-B.SCAint"] = "808"
e["overridden-chartA-B.SCAstring"] = "jaberwocky" e["overridden-chartA-B.SCAstring"] = "jabberwocky"
e["overridden-chartA-B.SCBbool"] = "false" e["overridden-chartA-B.SCBbool"] = "false"
e["overridden-chartA-B.SCBfloat"] = "1.99" e["overridden-chartA-B.SCBfloat"] = "1.99"
e["overridden-chartA-B.SCBint"] = "77" e["overridden-chartA-B.SCBint"] = "77"

@ -102,7 +102,7 @@ func TestSave(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if c2.Lock == nil { if c2.Lock == nil {
t.Fatal("Expected v2 chart archive to containe a Chart.lock file") t.Fatal("Expected v2 chart archive to contain a Chart.lock file")
} }
if c2.Lock.Digest != c.Lock.Digest { if c2.Lock.Digest != c.Lock.Digest {
t.Fatal("Chart.lock data did not match") t.Fatal("Chart.lock data did not match")
@ -139,7 +139,7 @@ func TestSavePreservesTimestamps(t *testing.T) {
Metadata: &chart.Metadata{ Metadata: &chart.Metadata{
APIVersion: chart.APIVersionV1, APIVersion: chart.APIVersionV1,
Name: "ahab", Name: "ahab",
Version: "1.2.3.4", Version: "1.2.3",
}, },
Values: map[string]interface{}{ Values: map[string]interface{}{
"imageName": "testimage", "imageName": "testimage",

@ -18,7 +18,7 @@ overridden-chartA:
SCAbool: true SCAbool: true
SCAfloat: 41.3 SCAfloat: 41.3
SCAint: 808 SCAint: 808
SCAstring: "jaberwocky" SCAstring: "jabberwocky"
SPextra4: true SPextra4: true
imported-chartA-B: imported-chartA-B:
@ -28,7 +28,7 @@ overridden-chartA-B:
SCAbool: true SCAbool: true
SCAfloat: 41.3 SCAfloat: 41.3
SCAint: 808 SCAint: 808
SCAstring: "jaberwocky" SCAstring: "jabberwocky"
SCBbool: false SCBbool: false
SCBfloat: 1.99 SCBfloat: 1.99
SCBint: 77 SCBint: 77

@ -61,7 +61,7 @@ const (
// ValidateReleaseName performs checks for an entry for a Helm release name // ValidateReleaseName performs checks for an entry for a Helm release name
// //
// For Helm to allow a name, it must be below a certain character count (53) and also match // For Helm to allow a name, it must be below a certain character count (53) and also match
// a reguar expression. // a regular expression.
// //
// According to the Kubernetes help text, the regular expression it uses is: // According to the Kubernetes help text, the regular expression it uses is:
// //

@ -40,6 +40,16 @@ func Formats() []string {
return []string{Table.String(), JSON.String(), YAML.String()} return []string{Table.String(), JSON.String(), YAML.String()}
} }
// FormatsWithDesc returns a list of the string representation of the supported formats
// including a description
func FormatsWithDesc() map[string]string {
return map[string]string{
Table.String(): "Output result in human-readable format",
JSON.String(): "Output result in JSON format",
YAML.String(): "Output result in YAML format",
}
}
// ErrInvalidFormatType is returned when an unsupported format type is used // ErrInvalidFormatType is returned when an unsupported format type is used
var ErrInvalidFormatType = fmt.Errorf("invalid format type") var ErrInvalidFormatType = fmt.Errorf("invalid format type")

@ -176,7 +176,7 @@ func (m *Manager) Update() error {
// TODO(mattfarina): Repositories should be explicitly added by end users // TODO(mattfarina): Repositories should be explicitly added by end users
// rather than automattic. In Helm v4 require users to add repositories. They // rather than automattic. In Helm v4 require users to add repositories. They
// should have to add them in order to make sure they are aware of the // should have to add them in order to make sure they are aware of the
// respoitories and opt-in to any locations, for security. // repositories and opt-in to any locations, for security.
repoNames, err = m.ensureMissingRepos(repoNames, req) repoNames, err = m.ensureMissingRepos(repoNames, req)
if err != nil { if err != nil {
return err return err

@ -13,6 +13,7 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
- name: alpine - name: alpine
urls: urls:
- https://charts.helm.sh/stable/alpine-0.2.0.tgz - https://charts.helm.sh/stable/alpine-0.2.0.tgz
@ -25,6 +26,7 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
mariadb: mariadb:
- name: mariadb - name: mariadb
urls: urls:
@ -44,3 +46,4 @@ entries:
- name: Bitnami - name: Bitnami
email: containers@bitnami.com email: containers@bitnami.com
icon: "" icon: ""
apiVersion: v2

@ -13,3 +13,4 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2

@ -12,3 +12,4 @@ entries:
- http://username:password@example.com/foo-1.2.3.tgz - http://username:password@example.com/foo-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2

@ -12,3 +12,4 @@ entries:
- https://example.com/foo-1.2.3.tgz - https://example.com/foo-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2

@ -12,3 +12,4 @@ entries:
- https://example.com/foo-1.2.3.tgz - https://example.com/foo-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2

@ -13,6 +13,7 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
- name: alpine - name: alpine
urls: urls:
- http://example.com/alpine-0.2.0.tgz - http://example.com/alpine-0.2.0.tgz
@ -26,6 +27,7 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2
foo: foo:
- name: foo - name: foo
description: Foo Chart description: Foo Chart
@ -38,3 +40,4 @@ entries:
- http://example.com/foo-1.2.3.tgz - http://example.com/foo-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2

@ -13,3 +13,4 @@ entries:
keywords: [] keywords: []
maintainers: [] maintainers: []
icon: "" icon: ""
apiVersion: v2

@ -12,6 +12,7 @@ entries:
- charts/foo-1.2.3.tgz - charts/foo-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2
bar: bar:
- name: bar - name: bar
description: Bar Chart With Relative Path description: Bar Chart With Relative Path
@ -24,3 +25,4 @@ entries:
- bar-1.2.3.tgz - bar-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2

@ -12,6 +12,7 @@ entries:
- charts/foo-1.2.3.tgz - charts/foo-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2
bar: bar:
- name: bar - name: bar
description: Bar Chart With Relative Path description: Bar Chart With Relative Path
@ -24,3 +25,4 @@ entries:
- bar-1.2.3.tgz - bar-1.2.3.tgz
version: 1.2.3 version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d
apiVersion: v2

@ -160,7 +160,7 @@ func TestRenderRefsOrdering(t *testing.T) {
for name, data := range expect { for name, data := range expect {
if out[name] != data { if out[name] != data {
t.Fatalf("Expected %q, got %q (iteraction %d)", data, out[name], i+1) t.Fatalf("Expected %q, got %q (iteration %d)", data, out[name], i+1)
} }
} }
} }

@ -80,7 +80,7 @@ func NewLookupFunction(config *rest.Config) lookupFunc {
// getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced. // getDynamicClientOnUnstructured returns a dynamic client on an Unstructured type. This client can be further namespaced.
func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) { func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config) (dynamic.NamespaceableResourceInterface, bool, error) {
gvk := schema.FromAPIVersionAndKind(apiversion, kind) gvk := schema.FromAPIVersionAndKind(apiversion, kind)
apiRes, err := getAPIReourceForGVK(gvk, config) apiRes, err := getAPIResourceForGVK(gvk, config)
if err != nil { if err != nil {
log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err) log.Printf("[ERROR] unable to get apiresource from unstructured: %s , error %s", gvk.String(), err)
return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String()) return nil, false, errors.Wrapf(err, "unable to get apiresource from unstructured: %s", gvk.String())
@ -99,7 +99,7 @@ func getDynamicClientOnKind(apiversion string, kind string, config *rest.Config)
return res, apiRes.Namespaced, nil return res, apiRes.Namespaced, nil
} }
func getAPIReourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) { func getAPIResourceForGVK(gvk schema.GroupVersionKind, config *rest.Config) (metav1.APIResource, error) {
res := metav1.APIResource{} res := metav1.APIResource{}
discoveryClient, err := discovery.NewDiscoveryClientForConfig(config) discoveryClient, err := discovery.NewDiscoveryClientForConfig(config)
if err != nil { if err != nil {

@ -233,7 +233,7 @@ func TestDownloadInsecureSkipTLSVerify(t *testing.T) {
u, _ := url.ParseRequestURI(ts.URL) u, _ := url.ParseRequestURI(ts.URL)
// Ensure the default behaviour did not change // Ensure the default behavior did not change
g, err := NewHTTPGetter( g, err := NewHTTPGetter(
WithURL(u.String()), WithURL(u.String()),
) )

@ -58,10 +58,19 @@ func (g *OCIGetter) get(href string) (*bytes.Buffer, error) {
} }
// NewOCIGetter constructs a valid http/https client as a Getter // NewOCIGetter constructs a valid http/https client as a Getter
func NewOCIGetter(options ...Option) (Getter, error) { func NewOCIGetter(ops ...Option) (Getter, error) {
var client OCIGetter registryClient, err := registry.NewClient()
if err != nil {
return nil, err
}
for _, opt := range options { client := OCIGetter{
opts: options{
registryClient: registryClient,
},
}
for _, opt := range ops {
opt(&client.opts) opt(&client.opts)
} }

@ -0,0 +1,30 @@
/*
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 getter
import (
"testing"
)
func TestNewOCIGetter(t *testing.T) {
testfn := func(ops *options) {
if ops.registryClient == nil {
t.Fatalf("the OCIGetter's registryClient should not be null")
}
}
NewOCIGetter(testfn)
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save