Merge branch 'master' into add-390x-support

pull/6988/head
Dean Coakley 5 years ago committed by GitHub
commit e4c0eb8720
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

230
KEYS

@ -622,3 +622,233 @@ BlA4kJPTfla4LmKRg/T/xow/naen/aM9mQCs7k2UAoeqNZ6IfQ6G5BZ81H9JNvHC
beriLZDBuRy1LJRjBmZEz+UDBgZoR9oz5DOLh8dGVpkt beriLZDBuRy1LJRjBmZEz+UDBgZoR9oz5DOLh8dGVpkt
=HZO9 =HZO9
-----END PGP PUBLIC KEY BLOCK----- -----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2014-05-13 [SCEA]
ABA2529598F6626C420D335B62F49E747D911B60
uid [ unknown] Matt Butcher <matt.butcher@microsoft.com>
sig 3 62F49E747D911B60 2017-08-11 Matt Butcher <matt.butcher@microsoft.com>
sig 461449C25E36B98E 2018-12-12 Matthew Farina <matt@mattfarina.com>
sig 1EF612347F8A9958 2018-12-12 Adam Reese <adam@reese.io>
sig 2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein <prydonius@gmail.com>
uid [ unknown] technosophos (keybase.io/technosophos) <technosophos@gmail.com>
sig 3 62F49E747D911B60 2016-10-24 Matt Butcher <matt.butcher@microsoft.com>
sig 461449C25E36B98E 2018-12-12 Matthew Farina <matt@mattfarina.com>
sig 1EF612347F8A9958 2018-12-12 Adam Reese <adam@reese.io>
sig 2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein <prydonius@gmail.com>
uid [ unknown] keybase.io/technosophos <technosophos@keybase.io>
sig 3 62F49E747D911B60 2014-05-13 Matt Butcher <matt.butcher@microsoft.com>
sig 461449C25E36B98E 2018-12-12 Matthew Farina <matt@mattfarina.com>
sig 1EF612347F8A9958 2018-12-12 Adam Reese <adam@reese.io>
sig 2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein <prydonius@gmail.com>
sub rsa2048 2014-05-13 [S] [expires: 2022-05-11]
sig 62F49E747D911B60 2014-05-13 Matt Butcher <matt.butcher@microsoft.com>
sub rsa2048 2014-05-13 [E] [expires: 2022-05-11]
sig 62F49E747D911B60 2014-05-13 Matt Butcher <matt.butcher@microsoft.com>
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
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
=nEJu
-----END PGP PUBLIC KEY BLOCK-----

