File chart-testing-3.13.0.obscpio of Package chart-testing
07070100000000000081A4000000000000000000000001683D800300000021000000000000000000000000000000000000002300000000chart-testing-3.13.0/.dockerignore.circleci
.history
.idea
.vscode
07070100000001000081A4000000000000000000000001683D800300000111000000000000000000000000000000000000002300000000chart-testing-3.13.0/.editorconfig# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true
charset = utf-8
[*.{yml, yaml}]
indent_size = 2
[*.go]
indent_style = tab
[Makefile]
indent_style = tab
07070100000002000081A4000000000000000000000001683D800300000297000000000000000000000000000000000000002300000000chart-testing-3.13.0/.golangci.yml---
version: "2"
run:
issues-exit-code: 1
linters:
enable:
- asciicheck
- errorlint
- gocritic
- gosec
- importas
- misspell
- prealloc
- revive
- staticcheck
- unconvert
- whitespace
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
issues:
max-issues-per-linter: 0
max-same-issues: 0
uniq-by-line: false
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
07070100000003000081A4000000000000000000000001683D800300000DE8000000000000000000000000000000000000002500000000chart-testing-3.13.0/.goreleaser.ymlproject_name: chart-testing
version: 2
env:
- COSIGN_YES=true
before:
hooks:
- go mod download
sboms:
- artifacts: archive
builds:
- main: ct/main.go
binary: ct
env:
- CGO_ENABLED=0
goarch:
- amd64
- arm64
- arm
goos:
- linux
- darwin
- windows
flags:
- -trimpath
mod_timestamp: '{{ .CommitTimestamp }}'
ldflags:
- >-
-X github.com/helm/chart-testing/v3/ct/cmd.Version={{ .Tag }}
-X github.com/helm/chart-testing/v3/ct/cmd.GitCommit={{ .Commit }}
-X github.com/helm/chart-testing/v3/ct/cmd.BuildDate={{ .Date }}
archives:
- format_overrides:
- goos: windows
format: zip
files:
- LICENSE
- README.md
- etc/chart_schema.yaml
- etc/lintconf.yaml
checksum:
name_template: 'checksums.txt'
snapshot:
version_template: "{{ .Tag }}-next"
dockers:
- skip_push: false
use: buildx
dockerfile: Dockerfile
image_templates:
- quay.io/helmpack/chart-testing:{{ .Tag }}-amd64
- quay.io/helmpack/chart-testing:latest-amd64
build_flag_templates:
- --platform=linux/amd64
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.revision={{ .Commit }}
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.created={{ .Date }}
- --label=org.opencontainers.image.description=ct - The chart testing tool
- --label=org.opencontainers.image.vendor=Helm
- --label=org.opencontainers.image.licenses=Apache-2.0
- --label=org.opencontainers.image.source=https://github.com/helm/chart-testing
- --label=org.opencontainers.image.authors=The Helm Authors
extra_files:
- etc/chart_schema.yaml
- etc/lintconf.yaml
- skip_push: false
goarch: arm64
use: buildx
dockerfile: Dockerfile
image_templates:
- quay.io/helmpack/chart-testing:{{ .Tag }}-arm64
- quay.io/helmpack/chart-testing:latest-arm64
build_flag_templates:
- --platform=linux/arm64
- --label=org.opencontainers.image.version={{ .Version }}
- --label=org.opencontainers.image.revision={{ .Commit }}
- --label=org.opencontainers.image.title={{ .ProjectName }}
- --label=org.opencontainers.image.created={{ .Date }}
- --label=org.opencontainers.image.description=ct - The chart testing tool
- --label=org.opencontainers.image.vendor=Helm
- --label=org.opencontainers.image.licenses=Apache-2.0
- --label=org.opencontainers.image.source=https://github.com/helm/chart-testing
- --label=org.opencontainers.image.authors=The Helm Authors
extra_files:
- etc/chart_schema.yaml
- etc/lintconf.yaml
docker_manifests:
- name_template: quay.io/helmpack/chart-testing:latest
image_templates:
- quay.io/helmpack/chart-testing:latest-amd64
- quay.io/helmpack/chart-testing:latest-arm64
- name_template: quay.io/helmpack/chart-testing:{{ .Tag }}
image_templates:
- quay.io/helmpack/chart-testing:{{ .Tag }}-amd64
- quay.io/helmpack/chart-testing:{{ .Tag }}-arm64
signs:
- id: all
signature: "${artifact}.sig"
certificate: "${artifact}.pem"
cmd: cosign
args: ["sign-blob", "--output-signature", "${artifact}.sig", "--output-certificate", "${artifact}.pem", "${artifact}"]
artifacts: all
docker_signs:
- id: images
cmd: cosign
args: ["sign", "${artifact}"]
artifacts: manifests
changelog:
use: github-native
07070100000004000081A4000000000000000000000001683D800300000662000000000000000000000000000000000000002000000000chart-testing-3.13.0/DockerfileFROM alpine:3.22
RUN apk --no-cache add \
bash \
curl \
git \
libc6-compat \
openssh-client \
py3-pip \
py3-wheel \
python3 \
yamllint
# Install Yamale YAML schema validator
ARG yamale_version=6.0.0
LABEL yamale-version=$yamale_version
RUN pip install --break-system-packages "yamale==$yamale_version"
ARG TARGETPLATFORM
# Install kubectl
ARG kubectl_version=v1.32.0
LABEL kubectl-version=$kubectl_version
RUN targetArch=$(echo $TARGETPLATFORM | cut -f2 -d '/') \
&& if [ ${targetArch} = "amd64" ]; then \
HELM_ARCH="linux/amd64"; \
elif [ ${targetArch} = "arm64" ]; then \
HELM_ARCH="linux/arm64"; \
fi \
&& curl -LO "dl.k8s.io/$kubectl_version/bin/$HELM_ARCH/kubectl" \
&& chmod +x kubectl \
&& mv kubectl /usr/local/bin/
# Install Helm
ARG helm_version=v3.16.4
LABEL helm-version=$helm_version
RUN targetArch=$(echo $TARGETPLATFORM | cut -f2 -d '/') \
&& if [ ${targetArch} = "amd64" ]; then \
HELM_ARCH="linux-amd64"; \
elif [ ${targetArch} = "arm64" ]; then \
HELM_ARCH="linux-arm64"; \
fi \
&& curl -LO "https://get.helm.sh/helm-$helm_version-$HELM_ARCH.tar.gz" \
&& mkdir -p "/usr/local/helm-$helm_version" \
&& tar -xzf "helm-$helm_version-$HELM_ARCH.tar.gz" -C "/usr/local/helm-$helm_version" \
&& ln -s "/usr/local/helm-$helm_version/$HELM_ARCH/helm" /usr/local/bin/helm \
&& rm -f "helm-$helm_version-$HELM_ARCH.tar.gz"
COPY ./etc/chart_schema.yaml /etc/ct/chart_schema.yaml
COPY ./etc/lintconf.yaml /etc/ct/lintconf.yaml
COPY ct /usr/local/bin/ct
# Ensure that the binary is available on path and is executable
RUN ct --help
07070100000005000081A4000000000000000000000001683D800300002C5E000000000000000000000000000000000000001D00000000chart-testing-3.13.0/LICENSE
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
07070100000006000081A4000000000000000000000001683D800300001AB9000000000000000000000000000000000000001F00000000chart-testing-3.13.0/README.md# Chart Testing
[](https://opensource.org/licenses/Apache-2.0)
[](https://goreportcard.com/report/github.com/helm/chart-testing)

`ct` is the tool for testing Helm charts.
It is meant to be used for linting and testing pull requests.
It automatically detects charts changed against the target branch.
## Installation
### Prerequisites
It is recommended to use the provided Docker image which can be [found on Quay](https://quay.io/repository/helmpack/chart-testing).
It comes with all necessary tools installed.
* [Helm](http://helm.sh)
* [Git](https://git-scm.com) (2.17.0 or later)
* [Yamllint](https://github.com/adrienverge/yamllint)
* [Yamale](https://github.com/23andMe/Yamale)
* [Kubectl](https://kubernetes.io/docs/reference/kubectl/overview/)
### Binary Distribution
Download the release distribution for your OS from the Releases page:
https://github.com/helm/chart-testing/releases
Unpack the `ct` binary, add it to your PATH, and you are good to go!
### Docker Image
A Docker image is available at `quay.io/helmpack/chart-testing` with list of
available tags [here](https://quay.io/repository/helmpack/chart-testing?tab=tags).
### Homebrew
```console
$ brew install chart-testing
```
## Usage
See documentation for individual commands:
* [ct](doc/ct.md)
* [ct install](doc/ct_install.md)
* [ct lint](doc/ct_lint.md)
* [ct lint-and-install](doc/ct_lint-and-install.md)
* [ct list-changed](doc/ct_list-changed.md)
* [ct version](doc/ct_version.md)
For a more extensive how-to guide, please see:
* [charts-repo-actions-demo](https://github.com/helm/charts-repo-actions-demo)
## Configuration
`ct` is a command-line application.
All command-line flags can also be set via environment variables or config file.
Environment variables must be prefixed with `CT_`.
Underscores must be used instead of hyphens.
CLI flags, environment variables, and a config file can be mixed.
The following order of precedence applies:
1. CLI flags
1. Environment variables
1. Config file
Note that linting requires config file for [yamllint](https://github.com/adrienverge/yamllint) and [yamale](https://github.com/23andMe/Yamale).
If not specified, these files are search in the current directory, the `.ct` directory in current directory, `$HOME/.ct`, and `/etc/ct`, in that order.
Samples are provided in the [etc](etc) folder.
### Examples
The following example show various way of configuring the same thing:
#### CLI
#### Remote repo
With remote repo:
ct install --remote upstream --chart-dirs stable,incubator --build-id pr-42
#### Local repo
If you have a chart in current directory and ct installed on the host then you can run:
ct install --chart-dirs . --charts .
With docker it works with:
docker run -it --network host --workdir=/data --volume ~/.kube/config:/root/.kube/config:ro --volume $(pwd):/data quay.io/helmpack/chart-testing:v3.7.1 ct install --chart-dirs . --charts .
Notice that `workdir` param is important and must be the same as volume mounted.
#### Environment Variables
export CT_REMOTE=upstream
export CT_CHART_DIRS=stable,incubator
export CT_BUILD_ID
ct install
#### Config File
`config.yaml`:
```yaml
remote: upstream
chart-dirs:
- stable
- incubator
build-id: pr-42
```
#### Config Usage
ct install --config config.yaml
`ct` supports any format [Viper](https://github.com/spf13/viper) can read, i. e. JSON, TOML, YAML, HCL, and Java properties files.
Notice that if no config file is specified, then `ct.yaml` (or any of the supported formats) is loaded from the current directory, `$HOME/.ct`, or `/etc/ct`, in that order, if found.
#### Using private chart repositories
When adding chart-repos you can specify additional arguments for the `helm repo add` command using `helm-repo-extra-args` on a per-repo basis.
You can also specify OCI registries which will be added using the `helm registry login` command, they also support the `helm-repo-extra-args` for authentication.
This could for example be used to authenticate a private chart repository.
`config.yaml`:
```yaml
chart-repos:
- incubator=https://incubator.io
- basic-auth=https://private.com
- ssl-repo=https://self-signed.ca
- oci-registry=oci://nice-oci-registry.pt
helm-repo-extra-args:
- ssl-repo=--ca-file ./my-ca.crt
```
ct install --config config.yaml --helm-repo-extra-args "basic-auth=--username user --password secret"
## Building from Source
`ct` is built using Go 1.13 or higher.
`build.sh` is used to build and release the tool.
It uses [Goreleaser](https://goreleaser.com/) under the covers.
Note: on MacOS you will need `GNU Coreutils readlink`.
You can install it with:
```console
brew install coreutils
```
Then add `gnubin` to your `$PATH`, with:
```console
echo 'export PATH="$(brew --prefix coreutils)/libexec/gnubin:$PATH"' >> ~/.bash_profile
bash --login
```
To use the build script:
```console
$ ./build.sh -h
Usage: build.sh <options>
Build ct using Goreleaser.
-h, --help Display help
-d, --debug Display verbose output and run Goreleaser with --debug
-r, --release Create a release using Goreleaser. This includes the creation
of a GitHub release and building and pushing the Docker image.
If this flag is not specified, Goreleaser is run with --snapshot
```
## Releasing
### Prepare Release
Before a release is created, versions have to be updated in the examples.
A pull request needs to be created for this, which should be merged right before the release is cut.
Here's a previous one for reference: https://github.com/helm/chart-testing/pull/89
### Create Release
The release workflow is [dispatched from github actions](https://github.com/helm/chart-testing/actions)
Versions must start with a lower-case `v`, e. g. `v3.7.1`.
## Supported versions
The previous MAJOR version will be supported for three months after each new MAJOR release.
Within this support window, pull requests for the previous MAJOR version should be made against the previous release branch.
For example, if the current MAJOR version is `v2`, the pull request base branch should be `release-v1`.
## Upgrading
When upgrading from `< v2.0.0` you will also need to change the usage in your scripts.
This is because, while the [v2.0.0](https://github.com/helm/chart-testing/releases/tag/v2.0.0) release has parity with `v1`, it was refactored from a bash library to Go so there are minor syntax differences.
Compare [v1 usage](https://github.com/helm/chart-testing/tree/release-v1#usage) with this (`v2`) version's README [usage](#usage) section above.
07070100000007000081ED000000000000000000000001683D8003000007DC000000000000000000000000000000000000001E00000000chart-testing-3.13.0/build.sh#!/usr/bin/env bash
# Copyright The Helm Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
SCRIPT_DIR=$(dirname -- "$(readlink -e "${BASH_SOURCE[0]}" || realpath "${BASH_SOURCE[0]}")")
readonly SCRIPT_DIR
show_help() {
cat << EOF
Usage: $(basename "$0") <options>
Build ct using Goreleaser.
-h, --help Display help
-d, --debug Display verbose output and run Goreleaser with --debug
-r, --release Create a release using Goreleaser. This includes the creation
of a GitHub release and building and pushing the Docker image.
If this flag is not specified, Goreleaser is run with --snapshot
EOF
}
main() {
local debug=
local release=
while :; do
case "${1:-}" in
-h | --help)
show_help
exit
;;
-d | --debug)
debug=true
;;
-r | --release)
release=true
;;
*)
break
;;
esac
shift
done
local goreleaser_args=(--clean)
if [[ -n "$debug" ]]; then
goreleaser_args+=(--debug)
set -x
fi
if [[ -z "$release" ]]; then
goreleaser_args+=(--snapshot --skip=sign)
fi
pushd "$SCRIPT_DIR" > /dev/null
go test -race ./...
goreleaser "${goreleaser_args[@]}"
popd > /dev/null
}
main "$@"
07070100000008000081A4000000000000000000000001683D800300000087000000000000000000000000000000000000002800000000chart-testing-3.13.0/code-of-conduct.md# Community Code of Conduct
Helm follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md).
07070100000009000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001800000000chart-testing-3.13.0/ct0707010000000A000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001C00000000chart-testing-3.13.0/ct/cmd0707010000000B000081A4000000000000000000000001683D8003000004A3000000000000000000000000000000000000002600000000chart-testing-3.13.0/ct/cmd/docGen.go// 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 cmd
import (
"fmt"
"github.com/MakeNowJust/heredoc"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
func newGenerateDocsCmd() *cobra.Command {
return &cobra.Command{
Use: "doc-gen",
Short: "Generate documentation",
Long: heredoc.Doc(`
Generate documentation for all commands
to the 'docs' directory.`),
Hidden: true,
RunE: generateDocs,
}
}
func generateDocs(_ *cobra.Command, _ []string) error {
fmt.Println("Generating docs...")
err := doc.GenMarkdownTree(NewRootCmd(), "doc")
if err != nil {
return err
}
fmt.Println("Done.")
return nil
}
0707010000000C000081A4000000000000000000000001683D800300001074000000000000000000000000000000000000002700000000chart-testing-3.13.0/ct/cmd/install.go// 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 cmd
import (
"fmt"
"github.com/MakeNowJust/heredoc"
"github.com/helm/chart-testing/v3/pkg/chart"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
func newInstallCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "install",
Short: "Install and test a chart",
Long: heredoc.Doc(`
Run 'helm install', 'helm test', and optionally 'helm upgrade' on
* changed charts (default)
* specific charts (--charts)
* all charts (--all)
in given chart directories. If upgrade (--upgrade) is true, then this
command will validate that 'helm test' passes for the following upgrade paths:
* previous chart revision => current chart version (if non-breaking SemVer change)
* current chart version => current chart version
Charts may have multiple custom values files matching the glob pattern
'*-values.yaml' in a directory named 'ci' in the root of the chart's
directory. The chart is installed and tested for each of these files.
If no custom values file is present, the chart is installed and
tested with defaults.`),
RunE: install,
}
flags := cmd.Flags()
addInstallFlags(flags)
addCommonLintAndInstallFlags(flags)
return cmd
}
func addInstallFlags(flags *flag.FlagSet) {
flags.String("build-id", "", heredoc.Doc(`
An optional, arbitrary identifier that is added to the name of the namespace a
chart is installed into. In a CI environment, this could be the build number or
the ID of a pull request. If not specified, the name of the chart is used`))
flags.Bool("upgrade", false, heredoc.Doc(`
Whether to test an in-place upgrade of each chart from its previous revision if the
current version should not introduce a breaking change according to the SemVer spec`))
flags.Bool("skip-missing-values", false, heredoc.Doc(`
When --upgrade has been passed, this flag will skip testing CI values files from the
previous chart revision if they have been deleted or renamed at the current chart
revision`))
flags.String("namespace", "", heredoc.Doc(`
Namespace to install the release(s) into. If not specified, each release will be
installed in its own randomly generated namespace`))
flags.String("release-name", "", heredoc.Doc(`
Name for the release. If not specified, is set to the chart name and a random
identifier.`))
flags.String("release-label", "app.kubernetes.io/instance", heredoc.Doc(`
The label to be used as a selector when inspecting resources created by charts.
This is only used if namespace is specified`))
flags.String("helm-extra-set-args", "", heredoc.Doc(`
Additional arguments for Helm. Must be passed as a single quoted string
(e.g. "--set=name=value"`))
flags.Bool("skip-clean-up", false, heredoc.Doc(`
Skip resources clean-up. Used if need to continue other flows or keep it around.`))
}
func install(cmd *cobra.Command, _ []string) error {
fmt.Println("Installing charts...")
printConfig, err := cmd.Flags().GetBool("print-config")
if err != nil {
return err
}
configuration, err := config.LoadConfiguration(cfgFile, cmd, printConfig)
if err != nil {
return fmt.Errorf("failed loading configuration: %w", err)
}
extraSetArgs, err := cmd.Flags().GetString("helm-extra-set-args")
if err != nil {
return err
}
testing, err := chart.NewTesting(*configuration, extraSetArgs)
if err != nil {
fmt.Println(err)
}
results, err := testing.InstallCharts()
testing.PrintResults(results)
if err != nil {
return fmt.Errorf("failed installing charts: %w", err)
}
fmt.Println("All charts installed successfully")
return nil
}
0707010000000D000081A4000000000000000000000001683D800300000E4C000000000000000000000000000000000000002400000000chart-testing-3.13.0/ct/cmd/lint.go// 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 cmd
import (
"fmt"
"github.com/MakeNowJust/heredoc"
"github.com/helm/chart-testing/v3/pkg/chart"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
)
func newLintCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "lint",
Short: "Lint and validate a chart",
Long: heredoc.Doc(`
Run 'helm lint', version checking, YAML schema validation
on 'Chart.yaml', YAML linting on 'Chart.yaml' and 'values.yaml',
and maintainer validation on
* changed charts (default)
* specific charts (--charts)
* all charts (--all)
in given chart directories.
Charts may have multiple custom values files matching the glob pattern
'*-values.yaml' in a directory named 'ci' in the root of the chart's
directory. The chart is linted for each of these files. If no custom
values file is present, the chart is linted with defaults.`),
RunE: lint,
}
flags := cmd.Flags()
addLintFlags(flags)
addCommonLintAndInstallFlags(flags)
return cmd
}
func addLintFlags(flags *flag.FlagSet) {
flags.String("lint-conf", "", heredoc.Doc(`
The config file for YAML linting. If not specified, 'lintconf.yaml'
is searched in the current directory, '$HOME/.ct', and '/etc/ct', in
that order`))
flags.String("chart-yaml-schema", "", heredoc.Doc(`
The schema for chart.yml validation. If not specified, 'chart_schema.yaml'
is searched in the current directory, '$HOME/.ct', and '/etc/ct', in
that order.`))
flags.Bool("validate-maintainers", true, heredoc.Doc(`
Enable validation of maintainer account names in chart.yml.
Works for GitHub, GitLab, and Bitbucket`))
flags.Bool("check-version-increment", true, "Activates a check for chart version increments")
flags.Bool("validate-chart-schema", true, heredoc.Doc(`
Enable schema validation of 'Chart.yaml' using Yamale`))
flags.Bool("validate-yaml", true, heredoc.Doc(`
Enable linting of 'Chart.yaml' and values files`))
flags.Bool("skip-helm-dependencies", false, heredoc.Doc(`
Skip running 'helm dependency build' before linting`))
flags.StringSlice("additional-commands", []string{}, heredoc.Doc(`
Additional commands to run per chart (default: [])
Commands will be executed in the same order as provided in the list and will
be rendered with go template before being executed.
Example: "helm unittest --helm3 -f tests/*.yaml {{ .Path }}"`))
}
func lint(cmd *cobra.Command, _ []string) error {
fmt.Println("Linting charts...")
printConfig, err := cmd.Flags().GetBool("print-config")
if err != nil {
return err
}
configuration, err := config.LoadConfiguration(cfgFile, cmd, printConfig)
if err != nil {
return fmt.Errorf("failed loading configuration: %w", err)
}
emptyExtraSetArgs := ""
testing, err := chart.NewTesting(*configuration, emptyExtraSetArgs)
if err != nil {
return err
}
results, err := testing.LintCharts()
testing.PrintResults(results)
if err != nil {
return fmt.Errorf("failed linting charts: %w", err)
}
fmt.Println("All charts linted successfully")
return nil
}
0707010000000E000081A4000000000000000000000001683D80030000077E000000000000000000000000000000000000002E00000000chart-testing-3.13.0/ct/cmd/lintAndInstall.go// 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 cmd
import (
"fmt"
"github.com/helm/chart-testing/v3/pkg/chart"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/spf13/cobra"
)
func newLintAndInstallCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "lint-and-install",
Aliases: []string{"li"},
Short: "Lint, install, and test a chart",
Long: "Combines 'lint' and 'install' commands.",
RunE: lintAndInstall,
}
flags := cmd.Flags()
addLintFlags(flags)
addInstallFlags(flags)
addCommonLintAndInstallFlags(flags)
return cmd
}
func lintAndInstall(cmd *cobra.Command, _ []string) error {
fmt.Println("Linting and installing charts...")
printConfig, err := cmd.Flags().GetBool("print-config")
if err != nil {
return err
}
configuration, err := config.LoadConfiguration(cfgFile, cmd, printConfig)
if err != nil {
return fmt.Errorf("failed loading configuration: %w", err)
}
extraSetArgs, err := cmd.Flags().GetString("helm-extra-set-args")
if err != nil {
return err
}
testing, err := chart.NewTesting(*configuration, extraSetArgs)
if err != nil {
return err
}
results, err := testing.LintAndInstallCharts()
testing.PrintResults(results)
if err != nil {
return fmt.Errorf("failed linting and installing charts: %w", err)
}
fmt.Println("All charts linted and installed successfully")
return nil
}
0707010000000F000081A4000000000000000000000001683D8003000006D0000000000000000000000000000000000000002B00000000chart-testing-3.13.0/ct/cmd/listChanged.go// 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 cmd
import (
"fmt"
"github.com/MakeNowJust/heredoc"
"github.com/helm/chart-testing/v3/pkg/chart"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/spf13/cobra"
)
func newListChangedCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "list-changed",
Aliases: []string{"ls-changed", "lsc"},
Short: "List changed charts",
Long: heredoc.Doc(`
"List changed charts based on configured charts directories,
"remote, and target branch`),
RunE: listChanged,
}
flags := cmd.Flags()
addCommonFlags(flags)
return cmd
}
func listChanged(cmd *cobra.Command, _ []string) error {
printConfig, err := cmd.Flags().GetBool("print-config")
if err != nil {
return err
}
configuration, err := config.LoadConfiguration(cfgFile, cmd, printConfig)
if err != nil {
return fmt.Errorf("failed loading configuration: %w", err)
}
emptyExtraSetArgs := ""
testing, err := chart.NewTesting(*configuration, emptyExtraSetArgs)
if err != nil {
return err
}
chartDirs, err := testing.ComputeChangedChartDirectories()
if err != nil {
return err
}
for _, dir := range chartDirs {
fmt.Println(dir)
}
return nil
}
07070100000010000081A4000000000000000000000001683D800300001100000000000000000000000000000000000000002400000000chart-testing-3.13.0/ct/cmd/root.go// 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 cmd
import (
"fmt"
"os"
"github.com/MakeNowJust/heredoc"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var (
cfgFile string
)
func NewRootCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "ct",
Short: "The Helm chart testing tool",
Long: heredoc.Doc(`
Lint and test
* changed charts
* specific charts
* all charts
in given chart directories.`),
SilenceUsage: true,
}
cmd.AddCommand(newLintCmd())
cmd.AddCommand(newInstallCmd())
cmd.AddCommand(newLintAndInstallCmd())
cmd.AddCommand(newListChangedCmd())
cmd.AddCommand(newVersionCmd())
cmd.AddCommand(newGenerateDocsCmd())
cmd.DisableAutoGenTag = true
return cmd
}
// Execute runs the application
func Execute() {
if err := NewRootCmd().Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func addCommonFlags(flags *pflag.FlagSet) {
flags.StringVar(&cfgFile, "config", "", "Config file")
flags.String("remote", "origin", "The name of the Git remote used to identify changed charts")
flags.String("target-branch", "main", "The name of the target branch used to identify changed charts")
flags.String("since", "HEAD", "The Git reference used to identify changed charts")
flags.StringSlice("chart-dirs", []string{"charts"}, heredoc.Doc(`
Directories containing Helm charts. May be specified multiple times
or separate values with commas`))
flags.StringSlice("excluded-charts", []string{}, heredoc.Doc(`
Charts that should be skipped. May be specified multiple times
or separate values with commas`))
flags.Bool("print-config", false, heredoc.Doc(`
Prints the configuration to stderr (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)`))
flags.Bool("exclude-deprecated", false, "Skip charts that are marked as deprecated")
flags.Bool("github-groups", false, heredoc.Doc(`
Change the delimiters for github to create collapsible groups
for command output`))
flags.Bool("use-helmignore", false, "Use .helmignore when identifying changed charts")
}
func addCommonLintAndInstallFlags(flags *pflag.FlagSet) {
addCommonFlags(flags)
flags.Bool("all", false, heredoc.Doc(`
Process all charts except those explicitly excluded.
Disables changed charts detection and version increment checking`))
flags.StringSlice("charts", []string{}, heredoc.Doc(`
Specific charts to test. Disables changed charts detection and
version increment checking. May be specified multiple times
or separate values with commas`))
flags.StringSlice("chart-repos", []string{}, heredoc.Doc(`
Additional chart repositories for dependency resolutions.
Repositories should be formatted as 'name=url' (ex: local=http://127.0.0.1:8879/charts).
May be specified multiple times or separate values with commas`))
flags.String("helm-extra-args", "", heredoc.Doc(`
Additional arguments for Helm. Must be passed as a single quoted string
(e.g. '--timeout 500s')`))
flags.String("helm-lint-extra-args", "", heredoc.Doc(`
Additional arguments for Helm lint subcommand. Must be passed as a single quoted string
(e.g. '--quiet')`))
flags.StringSlice("helm-repo-extra-args", []string{}, heredoc.Doc(`
Additional arguments for the 'helm repo add' command to be
specified on a per-repo basis with an equals sign as delimiter
(e.g. 'myrepo=--username test --password secret'). May be specified
multiple times or separate values with commas`))
flags.StringSlice("helm-dependency-extra-args", []string{}, heredoc.Doc(`
Additional arguments for 'helm dependency build' (e.g. ["--skip-refresh"]`))
flags.Bool("debug", false, heredoc.Doc(`
Print CLI calls of external tools to stdout (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)`))
}
07070100000011000081A4000000000000000000000001683D800300000503000000000000000000000000000000000000002700000000chart-testing-3.13.0/ct/cmd/version.go// 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 cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var (
// GitCommit is updated with the Git tag by the Goreleaser build
GitCommit = "unknown"
// BuildDate is updated with the current ISO timestamp by the Goreleaser build
BuildDate = "unknown"
// Version is updated with the latest tag by the Goreleaser build
Version = "unreleased"
)
func newVersionCmd() *cobra.Command {
return &cobra.Command{
Use: "version",
Short: "Print version information",
Run: version,
}
}
func version(_ *cobra.Command, _ []string) {
fmt.Println("Version:\t", Version)
fmt.Println("Git commit:\t", GitCommit)
fmt.Println("Date:\t\t", BuildDate)
fmt.Println("License:\t Apache 2.0")
}
07070100000012000081A4000000000000000000000001683D8003000002B1000000000000000000000000000000000000002000000000chart-testing-3.13.0/ct/main.go// Copyright The Helm Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"github.com/helm/chart-testing/v3/ct/cmd"
)
func main() {
cmd.Execute()
}
07070100000013000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001900000000chart-testing-3.13.0/doc07070100000014000081A4000000000000000000000001683D80030000020B000000000000000000000000000000000000001F00000000chart-testing-3.13.0/doc/ct.md## ct
The Helm chart testing tool
### Synopsis
Lint and test
* changed charts
* specific charts
* all charts
in given chart directories.
### Options
```
-h, --help help for ct
```
### SEE ALSO
* [ct install](ct_install.md) - Install and test a chart
* [ct lint](ct_lint.md) - Lint and validate a chart
* [ct lint-and-install](ct_lint-and-install.md) - Lint, install, and test a chart
* [ct list-changed](ct_list-changed.md) - List changed charts
* [ct version](ct_version.md) - Print version information
07070100000015000081A4000000000000000000000001683D80030000184F000000000000000000000000000000000000002700000000chart-testing-3.13.0/doc/ct_install.md## ct install
Install and test a chart
### Synopsis
Run 'helm install', 'helm test', and optionally 'helm upgrade' on
* changed charts (default)
* specific charts (--charts)
* all charts (--all)
in given chart directories. If upgrade (--upgrade) is true, then this
command will validate that 'helm test' passes for the following upgrade paths:
* previous chart revision => current chart version (if non-breaking SemVer change)
* current chart version => current chart version
Charts may have multiple custom values files matching the glob pattern
'*-values.yaml' in a directory named 'ci' in the root of the chart's
directory. The chart is installed and tested for each of these files.
If no custom values file is present, the chart is installed and
tested with defaults.
```
ct install [flags]
```
### Options
```
--all Process all charts except those explicitly excluded.
Disables changed charts detection and version increment checking
--build-id string An optional, arbitrary identifier that is added to the name of the namespace a
chart is installed into. In a CI environment, this could be the build number or
the ID of a pull request. If not specified, the name of the chart is used
--chart-dirs strings Directories containing Helm charts. May be specified multiple times
or separate values with commas (default [charts])
--chart-repos strings Additional chart repositories for dependency resolutions.
Repositories should be formatted as 'name=url' (ex: local=http://127.0.0.1:8879/charts).
May be specified multiple times or separate values with commas
--charts strings Specific charts to test. Disables changed charts detection and
version increment checking. May be specified multiple times
or separate values with commas
--config string Config file
--debug Print CLI calls of external tools to stdout (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--exclude-deprecated Skip charts that are marked as deprecated
--excluded-charts strings Charts that should be skipped. May be specified multiple times
or separate values with commas
--github-groups Change the delimiters for github to create collapsible groups
for command output
--helm-dependency-extra-args strings Additional arguments for 'helm dependency build' (e.g. ["--skip-refresh"]
--helm-extra-args string Additional arguments for Helm. Must be passed as a single quoted string
(e.g. '--timeout 500s')
--helm-extra-set-args string Additional arguments for Helm. Must be passed as a single quoted string
(e.g. "--set=name=value"
--helm-lint-extra-args string Additional arguments for Helm lint subcommand. Must be passed as a single quoted string
(e.g. '--quiet')
--helm-repo-extra-args strings Additional arguments for the 'helm repo add' command to be
specified on a per-repo basis with an equals sign as delimiter
(e.g. 'myrepo=--username test --password secret'). May be specified
multiple times or separate values with commas
-h, --help help for install
--namespace string Namespace to install the release(s) into. If not specified, each release will be
installed in its own randomly generated namespace
--print-config Prints the configuration to stderr (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--release-label string The label to be used as a selector when inspecting resources created by charts.
This is only used if namespace is specified (default "app.kubernetes.io/instance")
--release-name string Name for the release. If not specified, is set to the chart name and a random
identifier.
--remote string The name of the Git remote used to identify changed charts (default "origin")
--since string The Git reference used to identify changed charts (default "HEAD")
--skip-clean-up Skip resources clean-up. Used if need to continue other flows or keep it around.
--skip-missing-values When --upgrade has been passed, this flag will skip testing CI values files from the
previous chart revision if they have been deleted or renamed at the current chart
revision
--target-branch string The name of the target branch used to identify changed charts (default "main")
--upgrade Whether to test an in-place upgrade of each chart from its previous revision if the
current version should not introduce a breaking change according to the SemVer spec
--use-helmignore Use .helmignore when identifying changed charts
```
### SEE ALSO
* [ct](ct.md) - The Helm chart testing tool
07070100000016000081A4000000000000000000000001683D800300001C1F000000000000000000000000000000000000003000000000chart-testing-3.13.0/doc/ct_lint-and-install.md## ct lint-and-install
Lint, install, and test a chart
### Synopsis
Combines 'lint' and 'install' commands.
```
ct lint-and-install [flags]
```
### Options
```
--additional-commands strings Additional commands to run per chart (default: [])
Commands will be executed in the same order as provided in the list and will
be rendered with go template before being executed.
Example: "helm unittest --helm3 -f tests/*.yaml {{ .Path }}"
--all Process all charts except those explicitly excluded.
Disables changed charts detection and version increment checking
--build-id string An optional, arbitrary identifier that is added to the name of the namespace a
chart is installed into. In a CI environment, this could be the build number or
the ID of a pull request. If not specified, the name of the chart is used
--chart-dirs strings Directories containing Helm charts. May be specified multiple times
or separate values with commas (default [charts])
--chart-repos strings Additional chart repositories for dependency resolutions.
Repositories should be formatted as 'name=url' (ex: local=http://127.0.0.1:8879/charts).
May be specified multiple times or separate values with commas
--chart-yaml-schema string The schema for chart.yml validation. If not specified, 'chart_schema.yaml'
is searched in the current directory, '$HOME/.ct', and '/etc/ct', in
that order.
--charts strings Specific charts to test. Disables changed charts detection and
version increment checking. May be specified multiple times
or separate values with commas
--check-version-increment Activates a check for chart version increments (default true)
--config string Config file
--debug Print CLI calls of external tools to stdout (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--exclude-deprecated Skip charts that are marked as deprecated
--excluded-charts strings Charts that should be skipped. May be specified multiple times
or separate values with commas
--github-groups Change the delimiters for github to create collapsible groups
for command output
--helm-dependency-extra-args strings Additional arguments for 'helm dependency build' (e.g. ["--skip-refresh"]
--helm-extra-args string Additional arguments for Helm. Must be passed as a single quoted string
(e.g. '--timeout 500s')
--helm-extra-set-args string Additional arguments for Helm. Must be passed as a single quoted string
(e.g. "--set=name=value"
--helm-lint-extra-args string Additional arguments for Helm lint subcommand. Must be passed as a single quoted string
(e.g. '--quiet')
--helm-repo-extra-args strings Additional arguments for the 'helm repo add' command to be
specified on a per-repo basis with an equals sign as delimiter
(e.g. 'myrepo=--username test --password secret'). May be specified
multiple times or separate values with commas
-h, --help help for lint-and-install
--lint-conf string The config file for YAML linting. If not specified, 'lintconf.yaml'
is searched in the current directory, '$HOME/.ct', and '/etc/ct', in
that order
--namespace string Namespace to install the release(s) into. If not specified, each release will be
installed in its own randomly generated namespace
--print-config Prints the configuration to stderr (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--release-label string The label to be used as a selector when inspecting resources created by charts.
This is only used if namespace is specified (default "app.kubernetes.io/instance")
--release-name string Name for the release. If not specified, is set to the chart name and a random
identifier.
--remote string The name of the Git remote used to identify changed charts (default "origin")
--since string The Git reference used to identify changed charts (default "HEAD")
--skip-clean-up Skip resources clean-up. Used if need to continue other flows or keep it around.
--skip-helm-dependencies Skip running 'helm dependency build' before linting
--skip-missing-values When --upgrade has been passed, this flag will skip testing CI values files from the
previous chart revision if they have been deleted or renamed at the current chart
revision
--target-branch string The name of the target branch used to identify changed charts (default "main")
--upgrade Whether to test an in-place upgrade of each chart from its previous revision if the
current version should not introduce a breaking change according to the SemVer spec
--use-helmignore Use .helmignore when identifying changed charts
--validate-chart-schema Enable schema validation of 'Chart.yaml' using Yamale (default true)
--validate-maintainers Enable validation of maintainer account names in chart.yml.
Works for GitHub, GitLab, and Bitbucket (default true)
--validate-yaml Enable linting of 'Chart.yaml' and values files (default true)
```
### SEE ALSO
* [ct](ct.md) - The Helm chart testing tool
07070100000017000081A4000000000000000000000001683D80030000166C000000000000000000000000000000000000002400000000chart-testing-3.13.0/doc/ct_lint.md## ct lint
Lint and validate a chart
### Synopsis
Run 'helm lint', version checking, YAML schema validation
on 'Chart.yaml', YAML linting on 'Chart.yaml' and 'values.yaml',
and maintainer validation on
* changed charts (default)
* specific charts (--charts)
* all charts (--all)
in given chart directories.
Charts may have multiple custom values files matching the glob pattern
'*-values.yaml' in a directory named 'ci' in the root of the chart's
directory. The chart is linted for each of these files. If no custom
values file is present, the chart is linted with defaults.
```
ct lint [flags]
```
### Options
```
--additional-commands strings Additional commands to run per chart (default: [])
Commands will be executed in the same order as provided in the list and will
be rendered with go template before being executed.
Example: "helm unittest --helm3 -f tests/*.yaml {{ .Path }}"
--all Process all charts except those explicitly excluded.
Disables changed charts detection and version increment checking
--chart-dirs strings Directories containing Helm charts. May be specified multiple times
or separate values with commas (default [charts])
--chart-repos strings Additional chart repositories for dependency resolutions.
Repositories should be formatted as 'name=url' (ex: local=http://127.0.0.1:8879/charts).
May be specified multiple times or separate values with commas
--chart-yaml-schema string The schema for chart.yml validation. If not specified, 'chart_schema.yaml'
is searched in the current directory, '$HOME/.ct', and '/etc/ct', in
that order.
--charts strings Specific charts to test. Disables changed charts detection and
version increment checking. May be specified multiple times
or separate values with commas
--check-version-increment Activates a check for chart version increments (default true)
--config string Config file
--debug Print CLI calls of external tools to stdout (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--exclude-deprecated Skip charts that are marked as deprecated
--excluded-charts strings Charts that should be skipped. May be specified multiple times
or separate values with commas
--github-groups Change the delimiters for github to create collapsible groups
for command output
--helm-dependency-extra-args strings Additional arguments for 'helm dependency build' (e.g. ["--skip-refresh"]
--helm-extra-args string Additional arguments for Helm. Must be passed as a single quoted string
(e.g. '--timeout 500s')
--helm-lint-extra-args string Additional arguments for Helm lint subcommand. Must be passed as a single quoted string
(e.g. '--quiet')
--helm-repo-extra-args strings Additional arguments for the 'helm repo add' command to be
specified on a per-repo basis with an equals sign as delimiter
(e.g. 'myrepo=--username test --password secret'). May be specified
multiple times or separate values with commas
-h, --help help for lint
--lint-conf string The config file for YAML linting. If not specified, 'lintconf.yaml'
is searched in the current directory, '$HOME/.ct', and '/etc/ct', in
that order
--print-config Prints the configuration to stderr (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--remote string The name of the Git remote used to identify changed charts (default "origin")
--since string The Git reference used to identify changed charts (default "HEAD")
--skip-helm-dependencies Skip running 'helm dependency build' before linting
--target-branch string The name of the target branch used to identify changed charts (default "main")
--use-helmignore Use .helmignore when identifying changed charts
--validate-chart-schema Enable schema validation of 'Chart.yaml' using Yamale (default true)
--validate-maintainers Enable validation of maintainer account names in chart.yml.
Works for GitHub, GitLab, and Bitbucket (default true)
--validate-yaml Enable linting of 'Chart.yaml' and values files (default true)
```
### SEE ALSO
* [ct](ct.md) - The Helm chart testing tool
07070100000018000081A4000000000000000000000001683D800300000604000000000000000000000000000000000000002C00000000chart-testing-3.13.0/doc/ct_list-changed.md## ct list-changed
List changed charts
### Synopsis
"List changed charts based on configured charts directories,
"remote, and target branch
```
ct list-changed [flags]
```
### Options
```
--chart-dirs strings Directories containing Helm charts. May be specified multiple times
or separate values with commas (default [charts])
--config string Config file
--exclude-deprecated Skip charts that are marked as deprecated
--excluded-charts strings Charts that should be skipped. May be specified multiple times
or separate values with commas
--github-groups Change the delimiters for github to create collapsible groups
for command output
-h, --help help for list-changed
--print-config Prints the configuration to stderr (caution: setting this may
expose sensitive data when helm-repo-extra-args contains passwords)
--remote string The name of the Git remote used to identify changed charts (default "origin")
--since string The Git reference used to identify changed charts (default "HEAD")
--target-branch string The name of the target branch used to identify changed charts (default "main")
--use-helmignore Use .helmignore when identifying changed charts
```
### SEE ALSO
* [ct](ct.md) - The Helm chart testing tool
07070100000019000081A4000000000000000000000001683D8003000000B8000000000000000000000000000000000000002700000000chart-testing-3.13.0/doc/ct_version.md## ct version
Print version information
```
ct version [flags]
```
### Options
```
-h, --help help for version
```
### SEE ALSO
* [ct](ct.md) - The Helm chart testing tool
0707010000001A000081ED000000000000000000000001683D800300000283000000000000000000000000000000000000002100000000chart-testing-3.13.0/e2e-kind.sh#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
CLUSTER_NAME=chart-testing
readonly CLUSTER_NAME
K8S_VERSION=v1.22.9
readonly K8S_VERSION
create_kind_cluster() {
kind create cluster --name "$CLUSTER_NAME" --image "kindest/node:$K8S_VERSION" --wait 60s
kubectl cluster-info || kubectl cluster-info dump
echo
kubectl get nodes
echo
echo 'Cluster ready!'
echo
}
test_e2e() {
go test -cover -race -tags=integration ./...
echo
}
cleanup() {
kind delete cluster --name "$CLUSTER_NAME"
echo 'Done!'
}
main() {
trap cleanup EXIT
create_kind_cluster
test_e2e
}
main
0707010000001B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001900000000chart-testing-3.13.0/etc0707010000001C000081A4000000000000000000000001683D8003000003F3000000000000000000000000000000000000002B00000000chart-testing-3.13.0/etc/chart_schema.yamlname: str()
home: str(required=False)
version: str()
apiVersion: str()
appVersion: any(str(), num(), required=False)
description: str(required=False)
keywords: list(str(), required=False)
sources: list(str(), required=False)
maintainers: list(include('maintainer'), required=False)
dependencies: list(include('dependency'), required=False)
icon: str(required=False)
engine: str(required=False)
condition: str(required=False)
tags: str(required=False)
deprecated: bool(required=False)
kubeVersion: str(required=False)
annotations: map(str(), str(), required=False)
type: str(required=False)
---
maintainer:
name: str()
email: str(required=False)
url: str(required=False)
---
dependency:
name: str()
version: str()
repository: str(required=False)
condition: str(required=False)
tags: list(str(), required=False)
enabled: bool(required=False)
import-values: list(any(str(), include('import-value')), required=False)
alias: str(required=False)
---
import-value:
child: str()
parent: str()
0707010000001D000081A4000000000000000000000001683D800300000409000000000000000000000000000000000000002700000000chart-testing-3.13.0/etc/lintconf.yaml---
rules:
braces:
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
brackets:
min-spaces-inside: 0
max-spaces-inside: 0
min-spaces-inside-empty: -1
max-spaces-inside-empty: -1
colons:
max-spaces-before: 0
max-spaces-after: 1
commas:
max-spaces-before: 0
min-spaces-after: 1
max-spaces-after: 1
comments:
require-starting-space: true
min-spaces-from-content: 2
document-end: disable
document-start: disable # No --- to start a file
empty-lines:
max: 2
max-start: 0
max-end: 0
hyphens:
max-spaces-after: 1
indentation:
spaces: consistent
indent-sequences: whatever # - list indentation will handle both indentation and without
check-multi-line-strings: false
key-duplicates: enable
line-length: disable # Lines can be any length
new-line-at-end-of-file: enable
new-lines:
type: unix
trailing-spaces: enable
truthy:
level: warning
0707010000001E000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001E00000000chart-testing-3.13.0/examples0707010000001F000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002D00000000chart-testing-3.13.0/examples/docker-for-mac07070100000020000081ED000000000000000000000001683D800300000A2D000000000000000000000000000000000000003800000000chart-testing-3.13.0/examples/docker-for-mac/my_test.sh#!/usr/bin/env bash
# Copyright The Helm Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
readonly IMAGE_TAG=v3.7.1
readonly IMAGE_REPOSITORY="quay.io/helmpack/chart-testing"
main() {
local testcontainer_id
testcontainer_id=$(create_testcontainer)
# shellcheck disable=SC2064
trap "docker container rm --force $testcontainer_id > /dev/null" EXIT
configure_kubectl "$testcontainer_id"
run_test
}
lookup_apiserver_container_id() {
docker container list --filter name=k8s_kube-apiserver --format '{{ .ID }}'
}
get_apiserver_arg() {
local container_id="$1"
local arg="$2"
docker container inspect "$container_id" | jq -r ".[].Args[] | capture(\"$arg=(?<arg>.*)\") | .arg"
}
create_testcontainer() {
docker container run --interactive --tty --detach \
--volume "$(pwd):/workdir" --workdir /workdir \
"$IMAGE_REPOSITORY:$IMAGE_TAG" cat
}
configure_kubectl() {
local testcontainer_id="$1"
local apiserver_id
apiserver_id=$(lookup_apiserver_container_id)
if [[ -z "$apiserver_id" ]]; then
echo "ERROR: API-Server container not found. Make sure 'Show system containers' is enabled in Docker4Mac 'Preferences'!" >&2
exit 1
fi
local ip
ip=$(get_apiserver_arg "$apiserver_id" --advertise-address)
local port
port=$(get_apiserver_arg "$apiserver_id" --secure-port)
docker cp "$HOME/.kube" "$testcontainer_id:/root/.kube"
docker exec "$testcontainer_id" kubectl config set-cluster docker-desktop "--server=https://$ip:$port"
docker exec "$testcontainer_id" kubectl config set-cluster docker-desktop --insecure-skip-tls-verify=true
docker exec "$testcontainer_id" kubectl config use-context docker-desktop
}
run_test() {
git remote add k8s https://github.com/helm/charts.git &> /dev/null || true
git fetch k8s
docker exec "$testcontainer_id" ct lint --chart-dirs stable,incubator --remote k8s
docker exec "$testcontainer_id" ct install --chart-dirs stable,incubator --remote k8s
}
main
07070100000021000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002E00000000chart-testing-3.13.0/examples/gcp-cloud-build07070100000022000081A4000000000000000000000001683D80030000017B000000000000000000000000000000000000003800000000chart-testing-3.13.0/examples/gcp-cloud-build/README.md# Chart testing example with Google Cloud Build
This example shows how to lint and test charts using [Google Cloud Build](https://cloud.google.com/cloud-build/)
Since Google Cloud Build will ignore copying over `.git` by default, you will need to initialize `git` and add a `remote`. This example assumes that there is a pre-existing GKE cluster with `helm` already installed.
07070100000023000081A4000000000000000000000001683D80030000028B000000000000000000000000000000000000003E00000000chart-testing-3.13.0/examples/gcp-cloud-build/cloudbuild.yamlsteps:
- name: 'gcr.io/cloud-builders/git'
id: 'git-init'
args: ['init']
waitFor: ['-']
- name: 'gcr.io/cloud-builders/git'
id: 'git-add-remote'
args: ['remote', 'add', 'origin', 'git@github.com:github-account/charts-repo.git']
waitFor: ['git-init']
- name: 'gcr.io/cloud-builders/kubectl'
id: 'cluster-info'
args: ['cluster-info']
waitFor: ['git-add-remote']
- name: quay.io/helmpack/chart-testing
id: 'lint-and-install-charts'
entrypoint: 'ct'
args: ['lint-and-install']
waitFor: ['cluster-info']
options:
env:
- CLOUDSDK_COMPUTE_ZONE=cluster-location
- CLOUDSDK_CONTAINER_CLUSTER=cluster-name
timeout:
3600s
07070100000024000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002200000000chart-testing-3.13.0/examples/gke07070100000025000081A4000000000000000000000001683D800300000515000000000000000000000000000000000000002D00000000chart-testing-3.13.0/examples/gke/Dockerfile# Copyright The Helm Authors. All rights reserved.
#
# 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.
FROM quay.io/helmpack/chart-testing:v3.7.1
ENV PATH /google-cloud-sdk/bin:$PATH
ARG CLOUD_SDK_VERSION=221.0.0
RUN curl -LO "https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-$CLOUD_SDK_VERSION-linux-x86_64.tar.gz" && \
tar xzf "google-cloud-sdk-$CLOUD_SDK_VERSION-linux-x86_64.tar.gz" && \
rm "google-cloud-sdk-$CLOUD_SDK_VERSION-linux-x86_64.tar.gz" && \
ln -s /lib /lib64 && \
rm -rf /google-cloud-sdk/.install/.backup && \
gcloud version
RUN gcloud config set core/disable_usage_reporting true && \
gcloud config set component_manager/disable_update_check true && \
gcloud config set metrics/environment github_docker_image
WORKDIR /workdir
07070100000026000081ED000000000000000000000001683D80030000061D000000000000000000000000000000000000002D00000000chart-testing-3.13.0/examples/gke/my_test.sh#!/usr/bin/env bash
# Copyright The Helm Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
set -o nounset
set -o pipefail
readonly IMAGE_REPOSITORY="myrepo/chart-testing"
readonly IMAGE_TAG="v1.0.0"
readonly REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel)}"
main() {
local config_container_id
config_container_id=$(docker run -ti -d \
-v "$GOOGLE_APPLICATION_CREDENTIALS:/service-account.json" \
-v "$REPO_ROOT:/workdir" \
-e "BUILD_ID=$PULL_NUMBER" \
"$IMAGE_REPOSITORY:$IMAGE_TAG" cat)
# shellcheck disable=SC2064
trap "docker rm -f $config_container_id" EXIT
docker exec "$config_container_id" gcloud auth activate-service-account --key-file /service-account.json
docker exec "$config_container_id" gcloud container clusters get-credentials my-cluster --project my-project --zone us-west1-a
docker exec "$config_container_id" kubectl cluster-info
docker exec "$config_container_id" ct lint-and-install --chart-dirs stable,incubator
}
main
07070100000027000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002300000000chart-testing-3.13.0/examples/kind07070100000028000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002D00000000chart-testing-3.13.0/examples/kind/.circleci07070100000029000081A4000000000000000000000001683D800300000246000000000000000000000000000000000000003800000000chart-testing-3.13.0/examples/kind/.circleci/config.ymlversion: 2.1
jobs:
lint-scripts:
docker:
- image: koalaman/shellcheck-alpine
steps:
- checkout
- run:
command: shellcheck -x test/e2e-kind.sh
lint-charts:
docker:
- image: quay.io/helmpack/chart-testing:v3.7.1
steps:
- checkout
- run:
command: ct lint --config test/ct.yaml
install-charts:
machine: true
steps:
- checkout
- run:
command: test/e2e-kind.sh
workflows:
version: 2
untagged-build:
jobs:
- lint-scripts
- lint-charts
- install-charts
0707010000002A000081A4000000000000000000000001683D80030000024C000000000000000000000000000000000000002D00000000chart-testing-3.13.0/examples/kind/README.md# Chart testing example with CircleCi and kind - `K`ubernetes `in` `D`ocker
`kind` is a tool for running local Kubernetes clusters using Docker container "nodes".
This example shows how to lint and test charts using CircleCi and [kind](https://github.com/kubernetes-sigs/kind).
It creates a cluster with a single control-plane node and one worker node.
The cluster configuration can be adjusted in [kind-config.yaml](test/kind-config.yaml). You can check for available configuration options in `kind` [docs](https://kind.sigs.k8s.io/docs/user/quick-start#configuring-your-kind-cluster)
0707010000002B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002800000000chart-testing-3.13.0/examples/kind/test0707010000002C000081A4000000000000000000000001683D800300000020000000000000000000000000000000000000003000000000chart-testing-3.13.0/examples/kind/test/ct.yamlhelm-extra-args: --timeout 800s
0707010000002D000081ED000000000000000000000001683D8003000005B5000000000000000000000000000000000000003400000000chart-testing-3.13.0/examples/kind/test/e2e-kind.sh#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
readonly CT_VERSION=v3.7.1
readonly KIND_VERSION=v0.12.0
readonly CLUSTER_NAME=chart-testing
readonly K8S_VERSION=v1.22.7
run_ct_container() {
echo 'Running ct container...'
docker run --rm --interactive --detach --network host --name ct \
--volume "$(pwd)/test/ct.yaml:/etc/ct/ct.yaml" \
--volume "$(pwd):/workdir" \
--workdir /workdir \
"quay.io/helmpack/chart-testing:$CT_VERSION" \
cat
echo
}
cleanup() {
echo 'Removing ct container...'
docker kill ct > /dev/null 2>&1
echo 'Done!'
}
docker_exec() {
docker exec --interactive ct "$@"
}
create_kind_cluster() {
echo 'Installing kind...'
curl -sSLo kind "https://github.com/kubernetes-sigs/kind/releases/download/$KIND_VERSION/kind-linux-amd64"
chmod +x kind
sudo mv kind /usr/local/bin/kind
kind create cluster --name "$CLUSTER_NAME" --config test/kind-config.yaml --image "kindest/node:$K8S_VERSION" --wait 60s
echo 'Copying kubeconfig to container...'
docker_exec mkdir /root/.kube
docker cp /root/.kube/kind-config ct:/root/.kube/config
docker_exec kubectl cluster-info
echo
docker_exec kubectl get nodes
echo
echo 'Cluster ready!'
echo
}
install_charts() {
docker_exec ct install
echo
}
main() {
run_ct_container
trap cleanup EXIT
create_kind_cluster
install_charts
}
main
0707010000002E000081A4000000000000000000000001683D800300000061000000000000000000000000000000000000003900000000chart-testing-3.13.0/examples/kind/test/kind-config.yamlkind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
0707010000002F000081A4000000000000000000000001683D800300000A51000000000000000000000000000000000000004400000000chart-testing-3.13.0/examples/kind/test/local-path-provisioner.yamlapiVersion: v1
kind: Namespace
metadata:
name: local-path-storage
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: local-path-provisioner-role
namespace: local-path-storage
rules:
- apiGroups: [""]
resources: ["nodes", "persistentvolumeclaims"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["endpoints", "persistentvolumes", "pods"]
verbs: ["*"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "patch"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: local-path-provisioner-bind
namespace: local-path-storage
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: local-path-provisioner-role
subjects:
- kind: ServiceAccount
name: local-path-provisioner-service-account
namespace: local-path-storage
---
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: local-path-provisioner
namespace: local-path-storage
spec:
replicas: 1
selector:
matchLabels:
app: local-path-provisioner
template:
metadata:
labels:
app: local-path-provisioner
spec:
serviceAccountName: local-path-provisioner-service-account
containers:
- name: local-path-provisioner
image: rancher/local-path-provisioner:v0.0.11
imagePullPolicy: Always
command:
- local-path-provisioner
- --debug
- start
- --config
- /etc/config/config.json
volumeMounts:
- name: config-volume
mountPath: /etc/config/
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
volumes:
- name: config-volume
configMap:
name: local-path-config
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-path
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer
reclaimPolicy: Delete
---
kind: ConfigMap
apiVersion: v1
metadata:
name: local-path-config
namespace: local-path-storage
data:
config.json: |-
{
"nodePathMap":[
{
"node":"DEFAULT_PATH_FOR_NON_LISTED_NODES",
"paths":["/opt/local-path-provisioner"]
}
]
}
07070100000030000081A4000000000000000000000001683D800300000684000000000000000000000000000000000000001C00000000chart-testing-3.13.0/go.modmodule github.com/helm/chart-testing/v3
go 1.24.0
require (
github.com/MakeNowJust/heredoc v1.0.0
github.com/Masterminds/semver v1.5.0
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/mattn/go-shellwords v1.0.12
github.com/mitchellh/go-homedir v1.1.0
github.com/spf13/cobra v1.9.1
github.com/spf13/pflag v1.0.6
github.com/spf13/viper v1.20.1
github.com/stretchr/testify v1.10.0
gopkg.in/yaml.v2 v2.4.0
helm.sh/helm/v3 v3.18.1
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.7.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.12.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/text v0.24.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
07070100000031000081A4000000000000000000000001683D80030000218E000000000000000000000000000000000000001C00000000chart-testing-3.13.0/go.sumgithub.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
helm.sh/helm/v3 v3.18.1 h1:qLhXmtqXOHQb0Xv9HJolOLlah8RWbgyzt50xrtTWAlg=
helm.sh/helm/v3 v3.18.1/go.mod h1:43QHS1W97RcoFJRk36ZBhHdTfykqBlJdsWp3yhzdq8w=
07070100000032000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001900000000chart-testing-3.13.0/pkg07070100000033000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001F00000000chart-testing-3.13.0/pkg/chart07070100000034000081A4000000000000000000000001683D800300007BE8000000000000000000000000000000000000002800000000chart-testing-3.13.0/pkg/chart/chart.go// 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 (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/Masterminds/semver"
helmignore "helm.sh/helm/v3/pkg/ignore"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/helm/chart-testing/v3/pkg/exec"
"github.com/helm/chart-testing/v3/pkg/ignore"
"github.com/helm/chart-testing/v3/pkg/tool"
"github.com/helm/chart-testing/v3/pkg/util"
)
const maxNameLength = 63
// Git is the Interface that wraps Git operations.
//
// FileExistsOnBranch checks whether file exists on the specified remote/branch.
//
// Show returns the contents of file on the specified remote/branch.
//
// AddWorktree checks out the contents of the repository at a commit ref into the specified path.
//
// RemoveWorktree removes the working tree at the specified path.
//
// MergeBase returns the SHA1 of the merge base of commit1 and commit2.
//
// ListChangedFilesInDirs diffs commit against HEAD and returns changed files for the specified dirs.
//
// GetURLForRemote returns the repo URL for the specified remote.
//
// ValidateRepository checks that the current working directory is a valid git repository,
// and returns nil if valid.
//
// BranchExists checks whether a given branch exists in the git repository.
type Git interface {
FileExistsOnBranch(file string, remote string, branch string) bool
Show(file string, remote string, branch string) (string, error)
AddWorktree(path string, ref string) error
RemoveWorktree(path string) error
MergeBase(commit1 string, commit2 string) (string, error)
ListChangedFilesInDirs(commit string, dirs ...string) ([]string, error)
GetURLForRemote(remote string) (string, error)
ValidateRepository() error
BranchExists(branch string) bool
}
// Helm is the interface that wraps Helm operations
//
// # AddRepo adds a chart repository to the local Helm configuration
//
// # BuildDependencies builds the chart's dependencies
//
// # BuildDependenciesWithArgs allows passing additional arguments to BuildDependencies
//
// LintWithValues runs `helm lint` for the given chart using the specified values file.
// Pass a zero value for valuesFile in order to run lint without specifying a values file.
//
// InstallWithValues runs `helm install` for the given chart using the specified values file.
// Pass a zero value for valuesFile in order to run install without specifying a values file.
//
// UpgradeWithValues runs `helm upgrade` against an existing release using the specified values file.
// Pass a zero value for valuesFile in order to run install without specifying a values file.
//
// Test runs `helm test` against an existing release. Set the cleanup argument to true in order
// to clean up test pods created by helm after the test command completes.
//
// DeleteRelease purges the specified Helm release.
type Helm interface {
AddRepo(name string, url string, extraArgs []string) error
BuildDependencies(chart string) error
BuildDependenciesWithArgs(chart string, extraArgs []string) error
LintWithValues(chart string, valuesFile string) error
InstallWithValues(chart string, valuesFile string, namespace string, release string) error
UpgradeWithValues(chart string, valuesFile string, namespace string, release string) error
Test(namespace string, release string) error
DeleteRelease(namespace string, release string)
Version() (string, error)
}
// Kubectl is the interface that wraps kubectl operations
//
// # DeleteNamespace deletes a namespace
//
// # WaitForDeployments waits for a deployment to become ready
//
// # GetPodsforDeployment gets all pods for a deployment
//
// # GetPods gets pods for the given args
//
// # GetEvents prints all events for namespace
//
// # DescribePod prints the pod's description
//
// # Logs prints the logs of container
//
// # GetInitContainers gets all init containers of pod
//
// GetContainers gets all containers of pod
type Kubectl interface {
CreateNamespace(namespace string) error
DeleteNamespace(namespace string)
WaitForDeployments(namespace string, selector string) error
GetPodsforDeployment(namespace string, deployment string) ([]string, error)
GetPods(args ...string) ([]string, error)
GetEvents(namespace string) error
DescribePod(namespace string, pod string) error
Logs(namespace string, pod string, container string) error
GetInitContainers(namespace string, pod string) ([]string, error)
GetContainers(namespace string, pod string) ([]string, error)
}
// Linter is the interface that wrap linting operations
//
// # YamlLint runs `yamllint` on the specified file with the specified configuration
//
// Yamale runs `yamale` on the specified file with the specified schema file
type Linter interface {
YamlLint(yamlFile string, configFile string) error
Yamale(yamlFile string, schemaFile string) error
}
// CmdExecutor is the interface
//
// RunCommand renders cmdTemplate as go template using data and executes the resulting command
type CmdExecutor interface {
RunCommand(cmdTemplate string, data interface{}) error
}
// DirectoryLister is the interface
//
// ListChildDirs lists direct child directories of parentDir given they pass the test function
type DirectoryLister interface {
ListChildDirs(parentDir string, test func(string) bool) ([]string, error)
}
// Utils is the interface that wraps chart-related methods
//
// LookupChartDir looks up the chart's root directory based on some chart file that has changed
type Utils interface {
LookupChartDir(chartDirs []string, dir string) (string, error)
}
// AccountValidator is the interface that wraps Git account validation
//
// Validate checks if account is valid on repoDomain
type AccountValidator interface {
Validate(repoDomain string, account string) error
}
// Chart represents a Helm chart, and can be initialized with the NewChart method.
type Chart struct {
path string
yaml *util.ChartYaml
ciValuesPaths []string
}
// Yaml returns the Chart metadata
func (c *Chart) Yaml() *util.ChartYaml {
return c.yaml
}
// Path returns the chart's directory path
func (c *Chart) Path() string {
return c.path
}
func (c *Chart) String() string {
return fmt.Sprintf(`%s => (version: "%s", path: "%s")`, c.yaml.Name, c.yaml.Version, c.Path())
}
// ValuesFilePathsForCI returns all file paths in the 'ci' subfolder of the chart directory matching the pattern '*-values.yaml'
func (c *Chart) ValuesFilePathsForCI() []string {
return c.ciValuesPaths
}
// HasCIValuesFile checks whether a given CI values file is present.
func (c *Chart) HasCIValuesFile(path string) bool {
fileName := filepath.Base(path)
for _, file := range c.ValuesFilePathsForCI() {
if fileName == filepath.Base(file) {
return true
}
}
return false
}
// CreateInstallParams generates a randomized release name and namespace based on the chart path
// and optional buildID. If release_name is specified, the release name is set to that string instead.
// If a buildID is specified, it will be part of the generated namespace.
func (c *Chart) CreateInstallParams(buildID string, releaseName string) (release string, namespace string) {
release = filepath.Base(c.Path())
if release == "." || release == "/" {
if releaseName != "" {
release = releaseName
} else {
yaml := c.Yaml()
release = yaml.Name
}
}
namespace = release
if buildID != "" {
namespace = fmt.Sprintf("%s-%s", namespace, buildID)
}
randomSuffix := util.RandomString(10)
release = util.SanitizeName(fmt.Sprintf("%s-%s", release, randomSuffix), maxNameLength)
namespace = util.SanitizeName(fmt.Sprintf("%s-%s", namespace, randomSuffix), maxNameLength)
return
}
// NewChart parses the path to a chart directory and allocates a new Chart object. If chartPath is
// not a valid chart directory an error is returned.
func NewChart(chartPath string) (*Chart, error) {
yaml, err := util.ReadChartYaml(chartPath)
if err != nil {
return nil, err
}
matches, _ := filepath.Glob(filepath.Join(chartPath, "ci", "*-values.yaml"))
return &Chart{chartPath, yaml, matches}, nil
}
type Testing struct {
config config.Configuration
helm Helm
kubectl Kubectl
git Git
linter Linter
cmdExecutor CmdExecutor
accountValidator AccountValidator
directoryLister DirectoryLister
utils Utils
previousRevisionWorktree string
loadRules func(string) (*helmignore.Rules, error)
}
// TestResults holds results and overall status
type TestResults struct {
OverallSuccess bool
TestResults []TestResult
}
// TestResult holds test results for a specific chart
type TestResult struct {
Chart *Chart
Error error
}
// NewTesting creates a new Testing struct with the given config.
func NewTesting(config config.Configuration, extraSetArgs string) (Testing, error) {
procExec := exec.NewProcessExecutor(config.Debug)
helmExtraArgs := strings.Fields(config.HelmExtraArgs)
helmLintExtraArgs := strings.Fields(config.HelmLintExtraArgs)
testing := Testing{
config: config,
helm: tool.NewHelm(procExec, helmExtraArgs, helmLintExtraArgs, strings.Fields(extraSetArgs)),
git: tool.NewGit(procExec),
kubectl: tool.NewKubectl(procExec, config.KubectlTimeout),
linter: tool.NewLinter(procExec),
cmdExecutor: tool.NewCmdTemplateExecutor(procExec),
accountValidator: tool.AccountValidator{},
directoryLister: util.DirectoryLister{},
utils: util.Utils{},
loadRules: ignore.LoadRules,
}
versionString, err := testing.helm.Version()
if err != nil {
return testing, err
}
version, err := semver.NewVersion(versionString)
if err != nil {
return testing, err
}
if version.Major() < 3 {
return testing, fmt.Errorf("minimum required Helm version is v3.0.0; found: %s", version)
}
return testing, nil
}
// computePreviousRevisionPath converts any file or directory path to the same path in the
// previous revision's working tree.
func (t *Testing) computePreviousRevisionPath(fileOrDirPath string) string {
return filepath.Join(t.previousRevisionWorktree, fileOrDirPath)
}
func (t *Testing) processCharts(action func(chart *Chart) TestResult) ([]TestResult, error) {
var results []TestResult // nolint: prealloc
chartDirs, err := t.FindChartDirsToBeProcessed()
if err != nil {
return nil, fmt.Errorf("failed identifying charts to process: %w", err)
} else if len(chartDirs) == 0 {
return results, nil
}
var charts []*Chart
for _, dir := range chartDirs {
chart, err := NewChart(dir)
if err != nil {
return nil, err
}
if t.config.ExcludeDeprecated && chart.yaml.Deprecated {
fmt.Printf("Chart %q is deprecated and will be ignored because '--exclude-deprecated' is set\n", chart.String())
} else {
charts = append(charts, chart)
}
}
if !t.config.GithubGroups {
fmt.Println()
util.PrintDelimiterLineToWriter(os.Stdout, "-")
fmt.Println(" Charts to be processed:")
util.PrintDelimiterLineToWriter(os.Stdout, "-")
} else {
util.GithubGroupsBegin(os.Stdout, "Charts to be processed")
}
for _, chart := range charts {
fmt.Printf(" %s\n", chart)
}
if !t.config.GithubGroups {
util.PrintDelimiterLineToWriter(os.Stdout, "-")
fmt.Println()
} else {
util.GithubGroupsEnd(os.Stdout)
}
repoArgs := map[string][]string{}
for _, repo := range t.config.HelmRepoExtraArgs {
repoSlice := strings.SplitN(repo, "=", 2)
name := repoSlice[0]
repoExtraArgs := strings.Fields(repoSlice[1])
repoArgs[name] = repoExtraArgs
}
for _, repo := range t.config.ChartRepos {
repoSlice := strings.SplitN(repo, "=", 2)
name := repoSlice[0]
url := repoSlice[1]
repoExtraArgs := repoArgs[name]
if err := t.helm.AddRepo(name, url, repoExtraArgs); err != nil {
return nil, fmt.Errorf("failed adding repo: %s=%s: %w", name, url, err)
}
}
testResults := TestResults{
OverallSuccess: true,
TestResults: results,
}
// Checkout previous chart revisions and build their dependencies
if t.config.Upgrade {
mergeBase, err := t.computeMergeBase()
if err != nil {
return results, fmt.Errorf("failed identifying merge base: %w", err)
}
// Add worktree for the target revision
worktreePath, err := os.MkdirTemp("./", "ct-previous-revision")
if err != nil {
return results, fmt.Errorf("could not create previous revision directory: %w", err)
}
t.previousRevisionWorktree = worktreePath
err = t.git.AddWorktree(worktreePath, mergeBase)
if err != nil {
return results, fmt.Errorf("could not create worktree for previous revision: %w", err)
}
defer t.git.RemoveWorktree(worktreePath) // nolint: errcheck
if !t.config.SkipHelmDependencies {
for _, chart := range charts {
if err := t.helm.BuildDependenciesWithArgs(t.computePreviousRevisionPath(chart.Path()), t.config.HelmDependencyExtraArgs); err != nil {
// Only print error (don't exit) if building dependencies for previous revision fails.
fmt.Printf("failed building dependencies for previous revision of chart %q: %v\n", chart, err.Error())
}
}
}
}
for _, chart := range charts {
if !t.config.SkipHelmDependencies {
if err := t.helm.BuildDependenciesWithArgs(chart.Path(), t.config.HelmDependencyExtraArgs); err != nil {
return nil, fmt.Errorf("failed building dependencies for chart %q: %w", chart, err)
}
}
result := action(chart)
if result.Error != nil {
testResults.OverallSuccess = false
}
results = append(results, result)
}
if testResults.OverallSuccess {
return results, nil
}
return results, fmt.Errorf("failed processing charts")
}
// LintCharts lints charts (changed, all, specific) depending on the configuration.
func (t *Testing) LintCharts() ([]TestResult, error) {
return t.processCharts(t.LintChart)
}
// InstallCharts install charts (changed, all, specific) depending on the configuration.
func (t *Testing) InstallCharts() ([]TestResult, error) {
return t.processCharts(t.InstallChart)
}
// LintAndInstallCharts first lints and then installs charts (changed, all, specific) depending on the configuration.
func (t *Testing) LintAndInstallCharts() ([]TestResult, error) {
return t.processCharts(t.LintAndInstallChart)
}
// PrintResults writes test results to stdout.
func (t *Testing) PrintResults(results []TestResult) {
if !t.config.GithubGroups {
fmt.Println()
util.PrintDelimiterLineToWriter(os.Stdout, "-")
} else {
util.GithubGroupsBegin(os.Stdout, "Test Results")
}
if results != nil {
for _, result := range results {
err := result.Error
if err != nil {
fmt.Printf(" %s %s > %s\n", "✖︎", result.Chart, err)
} else {
fmt.Printf(" %s %s\n", "✔︎", result.Chart)
}
}
} else {
fmt.Println("No chart changes detected.")
}
if !t.config.GithubGroups {
util.PrintDelimiterLineToWriter(os.Stdout, "-")
} else {
util.GithubGroupsEnd(os.Stdout)
}
}
// LintChart lints the specified chart.
func (t *Testing) LintChart(chart *Chart) TestResult {
fmt.Printf("Linting chart %q\n", chart)
result := TestResult{Chart: chart}
if t.config.CheckVersionIncrement {
if err := t.CheckVersionIncrement(chart); err != nil {
result.Error = err
return result
}
}
chartYaml := filepath.Join(chart.Path(), "Chart.yaml")
valuesYaml := filepath.Join(chart.Path(), "values.yaml")
valuesFiles := chart.ValuesFilePathsForCI()
if t.config.ValidateChartSchema {
if err := t.linter.Yamale(chartYaml, t.config.ChartYamlSchema); err != nil {
result.Error = err
return result
}
}
if t.config.ValidateYaml {
yamlFiles := append([]string{chartYaml, valuesYaml}, valuesFiles...)
for _, yamlFile := range yamlFiles {
if err := t.linter.YamlLint(yamlFile, t.config.LintConf); err != nil {
result.Error = err
return result
}
}
}
if t.config.ValidateMaintainers {
if err := t.ValidateMaintainers(chart); err != nil {
result.Error = err
return result
}
}
for _, cmd := range t.config.AdditionalCommands {
if err := t.cmdExecutor.RunCommand(cmd, chart); err != nil {
result.Error = err
return result
}
}
// Lint with defaults if no values files are specified.
if len(valuesFiles) == 0 {
valuesFiles = append(valuesFiles, "")
}
for _, valuesFile := range valuesFiles {
if valuesFile != "" {
fmt.Printf("\nLinting chart with values file %q...\n\n", valuesFile)
}
if err := t.helm.LintWithValues(chart.Path(), valuesFile); err != nil {
result.Error = err
break
}
}
return result
}
// InstallChart installs the specified chart into a new namespace, waits for resources to become ready, and eventually
// uninstalls it and deletes the namespace again.
func (t *Testing) InstallChart(chart *Chart) TestResult {
var result TestResult
if t.config.Upgrade {
// Test upgrade from previous version
result = t.UpgradeChart(chart)
if result.Error != nil {
return result
}
// Test upgrade of current version (related: https://github.com/helm/chart-testing/issues/19)
if err := t.doUpgrade(chart, chart, true); err != nil {
result.Error = err
return result
}
}
result = TestResult{Chart: chart}
if err := t.doInstall(chart); err != nil {
result.Error = err
}
return result
}
// UpgradeChart tests in-place upgrades of the specified chart relative to its previous revisions. If the
// initial install or helm test of a previous revision of the chart fails, that release is ignored and no
// error will be returned. If the latest revision of the chart introduces a potentially breaking change
// according to the SemVer specification, upgrade testing will be skipped.
func (t *Testing) UpgradeChart(chart *Chart) TestResult {
result := TestResult{Chart: chart}
breakingChangeAllowed, err := t.checkBreakingChangeAllowed(chart)
if breakingChangeAllowed {
if err != nil {
fmt.Printf("Skipping upgrade test of %q because: %v\n", chart, err.Error())
}
return result
} else if err != nil {
fmt.Printf("Error comparing chart versions for %q\n", chart)
result.Error = err
return result
}
if oldChart, err := NewChart(t.computePreviousRevisionPath(chart.Path())); err == nil {
result.Error = t.doUpgrade(oldChart, chart, false)
}
return result
}
func (t *Testing) doInstall(chart *Chart) error {
fmt.Printf("Installing chart %q...\n", chart)
valuesFiles := chart.ValuesFilePathsForCI()
// Test with defaults if no values files are specified.
if len(valuesFiles) == 0 {
valuesFiles = append(valuesFiles, "")
}
for _, valuesFile := range valuesFiles {
if valuesFile != "" {
fmt.Printf("\nInstalling chart with values file %q...\n\n", valuesFile)
}
// Use anonymous function. Otherwise deferred calls would pile up
// and be executed in reverse order after the loop.
fun := func() error {
namespace, release, releaseSelector, cleanup := t.generateInstallConfig(chart)
if !t.config.SkipCleanUp {
defer cleanup()
}
if t.config.Namespace == "" {
if err := t.kubectl.CreateNamespace(namespace); err != nil {
return err
}
}
if err := t.helm.InstallWithValues(chart.Path(), valuesFile, namespace, release); err != nil {
return err
}
return t.testRelease(namespace, release, releaseSelector)
}
if err := fun(); err != nil {
return err
}
}
return nil
}
func (t *Testing) doUpgrade(oldChart, newChart *Chart, oldChartMustPass bool) error {
fmt.Printf("Testing upgrades of chart %q relative to previous revision %q...\n", newChart, oldChart)
valuesFiles := oldChart.ValuesFilePathsForCI()
if len(valuesFiles) == 0 {
valuesFiles = append(valuesFiles, "")
}
for _, valuesFile := range valuesFiles {
if valuesFile != "" {
if t.config.SkipMissingValues && !newChart.HasCIValuesFile(valuesFile) {
fmt.Printf("Upgrade testing for values file %q skipped because a corresponding values file was not found in %s/ci\n", valuesFile, newChart.Path())
continue
}
fmt.Printf("\nInstalling chart %q with values file %q...\n\n", oldChart, valuesFile)
}
// Use anonymous function. Otherwise deferred calls would pile up
// and be executed in reverse order after the loop.
fun := func() error {
namespace, release, releaseSelector, cleanup := t.generateInstallConfig(oldChart)
if !t.config.SkipCleanUp {
defer cleanup()
}
if t.config.Namespace == "" {
if err := t.kubectl.CreateNamespace(namespace); err != nil {
return err
}
}
// Install previous version of chart. If installation fails, ignore this release.
if err := t.helm.InstallWithValues(oldChart.Path(), valuesFile, namespace, release); err != nil {
if oldChartMustPass {
return err
}
fmt.Printf("Upgrade testing for release %q skipped because of previous revision installation error: %v\n", release, err.Error())
return nil
}
if err := t.testRelease(namespace, release, releaseSelector); err != nil {
if oldChartMustPass {
return err
}
fmt.Printf("Upgrade testing for release %q skipped because of previous revision testing error: %v\n", release, err.Error())
return nil
}
if err := t.helm.UpgradeWithValues(newChart.Path(), valuesFile, namespace, release); err != nil {
return err
}
return t.testRelease(namespace, release, releaseSelector)
}
if err := fun(); err != nil {
return err
}
}
return nil
}
func (t *Testing) testRelease(namespace, release, releaseSelector string) error {
if err := t.kubectl.WaitForDeployments(namespace, releaseSelector); err != nil {
return err
}
return t.helm.Test(namespace, release)
}
func (t *Testing) generateInstallConfig(chart *Chart) (namespace, release, releaseSelector string, cleanup func()) {
if t.config.Namespace != "" {
namespace = t.config.Namespace
release, _ = chart.CreateInstallParams(t.config.BuildID, t.config.ReleaseName)
releaseSelector = fmt.Sprintf("%s=%s", t.config.ReleaseLabel, release)
cleanup = func() {
t.PrintEventsPodDetailsAndLogs(namespace, releaseSelector)
t.helm.DeleteRelease(namespace, release)
}
} else {
release, namespace = chart.CreateInstallParams(t.config.BuildID, t.config.ReleaseName)
cleanup = func() {
t.PrintEventsPodDetailsAndLogs(namespace, releaseSelector)
t.helm.DeleteRelease(namespace, release)
t.kubectl.DeleteNamespace(namespace)
}
}
return
}
// LintAndInstallChart first lints and then installs the specified chart.
func (t *Testing) LintAndInstallChart(chart *Chart) TestResult {
result := t.LintChart(chart)
if result.Error != nil {
return result
}
return t.InstallChart(chart)
}
// FindChartDirsToBeProcessed identifies charts to be processed depending on the configuration
// (changed charts, all charts, or specific charts).
func (t *Testing) FindChartDirsToBeProcessed() ([]string, error) {
cfg := t.config
if cfg.ProcessAllCharts {
return t.ReadAllChartDirectories()
} else if len(cfg.Charts) > 0 {
return t.config.Charts, nil
}
return t.ComputeChangedChartDirectories()
}
func (t *Testing) computeMergeBase() (string, error) {
err := t.git.ValidateRepository()
if err != nil {
return "", errors.New("must be in a git repository")
}
branch := fmt.Sprintf("%s/%s", t.config.Remote, t.config.TargetBranch)
if !t.git.BranchExists(branch) {
return "", fmt.Errorf("targetBranch '%s' does not exist", branch)
}
return t.git.MergeBase(branch, t.config.Since)
}
// ComputeChangedChartDirectories takes the merge base of HEAD and the configured remote and target branch and computes a
// slice of changed charts from that in the configured chart directories excluding those configured to be excluded.
func (t *Testing) ComputeChangedChartDirectories() ([]string, error) {
cfg := t.config
mergeBase, err := t.computeMergeBase()
if err != nil {
return nil, err
}
allChangedChartFiles, err := t.git.ListChangedFilesInDirs(mergeBase, cfg.ChartDirs...)
if err != nil {
return nil, fmt.Errorf("failed creating diff: %w", err)
}
changedChartFiles := map[string][]string{}
for _, file := range allChangedChartFiles {
pathElements := strings.SplitN(filepath.ToSlash(file), "/", 3)
if len(pathElements) < 2 || util.StringSliceContains(cfg.ExcludedCharts, pathElements[1]) {
continue
}
dir := filepath.Dir(file)
// Make sure directory is really a chart directory
chartDir, err := t.utils.LookupChartDir(cfg.ChartDirs, dir)
chartDirElement := strings.Split(chartDir, "/")
if err == nil {
if len(chartDirElement) > 1 {
chartDirName := chartDirElement[len(chartDirElement)-1]
if util.StringSliceContains(cfg.ExcludedCharts, chartDirName) {
continue
}
}
changedChartFiles[chartDir] = append(changedChartFiles[chartDir], strings.TrimPrefix(file, chartDir+"/"))
} else {
fmt.Fprintf(os.Stderr, "Directory %q is not a valid chart directory. Skipping...\n", dir)
}
}
changedChartDirs := []string{}
if t.config.UseHelmignore {
for chartDir, changedChartFiles := range changedChartFiles {
rules, err := t.loadRules(chartDir)
if err != nil {
return nil, err
}
filteredChartFiles, err := ignore.FilterFiles(changedChartFiles, rules)
if err != nil {
return nil, err
}
if len(filteredChartFiles) > 0 {
changedChartDirs = append(changedChartDirs, chartDir)
}
}
} else {
for chartDir := range changedChartFiles {
changedChartDirs = append(changedChartDirs, chartDir)
}
}
return changedChartDirs, nil
}
// ReadAllChartDirectories returns a slice of all charts in the configured chart directories except those
// configured to be excluded.
func (t *Testing) ReadAllChartDirectories() ([]string, error) {
cfg := t.config
var chartDirs []string
for _, chartParentDir := range cfg.ChartDirs {
dirs, err := t.directoryLister.ListChildDirs(chartParentDir,
func(dir string) bool {
_, err := t.utils.LookupChartDir(cfg.ChartDirs, dir)
return err == nil && !util.StringSliceContains(cfg.ExcludedCharts, filepath.Base(dir))
})
if err != nil {
return nil, fmt.Errorf("failed reading chart directories: %w", err)
}
chartDirs = append(chartDirs, dirs...)
}
return chartDirs, nil
}
// CheckVersionIncrement checks that the new chart version is greater than the old one using semantic version comparison.
func (t *Testing) CheckVersionIncrement(chart *Chart) error {
fmt.Printf("Checking chart %q for a version bump...\n", chart)
oldVersion, err := t.GetOldChartVersion(chart.Path())
if err != nil {
return err
}
if oldVersion == "" {
// new chart, skip version check
return nil
}
fmt.Println("Old chart version:", oldVersion)
chartYaml := chart.Yaml()
newVersion := chartYaml.Version
fmt.Println("New chart version:", newVersion)
result, err := util.CompareVersions(oldVersion, newVersion)
if err != nil {
return err
}
if result >= 0 {
return errors.New("chart version not ok. Needs a version bump! ")
}
fmt.Println("Chart version ok.")
return nil
}
func (t *Testing) checkBreakingChangeAllowed(chart *Chart) (allowed bool, err error) {
oldVersion, err := t.GetOldChartVersion(chart.Path())
if err != nil {
return false, err
}
if oldVersion == "" {
// new chart, skip upgrade check
return true, fmt.Errorf("chart has no previous revision")
}
newVersion := chart.Yaml().Version
return util.BreakingChangeAllowed(oldVersion, newVersion)
}
// GetOldChartVersion gets the version of the old Chart.yaml file from the target branch.
func (t *Testing) GetOldChartVersion(chartPath string) (string, error) {
cfg := t.config
chartYamlFile := filepath.Join(chartPath, "Chart.yaml")
if !t.git.FileExistsOnBranch(chartYamlFile, cfg.Remote, cfg.TargetBranch) {
fmt.Printf("Unable to find chart on %s. New chart detected.\n", cfg.TargetBranch)
return "", nil
}
chartYamlContents, err := t.git.Show(chartYamlFile, cfg.Remote, cfg.TargetBranch)
if err != nil {
return "", fmt.Errorf("failed reading old Chart.yaml: %w", err)
}
chartYaml, err := util.UnmarshalChartYaml([]byte(chartYamlContents))
if err != nil {
return "", fmt.Errorf("failed reading old chart version: %w", err)
}
return chartYaml.Version, nil
}
// ValidateMaintainers validates maintainers in the Chart.yaml file. Maintainer names must be valid accounts
// (GitHub, Bitbucket, GitLab) names. Deprecated charts must not have maintainers.
func (t *Testing) ValidateMaintainers(chart *Chart) error {
fmt.Println("Validating maintainers...")
chartYaml := chart.Yaml()
if chartYaml.Deprecated {
if len(chartYaml.Maintainers) > 0 {
return errors.New("deprecated chart must not have maintainers")
}
return nil
}
if len(chartYaml.Maintainers) == 0 {
return errors.New("chart doesn't have maintainers")
}
repoURL, err := t.git.GetURLForRemote(t.config.Remote)
if err != nil {
return err
}
for _, maintainer := range chartYaml.Maintainers {
if err := t.accountValidator.Validate(repoURL, maintainer.Name); err != nil {
return err
}
}
return nil
}
func (t *Testing) PrintEventsPodDetailsAndLogs(namespace string, selector string) {
util.PrintDelimiterLineToWriter(os.Stdout, "=")
t.printDetails(namespace, "Events of namespace", ".", func(_ string) error {
return t.kubectl.GetEvents(namespace)
}, namespace)
pods, err := t.kubectl.GetPods(
"--no-headers",
"--namespace",
namespace,
"--selector",
selector,
"--output",
"jsonpath={.items[*].metadata.name}",
)
if err != nil {
fmt.Println("Error printing logs:", err)
return
}
for _, pod := range pods {
t.printDetails(pod, "Description of pod", "~", func(_ string) error {
return t.kubectl.DescribePod(namespace, pod)
}, pod)
initContainers, err := t.kubectl.GetInitContainers(namespace, pod)
if err != nil {
fmt.Println("Error printing logs:", err)
return
}
if t.config.PrintLogs {
t.printDetails(pod, "Logs of init container", "-",
func(item string) error {
return t.kubectl.Logs(namespace, pod, item)
}, initContainers...)
containers, err := t.kubectl.GetContainers(namespace, pod)
if err != nil {
fmt.Printf("failed printing logs: %v\n", err.Error())
return
}
t.printDetails(pod, "Logs of container", "-",
func(item string) error {
return t.kubectl.Logs(namespace, pod, item)
},
containers...)
}
}
util.PrintDelimiterLineToWriter(os.Stdout, "=")
}
func (t *Testing) printDetails(resource string, text string, delimiterChar string, printFunc func(item string) error, items ...string) {
for _, item := range items {
item = strings.Trim(item, "'")
if !t.config.GithubGroups {
util.PrintDelimiterLineToWriter(os.Stdout, delimiterChar)
fmt.Printf("==> %s %s\n", text, resource)
util.PrintDelimiterLineToWriter(os.Stdout, delimiterChar)
} else {
util.GithubGroupsBegin(os.Stdout, fmt.Sprintf("%s %s", text, resource))
}
if err := printFunc(item); err != nil {
fmt.Println("Error printing details:", err)
return
}
if !t.config.GithubGroups {
util.PrintDelimiterLineToWriter(os.Stdout, delimiterChar)
fmt.Printf("<== %s %s\n", text, resource)
util.PrintDelimiterLineToWriter(os.Stdout, delimiterChar)
} else {
util.GithubGroupsEnd(os.Stdout)
}
}
}
07070100000035000081A4000000000000000000000001683D800300003A3E000000000000000000000000000000000000002D00000000chart-testing-3.13.0/pkg/chart/chart_test.go// 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 (
"fmt"
"strings"
"testing"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/helm/chart-testing/v3/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
helmignore "helm.sh/helm/v3/pkg/ignore"
)
type fakeGit struct{}
func (g fakeGit) FileExistsOnBranch(_ string, _ string, _ string) bool {
return true
}
func (g fakeGit) Show(_ string, _ string, _ string) (string, error) {
return "", nil
}
func (g fakeGit) MergeBase(_ string, _ string) (string, error) {
return "HEAD", nil
}
func (g fakeGit) ListChangedFilesInDirs(_ string, _ ...string) ([]string, error) {
return []string{
"test_charts/foo/Chart.yaml",
"test_charts/bar/Chart.yaml",
"test_charts/bar/bar_sub/templates/bar_sub.yaml",
"test_charts/excluded/Chart.yaml",
"test_chart_at_root/templates/foo.yaml",
"test_chart_at_multi_level/foo/bar/Chart.yaml",
"test_chart_at_multi_level/foo/baz/Chart.yaml",
"test_chart_at_multi_level/foo/excluded/Chart.yaml",
"some_non_chart_dir/some_non_chart_file",
"some_non_chart_file",
}, nil
}
func (g fakeGit) AddWorktree(_ string, _ string) error {
return nil
}
func (g fakeGit) RemoveWorktree(_ string) error {
return nil
}
func (g fakeGit) GetURLForRemote(_ string) (string, error) {
return "git@github.com/helm/chart-testing", nil
}
func (g fakeGit) ValidateRepository() error {
return nil
}
func (g fakeGit) BranchExists(_ string) bool {
return true
}
type fakeAccountValidator struct{}
func (v fakeAccountValidator) Validate(_ string, account string) error {
if strings.HasPrefix(account, "valid") {
return nil
}
return fmt.Errorf("failed validating account: %s", account)
}
type fakeLinter struct {
mock.Mock
}
func (l *fakeLinter) YamlLint(yamlFile, configFile string) error {
l.Called(yamlFile, configFile)
return nil
}
func (l *fakeLinter) Yamale(yamlFile, schemaFile string) error {
l.Called(yamlFile, schemaFile)
return nil
}
type fakeHelm struct {
mock.Mock
}
func (h *fakeHelm) AddRepo(_, _ string, _ []string) error { return nil }
func (h *fakeHelm) BuildDependencies(_ string) error { return nil }
func (h *fakeHelm) BuildDependenciesWithArgs(chart string, extraArgs []string) error {
h.Called(chart, extraArgs)
return nil
}
func (h *fakeHelm) LintWithValues(_ string, _ string) error { return nil }
func (h *fakeHelm) InstallWithValues(_ string, _ string, _ string, _ string) error {
return nil
}
func (h *fakeHelm) UpgradeWithValues(_ string, _ string, _ string, _ string) error {
return nil
}
func (h *fakeHelm) Test(_ string, _ string) error {
return nil
}
func (h *fakeHelm) DeleteRelease(_ string, _ string) {}
func (h *fakeHelm) Version() (string, error) {
return "v3.0.0", nil
}
type fakeCmdExecutor struct {
mock.Mock
}
func (c *fakeCmdExecutor) RunCommand(cmdTemplate string, data interface{}) error {
c.Called(cmdTemplate, data)
return nil
}
var ct Testing
func init() {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
ChartDirs: []string{"test_charts", "."},
}
ct = newTestingMock(cfg)
}
func newTestingMock(cfg config.Configuration) Testing {
fakeMockLinter := new(fakeLinter)
return Testing{
config: cfg,
directoryLister: util.DirectoryLister{},
git: fakeGit{},
utils: util.Utils{},
accountValidator: fakeAccountValidator{},
linter: fakeMockLinter,
helm: new(fakeHelm),
loadRules: func(dir string) (*helmignore.Rules, error) {
rules := helmignore.Empty()
if dir == "test_charts/foo" {
var err error
rules, err = helmignore.Parse(strings.NewReader("Chart.yaml\n"))
if err != nil {
return nil, err
}
rules.AddDefaults()
}
if dir == "test_chart_at_multi_level/foo/baz" {
var err error
rules, err = helmignore.Parse(strings.NewReader("Chart.yaml\n"))
if err != nil {
return nil, err
}
rules.AddDefaults()
}
return rules, nil
},
}
}
func TestComputeChangedChartDirectories(t *testing.T) {
actual, err := ct.ComputeChangedChartDirectories()
expected := []string{"test_charts/foo", "test_charts/bar", "test_chart_at_root"}
for _, chart := range actual {
assert.Contains(t, expected, chart)
}
assert.Len(t, actual, 3)
assert.Nil(t, err)
}
func TestComputeChangedChartDirectoriesWithHelmignore(t *testing.T) {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
ChartDirs: []string{"test_charts", "."},
UseHelmignore: true,
}
ct := newTestingMock(cfg)
actual, err := ct.ComputeChangedChartDirectories()
expected := []string{"test_charts/bar", "test_chart_at_root"}
assert.Nil(t, err)
assert.ElementsMatch(t, expected, actual)
}
func TestComputeChangedChartDirectoriesWithMultiLevelChart(t *testing.T) {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
ChartDirs: []string{"test_chart_at_multi_level/foo"},
}
ct := newTestingMock(cfg)
actual, err := ct.ComputeChangedChartDirectories()
expected := []string{"test_chart_at_multi_level/foo/bar", "test_chart_at_multi_level/foo/baz"}
for _, chart := range actual {
assert.Contains(t, expected, chart)
}
assert.Len(t, actual, 2)
assert.Nil(t, err)
}
func TestComputeChangedChartDirectoriesWithMultiLevelChartWithHelmIgnore(t *testing.T) {
cfg := config.Configuration{
ExcludedCharts: []string{"excluded"},
ChartDirs: []string{"test_chart_at_multi_level/foo"},
UseHelmignore: true,
}
ct := newTestingMock(cfg)
actual, err := ct.ComputeChangedChartDirectories()
expected := []string{"test_chart_at_multi_level/foo/bar"}
assert.Nil(t, err)
assert.ElementsMatch(t, expected, actual)
}
func TestReadAllChartDirectories(t *testing.T) {
actual, err := ct.ReadAllChartDirectories()
expected := []string{
"test_charts/foo",
"test_charts/bar",
"test_charts/must-pass-upgrade-install",
"test_charts/mutating-deployment-selector",
"test_charts/simple-deployment",
"test_charts/simple-deployment-different-selector",
"test_charts/mutating-sfs-volumeclaim",
"test_chart_at_root",
}
for _, chart := range actual {
assert.Contains(t, expected, chart)
}
assert.Len(t, actual, 8)
assert.Nil(t, err)
}
func TestValidateMaintainers(t *testing.T) {
var testDataSlice = []struct {
name string
chartDir string
expected bool
}{
{"valid", "testdata/valid_maintainers", true},
{"invalid", "testdata/invalid_maintainers", false},
{"no-maintainers", "testdata/no_maintainers", false},
{"empty-maintainers", "testdata/empty_maintainers", false},
{"valid-deprecated", "testdata/valid_maintainers_deprecated", false},
{"no-maintainers-deprecated", "testdata/no_maintainers_deprecated", true},
}
for _, testData := range testDataSlice {
t.Run(testData.name, func(t *testing.T) {
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
validationErr := ct.ValidateMaintainers(chart)
assert.Equal(t, testData.expected, validationErr == nil)
})
}
}
func TestLintChartMaintainerValidation(t *testing.T) {
type testData struct {
name string
chartDir string
expected bool
}
runTests := func(validate bool) {
ct.config.ValidateMaintainers = validate
var suffix string
if validate {
suffix = "with-validation"
} else {
suffix = "without-validation"
}
testCases := []testData{
{fmt.Sprintf("maintainers-%s", suffix), "testdata/valid_maintainers", true},
{fmt.Sprintf("no-maintainers-%s", suffix), "testdata/no_maintainers", !validate},
}
for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
result := ct.LintChart(chart)
assert.Equal(t, testData.expected, result.Error == nil)
})
}
}
runTests(true)
runTests(false)
}
func TestLintChartSchemaValidation(t *testing.T) {
type testData struct {
name string
chartDir string
expected bool
}
runTests := func(validate bool, callsYamlLint int, callsYamale int) {
fakeMockLinter := new(fakeLinter)
fakeMockLinter.On("Yamale", mock.Anything, mock.Anything).Return(true)
fakeMockLinter.On("YamlLint", mock.Anything, mock.Anything).Return(true)
ct.linter = fakeMockLinter
ct.config.ValidateChartSchema = validate
ct.config.ValidateMaintainers = false
ct.config.ValidateYaml = false
var suffix string
if validate {
suffix = "with-validation"
} else {
suffix = "without-validation"
}
testCases := []testData{
{fmt.Sprintf("schema-%s", suffix), "testdata/test_lints", true},
}
for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
result := ct.LintChart(chart)
assert.Equal(t, testData.expected, result.Error == nil)
fakeMockLinter.AssertNumberOfCalls(t, "Yamale", callsYamale)
fakeMockLinter.AssertNumberOfCalls(t, "YamlLint", callsYamlLint)
})
}
}
runTests(true, 0, 1)
runTests(false, 0, 0)
}
func TestLintYamlValidation(t *testing.T) {
type testData struct {
name string
chartDir string
expected bool
}
runTests := func(validate bool, callsYamlLint int, callsYamale int) {
fakeMockLinter := new(fakeLinter)
fakeMockLinter.On("Yamale", mock.Anything, mock.Anything).Return(true)
fakeMockLinter.On("YamlLint", mock.Anything, mock.Anything).Return(true)
ct.linter = fakeMockLinter
ct.config.ValidateYaml = validate
ct.config.ValidateChartSchema = false
ct.config.ValidateMaintainers = false
var suffix string
if validate {
suffix = "with-validation"
} else {
suffix = "without-validation"
}
testCases := []testData{
{fmt.Sprintf("lint-%s", suffix), "testdata/test_lints", true},
}
for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
chart, err := NewChart(testData.chartDir)
assert.Nil(t, err)
result := ct.LintChart(chart)
assert.Equal(t, testData.expected, result.Error == nil)
fakeMockLinter.AssertNumberOfCalls(t, "Yamale", callsYamale)
fakeMockLinter.AssertNumberOfCalls(t, "YamlLint", callsYamlLint)
})
}
}
runTests(true, 2, 0)
runTests(false, 0, 0)
}
func TestLintDependencyExtraArgs(t *testing.T) {
chart := "testdata/test_lints"
args := []string{"--skip-refresh"}
fakeMockHelm := new(fakeHelm)
ct.helm = fakeMockHelm
ct.config.HelmDependencyExtraArgs = args
ct.config.Charts = []string{chart}
t.Run("lint-helm-dependency-extra-args", func(t *testing.T) {
call := fakeMockHelm.On("BuildDependenciesWithArgs", chart, args).Return(nil)
call.Repeatability = 1
results, err := ct.LintCharts()
assert.Nil(t, err)
for _, result := range results {
assert.Nil(t, result.Error)
}
// -1 is set after Repeatability runs out
assert.Equal(t, -1, call.Repeatability)
})
}
func TestGenerateInstallConfig(t *testing.T) {
type testData struct {
name string
cfg config.Configuration
chart *Chart
}
testCases := []testData{
{
"custom namespace",
config.Configuration{
Namespace: "default",
ReleaseLabel: "app.kubernetes.io/instance",
},
&Chart{
yaml: &util.ChartYaml{
Name: "bar",
},
},
},
{
"random namespace",
config.Configuration{
ReleaseLabel: "app.kubernetes.io/instance",
},
&Chart{
yaml: &util.ChartYaml{
Name: "bar",
},
},
},
{
"long chart name",
config.Configuration{
ReleaseLabel: "app.kubernetes.io/instance",
},
&Chart{
yaml: &util.ChartYaml{
Name: "test_charts/barbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbarbar",
},
},
},
}
for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
ct := newTestingMock(testData.cfg)
namespace, release, releaseSelector, _ := ct.generateInstallConfig(testData.chart)
assert.NotEqual(t, "", namespace)
assert.NotEqual(t, "", release)
assert.True(t, len(release) < 64, "release should be less than 64 chars")
assert.True(t, len(namespace) < 64, "namespace should be less than 64 chars")
if testData.cfg.Namespace != "" {
assert.Equal(t, testData.cfg.Namespace, namespace)
assert.Equal(t, fmt.Sprintf("%s=%s", testData.cfg.ReleaseLabel, release), releaseSelector)
} else {
assert.Equal(t, "", releaseSelector)
assert.Contains(t, namespace, release)
}
})
}
}
func TestChart_HasCIValuesFile(t *testing.T) {
type testData struct {
name string
chart *Chart
file string
expected bool
}
testCases := []testData{
{
name: "has file",
chart: &Chart{
ciValuesPaths: []string{"foo-values.yaml"},
},
file: "foo-values.yaml",
expected: true,
},
{
name: "different paths",
chart: &Chart{
ciValuesPaths: []string{"ci/foo-values.yaml"},
},
file: "foo/bar/foo-values.yaml",
expected: true,
},
{
name: "does not have file",
chart: &Chart{
ciValuesPaths: []string{"foo-values.yaml"},
},
file: "bar-values.yaml",
expected: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
actual := tc.chart.HasCIValuesFile(tc.file)
assert.Equal(t, tc.expected, actual)
})
}
}
func TestChart_AdditionalCommandsAreRun(t *testing.T) {
type testData struct {
name string
cfg config.Configuration
callsRunCommand int
}
testCases := []testData{
{
name: "no additional commands",
cfg: config.Configuration{},
callsRunCommand: 0,
},
{
name: "one command",
cfg: config.Configuration{
AdditionalCommands: []string{"helm unittest --helm3 -f tests/*.yaml {{ .Path }}"},
},
callsRunCommand: 1,
},
{
name: "multiple commands",
cfg: config.Configuration{
AdditionalCommands: []string{"echo", "helm unittest --helm3 -f tests/*.yaml {{ .Path }}"},
},
callsRunCommand: 2,
},
}
for _, testData := range testCases {
t.Run(testData.name, func(t *testing.T) {
fakeCmdExecutor := new(fakeCmdExecutor)
fakeCmdExecutor.On("RunCommand", mock.Anything, mock.Anything).Return(nil)
ct := newTestingMock(testData.cfg)
ct.cmdExecutor = fakeCmdExecutor
ct.LintChart(&Chart{})
fakeCmdExecutor.AssertNumberOfCalls(t, "RunCommand", testData.callsRunCommand)
})
}
}
07070100000036000081A4000000000000000000000001683D8003000011D6000000000000000000000000000000000000003300000000chart-testing-3.13.0/pkg/chart/integration_test.go// 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.
//go:build integration
// +build integration
package chart
import (
"fmt"
"strings"
"testing"
"time"
"github.com/helm/chart-testing/v3/pkg/config"
"github.com/helm/chart-testing/v3/pkg/exec"
"github.com/helm/chart-testing/v3/pkg/tool"
"github.com/helm/chart-testing/v3/pkg/util"
"github.com/stretchr/testify/assert"
)
func newTestingHelmIntegration(cfg config.Configuration, extraSetArgs string) Testing {
fakeMockLinter := new(fakeLinter)
procExec := exec.NewProcessExecutor(true)
extraArgs := strings.Fields(cfg.HelmExtraArgs)
extraLintArgs := strings.Fields(cfg.HelmLintExtraArgs)
return Testing{
config: cfg,
directoryLister: util.DirectoryLister{},
git: fakeGit{},
utils: util.Utils{},
accountValidator: fakeAccountValidator{},
linter: fakeMockLinter,
helm: tool.NewHelm(procExec, extraArgs, extraLintArgs, strings.Fields(extraSetArgs)),
kubectl: tool.NewKubectl(procExec, 30*time.Second),
}
}
func TestInstallChart(t *testing.T) {
type testCase struct {
name string
cfg config.Configuration
chartDir string
output TestResult
extraSet string
}
cases := []testCase{
{
"install only in custom namespace",
config.Configuration{
Debug: true,
Namespace: "foobar",
ReleaseLabel: "app.kubernetes.io/instance",
},
"test_charts/must-pass-upgrade-install",
TestResult{mustNewChart("test_charts/must-pass-upgrade-install"), nil},
"",
},
{
"install only in random namespace",
config.Configuration{
Debug: true,
},
"test_charts/must-pass-upgrade-install",
TestResult{mustNewChart("test_charts/must-pass-upgrade-install"), nil},
"",
},
{
"install with override set",
config.Configuration{
Debug: true,
},
"test_charts/must-pass-upgrade-install",
TestResult{mustNewChart("test_charts/must-pass-upgrade-install"), nil},
"--set=image.tag=latest",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
ct := newTestingHelmIntegration(tc.cfg, tc.extraSet)
namespace := tc.cfg.Namespace
if namespace != "" {
ct.kubectl.CreateNamespace(namespace)
defer ct.kubectl.DeleteNamespace(namespace)
}
result := ct.InstallChart(mustNewChart(tc.chartDir))
if result.Error != tc.output.Error {
if result.Error != nil && tc.output.Error != nil {
assert.Equal(t, tc.output.Error.Error(), result.Error.Error())
} else {
assert.Equal(t, tc.output.Error, result.Error)
}
}
})
}
}
func TestUpgradeChart(t *testing.T) {
type testCase struct {
name string
old string
new string
err error
}
cfg := config.Configuration{
Debug: true,
Upgrade: true,
}
ct := newTestingHelmIntegration(cfg, "")
processError := fmt.Errorf("failed waiting for process: exit status 1")
cases := []testCase{
{
"upgrade nginx",
"test_charts/must-pass-upgrade-install",
"test_charts/must-pass-upgrade-install",
nil,
},
{
"change immutable deployment.spec.selector field",
"test_charts/mutating-deployment-selector",
"test_charts/mutating-deployment-selector",
processError,
},
{
"change immutable statefulset.spec.volumeClaimTemplates field",
"test_charts/mutating-sfs-volumeclaim",
"test_charts/mutating-sfs-volumeclaim",
processError,
},
{
"change immutable deployment.spec.selector.matchLabels field",
"test_charts/simple-deployment",
"test_charts/simple-deployment-different-selector",
processError,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := ct.doUpgrade(mustNewChart(tc.old), mustNewChart(tc.new), true)
if err != tc.err {
if err != nil && tc.err != nil {
assert.Equal(t, tc.err.Error(), err.Error())
} else {
assert.Equal(t, tc.err, err)
}
}
})
}
}
func mustNewChart(chartPath string) *Chart {
c, err := NewChart(chartPath)
if err != nil {
panic(err)
}
return c
}
07070100000037000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003200000000chart-testing-3.13.0/pkg/chart/some_non_chart_dir07070100000038000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004600000000chart-testing-3.13.0/pkg/chart/some_non_chart_dir/some_non_chart_file07070100000039000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003300000000chart-testing-3.13.0/pkg/chart/some_non_chart_file0707010000003A000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003900000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level0707010000003B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003D00000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo0707010000003C000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004100000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo/bar0707010000003D000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004C00000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo/bar/Chart.yaml0707010000003E000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004100000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo/baz0707010000003F000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004C00000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo/baz/Chart.yaml07070100000040000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004600000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo/excluded07070100000041000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000005100000000chart-testing-3.13.0/pkg/chart/test_chart_at_multi_level/foo/excluded/Chart.yaml07070100000042000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003200000000chart-testing-3.13.0/pkg/chart/test_chart_at_root07070100000043000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003D00000000chart-testing-3.13.0/pkg/chart/test_chart_at_root/Chart.yaml07070100000044000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003C00000000chart-testing-3.13.0/pkg/chart/test_chart_at_root/templates07070100000045000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004500000000chart-testing-3.13.0/pkg/chart/test_chart_at_root/templates/foo.yaml07070100000046000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002B00000000chart-testing-3.13.0/pkg/chart/test_charts07070100000047000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002F00000000chart-testing-3.13.0/pkg/chart/test_charts/bar07070100000048000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003A00000000chart-testing-3.13.0/pkg/chart/test_charts/bar/Chart.yaml07070100000049000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003700000000chart-testing-3.13.0/pkg/chart/test_charts/bar/bar_sub0707010000004A000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004200000000chart-testing-3.13.0/pkg/chart/test_charts/bar/bar_sub/Chart.yaml0707010000004B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004100000000chart-testing-3.13.0/pkg/chart/test_charts/bar/bar_sub/templates0707010000004C000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004E00000000chart-testing-3.13.0/pkg/chart/test_charts/bar/bar_sub/templates/bar_sub.yaml0707010000004D000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003400000000chart-testing-3.13.0/pkg/chart/test_charts/excluded0707010000004E000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003F00000000chart-testing-3.13.0/pkg/chart/test_charts/excluded/Chart.yaml0707010000004F000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002F00000000chart-testing-3.13.0/pkg/chart/test_charts/foo07070100000050000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003A00000000chart-testing-3.13.0/pkg/chart/test_charts/foo/Chart.yaml07070100000051000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004500000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install07070100000052000081A4000000000000000000000001683D800300000065000000000000000000000000000000000000005000000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/Chart.yamlapiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: nginx
version: 0.2.0
07070100000053000081A4000000000000000000000001683D8003000000B5000000000000000000000000000000000000004F00000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/README.mdChart generated with `helm create nginx`, and is intended to pass upgrades and installs.
A fake "previous revision" can be found at `./ct_prev_revision/must-pass-upgrade-install`.
07070100000054000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004800000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/ci07070100000055000081A4000000000000000000000001683D800300000010000000000000000000000000000000000000005900000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/ci/test-values.yamlreplicaCount: 2
07070100000056000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004F00000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/templates07070100000057000081A4000000000000000000000001683D80030000040F000000000000000000000000000000000000005C00000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nginx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "nginx.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nginx.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
07070100000058000081A4000000000000000000000001683D800300000600000000000000000000000000000000000000005F00000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
07070100000059000081A4000000000000000000000001683D80030000023F000000000000000000000000000000000000005C00000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/templates/service.yamlapiVersion: v1
kind: Service
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
0707010000005A000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000005500000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/templates/tests0707010000005B000081A4000000000000000000000001683D800300000225000000000000000000000000000000000000006A00000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/templates/tests/test-connection.yamlapiVersion: v1
kind: Pod
metadata:
name: "{{ include "nginx.fullname" . }}-test-connection"
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "nginx.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
0707010000005C000081A4000000000000000000000001683D80030000031F000000000000000000000000000000000000005100000000chart-testing-3.13.0/pkg/chart/test_charts/must-pass-upgrade-install/values.yaml# Default values for nginx.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
service:
type: ClusterIP
port: 80
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
0707010000005D000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004800000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector0707010000005E000081A4000000000000000000000001683D800300000065000000000000000000000000000000000000005300000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/Chart.yamlapiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: nginx
version: 0.1.0
0707010000005F000081A4000000000000000000000001683D800300000036000000000000000000000000000000000000005200000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/README.mdReproduces https://github.com/helm/charts/issues/7726
07070100000060000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000005200000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/templates07070100000061000081A4000000000000000000000001683D80030000040F000000000000000000000000000000000000005F00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nginx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "nginx.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nginx.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
07070100000062000081A4000000000000000000000001683D800300000798000000000000000000000000000000000000006200000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
# Revision should change with each helm upgrade, so if we use it here
# and don't specify spec.selector, we run into https://github.com/helm/charts/issues/7726
revision: {{ .Release.Revision | quote }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
revision: {{ .Release.Revision | quote }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
# This should change with each helm upgrade, so we shouldn't be using it as a selector!
revision: {{ .Release.Revision | quote }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
07070100000063000081A4000000000000000000000001683D80030000023F000000000000000000000000000000000000005F00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/templates/service.yamlapiVersion: v1
kind: Service
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
07070100000064000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000005800000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/templates/tests07070100000065000081A4000000000000000000000001683D800300000225000000000000000000000000000000000000006D00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/templates/tests/test-connection.yamlapiVersion: v1
kind: Pod
metadata:
name: "{{ include "nginx.fullname" . }}-test-connection"
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "nginx.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
07070100000066000081A4000000000000000000000001683D80030000031F000000000000000000000000000000000000005400000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-deployment-selector/values.yaml# Default values for nginx.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
service:
type: ClusterIP
port: 80
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
07070100000067000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004400000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim07070100000068000081A4000000000000000000000001683D800300000065000000000000000000000000000000000000004F00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/Chart.yamlapiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: nginx
version: 0.1.0
07070100000069000081A4000000000000000000000001683D800300000036000000000000000000000000000000000000004E00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/README.mdReproduces https://github.com/helm/charts/issues/7803
0707010000006A000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004E00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/templates0707010000006B000081A4000000000000000000000001683D80030000040F000000000000000000000000000000000000005B00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nginx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "nginx.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nginx.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
0707010000006C000081A4000000000000000000000001683D800300000251000000000000000000000000000000000000005B00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/templates/service.yamlapiVersion: v1
kind: Service
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
type: {{ .Values.service.type }}
clusterIP: None
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
0707010000006D000081A4000000000000000000000001683D8003000008AB000000000000000000000000000000000000005F00000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/templates/statefulset.yamlapiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: {{ .Values.replicaCount }}
serviceName: {{ include "nginx.fullname" . }}
selector:
matchLabels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
volumeClaimTemplates:
- metadata:
name: data
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
# Revision should change with each helm upgrade, so if we use it here
# and don't specify spec.selector, we run into https://github.com/helm/charts/issues/7726
revision: {{ .Release.Revision | quote }}
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 50Mi
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
0707010000006E000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000005400000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/templates/tests0707010000006F000081A4000000000000000000000001683D800300000225000000000000000000000000000000000000006900000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/templates/tests/test-connection.yamlapiVersion: v1
kind: Pod
metadata:
name: "{{ include "nginx.fullname" . }}-test-connection"
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
annotations:
"helm.sh/hook": test-success
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "nginx.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
07070100000070000081A4000000000000000000000001683D80030000031F000000000000000000000000000000000000005000000000chart-testing-3.13.0/pkg/chart/test_charts/mutating-sfs-volumeclaim/values.yaml# Default values for nginx.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
service:
type: ClusterIP
port: 80
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
nodeSelector: {}
tolerations: []
affinity: {}
07070100000071000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003D00000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment07070100000072000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000005000000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector07070100000073000081A4000000000000000000000001683D800300000065000000000000000000000000000000000000005B00000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector/Chart.yamlapiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: nginx
version: 0.1.0
07070100000074000081A4000000000000000000000001683D8003000000CB000000000000000000000000000000000000005A00000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector/README.mdSimple chart with a Deployment having a different selector.
The integration test will install first simple-deployment and then try to upgrade
to simple-deployment-different-selector failing as expected
07070100000075000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000005A00000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector/templates07070100000076000081A4000000000000000000000001683D80030000040F000000000000000000000000000000000000006700000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector/templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nginx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "nginx.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nginx.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
07070100000077000081A4000000000000000000000001683D8003000003C8000000000000000000000000000000000000006A00000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector/templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
extra: label
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
extra: label
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
07070100000078000081A4000000000000000000000001683D8003000000DF000000000000000000000000000000000000005C00000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment-different-selector/values.yaml# Default values for nginx.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
07070100000079000081A4000000000000000000000001683D800300000065000000000000000000000000000000000000004800000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment/Chart.yamlapiVersion: v1
appVersion: "1.0"
description: A Helm chart for Kubernetes
name: nginx
version: 0.1.0
0707010000007A000081A4000000000000000000000001683D8003000000AF000000000000000000000000000000000000004700000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment/README.mdSimple chart with a Deployment.
The integration test will install first simple-deployment and then try to upgrade
to simple-deployment-different-selector failing as expected
0707010000007B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004700000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment/templates0707010000007C000081A4000000000000000000000001683D80030000040F000000000000000000000000000000000000005400000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment/templates/_helpers.tpl{{/* vim: set filetype=mustache: */}}
{{/*
Expand the name of the chart.
*/}}
{{- define "nginx.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "nginx.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := default .Chart.Name .Values.nameOverride -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "nginx.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
0707010000007D000081A4000000000000000000000001683D8003000003A0000000000000000000000000000000000000005700000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment/templates/deployment.yamlapiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "nginx.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
helm.sh/chart: {{ include "nginx.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
labels:
app.kubernetes.io/name: {{ include "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
spec:
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 80
protocol: TCP
0707010000007E000081A4000000000000000000000001683D8003000000DF000000000000000000000000000000000000004900000000chart-testing-3.13.0/pkg/chart/test_charts/simple-deployment/values.yaml# Default values for nginx.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
image:
repository: nginx
tag: stable
pullPolicy: IfNotPresent
nameOverride: ""
fullnameOverride: ""
0707010000007F000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002800000000chart-testing-3.13.0/pkg/chart/testdata07070100000080000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003A00000000chart-testing-3.13.0/pkg/chart/testdata/empty_maintainers07070100000081000081A4000000000000000000000001683D800300000010000000000000000000000000000000000000004500000000chart-testing-3.13.0/pkg/chart/testdata/empty_maintainers/Chart.yamlmaintainers: []
07070100000082000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003C00000000chart-testing-3.13.0/pkg/chart/testdata/invalid_maintainers07070100000083000081A4000000000000000000000001683D80030000006B000000000000000000000000000000000000004700000000chart-testing-3.13.0/pkg/chart/testdata/invalid_maintainers/Chart.yamlmaintainers:
- name: invalid
email: invalid@example.com
- name: valid
email: valid@example.com
07070100000084000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003700000000chart-testing-3.13.0/pkg/chart/testdata/no_maintainers07070100000085000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000004200000000chart-testing-3.13.0/pkg/chart/testdata/no_maintainers/Chart.yaml07070100000086000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004200000000chart-testing-3.13.0/pkg/chart/testdata/no_maintainers_deprecated07070100000087000081A4000000000000000000000001683D800300000011000000000000000000000000000000000000004D00000000chart-testing-3.13.0/pkg/chart/testdata/no_maintainers_deprecated/Chart.yamldeprecated: true
07070100000088000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003300000000chart-testing-3.13.0/pkg/chart/testdata/test_lints07070100000089000081A4000000000000000000000001683D8003000000CE000000000000000000000000000000000000003E00000000chart-testing-3.13.0/pkg/chart/testdata/test_lints/Chart.yamlapiVersion: v1
appVersion: xoxo
description: A Helm chart for testing
name: invalid
version: 1.2.3
home: https://github.com/helm/chart-testing
maintainers:
- name: invalid
- email: invalid@example.com
0707010000008A000081A4000000000000000000000001683D800300000018000000000000000000000000000000000000003F00000000chart-testing-3.13.0/pkg/chart/testdata/test_lints/values.yamltest: abc
list:
- abc
0707010000008B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003A00000000chart-testing-3.13.0/pkg/chart/testdata/valid_maintainers0707010000008C000081A4000000000000000000000001683D80030000006F000000000000000000000000000000000000004500000000chart-testing-3.13.0/pkg/chart/testdata/valid_maintainers/Chart.yamlmaintainers:
- name: valid
email: valid@example.com
- name: valid-too
email: valid-too@example.com
0707010000008D000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000004500000000chart-testing-3.13.0/pkg/chart/testdata/valid_maintainers_deprecated0707010000008E000081A4000000000000000000000001683D800300000080000000000000000000000000000000000000005000000000chart-testing-3.13.0/pkg/chart/testdata/valid_maintainers_deprecated/Chart.yamldeprecated: true
maintainers:
- name: valid
email: valid@example.com
- name: valid-too
email: valid-too@example.com
0707010000008F000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002000000000chart-testing-3.13.0/pkg/config07070100000090000081A4000000000000000000000001683D800300001D56000000000000000000000000000000000000002A00000000chart-testing-3.13.0/pkg/config/config.go// 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 config
import (
"errors"
"fmt"
"os"
"path/filepath"
"reflect"
"strings"
"time"
"github.com/mitchellh/go-homedir"
"github.com/helm/chart-testing/v3/pkg/util"
"github.com/spf13/cobra"
flag "github.com/spf13/pflag"
"github.com/spf13/viper"
)
var (
homeDir, _ = homedir.Dir()
configSearchLocations = []string{
".",
".ct",
filepath.Join(homeDir, ".ct"),
"/usr/local/etc/ct",
"/etc/ct",
}
)
type Configuration struct {
Remote string `mapstructure:"remote"`
TargetBranch string `mapstructure:"target-branch"`
Since string `mapstructure:"since"`
BuildID string `mapstructure:"build-id"`
LintConf string `mapstructure:"lint-conf"`
ChartYamlSchema string `mapstructure:"chart-yaml-schema"`
ValidateMaintainers bool `mapstructure:"validate-maintainers"`
ValidateChartSchema bool `mapstructure:"validate-chart-schema"`
ValidateYaml bool `mapstructure:"validate-yaml"`
SkipHelmDependencies bool `mapstructure:"skip-helm-dependencies"`
AdditionalCommands []string `mapstructure:"additional-commands"`
CheckVersionIncrement bool `mapstructure:"check-version-increment"`
ProcessAllCharts bool `mapstructure:"all"`
Charts []string `mapstructure:"charts"`
ChartRepos []string `mapstructure:"chart-repos"`
ChartDirs []string `mapstructure:"chart-dirs"`
ExcludedCharts []string `mapstructure:"excluded-charts"`
HelmExtraArgs string `mapstructure:"helm-extra-args"`
HelmExtraSetArgs string `mapstructure:"helm-extra-set-args"`
HelmLintExtraArgs string `mapstructure:"helm-lint-extra-args"`
HelmRepoExtraArgs []string `mapstructure:"helm-repo-extra-args"`
HelmDependencyExtraArgs []string `mapstructure:"helm-dependency-extra-args"`
Debug bool `mapstructure:"debug"`
Upgrade bool `mapstructure:"upgrade"`
SkipMissingValues bool `mapstructure:"skip-missing-values"`
SkipCleanUp bool `mapstructure:"skip-clean-up"`
Namespace string `mapstructure:"namespace"`
ReleaseName string `mapstructure:"release-name"`
ReleaseLabel string `mapstructure:"release-label"`
ExcludeDeprecated bool `mapstructure:"exclude-deprecated"`
KubectlTimeout time.Duration `mapstructure:"kubectl-timeout"`
PrintLogs bool `mapstructure:"print-logs"`
GithubGroups bool `mapstructure:"github-groups"`
UseHelmignore bool `mapstructure:"use-helmignore"`
}
func LoadConfiguration(cfgFile string, cmd *cobra.Command, printConfig bool) (*Configuration, error) {
v := viper.New()
v.SetDefault("kubectl-timeout", 30*time.Second)
v.SetDefault("print-logs", bool(true))
cmd.Flags().VisitAll(func(flag *flag.Flag) {
flagName := flag.Name
if flagName != "config" && flagName != "help" {
if err := v.BindPFlag(flagName, flag); err != nil {
// can't really happen
panic(fmt.Sprintf("failed binding flag %q: %v\n", flagName, err.Error()))
}
}
})
v.AutomaticEnv()
v.SetEnvKeyReplacer(strings.NewReplacer("-", "_"))
v.SetEnvPrefix("CT")
if cfgFile != "" {
v.SetConfigFile(cfgFile)
} else {
v.SetConfigName("ct")
if cfgFile, ok := os.LookupEnv("CT_CONFIG_DIR"); ok {
v.AddConfigPath(cfgFile)
} else {
for _, searchLocation := range configSearchLocations {
v.AddConfigPath(searchLocation)
}
}
}
if err := v.ReadInConfig(); err != nil {
if cfgFile != "" {
// Only error out for specified config file. Ignore for default locations.
return nil, fmt.Errorf("failed loading config file: %w", err)
}
} else {
if printConfig {
fmt.Fprintln(os.Stderr, "Using config file:", v.ConfigFileUsed())
}
}
isLint := strings.Contains(cmd.Use, "lint")
isInstall := strings.Contains(cmd.Use, "install")
cfg := &Configuration{}
if err := v.Unmarshal(cfg); err != nil {
return nil, fmt.Errorf("failed unmarshaling configuration: %w", err)
}
if cfg.ProcessAllCharts && len(cfg.Charts) > 0 {
return nil, errors.New("specifying both, '--all' and '--charts', is not allowed")
}
if cfg.Namespace != "" && cfg.ReleaseLabel == "" {
return nil, errors.New("specifying '--namespace' without '--release-label' is not allowed")
}
// Disable upgrade (this does some expensive dependency building on previous revisions)
// when neither "install" nor "lint-and-install" have not been specified.
cfg.Upgrade = isInstall && cfg.Upgrade
if (cfg.TargetBranch == "" || cfg.Remote == "") && cfg.Upgrade {
return nil, errors.New("specifying '--upgrade=true' without '--target-branch' or '--remote', is not allowed")
}
chartYamlSchemaPath := cfg.ChartYamlSchema
if chartYamlSchemaPath == "" {
var err error
cfgFile, err = findConfigFile("chart_schema.yaml")
if err != nil && isLint && cfg.ValidateChartSchema {
return nil, errors.New("'chart_schema.yaml' neither specified nor found in default locations")
}
cfg.ChartYamlSchema = cfgFile
}
lintConfPath := cfg.LintConf
if lintConfPath == "" {
var err error
cfgFile, err = findConfigFile("lintconf.yaml")
if err != nil && isLint && cfg.ValidateYaml {
return nil, errors.New("'lintconf.yaml' neither specified nor found in default locations")
}
cfg.LintConf = cfgFile
}
if len(cfg.Charts) > 0 || cfg.ProcessAllCharts {
fmt.Fprintln(os.Stderr, "Version increment checking disabled.")
cfg.CheckVersionIncrement = false
}
if printConfig {
printCfg(cfg)
}
return cfg, nil
}
func printCfg(cfg *Configuration) {
if !cfg.GithubGroups {
util.PrintDelimiterLineToWriter(os.Stderr, "-")
fmt.Fprintln(os.Stderr, " Configuration")
util.PrintDelimiterLineToWriter(os.Stderr, "-")
} else {
util.GithubGroupsBegin(os.Stderr, "Configuration")
}
e := reflect.ValueOf(cfg).Elem()
typeOfCfg := e.Type()
for i := 0; i < e.NumField(); i++ {
var pattern string
switch e.Field(i).Kind() {
case reflect.Bool:
pattern = "%s: %t\n"
default:
pattern = "%s: %s\n"
}
fmt.Fprintf(os.Stderr, pattern, typeOfCfg.Field(i).Name, e.Field(i).Interface())
}
if !cfg.GithubGroups {
util.PrintDelimiterLineToWriter(os.Stderr, "-")
} else {
util.GithubGroupsEnd(os.Stderr)
}
}
func findConfigFile(fileName string) (string, error) {
if dir, ok := os.LookupEnv("CT_CONFIG_DIR"); ok {
return filepath.Join(dir, fileName), nil
}
for _, location := range configSearchLocations {
filePath := filepath.Join(location, fileName)
if util.FileExists(filePath) {
return filePath, nil
}
}
return "", fmt.Errorf("config file not found: %s", fileName)
}
07070100000091000081A4000000000000000000000001683D800300000DFF000000000000000000000000000000000000002F00000000chart-testing-3.13.0/pkg/config/config_test.go// 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 config
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/spf13/cobra"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUnmarshalYaml(t *testing.T) {
loadAndAssertConfigFromFile(t, "test_config.yaml")
}
func TestUnmarshalJson(t *testing.T) {
loadAndAssertConfigFromFile(t, "test_config.json")
}
func loadAndAssertConfigFromFile(t *testing.T, configFile string) {
cfg, _ := LoadConfiguration(configFile, &cobra.Command{
Use: "install",
}, true)
require.Equal(t, "origin", cfg.Remote)
require.Equal(t, "main", cfg.TargetBranch)
require.Equal(t, "pr-42", cfg.BuildID)
require.Equal(t, "my-lint-conf.yaml", cfg.LintConf)
require.Equal(t, "my-chart-yaml-schema.yaml", cfg.ChartYamlSchema)
require.Equal(t, true, cfg.ValidateMaintainers)
require.Equal(t, true, cfg.ValidateChartSchema)
require.Equal(t, true, cfg.ValidateYaml)
require.Equal(t, true, cfg.CheckVersionIncrement)
require.Equal(t, false, cfg.ProcessAllCharts)
require.Equal(t, []string{"incubator=https://incubator"}, cfg.ChartRepos)
require.Equal(t, []string{"incubator=--username test"}, cfg.HelmRepoExtraArgs)
require.Equal(t, []string{"stable", "incubator"}, cfg.ChartDirs)
require.Equal(t, []string{"common"}, cfg.ExcludedCharts)
require.Equal(t, "--timeout 300s", cfg.HelmExtraArgs)
require.Equal(t, "--quiet", cfg.HelmLintExtraArgs)
require.Equal(t, true, cfg.Upgrade)
require.Equal(t, true, cfg.SkipMissingValues)
require.Equal(t, "default", cfg.Namespace)
require.Equal(t, "release", cfg.ReleaseLabel)
require.Equal(t, true, cfg.ExcludeDeprecated)
require.Equal(t, 120*time.Second, cfg.KubectlTimeout)
require.Equal(t, true, cfg.SkipCleanUp)
require.Equal(t, true, cfg.UseHelmignore)
}
func Test_findConfigFile(t *testing.T) {
tests := []struct {
name string
envVar string
defaultDir string
want string
wantErr bool
}{
{
name: "without env var",
defaultDir: filepath.Join("testdata", "default"),
want: filepath.Join("testdata", "default", "test.yaml"),
},
{
name: "with env var",
envVar: filepath.Join("testdata", "env"),
want: filepath.Join("testdata", "env", "test.yaml"),
},
{
name: "with env var and default location",
envVar: filepath.Join("testdata", "env"),
defaultDir: filepath.Join("testdata", "default"),
want: filepath.Join("testdata", "env", "test.yaml"),
},
{
name: "not found",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.envVar != "" {
err := os.Setenv("CT_CONFIG_DIR", tt.envVar)
require.NoError(t, err)
t.Cleanup(func() {
err := os.Unsetenv("CT_CONFIG_DIR")
require.NoError(t, err)
})
}
configSearchLocations = []string{tt.defaultDir}
got, err := findConfigFile("test.yaml")
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.want, got)
}
})
}
}
07070100000092000081A4000000000000000000000001683D8003000003BD000000000000000000000000000000000000003100000000chart-testing-3.13.0/pkg/config/test_config.json{
"remote": "origin",
"target-branch": "main",
"since": "HEAD~1",
"build-id": "pr-42",
"lint-conf": "my-lint-conf.yaml",
"chart-yaml-schema": "my-chart-yaml-schema.yaml",
"github-instance": "https://github.com",
"validate-maintainers": true,
"validate-chart-schema": true,
"validate-yaml": true,
"check-version-increment": true,
"all": false,
"chart-repos": [
"incubator=https://incubator"
],
"helm-repo-extra-args": [
"incubator=--username test"
],
"chart-dirs": [
"stable",
"incubator"
],
"excluded-charts": [
"common"
],
"helm-extra-args": "--timeout 300s",
"helm-lint-extra-args": "--quiet",
"upgrade": true,
"skip-missing-values": true,
"namespace": "default",
"release-label": "release",
"exclude-deprecated": true,
"kubectl-timeout": "120s",
"skip-clean-up": true,
"use-helmignore": true
}
07070100000093000081A4000000000000000000000001683D8003000002AD000000000000000000000000000000000000003100000000chart-testing-3.13.0/pkg/config/test_config.yamlremote: origin
target-branch: main
since: HEAD~1
build-id: pr-42
lint-conf: my-lint-conf.yaml
chart-yaml-schema: my-chart-yaml-schema.yaml
github-instance: https://github.com
validate-maintainers: true
validate-chart-schema: true
validate-yaml: true
check-version-increment: true
all: false
chart-repos:
- incubator=https://incubator
helm-repo-extra-args:
- incubator=--username test
chart-dirs:
- stable
- incubator
excluded-charts:
- common
helm-extra-args: --timeout 300s
helm-lint-extra-args: --quiet
upgrade: true
skip-missing-values: true
namespace: default
release-label: release
exclude-deprecated: true
kubectl-timeout: 120s
skip-clean-up: true
use-helmignore: true
07070100000094000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002900000000chart-testing-3.13.0/pkg/config/testdata07070100000095000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000003100000000chart-testing-3.13.0/pkg/config/testdata/default07070100000096000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003B00000000chart-testing-3.13.0/pkg/config/testdata/default/test.yaml07070100000097000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002D00000000chart-testing-3.13.0/pkg/config/testdata/env07070100000098000081A4000000000000000000000001683D800300000000000000000000000000000000000000000000003700000000chart-testing-3.13.0/pkg/config/testdata/env/test.yaml07070100000099000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001E00000000chart-testing-3.13.0/pkg/exec0707010000009A000081A4000000000000000000000001683D800300000FD8000000000000000000000000000000000000002600000000chart-testing-3.13.0/pkg/exec/exec.go// 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 exec
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"strings"
"github.com/helm/chart-testing/v3/pkg/util"
)
type ProcessExecutor struct {
debug bool
}
func NewProcessExecutor(debug bool) ProcessExecutor {
return ProcessExecutor{
debug: debug,
}
}
func (p ProcessExecutor) RunProcessAndCaptureOutput(executable string, execArgs ...interface{}) (string, error) {
return p.RunProcessInDirAndCaptureOutput("", executable, execArgs)
}
func (p ProcessExecutor) RunProcessAndCaptureStdout(executable string, execArgs ...interface{}) (string, error) {
return p.RunProcessInDirAndCaptureStdout("", executable, execArgs)
}
func (p ProcessExecutor) RunProcessInDirAndCaptureOutput(workingDirectory string, executable string, execArgs ...interface{}) (string, error) {
cmd, err := p.CreateProcess(executable, execArgs...)
if err != nil {
return "", err
}
cmd.Dir = workingDirectory
bytes, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed running process: %w", err)
}
return strings.TrimSpace(string(bytes)), nil
}
func (p ProcessExecutor) RunProcessInDirAndCaptureStdout(workingDirectory string, executable string, execArgs ...interface{}) (string, error) {
cmd, err := p.CreateProcess(executable, execArgs...)
if err != nil {
return "", err
}
cmd.Dir = workingDirectory
bytes, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed running process: %w", err)
}
return strings.TrimSpace(string(bytes)), nil
}
func (p ProcessExecutor) RunProcess(executable string, execArgs ...interface{}) error {
cmd, err := p.CreateProcess(executable, execArgs...)
if err != nil {
return err
}
outReader, err := cmd.StdoutPipe()
if err != nil {
return fmt.Errorf("failed getting StdoutPipe for command: %w", err)
}
errReader, err := cmd.StderrPipe()
if err != nil {
return fmt.Errorf("failed getting StderrPipe for command: %w", err)
}
scanner := bufio.NewScanner(io.MultiReader(outReader, errReader))
go func() {
defer outReader.Close()
defer errReader.Close()
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
err = cmd.Start()
if err != nil {
return fmt.Errorf("failed running process: %w", err)
}
err = cmd.Wait()
if err != nil {
return fmt.Errorf("failed waiting for process: %w", err)
}
return nil
}
func (p ProcessExecutor) CreateProcess(executable string, execArgs ...interface{}) (*exec.Cmd, error) {
args, err := util.Flatten(execArgs)
if p.debug {
fmt.Println(">>>", executable, strings.Join(args, " "))
}
if err != nil {
return nil, fmt.Errorf("invalid arguments supplied: %w", err)
}
cmd := exec.Command(executable, args...)
return cmd, nil
}
type fn func(port int) error
func (p ProcessExecutor) RunWithProxy(withProxy fn) error {
randomPort, err := util.GetRandomPort()
if err != nil {
return fmt.Errorf("could not find a free port for running 'kubectl proxy': %w", err)
}
fmt.Printf("Running 'kubectl proxy' on port %d\n", randomPort)
cmdProxy, err := p.CreateProcess("kubectl", "proxy", fmt.Sprintf("--port=%d", randomPort))
if err != nil {
return fmt.Errorf("failed creating the 'kubectl proxy' process: %w", err)
}
err = cmdProxy.Start()
if err != nil {
return fmt.Errorf("failed starting the 'kubectl proxy' process: %w", err)
}
err = withProxy(randomPort)
_ = cmdProxy.Process.Signal(os.Kill)
if err != nil {
return fmt.Errorf("failed running command with proxy: %w", err)
}
return nil
}
0707010000009B000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000002000000000chart-testing-3.13.0/pkg/ignore0707010000009C000081A4000000000000000000000001683D800300000750000000000000000000000000000000000000002A00000000chart-testing-3.13.0/pkg/ignore/ignore.go/*
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 ignore
import (
"io/fs"
"os"
"path/filepath"
"testing/fstest"
helmignore "helm.sh/helm/v3/pkg/ignore"
)
func LoadRules(dir string) (*helmignore.Rules, error) {
rules, err := helmignore.ParseFile(filepath.Join(dir, helmignore.HelmIgnore))
if err != nil && !os.IsNotExist(err) {
return nil, err
}
if rules == nil {
rules = helmignore.Empty()
}
rules.AddDefaults()
return rules, nil
}
func FilterFiles(files []string, rules *helmignore.Rules) ([]string, error) {
fsys := fstest.MapFS{}
for _, file := range files {
fsys[file] = &fstest.MapFile{}
}
filteredFiles := []string{}
err := fs.WalkDir(fsys, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
fi, err := d.Info()
if err != nil {
return err
}
// Normalize to / since it will also work on Windows
path = filepath.ToSlash(path)
if fi.IsDir() {
// Directory-based ignore rules should involve skipping the entire
// contents of that directory.
if rules.Ignore(path, fi) {
return filepath.SkipDir
}
return nil
}
// If a .helmignore file matches, skip this file.
if rules.Ignore(path, fi) {
return nil
}
filteredFiles = append(filteredFiles, path)
return nil
})
if err != nil {
return nil, err
}
return filteredFiles, nil
}
0707010000009D000081A4000000000000000000000001683D800300000447000000000000000000000000000000000000002F00000000chart-testing-3.13.0/pkg/ignore/ignore_test.go/*
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 ignore
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
helmignore "helm.sh/helm/v3/pkg/ignore"
)
func TestFilter(t *testing.T) {
rules, err := helmignore.Parse(strings.NewReader("/bar/\nREADME.md\n"))
assert.Nil(t, err)
files := []string{"Chart.yaml", "bar/xxx", "template/svc.yaml", "baz/bar/biz.txt", "README.md"}
actual, err := FilterFiles(files, rules)
assert.Nil(t, err)
expected := []string{"Chart.yaml", "baz/bar/biz.txt", "template/svc.yaml"}
assert.ElementsMatch(t, expected, actual)
}
0707010000009E000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001E00000000chart-testing-3.13.0/pkg/tool0707010000009F000081A4000000000000000000000001683D80030000060D000000000000000000000000000000000000002900000000chart-testing-3.13.0/pkg/tool/account.go// 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 tool
import (
"fmt"
"net/http"
"regexp"
)
type AccountValidator struct{}
var repoDomainPattern = regexp.MustCompile("(?:https://(?:[^@:]+:[^@:]+@)?|git@)([^/:]+)")
func (v AccountValidator) Validate(repoURL string, account string) error {
domain, err := parseOutGitRepoDomain(repoURL)
if err != nil {
return err
}
url := fmt.Sprintf("https://%s/%s", domain, account)
response, err := http.Head(url) // nolint: gosec
if err != nil {
return fmt.Errorf("failed validating maintainers: %w", err)
}
if response.StatusCode != 200 {
return fmt.Errorf("failed validating maintainer %q: %s", account, response.Status)
}
return nil
}
func parseOutGitRepoDomain(repoURL string) (string, error) {
// This works for GitHub, Bitbucket, and Gitlab
submatch := repoDomainPattern.FindStringSubmatch(repoURL)
if len(submatch) < 2 {
return "", fmt.Errorf("could not parse git repository domain for %q", repoURL)
}
return submatch[1], nil
}
070701000000A0000081A4000000000000000000000001683D800300000526000000000000000000000000000000000000002E00000000chart-testing-3.13.0/pkg/tool/account_test.gopackage tool
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseOutGitDomain(t *testing.T) {
var testDataSlice = []struct {
name string
repoURL string
expected string
err error
}{
{"GitHub SSH", "git@github.com:foo/bar", "github.com", nil},
{"GitHub HTTPS", "https://github.com/foo/bar", "github.com", nil},
{"GitHub HTTPS with username/password", "https://foo:token@github.com/foo/bar", "github.com", nil},
{"Gitlab SSH", "git@gitlab.com:foo/bar", "gitlab.com", nil},
{"Gitlab HTTPS", "https://gitlab.com/foo/bar", "gitlab.com", nil},
{"Gitlab HTTPS with username/password", "https://gitlab-ci-token:password@gitlab.com/foo/bar", "gitlab.com", nil},
{"Bitbucket SSH", "git@bitbucket.com:foo/bar", "bitbucket.com", nil},
{"Bitbucket HTTPS", "https://bitbucket.com/foo/bar", "bitbucket.com", nil},
{"Bitbucket HTTPS with username/password", "https://user:pass@bitbucket.com/foo/bar", "bitbucket.com", nil},
{"Invalid", "foo/bar", "", fmt.Errorf("could not parse git repository domain for \"foo/bar\"")},
}
for _, testData := range testDataSlice {
t.Run(testData.name, func(t *testing.T) {
actual, err := parseOutGitRepoDomain(testData.repoURL)
assert.Equal(t, err, testData.err)
assert.Equal(t, testData.expected, actual)
})
}
}
070701000000A1000081A4000000000000000000000001683D800300000319000000000000000000000000000000000000002D00000000chart-testing-3.13.0/pkg/tool/cmdexecutor.gopackage tool
import (
"strings"
"text/template"
"github.com/mattn/go-shellwords"
)
type ProcessExecutor interface {
RunProcess(executable string, execArgs ...interface{}) error
}
type CmdTemplateExecutor struct {
exec ProcessExecutor
}
func NewCmdTemplateExecutor(exec ProcessExecutor) CmdTemplateExecutor {
return CmdTemplateExecutor{
exec: exec,
}
}
func (t CmdTemplateExecutor) RunCommand(cmdTemplate string, data interface{}) error {
var template = template.Must(template.New("command").Parse(cmdTemplate))
var b strings.Builder
if err := template.Execute(&b, data); err != nil {
return err
}
rendered := b.String()
words, err := shellwords.Parse(rendered)
if err != nil {
return err
}
name, args := words[0], words[1:]
return t.exec.RunProcess(name, args)
}
070701000000A2000081A4000000000000000000000001683D8003000007AC000000000000000000000000000000000000003200000000chart-testing-3.13.0/pkg/tool/cmdexecutor_test.gopackage tool
import (
"testing"
"github.com/stretchr/testify/mock"
)
type fakeProcessExecutor struct {
mock.Mock
}
func (c *fakeProcessExecutor) RunProcess(executable string, execArgs ...interface{}) error {
c.Called(executable, execArgs[0])
return nil
}
func TestCmdTemplateExecutor_RunCommand(t *testing.T) {
type args struct {
cmdTemplate string
data interface{}
}
tests := []struct {
name string
args args
wantErr bool
validate func(t *testing.T, executor *fakeProcessExecutor)
}{
{
name: "command without arguments",
args: args{
cmdTemplate: "echo",
data: nil,
},
validate: func(t *testing.T, executor *fakeProcessExecutor) {
executor.AssertCalled(t, "RunProcess", "echo", []string{})
},
wantErr: false,
},
{
name: "command with args",
args: args{
cmdTemplate: "echo hello world",
},
validate: func(t *testing.T, executor *fakeProcessExecutor) {
executor.AssertCalled(t, "RunProcess", "echo", []string{"hello", "world"})
},
wantErr: false,
},
{
name: "interpolate args",
args: args{
cmdTemplate: "helm unittest --helm3 -f tests/*.yaml {{ .Path }}",
data: map[string]string{"Path": "charts/my-chart"},
},
validate: func(t *testing.T, executor *fakeProcessExecutor) {
executor.AssertCalled(t, "RunProcess", "helm", []string{"unittest", "--helm3", "-f", "tests/*.yaml", "charts/my-chart"})
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
processExecutor := new(fakeProcessExecutor)
processExecutor.On("RunProcess", mock.Anything, mock.Anything).Return(nil)
templateExecutor := CmdTemplateExecutor{
exec: processExecutor,
}
if err := templateExecutor.RunCommand(tt.args.cmdTemplate, tt.args.data); (err != nil) != tt.wantErr {
t.Errorf("RunCommand() error = %v, wantErr %v", err, tt.wantErr)
}
tt.validate(t, processExecutor)
})
}
}
070701000000A3000081A4000000000000000000000001683D8003000009D7000000000000000000000000000000000000002500000000chart-testing-3.13.0/pkg/tool/git.go// 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 tool
import (
"fmt"
"strings"
"github.com/helm/chart-testing/v3/pkg/exec"
)
type Git struct {
exec exec.ProcessExecutor
}
func NewGit(exec exec.ProcessExecutor) Git {
return Git{
exec: exec,
}
}
func (g Git) FileExistsOnBranch(file string, remote string, branch string) bool {
fileSpec := fmt.Sprintf("%s/%s:%s", remote, branch, file)
_, err := g.exec.RunProcessAndCaptureOutput("git", "cat-file", "-e", fileSpec)
return err == nil
}
func (g Git) AddWorktree(path string, ref string) error {
return g.exec.RunProcess("git", "worktree", "add", path, ref)
}
func (g Git) RemoveWorktree(path string) error {
return g.exec.RunProcess("git", "worktree", "remove", path)
}
func (g Git) Show(file string, remote string, branch string) (string, error) {
fileSpec := fmt.Sprintf("%s/%s:%s", remote, branch, file)
return g.exec.RunProcessAndCaptureOutput("git", "show", fileSpec)
}
func (g Git) MergeBase(commit1 string, commit2 string) (string, error) {
return g.exec.RunProcessAndCaptureOutput("git", "merge-base", commit1, commit2)
}
func (g Git) ListChangedFilesInDirs(commit string, dirs ...string) ([]string, error) {
changedChartFilesString, err :=
g.exec.RunProcessAndCaptureOutput("git", "diff", "--find-renames", "--name-only", commit, "--", dirs)
if err != nil {
return nil, fmt.Errorf("failed creating diff: %w", err)
}
if changedChartFilesString == "" {
return nil, nil
}
return strings.Split(changedChartFilesString, "\n"), nil
}
func (g Git) GetURLForRemote(remote string) (string, error) {
return g.exec.RunProcessAndCaptureOutput("git", "ls-remote", "--get-url", remote)
}
func (g Git) ValidateRepository() error {
_, err := g.exec.RunProcessAndCaptureOutput("git", "rev-parse", "--is-inside-work-tree")
return err
}
func (g Git) BranchExists(branch string) bool {
_, err := g.exec.RunProcessAndCaptureOutput("git", "rev-parse", "--verify", branch)
return err == nil
}
070701000000A4000081A4000000000000000000000001683D800300000C5A000000000000000000000000000000000000002600000000chart-testing-3.13.0/pkg/tool/helm.go// 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 tool
import (
"fmt"
"strings"
"github.com/helm/chart-testing/v3/pkg/exec"
)
type Helm struct {
exec exec.ProcessExecutor
extraArgs []string
lintExtraArgs []string
extraSetArgs []string
}
func NewHelm(exec exec.ProcessExecutor, extraArgs, lintExtraArgs, extraSetArgs []string) Helm {
return Helm{
exec: exec,
extraArgs: extraArgs,
lintExtraArgs: lintExtraArgs,
extraSetArgs: extraSetArgs,
}
}
func (h Helm) AddRepo(name string, url string, extraArgs []string) error {
const ociPrefix string = "oci://"
if strings.HasPrefix(url, ociPrefix) {
registryDomain := url[len(ociPrefix):]
return h.exec.RunProcess("helm", "registry", "login", registryDomain, extraArgs)
}
return h.exec.RunProcess("helm", "repo", "add", name, url, extraArgs)
}
func (h Helm) BuildDependencies(chart string) error {
return h.BuildDependenciesWithArgs(chart, []string{})
}
func (h Helm) BuildDependenciesWithArgs(chart string, extraArgs []string) error {
return h.exec.RunProcess("helm", "dependency", "build", chart, extraArgs)
}
func (h Helm) LintWithValues(chart string, valuesFile string) error {
var values []string
if valuesFile != "" {
values = []string{"--values", valuesFile}
}
return h.exec.RunProcess("helm", "lint", chart, values, h.lintExtraArgs)
}
func (h Helm) InstallWithValues(chart string, valuesFile string, namespace string, release string) error {
var values []string
if valuesFile != "" {
values = []string{"--values", valuesFile}
}
return h.exec.RunProcess("helm", "install", release, chart, "--namespace", namespace,
"--wait", values, h.extraArgs, h.extraSetArgs)
}
func (h Helm) UpgradeWithValues(chart string, valuesFile string, namespace string, release string) error {
var values []string
if valuesFile != "" {
values = []string{"--values", valuesFile}
}
return h.exec.RunProcess("helm", "upgrade", release, chart, "--namespace", namespace,
"--wait", values, h.extraArgs, h.extraSetArgs)
}
func (h Helm) Test(namespace string, release string) error {
return h.exec.RunProcess("helm", "test", release, "--namespace", namespace, h.extraArgs)
}
func (h Helm) DeleteRelease(namespace string, release string) {
fmt.Printf("Deleting release %q...\n", release)
if err := h.exec.RunProcess("helm", "uninstall", release, "--namespace", namespace, "--wait", h.extraArgs); err != nil {
fmt.Println("Error deleting Helm release:", err)
}
}
func (h Helm) Version() (string, error) {
return h.exec.RunProcessAndCaptureStdout("helm", "version", "--template", "{{ .Version }}")
}
070701000000A5000081A4000000000000000000000001683D80030000200A000000000000000000000000000000000000002900000000chart-testing-3.13.0/pkg/tool/kubectl.gopackage tool
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"strings"
"time"
"github.com/hashicorp/go-retryablehttp"
"github.com/helm/chart-testing/v3/pkg/exec"
)
type Kubectl struct {
exec exec.ProcessExecutor
timeout time.Duration
}
func NewKubectl(exec exec.ProcessExecutor, timeout time.Duration) Kubectl {
return Kubectl{
exec: exec,
timeout: timeout,
}
}
// CreateNamespace creates a new namespace with the given name.
func (k Kubectl) CreateNamespace(namespace string) error {
fmt.Printf("Creating namespace %q...\n", namespace)
return k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"create", "namespace", namespace)
}
// DeleteNamespace deletes the specified namespace. If the namespace does not terminate within 120s, pods running in the
// namespace and, eventually, the namespace itself are force-deleted.
func (k Kubectl) DeleteNamespace(namespace string) {
fmt.Printf("Deleting namespace %q...\n", namespace)
timeoutSec := "180s"
err := k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"delete", "namespace", namespace, "--timeout", timeoutSec)
if err != nil {
fmt.Printf("Namespace %q did not terminate after %s.\n", namespace, timeoutSec)
}
if k.getNamespace(namespace) {
fmt.Printf("Namespace %q did not terminate after %s.\n", namespace, timeoutSec)
fmt.Println("Force-deleting everything...")
err = k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"delete", "all", "--namespace", namespace, "--all", "--force",
"--grace-period=0")
if err != nil {
fmt.Printf("Error deleting everything in the namespace %v: %v", namespace, err)
}
// Give it some more time to be deleted by K8s
time.Sleep(5 * time.Second)
if k.getNamespace(namespace) {
if err := k.forceNamespaceDeletion(namespace); err != nil {
fmt.Println("Error force deleting namespace:", err)
}
}
}
}
func (k Kubectl) forceNamespaceDeletion(namespace string) error {
// Getting the namespace json to remove the finalizer
cmdOutput, err := k.exec.RunProcessAndCaptureStdout("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "namespace", namespace, "--output=json")
if err != nil {
fmt.Println("Error getting namespace json:", err)
return err
}
namespaceUpdate := map[string]interface{}{}
err = json.Unmarshal([]byte(cmdOutput), &namespaceUpdate)
if err != nil {
fmt.Println("Error in unmarshalling the payload:", err)
return err
}
namespaceUpdate["spec"] = nil
namespaceUpdateBytes, err := json.Marshal(&namespaceUpdate)
if err != nil {
fmt.Println("Error in marshalling the payload:", err)
return err
}
// Remove finalizer from the namespace
fun := func(port int) error {
fmt.Printf("Removing finalizers from namespace %q...\n", namespace)
k8sURL := fmt.Sprintf("http://127.0.0.1:%d/api/v1/namespaces/%s/finalize", port, namespace)
req, err := retryablehttp.NewRequest("PUT", k8sURL, bytes.NewReader(namespaceUpdateBytes))
if err != nil {
fmt.Println("Error creating the request to update the namespace:", err)
return err
}
req.Header.Set("Content-Type", "application/json")
errMsg := "Error removing finalizer from namespace"
client := retryablehttp.NewClient()
client.Logger = nil
if resp, err := client.Do(req); err != nil {
return fmt.Errorf("%s:%w", errMsg, err)
} else if resp.StatusCode != http.StatusOK {
return errors.New(errMsg)
}
return nil
}
err = k.exec.RunWithProxy(fun)
if err != nil {
return fmt.Errorf("cannot force-delete namespace %q: %w", namespace, err)
}
// Give it some more time to be deleted by K8s
time.Sleep(5 * time.Second)
// Check again
_, err = k.exec.RunProcessAndCaptureOutput("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "namespace", namespace)
if err != nil {
fmt.Printf("Namespace %q terminated.\n", namespace)
return nil
}
fmt.Printf("Force-deleting namespace %q...\n", namespace)
err = k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"delete", "namespace", namespace, "--force", "--grace-period=0",
"--ignore-not-found=true")
if err != nil {
fmt.Println("Error deleting namespace:", err)
return err
}
return nil
}
func (k Kubectl) WaitForDeployments(namespace string, selector string) error {
output, err := k.exec.RunProcessAndCaptureStdout("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "deployments", "--namespace", namespace, "--selector", selector,
"--output", "jsonpath={.items[*].metadata.name}")
if err != nil {
return err
}
deployments := strings.Fields(output)
for _, deployment := range deployments {
deployment = strings.Trim(deployment, "'")
err = k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"rollout", "status", "deployment", deployment, "--namespace", namespace)
if err != nil {
return err
}
// 'kubectl rollout status' does not return a non-zero exit code when rollouts fail.
// We, thus, need to double-check here.
//
// Just after rollout, pods from the previous deployment revision may still be in a
// terminating state.
unavailable, err := k.exec.RunProcessAndCaptureStdout("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "deployment", deployment, "--namespace", namespace, "--output",
`jsonpath={.status.unavailableReplicas}`)
if err != nil {
return err
}
if unavailable != "" && unavailable != "0" {
return fmt.Errorf("%s replicas unavailable", unavailable)
}
}
return nil
}
func (k Kubectl) GetPodsforDeployment(namespace string, deployment string) ([]string, error) {
jsonString, _ := k.exec.RunProcessAndCaptureStdout("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "deployment", deployment, "--namespace", namespace, "--output=json")
var deploymentMap map[string]interface{}
err := json.Unmarshal([]byte(jsonString), &deploymentMap)
if err != nil {
return nil, err
}
spec := deploymentMap["spec"].(map[string]interface{})
selector := spec["selector"].(map[string]interface{})
matchLabels := selector["matchLabels"].(map[string]interface{})
var ls string
for name, value := range matchLabels {
if ls != "" {
ls += ","
}
ls += fmt.Sprintf("%s=%s", name, value)
}
return k.GetPods("--selector", ls, "--namespace", namespace, "--output", "jsonpath={.items[*].metadata.name}")
}
func (k Kubectl) GetPods(args ...string) ([]string, error) {
kubectlArgs := []string{"get", "pods"}
kubectlArgs = append(kubectlArgs, args...)
pods, err := k.exec.RunProcessAndCaptureStdout("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout), kubectlArgs)
if err != nil {
return nil, err
}
return strings.Fields(pods), nil
}
func (k Kubectl) GetEvents(namespace string) error {
return k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "events", "--output", "wide", "--namespace", namespace, "--sort-by", "lastTimestamp")
}
func (k Kubectl) DescribePod(namespace string, pod string) error {
return k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"describe", "pod", pod, "--namespace", namespace)
}
func (k Kubectl) Logs(namespace string, pod string, container string) error {
return k.exec.RunProcess("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"logs", pod, "--namespace", namespace, "--container", container)
}
func (k Kubectl) GetInitContainers(namespace string, pod string) ([]string, error) {
return k.GetPods(pod, "--no-headers", "--namespace", namespace, "--output", "jsonpath={.spec.initContainers[*].name}")
}
func (k Kubectl) GetContainers(namespace string, pod string) ([]string, error) {
return k.GetPods(pod, "--no-headers", "--namespace", namespace, "--output", "jsonpath={.spec.containers[*].name}")
}
func (k Kubectl) getNamespace(namespace string) bool {
_, err := k.exec.RunProcessAndCaptureOutput("kubectl",
fmt.Sprintf("--request-timeout=%s", k.timeout),
"get", "namespace", namespace)
if err != nil {
fmt.Printf("Namespace %q terminated.\n", namespace)
return false
}
return true
}
070701000000A6000081A4000000000000000000000001683D800300000439000000000000000000000000000000000000002800000000chart-testing-3.13.0/pkg/tool/linter.go// 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 tool
import "github.com/helm/chart-testing/v3/pkg/exec"
type Linter struct {
exec exec.ProcessExecutor
}
func NewLinter(exec exec.ProcessExecutor) Linter {
return Linter{
exec: exec,
}
}
func (l Linter) YamlLint(yamlFile string, configFile string) error {
return l.exec.RunProcess("yamllint", "--config-file", configFile, yamlFile)
}
func (l Linter) Yamale(yamlFile string, schemaFile string) error {
return l.exec.RunProcess("yamale", "--schema", schemaFile, yamlFile)
}
070701000000A7000041ED000000000000000000000002683D800300000000000000000000000000000000000000000000001E00000000chart-testing-3.13.0/pkg/util070701000000A8000081A4000000000000000000000001683D8003000019D6000000000000000000000000000000000000002600000000chart-testing-3.13.0/pkg/util/util.go// 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 util
import (
"errors"
"fmt"
"io"
"io/fs"
"math/rand"
"net"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/Masterminds/semver"
"github.com/hashicorp/go-multierror"
"gopkg.in/yaml.v2"
)
const chars = "1234567890abcdefghijklmnopqrstuvwxyz"
type Maintainer struct {
Name string `yaml:"name"`
Email string `yaml:"email"`
}
type ChartYaml struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Deprecated bool `yaml:"deprecated"`
Maintainers []Maintainer
}
func Flatten(items []interface{}) ([]string, error) {
return doFlatten([]string{}, items)
}
func init() {
rand.New(rand.NewSource(time.Now().UnixNano())) // nolint: gosec
}
func doFlatten(result []string, items interface{}) ([]string, error) {
var err error
switch v := items.(type) {
case string:
result = append(result, v)
case []string:
result = append(result, v...)
case []interface{}:
for _, item := range v {
result, err = doFlatten(result, item)
if err != nil {
return nil, err
}
}
default:
return nil, fmt.Errorf("flatten does not support %T", v)
}
return result, err
}
func StringSliceContains(slice []string, s string) bool {
for _, element := range slice {
if s == element {
return true
}
}
return false
}
func FileExists(file string) bool {
if _, err := os.Stat(file); err != nil {
return false
}
return true
}
// RandomString string creates a random string of numbers and lower-case ascii characters with the specified length.
func RandomString(length int) string {
n := len(chars)
bytes := make([]byte, length)
for i := range bytes {
bytes[i] = chars[rand.Intn(n)] // nolint: gosec
}
return string(bytes)
}
type DirectoryLister struct{}
// ListChildDirs lists subdirectories of parentDir matching the test function.
func (l DirectoryLister) ListChildDirs(parentDir string, test func(dir string) bool) ([]string, error) {
entries, err := os.ReadDir(parentDir)
if err != nil {
return nil, err
}
fileInfos := make([]fs.FileInfo, 0, len(entries))
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
return nil, err
}
fileInfos = append(fileInfos, info)
}
var dirs []string
for _, dir := range fileInfos {
dirName := dir.Name()
parentSlashChildDir := filepath.Join(parentDir, dirName)
if test(parentSlashChildDir) {
dirs = append(dirs, parentSlashChildDir)
}
}
return dirs, nil
}
type Utils struct{}
func (u Utils) LookupChartDir(chartDirs []string, dir string) (string, error) {
for _, chartDir := range chartDirs {
currentDir := dir
for {
chartYaml := filepath.Join(currentDir, "Chart.yaml")
parent := filepath.Dir(filepath.Dir(chartYaml))
chartDir = strings.TrimRight(chartDir, "/") // remove any trailing slash from the dir
// check directory has a Chart.yaml and that it is in a
// direct subdirectory of a configured charts directory
if FileExists(chartYaml) && (parent == chartDir) {
return currentDir, nil
}
currentDir = filepath.Dir(currentDir)
relativeDir, _ := filepath.Rel(chartDir, currentDir)
joined := filepath.Join(chartDir, relativeDir)
if (joined == chartDir) || strings.HasPrefix(relativeDir, "..") {
break
}
}
}
return "", errors.New("no chart directory")
}
// ReadChartYaml attempts to parse Chart.yaml within the specified directory
// and return a newly allocated ChartYaml object. If no Chart.yaml is present
// or there is an error unmarshaling the file contents, an error will be returned.
func ReadChartYaml(dir string) (*ChartYaml, error) {
yamlBytes, err := os.ReadFile(filepath.Join(dir, "Chart.yaml"))
if err != nil {
return nil, fmt.Errorf("could not read 'Chart.yaml': %w", err)
}
return UnmarshalChartYaml(yamlBytes)
}
// UnmarshalChartYaml parses the yaml encoded data and returns a newly
// allocated ChartYaml object.
func UnmarshalChartYaml(yamlBytes []byte) (*ChartYaml, error) {
chartYaml := &ChartYaml{}
if err := yaml.Unmarshal(yamlBytes, chartYaml); err != nil {
return nil, fmt.Errorf("could not unmarshal 'Chart.yaml': %w", err)
}
return chartYaml, nil
}
func CompareVersions(left string, right string) (int, error) {
leftVersion, err := semver.NewVersion(left)
if err != nil {
return 0, fmt.Errorf("failed parsing semantic version: %w", err)
}
rightVersion, err := semver.NewVersion(right)
if err != nil {
return 0, fmt.Errorf("failed parsing semantic version: %w", err)
}
return leftVersion.Compare(rightVersion), nil
}
func BreakingChangeAllowed(left string, right string) (bool, error) {
leftVersion, err := semver.NewVersion(left)
if err != nil {
return false, fmt.Errorf("failed parsing semantic version: %w", err)
}
rightVersion, err := semver.NewVersion(right)
if err != nil {
return false, fmt.Errorf("failed parsing semantic version: %w", err)
}
constraintOp := "^"
if leftVersion.Major() == 0 {
constraintOp = "~"
}
c, err := semver.NewConstraint(fmt.Sprintf("%s %s", constraintOp, leftVersion.String()))
if err != nil {
return false, fmt.Errorf("failed parsing semantic version constraint: %w", err)
}
minor, reasons := c.Validate(rightVersion)
if len(reasons) > 0 {
err = multierror.Append(err, reasons...)
}
return !minor, err
}
func PrintDelimiterLineToWriter(w io.Writer, delimiterChar string) {
fmt.Fprintln(w, strings.Repeat(delimiterChar, 120))
}
func GithubGroupsBegin(w io.Writer, title string) {
fmt.Fprintf(w, "::group::%s\n", title)
}
func GithubGroupsEnd(w io.Writer) {
fmt.Fprintln(w, "::endgroup::")
}
func SanitizeName(s string, maxLength int) string {
reg := regexp.MustCompile("^[^a-zA-Z0-9]+")
excess := len(s) - maxLength
result := s
if excess > 0 {
result = s[excess:]
}
return reg.ReplaceAllString(result, "")
}
func GetRandomPort() (int, error) {
listener, err := net.Listen("tcp", ":0") // nolint: gosec
defer listener.Close() // nolint: staticcheck
if err != nil {
return 0, err
}
return listener.Addr().(*net.TCPAddr).Port, nil
}
070701000000A9000081A4000000000000000000000001683D800300000EBA000000000000000000000000000000000000002B00000000chart-testing-3.13.0/pkg/util/util_test.go// 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 util
import (
"fmt"
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFlatten(t *testing.T) {
var testDataSlice = []struct {
input []interface{}
expected []string
}{
{[]interface{}{"foo", "bar", []string{"bla", "blubb"}}, []string{"foo", "bar", "bla", "blubb"}},
{[]interface{}{"foo", "bar", "bla", "blubb"}, []string{"foo", "bar", "bla", "blubb"}},
{[]interface{}{"foo", "bar", []interface{}{"bla", []string{"blubb"}}}, []string{"foo", "bar", "bla", "blubb"}},
{[]interface{}{"foo", 42, []interface{}{"bla", []string{"blubb"}}}, nil},
}
for index, testData := range testDataSlice {
t.Run(strconv.Itoa(index), func(t *testing.T) {
actual, err := Flatten(testData.input)
assert.Equal(t, testData.expected, actual)
if testData.expected != nil {
assert.Nil(t, err)
} else {
assert.NotNil(t, err)
}
})
}
}
func TestCompareVersions(t *testing.T) {
var testDataSlice = []struct {
oldVersion string
newVersion string
expected int
}{
{"1.2.3", "1.2.4+2", -1},
{"1+foo", "1+bar", 0},
{"1.4-beta", "1.3", 1},
{"1.3-beta", "1.3", -1},
{"1", "2", -1},
{"3", "3", 0},
{"3-alpha", "3-beta", -1},
}
for index, testData := range testDataSlice {
t.Run(strconv.Itoa(index), func(t *testing.T) {
actual, _ := CompareVersions(testData.oldVersion, testData.newVersion)
assert.Equal(t, testData.expected, actual)
})
}
}
func TestSanitizeName(t *testing.T) {
var testDataSlice = []struct {
input string
maxLength int
expected string
}{
{"way-shorter-than-max-length", 63, "way-shorter-than-max-length"},
{"max-length", len("max-length"), "max-length"},
{"way-longer-than-max-length", 10, "max-length"},
{"one-shorter-than-max-length", len("one-shorter-than-max-length") + 1, "one-shorter-than-max-length"},
{"oone-longer-than-max-length", len("oone-longer-than-max-length") - 1, "one-longer-than-max-length"},
{"foo-would-start-with-hyphen-after-trimming", len("foo-would-start-with-hyphen-after-trimming") - 3, "would-start-with-hyphen-after-trimming"},
}
for index, testData := range testDataSlice {
t.Run(strconv.Itoa(index), func(t *testing.T) {
actual := SanitizeName(testData.input, testData.maxLength)
fmt.Printf("actual: %s,%d, input: %s,%d\n", actual, len(actual), testData.input, testData.maxLength)
assert.Equal(t, testData.expected, actual)
})
}
}
func TestBreakingChangeAllowed(t *testing.T) {
var testDataSlice = []struct {
left string
right string
breaking bool
}{
{"0.1.0", "0.1.0", false},
{"0.1.0", "0.1.1", false},
{"0.1.0", "0.2.0", true},
{"0.1.0", "0.2.1", true},
{"1.2.3", "1.2.3", false},
{"1.2.3", "1.2.4", false},
{"1.2.3", "1.3.0", false},
{"1.2.3", "2.0.0", true},
{"1.2.3", "10.0.0", true},
{"foo", "1.0.0", false}, // version parse error
{"1.0.0", "bar", false}, // version parse error
}
for index, testData := range testDataSlice {
t.Run(strconv.Itoa(index), func(t *testing.T) {
actual, _ := BreakingChangeAllowed(testData.left, testData.right)
assert.Equal(t, testData.breaking, actual, fmt.Sprintf("input: %s,%s\n", testData.left, testData.right))
})
}
}
070701000000AA000081ED000000000000000000000001683D8003000002B0000000000000000000000000000000000000001E00000000chart-testing-3.13.0/setup.sh#!/usr/bin/env bash
# Copyright The Helm Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -o errexit
go install golang.org/x/tools/cmd/goimports@latest
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!478 blocks