@ -1,7 +1,8 @@
BINDIR := $(CURDIR)/bin BINDIR := $(CURDIR)/bin
DIST_DIRS := find * -type d -exec DIST_DIRS := find * -type d -exec
TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64 TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
BINNAME ?= helm TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256
BINNAME ?= helm
GOPATH = $(shell go env GOPATH) GOPATH = $(shell go env GOPATH)
DEP = $(GOPATH)/bin/dep DEP = $(GOPATH)/bin/dep
@ -138,6 +139,20 @@ dist:
$(DIST_DIRS) zip -r helm-${VERSION}-{}.zip {} \; \ $(DIST_DIRS) zip -r helm-${VERSION}-{}.zip {} \; \
) )
.PHONY: fetch-dist
fetch-dist:
mkdir -p _dist
cd _dist && \
for obj in ${TARGET_OBJS} ; do \
curl -sSL -o helm-${VERSION}-$${obj} https://get.helm.sh/helm-${VERSION}-$${obj} ; \
done
.PHONY: sign
sign:
for f in _dist/*.{gz,zip,sha256} ; do \
gpg --armor --detach-sign $${f} ; \
done
.PHONY: checksum .PHONY: checksum
checksum: checksum:
for f in _dist/*.{gz,zip} ; do \ for f in _dist/*.{gz,zip} ; do \

@ -4,6 +4,7 @@ maintainers:
- fibonacci1729 - fibonacci1729
- hickeyma - hickeyma
- jdolitsky - jdolitsky
- marckhouzam
- mattfarina - mattfarina
- michelleN - michelleN
- prydonius - prydonius

@ -2,7 +2,7 @@
[![CircleCI](https://circleci.com/gh/helm/helm.svg?style=shield)](https://circleci.com/gh/helm/helm) [![CircleCI](https://circleci.com/gh/helm/helm.svg?style=shield)](https://circleci.com/gh/helm/helm)
[![Go Report Card](https://goreportcard.com/badge/github.com/helm/helm)](https://goreportcard.com/report/github.com/helm/helm) [![Go Report Card](https://goreportcard.com/badge/github.com/helm/helm)](https://goreportcard.com/report/github.com/helm/helm)
[![GoDoc](https://godoc.org/helm.sh/helm?status.svg)](https://godoc.org/helm.sh/helm) [![GoDoc](https://img.shields.io/static/v1?label=godoc&message=reference&color=blue)](https://pkg.go.dev/helm.sh/helm/v3)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3131/badge)](https://bestpractices.coreinfrastructure.org/projects/3131) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/3131/badge)](https://bestpractices.coreinfrastructure.org/projects/3131)
Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources. Helm is a tool for managing Charts. Charts are packages of pre-configured Kubernetes resources.
@ -37,19 +37,19 @@ Unpack the `helm` binary and add it to your PATH and you are good to go!
If you want to use a package manager: If you want to use a package manager:
- [Homebrew](https://brew.sh/) users can use `brew install kubernetes-helm`. - [Homebrew](https://brew.sh/) users can use `brew install helm`.
- [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`. - [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`.
- [Scoop](https://scoop.sh/) users can use `scoop install helm`. - [Scoop](https://scoop.sh/) users can use `scoop install helm`.
- [GoFish](https://gofi.sh/) users can use `gofish install helm`. - [GoFish](https://gofi.sh/) users can use `gofish install helm`.
To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide). To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide).
See the [installation guide](https://docs.helm.sh/using_helm/#installing-helm) for more options, See the [installation guide](https://helm.sh/docs/intro/install/) for more options,
including installing pre-releases. including installing pre-releases.
## Docs ## Docs
Get started with the [Quick Start guide](https://docs.helm.sh/using_helm/#quickstart-guide) or plunge into the [complete documentation](https://docs.helm.sh) Get started with the [Quick Start guide](https://helm.sh/docs/intro/quickstart/) or plunge into the [complete documentation](https://helm.sh/docs)
## Roadmap ## Roadmap

@ -182,7 +182,7 @@ func TestDependencyUpdateCmd_DontDeleteOldChartsOnError(t *testing.T) {
func createTestingMetadata(name, baseURL string) *chart.Chart { func createTestingMetadata(name, baseURL string) *chart.Chart {
return &chart.Chart{ return &chart.Chart{
Metadata: &chart.Metadata{ Metadata: &chart.Metadata{
APIVersion: chart.APIVersionV1, APIVersion: chart.APIVersionV2,
Name: name, Name: name,
Version: "1.2.3", Version: "1.2.3",
Dependencies: []*chart.Dependency{ Dependencies: []*chart.Dependency{

@ -19,6 +19,7 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"sort"
"helm.sh/helm/v3/pkg/cli" "helm.sh/helm/v3/pkg/cli"
@ -55,8 +56,18 @@ type envOptions struct {
} }
func (o *envOptions) run(out io.Writer) error { func (o *envOptions) run(out io.Writer) error {
for k, v := range o.settings.EnvVars() { envVars := o.settings.EnvVars()
fmt.Printf("%s=\"%s\"\n", k, v)
// Sort the variables by alphabetical order.
// This allows for a constant output across calls to 'helm env'.
var keys []string
for k := range envVars {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
fmt.Printf("%s=\"%s\"\n", k, envVars[k])
} }
return nil return nil
} }

@ -17,6 +17,7 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"io" "io"
"time" "time"
@ -130,11 +131,12 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) { func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install")
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install")
f.BoolVar(&client.Replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") f.BoolVar(&client.Replace, "replace", false, "re-use the given name, only if that name is a deleted release which remains in the history. This is unsafe in production")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment, StatefulSet, or ReplicaSet are in a ready state before marking the release as successful. It will wait for as long as --timeout")
f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)")
f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release")
f.StringVar(&client.Description, "description", "", "add a custom description")
f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored") f.BoolVar(&client.Devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored")
f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart") f.BoolVar(&client.DependencyUpdate, "dependency-update", false, "run helm dependency update before installing the chart")
f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used") f.BoolVar(&client.Atomic, "atomic", false, "if set, installation process purges chart on fail. The --wait flag will be set automatically if --atomic is used")
@ -181,6 +183,10 @@ func runInstall(args []string, client *action.Install, valueOpts *values.Options
return nil, err return nil, err
} }
if chartRequested.Metadata.Deprecated {
fmt.Fprintln(out, "WARNING: This chart is deprecated")
}
if req := chartRequested.Metadata.Dependencies; req != nil { if req := chartRequested.Metadata.Dependencies; req != nil {
// If CheckDependencies returns an error, we have unfulfilled dependencies. // If CheckDependencies returns an error, we have unfulfilled dependencies.
// As of Helm 2.4.0, this is treated as a stopping condition: // As of Helm 2.4.0, this is treated as a stopping condition:

@ -183,6 +183,12 @@ func TestInstall(t *testing.T) {
wantError: true, wantError: true,
golden: "output/subchart-schema-cli-negative.txt", golden: "output/subchart-schema-cli-negative.txt",
}, },
// Install deprecated chart
{
name: "install with warning about deprecated chart",
cmd: "install aeneas testdata/testcharts/deprecated --namespace default",
golden: "output/deprecated-chart.txt",
},
} }
runTestActionCmd(t, tests) runTestActionCmd(t, tests)

@ -77,12 +77,15 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.SetStateMask() client.SetStateMask()
results, err := client.Run() results, err := client.Run()
if err != nil {
return err
}
if client.Short { if client.Short {
for _, res := range results { for _, res := range results {
fmt.Fprintln(out, res.Name) fmt.Fprintln(out, res.Name)
} }
return err return nil
} }
return outfmt.Write(out, newReleaseListWriter(results)) return outfmt.Write(out, newReleaseListWriter(results))
@ -94,7 +97,7 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date") f.BoolVarP(&client.ByDate, "date", "d", false, "sort by release date")
f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order") f.BoolVarP(&client.SortReverse, "reverse", "r", false, "reverse the sort order")
f.BoolVarP(&client.All, "all", "a", false, "show all releases, not just the ones marked deployed or failed") f.BoolVarP(&client.All, "all", "a", false, "show all releases, not just the ones marked deployed or failed")
f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases") f.BoolVar(&client.Uninstalled, "uninstalled", false, "show uninstalled releases (if 'helm uninstall --keep-history' was used)")
f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases") f.BoolVar(&client.Superseded, "superseded", false, "show superseded releases")
f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled") f.BoolVar(&client.Uninstalling, "uninstalling", false, "show releases that are currently being uninstalled")
f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled") f.BoolVar(&client.Deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")

@ -127,7 +127,7 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) {
func manuallyProcessArgs(args []string) ([]string, []string) { func manuallyProcessArgs(args []string) ([]string, []string) {
known := []string{} known := []string{}
unknown := []string{} unknown := []string{}
kvargs := []string{"--kube-context", "--namespace", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config"} kvargs := []string{"--kube-context", "--namespace", "-n", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config"}
knownArg := func(a string) bool { knownArg := func(a string) bool {
for _, pre := range kvargs { for _, pre := range kvargs {
if strings.HasPrefix(a, pre+"=") { if strings.HasPrefix(a, pre+"=") {
@ -136,13 +136,26 @@ func manuallyProcessArgs(args []string) ([]string, []string) {
} }
return false return false
} }
isKnown := func(v string) string {
for _, i := range kvargs {
if i == v {
return v
}
}
return ""
}
for i := 0; i < len(args); i++ { for i := 0; i < len(args); i++ {
switch a := args[i]; a { switch a := args[i]; a {
case "--debug": case "--debug":
known = append(known, a) known = append(known, a)
case "--kube-context", "--namespace", "-n", "--kubeconfig", "--registry-config", "--repository-cache", "--repository-config": case isKnown(a):
known = append(known, a, args[i+1]) known = append(known, a)
i++ i++
if i < len(args) {
known = append(known, args[i])
}
default: default:
if knownArg(a) { if knownArg(a) {
known = append(known, a) known = append(known, a)

@ -36,10 +36,15 @@ This command packages a chart into a versioned chart archive file. If a path
is given, this will look at that path for a chart (which must contain a is given, this will look at that path for a chart (which must contain a
Chart.yaml file) and then package that directory. Chart.yaml file) and then package that directory.
If no path is given, this will look in the present working directory for a
Chart.yaml file, and (if found) build the current directory into a chart.
Versioned chart archives are used by Helm package repositories. Versioned chart archives are used by Helm package repositories.
To sign a chart, use the '--sign' flag. In most cases, you should also
provide '--keyring path/to/secret/keys' and '--key keyname'.
$ helm package --sign ./mychart --key mykey --keyring ~/.gnupg/secring.gpg
If '--keyring' is not specified, Helm usually defaults to the public keyring
unless your environment is otherwise configured.
` `
func newPackageCmd(out io.Writer) *cobra.Command { func newPackageCmd(out io.Writer) *cobra.Command {

@ -30,14 +30,27 @@ func TestManuallyProcessArgs(t *testing.T) {
"--debug", "--debug",
"--foo", "bar", "--foo", "bar",
"--kubeconfig=/home/foo", "--kubeconfig=/home/foo",
"--kubeconfig", "/home/foo",
"--kube-context=test1",
"--kube-context", "test1", "--kube-context", "test1",
"-n=test2",
"-n", "test2", "-n", "test2",
"--namespace=test2",
"--namespace", "test2",
"--home=/tmp", "--home=/tmp",
"command", "command",
} }
expectKnown := []string{ expectKnown := []string{
"--debug", "--kubeconfig=/home/foo", "--kube-context", "test1", "-n", "test2", "--debug",
"--kubeconfig=/home/foo",
"--kubeconfig", "/home/foo",
"--kube-context=test1",
"--kube-context", "test1",
"-n=test2",
"-n", "test2",
"--namespace=test2",
"--namespace", "test2",
} }
expectUnknown := []string{ expectUnknown := []string{

@ -29,7 +29,7 @@ import (
"github.com/gofrs/flock" "github.com/gofrs/flock"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"gopkg.in/yaml.v2" "sigs.k8s.io/yaml"
"helm.sh/helm/v3/cmd/helm/require" "helm.sh/helm/v3/cmd/helm/require"
"helm.sh/helm/v3/pkg/getter" "helm.sh/helm/v3/pkg/getter"

@ -64,12 +64,12 @@ __helm_override_flags_to_kubectl_flags()
# --kubeconfig, -n, --namespace stay the same for kubectl # --kubeconfig, -n, --namespace stay the same for kubectl
# --kube-context becomes --context for kubectl # --kube-context becomes --context for kubectl
__helm_debug "${FUNCNAME[0]}: flags to convert: $1" __helm_debug "${FUNCNAME[0]}: flags to convert: $1"
echo "$1" | sed s/kube-context/context/ echo "$1" | \sed s/kube-context/context/
} }
__helm_get_repos() __helm_get_repos()
{ {
eval $(__helm_binary_name) repo list 2>/dev/null | tail +2 | cut -f1 eval $(__helm_binary_name) repo list 2>/dev/null | \tail +2 | \cut -f1
} }
__helm_get_contexts() __helm_get_contexts()
@ -125,7 +125,7 @@ __helm_zsh_comp_nospace() {
# We only do this if there is a single choice left for completion # We only do this if there is a single choice left for completion
# to reduce the times the user could be presented with the fake completion choice. # to reduce the times the user could be presented with the fake completion choice.
out=($(echo ${in[*]} | tr " " "\n" | \grep "^${cur}")) out=($(echo ${in[*]} | \tr " " "\n" | \grep "^${cur}"))
__helm_debug "${FUNCNAME[0]}: out is ${out[*]}" __helm_debug "${FUNCNAME[0]}: out is ${out[*]}"
[ ${#out[*]} -eq 1 ] && out+=("${out}.") [ ${#out[*]} -eq 1 ] && out+=("${out}.")
@ -145,7 +145,7 @@ __helm_list_charts()
for repo in $(__helm_get_repos); do for repo in $(__helm_get_repos); do
if [[ "${cur}" =~ ^${repo}/.* ]]; then if [[ "${cur}" =~ ^${repo}/.* ]]; then
# We are doing completion from within a repo # We are doing completion from within a repo
out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | cut -f1 | \grep ^${cur}) out=$(eval $(__helm_binary_name) search repo ${cur} 2>/dev/null | \cut -f1 | \grep ^${cur})
nospace=0 nospace=0
elif [[ ${repo} =~ ^${cur}.* ]]; then elif [[ ${repo} =~ ^${cur}.* ]]; then
# We are completing a repo name # We are completing a repo name
@ -236,7 +236,7 @@ __helm_list_plugins()
__helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}" __helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out local out
# Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3) # Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h3)
if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | tail +2 | cut -f1); then if out=$(eval $(__helm_binary_name) plugin list 2>/dev/null | \tail +2 | \cut -f1); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi fi
} }

@ -31,12 +31,13 @@ import (
"helm.sh/helm/v3/pkg/release" "helm.sh/helm/v3/pkg/release"
) )
// NOTE: Keep the list of statuses up-to-date with pkg/release/status.go.
var statusHelp = ` var statusHelp = `
This command shows the status of a named release. This command shows the status of a named release.
The status consists of: The status consists of:
- last deployment time - last deployment time
- k8s namespace in which the release lives - k8s namespace in which the release lives
- state of the release (can be: unknown, deployed, deleted, superseded, failed or deleting) - state of the release (can be: unknown, deployed, uninstalled, superseded, failed, uninstalling, pending-install, pending-upgrade or pending-rollback)
- list of resources that this release consists of, sorted by kind - list of resources that this release consists of, sorted by kind
- details on last test suite run, if applicable - details on last test suite run, if applicable
- additional notes provided by the chart - additional notes provided by the chart

@ -44,6 +44,7 @@ faked locally. Additionally, none of the server-side testing of chart validity
func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
var validate bool var validate bool
var includeCrds bool
client := action.NewInstall(cfg) client := action.NewInstall(cfg)
valueOpts := &values.Options{} valueOpts := &values.Options{}
var extraAPIs []string var extraAPIs []string
@ -67,7 +68,14 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
var manifests bytes.Buffer var manifests bytes.Buffer
if includeCrds {
for _, f := range rel.Chart.CRDs() {
fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", f.Name, f.Data)
}
}
fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest)) fmt.Fprintln(&manifests, strings.TrimSpace(rel.Manifest))
if !client.DisableHooks { if !client.DisableHooks {
for _, m := range rel.Hooks { for _, m := range rel.Hooks {
fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest) fmt.Fprintf(&manifests, "---\n# Source: %s\n%s\n", m.Path, m.Manifest)
@ -120,6 +128,8 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates") f.StringArrayVarP(&showFiles, "show-only", "s", []string{}, "only show manifests rendered from the given templates")
f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout") f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install") f.BoolVar(&validate, "validate", false, "validate your manifests against the Kubernetes cluster you are currently pointing at. This is the same validation performed on an install")
f.BoolVar(&includeCrds, "include-crds", false, "include CRDs in the templated output")
f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall")
f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions")
return cmd return cmd

@ -79,6 +79,11 @@ func TestTemplateCmd(t *testing.T) {
cmd: fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath), cmd: fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath),
golden: "output/template-with-api-version.txt", golden: "output/template-with-api-version.txt",
}, },
{
name: "template with CRDs",
cmd: fmt.Sprintf("template '%s' --include-crds", chartPath),
golden: "output/template-with-crds.txt",
},
} }
runTestCmd(t, tests) runTestCmd(t, tests)
} }

@ -0,0 +1,7 @@
WARNING: This chart is deprecated
NAME: aeneas
LAST DEPLOYED: Fri Sep 2 22:04:05 1977
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

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

@ -1,9 +0,0 @@
NAME: flummoxed-chickadee
LAST DEPLOYED: 2016-01-16 00:00:00 +0000 UTC
NAMESPACE: default
STATUS: deployed
RESOURCES:
resource A
resource B

@ -1,10 +0,0 @@
info:
deleted: "0001-01-01T00:00:00Z"
first_deployed: "0001-01-01T00:00:00Z"
last_deployed: "2016-01-16T00:00:00Z"
resources: |
resource A
resource B
status: deployed
name: flummoxed-chickadee
namespace: default

@ -0,0 +1,72 @@
---
# Source: crds/crdA.yaml
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: testCRDs
spec:
group: testCRDGroups
names:
kind: TestCRD
listKind: TestCRDList
plural: TestCRDs
shortNames:
- tc
singular: authconfig
---
# Source: subchart1/charts/subcharta/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: subcharta
labels:
helm.sh/chart: "subcharta-0.1.0"
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: apache
selector:
app.kubernetes.io/name: subcharta
---
# Source: subchart1/charts/subchartb/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: subchartb
labels:
helm.sh/chart: "subchartb-0.1.0"
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: nginx
selector:
app.kubernetes.io/name: subchartb
---
# Source: subchart1/templates/service.yaml
apiVersion: v1
kind: Service
metadata:
name: subchart1
labels:
helm.sh/chart: "subchart1-0.1.0"
app.kubernetes.io/instance: "RELEASE-NAME"
kube-version/major: "1"
kube-version/minor: "16"
kube-version/version: "v1.16.0"
kube-api-version/test: v1
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: nginx
selector:
app.kubernetes.io/name: subchart1

@ -1 +1 @@
version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"", GitTreeState:"", GoVersion:""} version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""}

@ -1 +1 @@
version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"", GitTreeState:"", GoVersion:""} version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""}

@ -1 +1 @@
v3.0+unreleased v3.0

@ -1 +1 @@
Version: v3.0+unreleased Version: v3.0

@ -1 +1 @@
version.BuildInfo{Version:"v3.0+unreleased", GitCommit:"", GitTreeState:"", GoVersion:""} version.BuildInfo{Version:"v3.0", GitCommit:"", GitTreeState:"", GoVersion:""}

@ -0,0 +1,8 @@
apiVersion: v1
description: Deprecated testing chart
home: https://helm.sh/helm
name: deprecated
sources:
- https://github.com/helm/helm
version: 0.1.0
deprecated: true

@ -0,0 +1,3 @@
#Deprecated
This space intentionally left blank.

@ -69,6 +69,7 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation")
f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history") f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history")
f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.StringVar(&client.Description, "description", "", "add a custom description")
return cmd return cmd
} }

@ -127,6 +127,10 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
} }
} }
if ch.Metadata.Deprecated {
fmt.Fprintln(out, "WARNING: This chart is deprecated")
}
rel, err := client.Run(args[0], ch, vals) rel, err := client.Run(args[0], ch, vals)
if err != nil { if err != nil {
return errors.Wrap(err, "UPGRADE FAILED") return errors.Wrap(err, "UPGRADE FAILED")
@ -156,6 +160,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
f.IntVar(&client.MaxHistory, "history-max", 10, "limit the maximum number of revisions saved per release. Use 0 for no limit") f.IntVar(&client.MaxHistory, "history-max", 10, "limit the maximum number of revisions saved per release. Use 0 for no limit")
f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails") f.BoolVar(&client.CleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade fails")
f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent") f.BoolVar(&client.SubNotes, "render-subchart-notes", false, "if set, render subchart notes along with the parent")
f.StringVar(&client.Description, "description", "", "add a custom description")
addChartPathOptionsFlags(f, &client.ChartPathOptions) addChartPathOptionsFlags(f, &client.ChartPathOptions)
addValueOptionsFlags(f, valueOpts) addValueOptionsFlags(f, valueOpts)
bindOutputFlag(cmd, &outfmt) bindOutputFlag(cmd, &outfmt)

@ -1,3 +1,3 @@
# Kubernetes Community Code of Conduct # Community Code of Conduct
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) Helm follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md).

@ -5,8 +5,8 @@ go 1.13
require ( require (
github.com/BurntSushi/toml v0.3.1 github.com/BurntSushi/toml v0.3.1
github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect github.com/MakeNowJust/heredoc v0.0.0-20171113091838-e9091a26100e // indirect
github.com/Masterminds/semver/v3 v3.0.1 github.com/Masterminds/semver/v3 v3.0.3
github.com/Masterminds/sprig/v3 v3.0.0 github.com/Masterminds/sprig/v3 v3.0.2
github.com/Masterminds/vcs v1.13.0 github.com/Masterminds/vcs v1.13.0
github.com/Microsoft/go-winio v0.4.12 // indirect github.com/Microsoft/go-winio v0.4.12 // indirect
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a

@ -27,8 +27,12 @@ github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RP
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk= github.com/Masterminds/semver/v3 v3.0.1 h1:2kKm5lb7dKVrt5TYUiAavE6oFc1cFT0057UVGT+JqLk=
github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.0.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g= github.com/Masterminds/sprig/v3 v3.0.0 h1:KSQz7Nb08/3VU9E4ns29dDxcczhOD1q7O1UfM4G3t3g=
github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U= github.com/Masterminds/sprig/v3 v3.0.0/go.mod h1:NEUY/Qq8Gdm2xgYA+NwJM6wmfdRV9xkh8h/Rld20R0U=
github.com/Masterminds/sprig/v3 v3.0.2 h1:wz22D0CiSctrliXiI9ZO3HoNApweeRGftyDN+BQa3B8=
github.com/Masterminds/sprig/v3 v3.0.2/go.mod h1:oesJ8kPONMONaZgtiHNzUShJbksypC5kWczhZAf6+aU=
github.com/Masterminds/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA= github.com/Masterminds/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA=
github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA=
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=

@ -27,18 +27,14 @@ import (
// Options represents configurable options used to create client and server TLS configurations. // Options represents configurable options used to create client and server TLS configurations.
type Options struct { type Options struct {
CaCertFile string CaCertFile string
// If either the KeyFile or CertFile is empty, ClientConfig() will not load them, // If either the KeyFile or CertFile is empty, ClientConfig() will not load them.
// preventing Helm from authenticating to Tiller. They are required to be non-empty
// when calling ServerConfig, otherwise an error is returned.
KeyFile string KeyFile string
CertFile string CertFile string
// Client-only options // Client-only options
InsecureSkipVerify bool InsecureSkipVerify bool
// Server-only options
ClientAuth tls.ClientAuthType
} }
// ClientConfig retusn a TLS configuration for use by a Helm client. // ClientConfig returns a TLS configuration for use by a Helm client.
func ClientConfig(opts Options) (cfg *tls.Config, err error) { func ClientConfig(opts Options) (cfg *tls.Config, err error) {
var cert *tls.Certificate var cert *tls.Certificate
var pool *x509.CertPool var pool *x509.CertPool
@ -60,24 +56,3 @@ func ClientConfig(opts Options) (cfg *tls.Config, err error) {
cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool} cfg = &tls.Config{InsecureSkipVerify: opts.InsecureSkipVerify, Certificates: []tls.Certificate{*cert}, RootCAs: pool}
return cfg, nil return cfg, nil
} }
// ServerConfig returns a TLS configuration for use by the Tiller server.
func ServerConfig(opts Options) (cfg *tls.Config, err error) {
var cert *tls.Certificate
var pool *x509.CertPool
if cert, err = CertFromFilePair(opts.CertFile, opts.KeyFile); err != nil {
if os.IsNotExist(err) {
return nil, errors.Wrapf(err, "could not load x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile)
}
return nil, errors.Wrapf(err, "could not read x509 key pair (cert: %q, key: %q)", opts.CertFile, opts.KeyFile)
}
if opts.ClientAuth >= tls.VerifyClientCertIfGiven && opts.CaCertFile != "" {
if pool, err = CertPoolFromFile(opts.CaCertFile); err != nil {
return nil, err
}
}
cfg = &tls.Config{MinVersion: tls.VersionTLS12, ClientAuth: opts.ClientAuth, Certificates: []tls.Certificate{*cert}, ClientCAs: pool}
return cfg, nil
}

@ -17,7 +17,6 @@ limitations under the License.
package tlsutil package tlsutil
import ( import (
"crypto/tls"
"path/filepath" "path/filepath"
"testing" "testing"
) )
@ -54,26 +53,6 @@ func TestClientConfig(t *testing.T) {
} }
} }
func TestServerConfig(t *testing.T) {
opts := Options{
CaCertFile: testfile(t, testCaCertFile),
CertFile: testfile(t, testCertFile),
KeyFile: testfile(t, testKeyFile),
ClientAuth: tls.RequireAndVerifyClientCert,
}
cfg, err := ServerConfig(opts)
if err != nil {
t.Fatalf("error building tls server config: %v", err)
}
if got := cfg.MinVersion; got != tls.VersionTLS12 {
t.Errorf("expecting TLS version 1.2, got %d", got)
}
if got := cfg.ClientCAs; got == nil {
t.Errorf("expecting non-nil CA pool")
}
}
func testfile(t *testing.T, file string) (path string) { func testfile(t *testing.T, file string) (path string) {
var err error var err error
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil { if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {

@ -33,7 +33,7 @@ var (
version = "v3.0" version = "v3.0"
// metadata is extra build time data // metadata is extra build time data
metadata = "unreleased" metadata = ""
// gitCommit is the git sha1 // gitCommit is the git sha1
gitCommit = "" gitCommit = ""
// gitTreeState is the state of the git tree // gitTreeState is the state of the git tree

@ -39,6 +39,10 @@ func NewGetValues(cfg *Configuration) *GetValues {
// Run executes 'helm get values' against the given release. // Run executes 'helm get values' against the given release.
func (g *GetValues) Run(name string) (map[string]interface{}, error) { func (g *GetValues) Run(name string) (map[string]interface{}, error) {
if err := g.cfg.KubeClient.IsReachable(); err != nil {
return nil, err
}
rel, err := g.cfg.releaseContent(name, g.Version) rel, err := g.cfg.releaseContent(name, g.Version)
if err != nil { if err != nil {
return nil, err return nil, err

@ -79,6 +79,7 @@ type Install struct {
ReleaseName string ReleaseName string
GenerateName bool GenerateName bool
NameTemplate string NameTemplate string
Description string
OutputDir string OutputDir string
Atomic bool Atomic bool
SkipCRDs bool SkipCRDs bool
@ -86,6 +87,8 @@ type Install struct {
// APIVersions allows a manual set of supported API Versions to be passed // APIVersions allows a manual set of supported API Versions to be passed
// (for things like templating). These are ignored if ClientOnly is false // (for things like templating). These are ignored if ClientOnly is false
APIVersions chartutil.VersionSet APIVersions chartutil.VersionSet
// Used by helm template to render charts with .Relase.IsUpgrade. Ignored if Dry-Run is false
IsUpgrade bool
} }
// ChartPathOptions captures common options used for controlling chart paths // ChartPathOptions captures common options used for controlling chart paths
@ -197,11 +200,14 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
return nil, err return nil, err
} }
//special case for helm template --is-upgrade
isUpgrade := i.IsUpgrade && i.DryRun
options := chartutil.ReleaseOptions{ options := chartutil.ReleaseOptions{
Name: i.ReleaseName, Name: i.ReleaseName,
Namespace: i.Namespace, Namespace: i.Namespace,
Revision: 1, Revision: 1,
IsInstall: true, IsInstall: !isUpgrade,
IsUpgrade: isUpgrade,
} }
valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps) valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
if err != nil { if err != nil {
@ -237,7 +243,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
// we'll end up in a state where we will delete those resources upon // we'll end up in a state where we will delete those resources upon
// deleting the release because the manifest will be pointing at that // deleting the release because the manifest will be pointing at that
// resource // resource
if !i.ClientOnly { if !i.ClientOnly && !isUpgrade {
if err := existingResourceConflict(resources); err != nil { if err := existingResourceConflict(resources); err != nil {
return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install") return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install")
} }
@ -292,7 +298,11 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
} }
} }
rel.SetStatus(release.StatusDeployed, "Install complete") if len(i.Description) > 0 {
rel.SetStatus(release.StatusDeployed, i.Description)
} else {
rel.SetStatus(release.StatusDeployed, "Install complete")
}
// This is a tricky case. The release has been created, but the result // This is a tricky case. The release has been created, but the result
// cannot be recorded. The truest thing to tell the user is that the // cannot be recorded. The truest thing to tell the user is that the
@ -301,7 +311,9 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
// //
// One possible strategy would be to do a timed retry to see if we can get // One possible strategy would be to do a timed retry to see if we can get
// this stored in the future. // this stored in the future.
i.recordRelease(rel) if err := i.recordRelease(rel); err != nil {
i.cfg.Log("failed to record the release: %s", err)
}
return rel, nil return rel, nil
} }
@ -420,7 +432,7 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values
if ch.Metadata.KubeVersion != "" { if ch.Metadata.KubeVersion != "" {
if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) { if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) {
return hs, b, "", errors.Errorf("chart requires kubernetesVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
} }
} }
@ -543,6 +555,10 @@ func (i *Install) NameAndChart(args []string) (string, string, error) {
return nil return nil
} }
if len(args) > 2 {
return args[0], args[1], errors.Errorf("expected at most two arguments, unexpected arguments: %v", strings.Join(args[2:], ", "))
}
if len(args) == 2 { if len(args) == 2 {
return args[0], args[1], flagsNotSet() return args[0], args[1], flagsNotSet()
} }

@ -306,7 +306,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) {
vals = map[string]interface{}{} vals = map[string]interface{}{}
_, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals) _, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals)
is.Error(err) is.Error(err)
is.Contains(err.Error(), "chart requires kubernetesVersion") is.Contains(err.Error(), "chart requires kubeVersion")
} }
func TestInstallRelease_Wait(t *testing.T) { func TestInstallRelease_Wait(t *testing.T) {
@ -505,6 +505,14 @@ func TestNameAndChart(t *testing.T) {
t.Fatal("expected an error") t.Fatal("expected an error")
} }
is.Equal("must either provide a name or specify --generate-name", err.Error()) is.Equal("must either provide a name or specify --generate-name", err.Error())
instAction.NameTemplate = ""
instAction.ReleaseName = ""
_, _, err = instAction.NameAndChart([]string{"foo", chartName, "bar"})
if err == nil {
t.Fatal("expected an error")
}
is.Equal("expected at most two arguments, unexpected arguments: bar", err.Error())
} }
func TestNameAndChartGenerateName(t *testing.T) { func TestNameAndChartGenerateName(t *testing.T) {

@ -237,27 +237,36 @@ func makeMeSomeReleases(store *storage.Storage, t *testing.T) {
} }
func TestFilterList(t *testing.T) { func TestFilterList(t *testing.T) {
one := releaseStub() t.Run("should filter old versions of the same release", func(t *testing.T) {
one.Name = "one" r1 := releaseStub()
one.Namespace = "default" r1.Name = "r"
one.Version = 1 r1.Version = 1
two := releaseStub() r2 := releaseStub()
two.Name = "two" r2.Name = "r"
two.Namespace = "default" r2.Version = 2
two.Version = 1 another := releaseStub()
anotherOldOne := releaseStub() another.Name = "another"
anotherOldOne.Name = "one" another.Version = 1
anotherOldOne.Namespace = "testing"
anotherOldOne.Version = 1 filteredList := filterList([]*release.Release{r1, r2, another})
anotherOne := releaseStub() expectedFilteredList := []*release.Release{r2, another}
anotherOne.Name = "one"
anotherOne.Namespace = "testing" assert.ElementsMatch(t, expectedFilteredList, filteredList)
anotherOne.Version = 2 })
list := []*release.Release{one, two, anotherOne} t.Run("should not filter out any version across namespaces", func(t *testing.T) {
expectedFilteredList := []*release.Release{one, two, anotherOne} r1 := releaseStub()
r1.Name = "r"
filteredList := filterList(list) r1.Namespace = "default"
r1.Version = 1
assert.ElementsMatch(t, expectedFilteredList, filteredList) r2 := releaseStub()
r2.Name = "r"
r2.Namespace = "testing"
r2.Version = 2
filteredList := filterList([]*release.Release{r1, r2})
expectedFilteredList := []*release.Release{r1, r2}
assert.ElementsMatch(t, expectedFilteredList, filteredList)
})
} }

@ -63,6 +63,7 @@ func (p *Pull) Run(chartRef string) (string, error) {
Getters: getter.All(p.Settings), Getters: getter.All(p.Settings),
Options: []getter.Option{ Options: []getter.Option{
getter.WithBasicAuth(p.Username, p.Password), getter.WithBasicAuth(p.Username, p.Password),
getter.WithTLSClientConfig(p.CertFile, p.KeyFile, p.CaFile),
}, },
RepositoryConfig: p.Settings.RepositoryConfig, RepositoryConfig: p.Settings.RepositoryConfig,
RepositoryCache: p.Settings.RepositoryCache, RepositoryCache: p.Settings.RepositoryCache,

@ -37,6 +37,7 @@ type Uninstall struct {
DryRun bool DryRun bool
KeepHistory bool KeepHistory bool
Timeout time.Duration Timeout time.Duration
Description string
} }
// NewUninstall creates a new Uninstall object with the given configuration. // NewUninstall creates a new Uninstall object with the given configuration.
@ -118,7 +119,11 @@ func (u *Uninstall) Run(name string) (*release.UninstallReleaseResponse, error)
} }
rel.Info.Status = release.StatusUninstalled rel.Info.Status = release.StatusUninstalled
rel.Info.Description = "Uninstallation complete" if len(u.Description) > 0 {
rel.Info.Description = u.Description
} else {
rel.Info.Description = "Uninstallation complete"
}
if !u.KeepHistory { if !u.KeepHistory {
u.cfg.Log("purge requested for %s", name) u.cfg.Log("purge requested for %s", name)

@ -58,6 +58,7 @@ type Upgrade struct {
Atomic bool Atomic bool
CleanupOnFail bool CleanupOnFail bool
SubNotes bool SubNotes bool
Description string
} }
// NewUpgrade creates a new Upgrade object with the given configuration. // NewUpgrade creates a new Upgrade object with the given configuration.
@ -218,7 +219,11 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
if u.DryRun { if u.DryRun {
u.cfg.Log("dry run for %s", upgradedRelease.Name) u.cfg.Log("dry run for %s", upgradedRelease.Name)
upgradedRelease.Info.Description = "Dry run complete" if len(u.Description) > 0 {
upgradedRelease.Info.Description = u.Description
} else {
upgradedRelease.Info.Description = "Dry run complete"
}
return upgradedRelease, nil return upgradedRelease, nil
} }
@ -270,7 +275,11 @@ func (u *Upgrade) performUpgrade(originalRelease, upgradedRelease *release.Relea
u.cfg.recordRelease(originalRelease) u.cfg.recordRelease(originalRelease)
upgradedRelease.Info.Status = release.StatusDeployed upgradedRelease.Info.Status = release.StatusDeployed
upgradedRelease.Info.Description = "Upgrade complete" if len(u.Description) > 0 {
upgradedRelease.Info.Description = u.Description
} else {
upgradedRelease.Info.Description = "Upgrade complete"
}
return upgradedRelease, nil return upgradedRelease, nil
} }

@ -126,6 +126,12 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
continue continue
} }
switch hd.Typeflag {
// We don't want to process these extension header files.
case tar.TypeXGlobalHeader, tar.TypeXHeader:
continue
}
// Archive could contain \ if generated on Windows // Archive could contain \ if generated on Windows
delimiter := "/" delimiter := "/"
if strings.ContainsRune(hd.Name, '\\') { if strings.ContainsRune(hd.Name, '\\') {

@ -0,0 +1,110 @@
/*
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 loader
import (
"archive/tar"
"bytes"
"compress/gzip"
"testing"
)
func TestLoadArchiveFiles(t *testing.T) {
tcs := []struct {
name string
generate func(w *tar.Writer)
check func(t *testing.T, files []*BufferedFile, err error)
}{
{
name: "empty input should return no files",
generate: func(w *tar.Writer) {},
check: func(t *testing.T, files []*BufferedFile, err error) {
if err.Error() != "no files in chart archive" {
t.Fatalf(`expected "no files in chart archive", got [%#v]`, err)
}
},
},
{
name: "should ignore files with XGlobalHeader type",
generate: func(w *tar.Writer) {
// simulate the presence of a `pax_global_header` file like you would get when
// processing a GitHub release archive.
_ = w.WriteHeader(&tar.Header{
Typeflag: tar.TypeXGlobalHeader,
Name: "pax_global_header",
})
// we need to have at least one file, otherwise we'll get the "no files in chart archive" error
_ = w.WriteHeader(&tar.Header{
Typeflag: tar.TypeReg,
Name: "dir/empty",
})
},
check: func(t *testing.T, files []*BufferedFile, err error) {
if err != nil {
t.Fatalf(`got unwanted error [%#v] for tar file with pax_global_header content`, err)
}
if len(files) != 1 {
t.Fatalf(`expected to get one file but got [%v]`, files)
}
},
},
{
name: "should ignore files with TypeXHeader type",
generate: func(w *tar.Writer) {
// simulate the presence of a `pax_header` file like you might get when
// processing a GitHub release archive.
_ = w.WriteHeader(&tar.Header{
Typeflag: tar.TypeXHeader,
Name: "pax_header",
})
// we need to have at least one file, otherwise we'll get the "no files in chart archive" error
_ = w.WriteHeader(&tar.Header{
Typeflag: tar.TypeReg,
Name: "dir/empty",
})
},
check: func(t *testing.T, files []*BufferedFile, err error) {
if err != nil {
t.Fatalf(`got unwanted error [%#v] for tar file with pax_header content`, err)
}
if len(files) != 1 {
t.Fatalf(`expected to get one file but got [%v]`, files)
}
},
},
}
for _, tc := range tcs {
t.Run(tc.name, func(t *testing.T) {
buf := &bytes.Buffer{}
gzw := gzip.NewWriter(buf)
tw := tar.NewWriter(gzw)
tc.generate(tw)
_ = tw.Close()
_ = gzw.Close()
files, err := LoadArchiveFiles(buf)
tc.check(t, files, err)
})
}
}

@ -114,12 +114,18 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) {
if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil {
return c, errors.Wrap(err, "cannot load requirements.yaml") return c, errors.Wrap(err, "cannot load requirements.yaml")
} }
if c.Metadata.APIVersion == chart.APIVersionV1 {
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
}
// Deprecated: requirements.lock is deprecated use Chart.lock. // Deprecated: requirements.lock is deprecated use Chart.lock.
case f.Name == "requirements.lock": case f.Name == "requirements.lock":
c.Lock = new(chart.Lock) c.Lock = new(chart.Lock)
if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil {
return c, errors.Wrap(err, "cannot load requirements.lock") return c, errors.Wrap(err, "cannot load requirements.lock")
} }
if c.Metadata.APIVersion == chart.APIVersionV1 {
c.Files = append(c.Files, &chart.File{Name: f.Name, Data: f.Data})
}
case strings.HasPrefix(f.Name, "templates/"): case strings.HasPrefix(f.Name, "templates/"):
c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data}) c.Templates = append(c.Templates, &chart.File{Name: f.Name, Data: f.Data})

@ -42,7 +42,16 @@ func LoadChartfile(filename string) (*chart.Metadata, error) {
// //
// 'filename' should be the complete path and filename ('foo/Chart.yaml') // 'filename' should be the complete path and filename ('foo/Chart.yaml')
func SaveChartfile(filename string, cf *chart.Metadata) error { func SaveChartfile(filename string, cf *chart.Metadata) error {
// Pull out the dependencies of a v1 Chart, since there's no way
// to tell the serialiser to skip a field for just this use case
savedDependencies := cf.Dependencies
if cf.APIVersion == chart.APIVersionV1 {
cf.Dependencies = nil
}
out, err := yaml.Marshal(cf) out, err := yaml.Marshal(cf)
if cf.APIVersion == chart.APIVersionV1 {
cf.Dependencies = savedDependencies
}
if err != nil { if err != nil {
return err return err
} }

@ -141,8 +141,17 @@ func Save(c *chart.Chart, outDir string) (string, error) {
func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error { func writeTarContents(out *tar.Writer, c *chart.Chart, prefix string) error {
base := filepath.Join(prefix, c.Name()) base := filepath.Join(prefix, c.Name())
// Pull out the dependencies of a v1 Chart, since there's no way
// to tell the serialiser to skip a field for just this use case
savedDependencies := c.Metadata.Dependencies
if c.Metadata.APIVersion == chart.APIVersionV1 {
c.Metadata.Dependencies = nil
}
// Save Chart.yaml // Save Chart.yaml
cdata, err := yaml.Marshal(c.Metadata) cdata, err := yaml.Marshal(c.Metadata)
if c.Metadata.APIVersion == chart.APIVersionV1 {
c.Metadata.Dependencies = savedDependencies
}
if err != nil { if err != nil {
return err return err
} }

@ -0,0 +1,13 @@
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: testCRDs
spec:
group: testCRDGroups
names:
kind: TestCRD
listKind: TestCRDList
plural: TestCRDs
shortNames:
- tc
singular: authconfig

@ -214,6 +214,10 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, er
c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password)) c.Options = append(c.Options, getter.WithBasicAuth(r.Config.Username, r.Config.Password))
} }
if r.Config.CertFile != "" || r.Config.KeyFile != "" || r.Config.CAFile != "" {
c.Options = append(c.Options, getter.WithTLSClientConfig(r.Config.CertFile, r.Config.KeyFile, r.Config.CAFile))
}
// Next, we need to load the index, and actually look up the chart. // Next, we need to load the index, and actually look up the chart.
idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name)) idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
i, err := repo.LoadIndexFile(idxFile) i, err := repo.LoadIndexFile(idxFile)

@ -80,6 +80,67 @@ func TestResolveChartRef(t *testing.T) {
} }
} }
func TestResolveChartOpts(t *testing.T) {
tests := []struct {
name, ref, version string
expect []getter.Option
}{
{
name: "repo with CA-file",
ref: "testing-ca-file/foo",
expect: []getter.Option{
getter.WithURL("https://example.com/foo-1.2.3.tgz"),
getter.WithTLSClientConfig("cert", "key", "ca"),
},
},
}
c := ChartDownloader{
Out: os.Stderr,
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
Getters: getter.All(&cli.EnvSettings{
RepositoryConfig: repoConfig,
RepositoryCache: repoCache,
}),
}
// snapshot options
snapshotOpts := c.Options
for _, tt := range tests {
// reset chart downloader options for each test case
c.Options = snapshotOpts
expect, err := getter.NewHTTPGetter(tt.expect...)
if err != nil {
t.Errorf("%s: failed to setup http client: %s", tt.name, err)
continue
}
u, err := c.ResolveChartVersion(tt.ref, tt.version)
if err != nil {
t.Errorf("%s: failed with error %s", tt.name, err)
continue
}
got, err := getter.NewHTTPGetter(
append(
c.Options,
getter.WithURL(u.String()),
)...,
)
if err != nil {
t.Errorf("%s: failed to create http client: %s", tt.name, err)
continue
}
if *(got.(*getter.HTTPGetter)) != *(expect.(*getter.HTTPGetter)) {
t.Errorf("%s: expected %s, got %s", tt.name, expect, got)
}
}
}
func TestVerifyChart(t *testing.T) { func TestVerifyChart(t *testing.T) {
v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub") v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub")
if err != nil { if err != nil {

@ -149,6 +149,13 @@ func (m *Manager) Update() error {
return err return err
} }
// downloadAll might overwrite dependency version, recalculate lock digest
newDigest, err := resolver.HashReq(req, lock.Dependencies)
if err != nil {
return err
}
lock.Digest = newDigest
// If the lock file hasn't changed, don't write a new one. // If the lock file hasn't changed, don't write a new one.
oldLock := c.Lock oldLock := c.Lock
if oldLock != nil && oldLock.Digest == lock.Digest { if oldLock != nil && oldLock.Digest == lock.Digest {
@ -156,7 +163,7 @@ func (m *Manager) Update() error {
} }
// Finally, we need to write the lockfile. // Finally, we need to write the lockfile.
return writeLock(m.ChartPath, lock) return writeLock(m.ChartPath, lock, c.Metadata.APIVersion == chart.APIVersionV1)
} }
func (m *Manager) loadChartDir() (*chart.Chart, error) { func (m *Manager) loadChartDir() (*chart.Chart, error) {
@ -627,12 +634,16 @@ func (m *Manager) loadChartRepositories() (map[string]*repo.ChartRepository, err
} }
// writeLock writes a lockfile to disk // writeLock writes a lockfile to disk
func writeLock(chartpath string, lock *chart.Lock) error { func writeLock(chartpath string, lock *chart.Lock, legacyLockfile bool) error {
data, err := yaml.Marshal(lock) data, err := yaml.Marshal(lock)
if err != nil { if err != nil {
return err return err
} }
dest := filepath.Join(chartpath, "Chart.lock") lockfileName := "Chart.lock"
if legacyLockfile {
lockfileName = "requirements.lock"
}
dest := filepath.Join(chartpath, lockfileName)
return ioutil.WriteFile(dest, data, 0644) return ioutil.WriteFile(dest, data, 0644)
} }

@ -181,6 +181,74 @@ func TestGetRepoNames(t *testing.T) {
} }
} }
func TestUpdateBeforeBuild(t *testing.T) {
// Set up a fake repo
srv, err := repotest.NewTempServer("testdata/*.tgz*")
if err != nil {
t.Fatal(err)
}
defer srv.Stop()
if err := srv.LinkIndices(); err != nil {
t.Fatal(err)
}
dir := func(p ...string) string {
return filepath.Join(append([]string{srv.Root()}, p...)...)
}
// Save dep
d := &chart.Chart{
Metadata: &chart.Metadata{
Name: "dep-chart",
Version: "0.1.0",
APIVersion: "v1",
},
}
if err := chartutil.SaveDir(d, dir()); err != nil {
t.Fatal(err)
}
// Save a chart
c := &chart.Chart{
Metadata: &chart.Metadata{
Name: "with-dependency",
Version: "0.1.0",
APIVersion: "v2",
Dependencies: []*chart.Dependency{{
Name: d.Metadata.Name,
Version: ">=0.1.0",
Repository: "file://../dep-chart",
}},
},
}
if err := chartutil.SaveDir(c, dir()); err != nil {
t.Fatal(err)
}
// Set-up a manager
b := bytes.NewBuffer(nil)
g := getter.Providers{getter.Provider{
Schemes: []string{"http", "https"},
New: getter.NewHTTPGetter,
}}
m := &Manager{
ChartPath: dir(c.Metadata.Name),
Out: b,
Getters: g,
RepositoryConfig: dir("repositories.yaml"),
RepositoryCache: dir(),
}
// Update before Build. see issue: https://github.com/helm/helm/issues/7101
err = m.Update()
if err != nil {
t.Fatal(err)
}
err = m.Build()
if err != nil {
t.Fatal(err)
}
}
// This function is the skeleton test code of failing tests for #6416 and #6871 and bugs due to #5874. // This function is the skeleton test code of failing tests for #6416 and #6871 and bugs due to #5874.
// //
// This function is used by below tests that ensures success of build operation // This function is used by below tests that ensures success of build operation
@ -217,7 +285,7 @@ func checkBuildWithOptionalFields(t *testing.T, chartName string, dep chart.Depe
Metadata: &chart.Metadata{ Metadata: &chart.Metadata{
Name: chartName, Name: chartName,
Version: "0.1.0", Version: "0.1.0",
APIVersion: "v1", APIVersion: "v2",
Dependencies: []*chart.Dependency{&dep}, Dependencies: []*chart.Dependency{&dep},
}, },
} }

@ -15,4 +15,9 @@ repositories:
- name: testing-relative - name: testing-relative
url: "http://example.com/helm" url: "http://example.com/helm"
- name: testing-relative-trailing-slash - name: testing-relative-trailing-slash
url: "http://example.com/helm/" url: "http://example.com/helm/"
- name: testing-ca-file
url: "https://example.com"
certFile: "cert"
keyFile: "key"
caFile: "ca"

@ -0,0 +1,14 @@
apiVersion: v1
entries:
foo:
- name: foo
description: Foo Chart
home: https://helm.sh/helm
keywords: []
maintainers: []
sources:
- https://github.com/helm/charts
urls:
- https://example.com/foo-1.2.3.tgz
version: 1.2.3
checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d

@ -93,11 +93,19 @@ func warnWrap(warn string) string {
// initFunMap creates the Engine's FuncMap and adds context-specific functions. // initFunMap creates the Engine's FuncMap and adds context-specific functions.
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
funcMap := funcMap() funcMap := funcMap()
includedNames := make([]string, 0)
// Add the 'include' function here so we can close over t. // Add the 'include' function here so we can close over t.
funcMap["include"] = func(name string, data interface{}) (string, error) { funcMap["include"] = func(name string, data interface{}) (string, error) {
var buf strings.Builder var buf strings.Builder
for _, n := range includedNames {
if n == name {
return "", errors.Wrapf(fmt.Errorf("unable to excute template"), "rendering template has a nested reference name: %s", name)
}
}
includedNames = append(includedNames, name)
err := t.ExecuteTemplate(&buf, name, data) err := t.ExecuteTemplate(&buf, name, data)
includedNames = includedNames[:len(includedNames)-1]
return buf.String(), err return buf.String(), err
} }

@ -460,6 +460,15 @@ func TestAlterFuncMap_include(t *testing.T) {
}, },
} }
// Check nested reference in include FuncMap
d := &chart.Chart{
Metadata: &chart.Metadata{Name: "nested"},
Templates: []*chart.File{
{Name: "templates/quote", Data: []byte(`{{include "nested/templates/quote" . | indent 2}} dead.`)},
{Name: "templates/_partial", Data: []byte(`{{.Release.Name}} - he`)},
},
}
v := chartutil.Values{ v := chartutil.Values{
"Values": "", "Values": "",
"Chart": c.Metadata, "Chart": c.Metadata,
@ -477,6 +486,12 @@ func TestAlterFuncMap_include(t *testing.T) {
if got := out["conrad/templates/quote"]; got != expect { if got := out["conrad/templates/quote"]; got != expect {
t.Errorf("Expected %q, got %q (%v)", expect, got, out) t.Errorf("Expected %q, got %q (%v)", expect, got, out)
} }
_, err = Render(d, v)
expectErrName := "nested/templates/quote"
if err == nil {
t.Errorf("Expected err of nested reference name: %v", expectErrName)
}
} }
func TestAlterFuncMap_require(t *testing.T) { func TestAlterFuncMap_require(t *testing.T) {

@ -34,6 +34,7 @@ import (
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch" "k8s.io/apimachinery/pkg/util/strategicpatch"
@ -41,6 +42,7 @@ import (
"k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/resource" "k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/kubernetes/scheme"
cachetools "k8s.io/client-go/tools/cache"
watchtools "k8s.io/client-go/tools/watch" watchtools "k8s.io/client-go/tools/watch"
cmdutil "k8s.io/kubectl/pkg/cmd/util" cmdutil "k8s.io/kubectl/pkg/cmd/util"
) )
@ -419,10 +421,13 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err
c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout) c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout)
w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) // Use a selector on the name of the resource. This should be unique for the
// given version and kind
selector, err := fields.ParseSelector(fmt.Sprintf("metadata.name=%s", info.Name))
if err != nil { if err != nil {
return err return err
} }
lw := cachetools.NewListWatchFromClient(info.Client, info.Mapping.Resource.Resource, info.Namespace, selector)
// What we watch for depends on the Kind. // What we watch for depends on the Kind.
// - For a Job, we watch for completion. // - For a Job, we watch for completion.
@ -432,7 +437,7 @@ func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) err
ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout) ctx, cancel := watchtools.ContextWithOptionalTimeout(context.Background(), timeout)
defer cancel() defer cancel()
_, err = watchtools.UntilWithoutRetry(ctx, w, func(e watch.Event) (bool, error) { _, err = watchtools.ListWatchUntil(ctx, lw, func(e watch.Event) (bool, error) {
// Make sure the incoming object is versioned as we use unstructured // Make sure the incoming object is versioned as we use unstructured
// objects when we build manifests // objects when we build manifests
obj := convertWithMapper(e.Object, info.Mapping) obj := convertWithMapper(e.Object, info.Mapping)

@ -35,12 +35,12 @@ const goodChartDir = "rules/testdata/goodone"
func TestBadChart(t *testing.T) { func TestBadChart(t *testing.T) {
m := All(badChartDir, values, namespace, strict).Messages m := All(badChartDir, values, namespace, strict).Messages
if len(m) != 8 { if len(m) != 7 {
t.Errorf("Number of errors %v", len(m)) t.Errorf("Number of errors %v", len(m))
t.Errorf("All didn't fail with expected errors, got %#v", m) t.Errorf("All didn't fail with expected errors, got %#v", m)
} }
// There should be one INFO, 2 WARNINGs and one ERROR messages, check for them // There should be one INFO, 2 WARNINGs and one ERROR messages, check for them
var i, w, e, e2, e3, e4, e5, e6 bool var i, w, e, e2, e3, e4, e5 bool
for _, msg := range m { for _, msg := range m {
if msg.Severity == support.InfoSev { if msg.Severity == support.InfoSev {
if strings.Contains(msg.Err.Error(), "icon is recommended") { if strings.Contains(msg.Err.Error(), "icon is recommended") {
@ -59,24 +59,21 @@ func TestBadChart(t *testing.T) {
if strings.Contains(msg.Err.Error(), "name is required") { if strings.Contains(msg.Err.Error(), "name is required") {
e2 = true e2 = true
} }
if strings.Contains(msg.Err.Error(), "directory name (badchartfile) and chart name () must be the same") {
e3 = true
}
if strings.Contains(msg.Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { if strings.Contains(msg.Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") {
e4 = true e3 = true
} }
if strings.Contains(msg.Err.Error(), "chart type is not valid in apiVersion") { if strings.Contains(msg.Err.Error(), "chart type is not valid in apiVersion") {
e5 = true e4 = true
} }
if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") { if strings.Contains(msg.Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
e6 = true e5 = true
} }
} }
} }
if !e || !e2 || !e3 || !e4 || !e5 || !e6 || !w || !i { if !e || !e2 || !e3 || !e4 || !e5 || !w || !i {
t.Errorf("Didn't find all the expected errors, got %#v", m) t.Errorf("Didn't find all the expected errors, got %#v", m)
} }
} }

@ -46,7 +46,6 @@ func Chartfile(linter *support.Linter) {
} }
linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartName(chartFile))
linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile))
// Chart metadata // Chart metadata
linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile))
@ -82,13 +81,6 @@ func validateChartName(cf *chart.Metadata) error {
return nil return nil
} }
func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error {
if cf.Name != filepath.Base(chartDir) {
return errors.Errorf("directory name (%s) and chart name (%s) must be the same", filepath.Base(chartDir), cf.Name)
}
return nil
}
func validateChartAPIVersion(cf *chart.Metadata) error { func validateChartAPIVersion(cf *chart.Metadata) error {
if cf.APIVersion == "" { if cf.APIVersion == "" {
return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"") return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"")

@ -30,18 +30,15 @@ import (
) )
const ( const (
badChartDir = "testdata/badchartfile" badChartDir = "testdata/badchartfile"
goodChartDir = "testdata/goodone"
) )
var ( var (
badChartFilePath = filepath.Join(badChartDir, "Chart.yaml") badChartFilePath = filepath.Join(badChartDir, "Chart.yaml")
goodChartFilePath = filepath.Join(goodChartDir, "Chart.yaml")
nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml") nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml")
) )
var badChart, _ = chartutil.LoadChartfile(badChartFilePath) var badChart, _ = chartutil.LoadChartfile(badChartFilePath)
var goodChart, _ = chartutil.LoadChartfile(goodChartFilePath)
// Validation functions Test // Validation functions Test
func TestValidateChartYamlNotDirectory(t *testing.T) { func TestValidateChartYamlNotDirectory(t *testing.T) {
@ -73,24 +70,6 @@ func TestValidateChartName(t *testing.T) {
} }
} }
func TestValidateChartNameDirMatch(t *testing.T) {
err := validateChartNameDirMatch(goodChartDir, goodChart)
if err != nil {
t.Errorf("validateChartNameDirMatch to return no error, gor a linter error")
}
// It has not name
err = validateChartNameDirMatch(badChartDir, badChart)
if err == nil {
t.Errorf("validatechartnamedirmatch to return a linter error, got no error")
}
// Wrong path
err = validateChartNameDirMatch(badChartDir, goodChart)
if err == nil {
t.Errorf("validatechartnamedirmatch to return a linter error, got no error")
}
}
func TestValidateChartVersion(t *testing.T) { func TestValidateChartVersion(t *testing.T) {
var failTest = []struct { var failTest = []struct {
Version string Version string
@ -209,36 +188,32 @@ func TestChartfile(t *testing.T) {
Chartfile(&linter) Chartfile(&linter)
msgs := linter.Messages msgs := linter.Messages
if len(msgs) != 7 { if len(msgs) != 6 {
t.Errorf("Expected 7 errors, got %d", len(msgs)) t.Errorf("Expected 6 errors, got %d", len(msgs))
} }
if !strings.Contains(msgs[0].Err.Error(), "name is required") { if !strings.Contains(msgs[0].Err.Error(), "name is required") {
t.Errorf("Unexpected message 0: %s", msgs[0].Err) t.Errorf("Unexpected message 0: %s", msgs[0].Err)
} }
if !strings.Contains(msgs[1].Err.Error(), "directory name (badchartfile) and chart name () must be the same") { if !strings.Contains(msgs[1].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") {
t.Errorf("Unexpected message 1: %s", msgs[1].Err) t.Errorf("Unexpected message 1: %s", msgs[1].Err)
} }
if !strings.Contains(msgs[2].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { if !strings.Contains(msgs[2].Err.Error(), "version '0.0.0.0' is not a valid SemVer") {
t.Errorf("Unexpected message 2: %s", msgs[2].Err) t.Errorf("Unexpected message 2: %s", msgs[2].Err)
} }
if !strings.Contains(msgs[3].Err.Error(), "version '0.0.0.0' is not a valid SemVer") { if !strings.Contains(msgs[3].Err.Error(), "icon is recommended") {
t.Errorf("Unexpected message 3: %s", msgs[3].Err) t.Errorf("Unexpected message 3: %s", msgs[3].Err)
} }
if !strings.Contains(msgs[4].Err.Error(), "icon is recommended") { if !strings.Contains(msgs[4].Err.Error(), "chart type is not valid in apiVersion") {
t.Errorf("Unexpected message 4: %s", msgs[4].Err) t.Errorf("Unexpected message 4: %s", msgs[4].Err)
} }
if !strings.Contains(msgs[5].Err.Error(), "chart type is not valid in apiVersion") { if !strings.Contains(msgs[5].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
t.Errorf("Unexpected message 5: %s", msgs[5].Err) t.Errorf("Unexpected message 5: %s", msgs[5].Err)
} }
if !strings.Contains(msgs[6].Err.Error(), "dependencies are not valid in the Chart file with apiVersion") {
t.Errorf("Unexpected message 6: %s", msgs[6].Err)
}
} }

@ -169,7 +169,7 @@ func (s *Signatory) DecryptKey(fn PassphraseFetcher) error {
if s.Entity == nil { if s.Entity == nil {
return errors.New("private key not found") return errors.New("private key not found")
} else if s.Entity.PrivateKey == nil { } else if s.Entity.PrivateKey == nil {
return errors.New("provided key is not a private key") return errors.New("provided key is not a private key. Try providing a keyring with secret keys")
} }
// Nothing else to do if key is not encrypted. // Nothing else to do if key is not encrypted.
@ -203,7 +203,7 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
if s.Entity == nil { if s.Entity == nil {
return "", errors.New("private key not found") return "", errors.New("private key not found")
} else if s.Entity.PrivateKey == nil { } else if s.Entity.PrivateKey == nil {
return "", errors.New("provided key is not a private key") return "", errors.New("provided key is not a private key. Try providing a keyring with secret keys")
} }
if fi, err := os.Stat(chartpath); err != nil { if fi, err := os.Stat(chartpath); err != nil {

@ -19,6 +19,7 @@ package release
type Status string type Status string
// Describe the status of a release // Describe the status of a release
// NOTE: Make sure to update cmd/helm/status.go when adding or modifying any of these statuses.
const ( const (
// StatusUnknown indicates that a release is in an uncertain state. // StatusUnknown indicates that a release is in an uncertain state.
StatusUnknown Status = "unknown" StatusUnknown Status = "unknown"

@ -27,14 +27,16 @@ find_files() {
\( -name '*.go' -o -name '*.sh' \) \( -name '*.go' -o -name '*.sh' \)
} }
mapfile -t failed_license_header < <(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")') # Use "|| :" to ignore the error code when grep returns empty
failed_license_header=($(find_files | xargs grep -L 'Licensed under the Apache License, Version 2.0 (the "License")' || :))
if (( ${#failed_license_header[@]} > 0 )); then if (( ${#failed_license_header[@]} > 0 )); then
echo "Some source files are missing license headers." echo "Some source files are missing license headers."
printf '%s\n' "${failed_license_header[@]}" printf '%s\n' "${failed_license_header[@]}"
exit 1 exit 1
fi fi
mapfile -t failed_copyright_header < <(find_files | xargs grep -L 'Copyright The Helm Authors.') # Use "|| :" to ignore the error code when grep returns empty
failed_copyright_header=($(find_files | xargs grep -L 'Copyright The Helm Authors.' || :))
if (( ${#failed_copyright_header[@]} > 0 )); then if (( ${#failed_copyright_header[@]} > 0 )); then
echo "Some source files are missing the copyright header." echo "Some source files are missing the copyright header."
printf '%s\n' "${failed_copyright_header[@]}" printf '%s\n' "${failed_copyright_header[@]}"

Loading…
Cancel
Save