fix: test lint/lint_test.go

Signed-off-by: Aurélien Lambert <aure@olli-ai.com>
pull/6876/head
Aurélien Lambert 6 years ago
commit f7d6f094da
No known key found for this signature in database
GPG Key ID: C749F51FF763C5DA

@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Copyright The Helm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
curl -sSL https://github.com/golangci/golangci-lint/releases/download/v$GOLANGCI_LINT_VERSION/golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64.tar.gz | tar xz
sudo mv golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64/golangci-lint /usr/local/bin/golangci-lint
rm -rf golangci-lint-$GOLANGCI_LINT_VERSION-linux-amd64

@ -9,9 +9,13 @@ jobs:
environment:
GOCACHE: "/tmp/go/cache"
GOLANGCI_LINT_VERSION: "1.21.0"
steps:
- checkout
- run:
name: install test dependencies
command: .circleci/bootstrap.sh
- run:
name: test style
command: make test-style

@ -1,5 +1,5 @@
run:
deadline: 2m
timeout: 2m
linters:
disable-all: true

@ -1,13 +1,13 @@
BINDIR := $(CURDIR)/bin
DIST_DIRS := find * -type d -exec
TARGETS := darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64
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)
DEP = $(GOPATH)/bin/dep
GOX = $(GOPATH)/bin/gox
GOIMPORTS = $(GOPATH)/bin/goimports
GOLANGCI_LINT = $(GOPATH)/bin/golangci-lint
ACCEPTANCE_DIR:=$(GOPATH)/src/helm.sh/acceptance-testing
# To specify the subset of acceptance tests to run. '.' means all tests
@ -81,8 +81,8 @@ test-coverage:
@ ./scripts/coverage.sh
.PHONY: test-style
test-style: $(GOLANGCI_LINT)
GO111MODULE=on $(GOLANGCI_LINT) run
test-style:
GO111MODULE=on golangci-lint run
@scripts/validate-license.sh
.PHONY: test-acceptance
@ -118,9 +118,6 @@ format: $(GOIMPORTS)
$(GOX):
(cd /; GO111MODULE=on go get -u github.com/mitchellh/gox)
$(GOLANGCI_LINT):
(cd /; GO111MODULE=on go get -u github.com/golangci/golangci-lint/cmd/golangci-lint)
$(GOIMPORTS):
(cd /; GO111MODULE=on go get -u golang.org/x/tools/cmd/goimports)
@ -142,6 +139,20 @@ dist:
$(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
checksum:
for f in _dist/*.{gz,zip} ; do \

@ -2,7 +2,7 @@
[![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)
[![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)
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:
- [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`.
- [Scoop](https://scoop.sh/) users can use `scoop 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).
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.
## 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

@ -130,7 +130,7 @@ func newInstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
func addInstallFlags(f *pflag.FlagSet, client *action.Install, valueOpts *values.Options) {
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.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.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)")

@ -77,12 +77,15 @@ func newListCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
client.SetStateMask()
results, err := client.Run()
if err != nil {
return err
}
if client.Short {
for _, res := range results {
fmt.Fprintln(out, res.Name)
}
return err
return nil
}
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.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.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.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")

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

@ -30,14 +30,27 @@ func TestManuallyProcessArgs(t *testing.T) {
"--debug",
"--foo", "bar",
"--kubeconfig=/home/foo",
"--kubeconfig", "/home/foo",
"--kube-context=test1",
"--kube-context", "test1",
"-n=test2",
"-n", "test2",
"--namespace=test2",
"--namespace", "test2",
"--home=/tmp",
"command",
}
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{

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

@ -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 {
var validate bool
var includeCrds bool
client := action.NewInstall(cfg)
valueOpts := &values.Options{}
var extraAPIs []string
@ -67,7 +68,14 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
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))
if !client.DisableHooks {
for _, m := range rel.Hooks {
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.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(&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")
return cmd

@ -79,6 +79,11 @@ func TestTemplateCmd(t *testing.T) {
cmd: fmt.Sprintf("template --api-versions helm.k8s.io/test '%s'", chartPath),
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)
}

@ -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,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).

@ -27,18 +27,14 @@ import (
// Options represents configurable options used to create client and server TLS configurations.
type Options struct {
CaCertFile string
// 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.
// If either the KeyFile or CertFile is empty, ClientConfig() will not load them.
KeyFile string
CertFile string
// Client-only options
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) {
var cert *tls.Certificate
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}
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
}

@ -26,13 +26,16 @@ import (
// NewClientTLS returns tls.Config appropriate for client auth.
func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
config := tls.Config{}
if certFile != "" && keyFile != "" {
cert, err := CertFromFilePair(certFile, keyFile)
if err != nil {
return nil, err
}
config := tls.Config{
Certificates: []tls.Certificate{*cert},
config.Certificates = []tls.Certificate{*cert}
}
if caFile != "" {
cp, err := CertPoolFromFile(caFile)
if err != nil {
@ -40,6 +43,7 @@ func NewClientTLS(certFile, keyFile, caFile string) (*tls.Config, error) {
}
config.RootCAs = cp
}
return &config, nil
}

@ -17,7 +17,6 @@ limitations under the License.
package tlsutil
import (
"crypto/tls"
"path/filepath"
"testing"
)
@ -54,30 +53,61 @@ 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,
func testfile(t *testing.T, file string) (path string) {
var err error
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {
t.Fatalf("error getting absolute path to test file %q: %v", file, err)
}
return path
}
func TestNewClientTLS(t *testing.T) {
certFile := testfile(t, testCertFile)
keyFile := testfile(t, testKeyFile)
caCertFile := testfile(t, testCaCertFile)
cfg, err := ServerConfig(opts)
cfg, err := NewClientTLS(certFile, keyFile, caCertFile)
if err != nil {
t.Fatalf("error building tls server config: %v", err)
t.Error(err)
}
if got := cfg.MinVersion; got != tls.VersionTLS12 {
t.Errorf("expecting TLS version 1.2, got %d", got)
if got := len(cfg.Certificates); got != 1 {
t.Fatalf("expecting 1 client certificates, got %d", got)
}
if got := cfg.ClientCAs; got == nil {
t.Errorf("expecting non-nil CA pool")
if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false")
}
if cfg.RootCAs == nil {
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
}
}
func testfile(t *testing.T, file string) (path string) {
var err error
if path, err = filepath.Abs(filepath.Join(tlsTestDir, file)); err != nil {
t.Fatalf("error getting absolute path to test file %q: %v", file, err)
cfg, err = NewClientTLS("", "", caCertFile)
if err != nil {
t.Error(err)
}
if got := len(cfg.Certificates); got != 0 {
t.Fatalf("expecting 0 client certificates, got %d", got)
}
if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false")
}
if cfg.RootCAs == nil {
t.Fatalf("mismatch tls RootCAs, expecting non-nil")
}
cfg, err = NewClientTLS(certFile, keyFile, "")
if err != nil {
t.Error(err)
}
if got := len(cfg.Certificates); got != 1 {
t.Fatalf("expecting 1 client certificates, got %d", got)
}
if cfg.InsecureSkipVerify {
t.Fatalf("insecure skip verify mistmatch, expecting false")
}
if cfg.RootCAs != nil {
t.Fatalf("mismatch tls RootCAs, expecting nil")
}
return path
}

@ -109,10 +109,20 @@ func (c *Configuration) getCapabilities() (*chartutil.Capabilities, error) {
if err != nil {
return nil, errors.Wrap(err, "could not get server version from Kubernetes")
}
// Issue #6361:
// Client-Go emits an error when an API service is registered but unimplemented.
// We trap that error here and print a warning. But since the discovery client continues
// building the API object, it is correctly populated with all valid APIs.
// See https://github.com/kubernetes/kubernetes/issues/72051#issuecomment-521157642
apiVersions, err := GetVersionSet(dc)
if err != nil {
if discovery.IsGroupDiscoveryFailedError(err) {
c.Log("WARNING: The Kubernetes server has an orphaned API service. Server reports: %s", err)
c.Log("WARNING: To fix this, kubectl delete apiservice <service-name>")
} else {
return nil, errors.Wrap(err, "could not get apiVersions from Kubernetes")
}
}
c.Capabilities = &chartutil.Capabilities{
APIVersions: apiVersions,

@ -39,6 +39,10 @@ func NewGetValues(cfg *Configuration) *GetValues {
// Run executes 'helm get values' against the given release.
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)
if err != nil {
return nil, err

@ -86,6 +86,8 @@ type Install struct {
// APIVersions allows a manual set of supported API Versions to be passed
// (for things like templating). These are ignored if ClientOnly is false
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
@ -126,7 +128,7 @@ func (i *Install) installCRDs(crds []*chart.File) error {
i.cfg.Log("CRD %s is already present. Skipping.", crdName)
continue
}
return errors.Wrapf(err, "failed to instal CRD %s", obj.Name)
return errors.Wrapf(err, "failed to install CRD %s", obj.Name)
}
totalItems = append(totalItems, res...)
}
@ -193,11 +195,14 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release.
return nil, err
}
//special case for helm template --is-upgrade
isUpgrade := i.IsUpgrade && i.DryRun
options := chartutil.ReleaseOptions{
Name: i.ReleaseName,
Namespace: i.Namespace,
Revision: 1,
IsInstall: true,
IsInstall: !isUpgrade,
IsUpgrade: isUpgrade,
}
valuesToRender, err := chartutil.ToRenderValues(chrt, vals, options, caps)
if err != nil {
@ -233,7 +238,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
// deleting the release because the manifest will be pointing at that
// resource
if !i.ClientOnly {
if !i.ClientOnly && !isUpgrade {
if err := existingResourceConflict(resources); err != nil {
return nil, errors.Wrap(err, "rendered manifests contain a resource that already exists. Unable to continue with install")
}
@ -297,7 +302,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
// 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
}
@ -416,7 +423,7 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values
if ch.Metadata.KubeVersion != "" {
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())
}
}

@ -306,7 +306,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) {
vals = map[string]interface{}{}
_, err = instAction.Run(buildChart(withKube(">=99.0.0")), vals)
is.Error(err)
is.Contains(err.Error(), "chart requires kubernetesVersion")
is.Contains(err.Error(), "chart requires kubeVersion")
}
func TestInstallRelease_Wait(t *testing.T) {

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

@ -126,6 +126,12 @@ func LoadArchiveFiles(in io.Reader) ([]*BufferedFile, error) {
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
delimiter := "/"
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)
})
}
}

@ -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))
}
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.
idxFile := filepath.Join(c.RepositoryCache, helmpath.CacheIndexFile(r.Config.Name))
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) {
v, err := VerifyChart("testdata/signtest-0.1.0.tgz", "testdata/helm-test-key.pub")
if err != nil {

@ -79,7 +79,12 @@ func (m *Manager) Build() error {
return m.Update()
}
// Check that all of the repos we're dependent on actually exist.
req := c.Metadata.Dependencies
if _, err := m.resolveRepoNames(req); err != nil {
return err
}
if sum, err := resolver.HashReq(req, lock.Dependencies); err != nil || sum != lock.Digest {
return errors.New("Chart.lock is out of sync with Chart.yaml")
}
@ -120,7 +125,7 @@ func (m *Manager) Update() error {
// Check that all of the repos we're dependent on actually exist and
// the repo index names.
repoNames, err := m.getRepoNames(req)
repoNames, err := m.resolveRepoNames(req)
if err != nil {
return err
}
@ -144,6 +149,13 @@ func (m *Manager) Update() error {
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.
oldLock := c.Lock
if oldLock != nil && oldLock.Digest == lock.Digest {
@ -372,8 +384,9 @@ Loop:
return nil
}
// getRepoNames returns the repo names of the referenced deps which can be used to fetch the cahced index file.
func (m *Manager) getRepoNames(deps []*chart.Dependency) (map[string]string, error) {
// resolveRepoNames returns the repo names of the referenced deps which can be used to fetch the cached index file
// and replaces aliased repository URLs into resolved URLs in dependencies.
func (m *Manager) resolveRepoNames(deps []*chart.Dependency) (map[string]string, error) {
rf, err := loadRepoConfig(m.RepositoryConfig)
if err != nil {
if os.IsNotExist(err) {

@ -161,7 +161,7 @@ func TestGetRepoNames(t *testing.T) {
}
for _, tt := range tests {
l, err := m.getRepoNames(tt.req)
l, err := m.resolveRepoNames(tt.req)
if err != nil {
if tt.err {
continue
@ -181,7 +181,76 @@ func TestGetRepoNames(t *testing.T) {
}
}
// This function is the skeleton test code of failing tests for #6416 and bugs due to #5874.
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: "v1",
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 used by below tests that ensures success of build operation
// with optional fields, alias, condition, tags, and even with ranged version.
// Parent chart includes local-subchart 0.1.0 subchart from a fake repository, by default.
@ -283,3 +352,11 @@ func TestBuild_WithTags(t *testing.T) {
Tags: []string{"tag1", "tag2"},
})
}
// Failing test for #6871
func TestBuild_WithRepositoryAlias(t *testing.T) {
// Dependency repository is aliased in Chart.yaml
checkBuildWithOptionalFields(t, "with-repository-alias", chart.Dependency{
Repository: "@test",
})
}

@ -16,3 +16,8 @@ repositories:
url: "http://example.com/helm"
- name: testing-relative-trailing-slash
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

@ -99,11 +99,19 @@ func warnWrap(warn string) string {
// initFunMap creates the Engine's FuncMap and adds context-specific functions.
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
funcMap := funcMap()
includedNames := make([]string, 0)
// Add the 'include' function here so we can close over t.
funcMap["include"] = func(name string, data interface{}) (string, error) {
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)
includedNames = includedNames[:len(includedNames)-1]
return buf.String(), err
}

@ -464,6 +464,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{
"Values": map[string]interface{}{},
"Chart": c.Metadata,
@ -481,6 +490,12 @@ func TestAlterFuncMap_include(t *testing.T) {
if got := out["conrad/templates/quote"]; got != expect {
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) {

@ -89,7 +89,7 @@ func NewHTTPGetter(options ...Option) (Getter, error) {
}
func (g *HTTPGetter) httpClient() (*http.Client, error) {
if g.opts.certFile != "" && g.opts.keyFile != "" {
if (g.opts.certFile != "" && g.opts.keyFile != "") || g.opts.caFile != "" {
tlsConf, err := tlsutil.NewClientTLS(g.opts.certFile, g.opts.keyFile, g.opts.caFile)
if err != nil {
return nil, errors.Wrap(err, "can't create TLS config for client")

@ -180,4 +180,14 @@ func TestDownloadTLS(t *testing.T) {
if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig(pub, priv, ca)); err != nil {
t.Error(err)
}
// test with only the CA file (see also #6635)
g, err = NewHTTPGetter()
if err != nil {
t.Fatal(err)
}
if _, err := g.Get(u.String(), WithURL(u.String()), WithTLSClientConfig("", "", ca)); err != nil {
t.Error(err)
}
}

@ -27,6 +27,7 @@ import (
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@ -114,6 +115,17 @@ func (w *waiter) waitForResources(created ResourceList) error {
if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil {
return false, err
}
if !w.crdBetaReady(*crd) {
return false, nil
}
case *apiextv1.CustomResourceDefinition:
if err := v.Get(); err != nil {
return false, err
}
crd := &apiextv1.CustomResourceDefinition{}
if err := scheme.Scheme.Convert(v.Object, crd, nil); err != nil {
return false, err
}
if !w.crdReady(*crd) {
return false, nil
}
@ -229,7 +241,10 @@ func (w *waiter) daemonSetReady(ds *appsv1.DaemonSet) bool {
return true
}
func (w *waiter) crdReady(crd apiextv1beta1.CustomResourceDefinition) bool {
// Because the v1 extensions API is not available on all supported k8s versions
// yet and because Go doesn't support generics, we need to have a duplicate
// function to support the v1beta1 types
func (w *waiter) crdBetaReady(crd apiextv1beta1.CustomResourceDefinition) bool {
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1beta1.Established:
@ -249,6 +264,26 @@ func (w *waiter) crdReady(crd apiextv1beta1.CustomResourceDefinition) bool {
return false
}
func (w *waiter) crdReady(crd apiextv1.CustomResourceDefinition) bool {
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1.Established:
if cond.Status == apiextv1.ConditionTrue {
return true
}
case apiextv1.NamesAccepted:
if cond.Status == apiextv1.ConditionFalse {
// This indicates a naming conflict, but it's probably not the
// job of this function to fail because of that. Instead,
// we treat it as a success, since the process should be able to
// continue.
return true
}
}
}
return false
}
func (w *waiter) statefulSetReady(sts *appsv1.StatefulSet) bool {
// If the update strategy is not a rolling update, there will be nothing to wait for
if sts.Spec.UpdateStrategy.Type != appsv1.RollingUpdateStatefulSetStrategyType {

@ -35,12 +35,12 @@ const goodChartDir = "rules/testdata/goodone"
func TestBadChart(t *testing.T) {
m := All(badChartDir, values, namespace, strict).Messages
if len(m) != 9 {
if len(m) != 8 {
t.Errorf("Number of errors %v", len(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
var i, w, e, e2, e3, e4, e5, e6, e7 bool
var i, w, e, e2, e3, e4, e5, e6 bool
for _, msg := range m {
if msg.Severity == support.InfoSev {
if strings.Contains(msg.Err.Error(), "icon is recommended") {
@ -59,28 +59,25 @@ func TestBadChart(t *testing.T) {
if strings.Contains(msg.Err.Error(), "name is required") {
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\"") {
e4 = true
e3 = true
}
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") {
e6 = true
e5 = true
}
if strings.Contains(msg.Err.Error(), "chart.metadata.name is required") {
e7 = true
e6 = true
}
}
}
if !e || !e2 || !e3 || !e4 || !e5 || !e6 || !e7 || !w || !i {
if !e || !e2 || !e3 || !e4 || !e5 || !e6 || !w || !i {
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, validateChartNameDirMatch(linter.ChartDir, chartFile))
// Chart metadata
linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile))
@ -82,13 +81,6 @@ func validateChartName(cf *chart.Metadata) error {
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 {
if cf.APIVersion == "" {
return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"")

@ -31,17 +31,14 @@ import (
const (
badChartDir = "testdata/badchartfile"
goodChartDir = "testdata/goodone"
)
var (
badChartFilePath = filepath.Join(badChartDir, "Chart.yaml")
goodChartFilePath = filepath.Join(goodChartDir, "Chart.yaml")
nonExistingChartFilePath = filepath.Join(os.TempDir(), "Chart.yaml")
)
var badChart, _ = chartutil.LoadChartfile(badChartFilePath)
var goodChart, _ = chartutil.LoadChartfile(goodChartFilePath)
// Validation functions Test
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) {
var failTest = []struct {
Version string
@ -209,36 +188,32 @@ func TestChartfile(t *testing.T) {
Chartfile(&linter)
msgs := linter.Messages
if len(msgs) != 7 {
t.Errorf("Expected 7 errors, got %d", len(msgs))
if len(msgs) != 6 {
t.Errorf("Expected 6 errors, got %d", len(msgs))
}
if !strings.Contains(msgs[0].Err.Error(), "name is required") {
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)
}
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)
}
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)
}
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)
}
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)
}
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 {
return errors.New("private key not found")
} 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.
@ -203,7 +203,7 @@ func (s *Signatory) ClearSign(chartpath string) (string, error) {
if s.Entity == nil {
return "", errors.New("private key not found")
} 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 {

@ -84,7 +84,7 @@ func ParseFile(s string, reader RunesValueReader) (map[string]interface{}, error
return vals, err
}
// ParseIntoString parses a strvals line nad merges the result into dest.
// ParseIntoString parses a strvals line and merges the result into dest.
//
// This method always returns a string as the value.
func ParseIntoString(s string, dest map[string]interface{}) error {
@ -108,6 +108,9 @@ type RunesValueReader func([]rune) (interface{}, error)
// parser is a simple parser that takes a strvals line and parses it into a
// map representation.
//
// where sc is the source of the original data being parsed
// where data is the final parsed data from the parses with correct types
type parser struct {
sc *bytes.Buffer
data map[string]interface{}
@ -285,8 +288,14 @@ func (t *parser) listItem(list []interface{}, i int) ([]interface{}, error) {
// We have a nested object. Send to t.key
inner := map[string]interface{}{}
if len(list) > i {
var ok bool
inner, ok = list[i].(map[string]interface{})
if !ok {
// We have indices out of order. Initialize empty value.
list[i] = map[string]interface{}{}
inner = list[i].(map[string]interface{})
}
}
// Recurse
e := t.key(inner)
@ -367,6 +376,11 @@ func inMap(k rune, m map[rune]bool) bool {
func typedVal(v []rune, st bool) interface{} {
val := string(v)
if st {
return val
}
if strings.EqualFold(val, "true") {
return true
}
@ -375,8 +389,16 @@ func typedVal(v []rune, st bool) interface{} {
return false
}
// If this value does not start with zero, and not returnString, try parsing it to an int
if !st && len(val) != 0 && val[0] != '0' {
if strings.EqualFold(val, "null") {
return nil
}
if strings.EqualFold(val, "0") {
return int64(0)
}
// If this value does not start with zero, try parsing it to an int
if len(val) != 0 && val[0] != '0' {
if iv, err := strconv.ParseInt(val, 10, 64); err == nil {
return iv
}

@ -75,12 +75,32 @@ func TestParseSet(t *testing.T) {
expect: map[string]interface{}{"long_int_string": "1234567890"},
err: false,
},
{
str: "boolean=true",
expect: map[string]interface{}{"boolean": "true"},
err: false,
},
{
str: "is_null=null",
expect: map[string]interface{}{"is_null": "null"},
err: false,
},
{
str: "zero=0",
expect: map[string]interface{}{"zero": "0"},
err: false,
},
}
tests := []struct {
str string
expect map[string]interface{}
err bool
}{
{
"name1=null,f=false,t=true",
map[string]interface{}{"name1": nil, "f": false, "t": true},
false,
},
{
"name1=value1",
map[string]interface{}{"name1": "value1"},
@ -108,10 +128,23 @@ func TestParseSet(t *testing.T) {
str: "leading_zeros=00009",
expect: map[string]interface{}{"leading_zeros": "00009"},
},
{
str: "zero_int=0",
expect: map[string]interface{}{"zero_int": 0},
},
{
str: "long_int=1234567890",
expect: map[string]interface{}{"long_int": 1234567890},
},
{
str: "boolean=true",
expect: map[string]interface{}{"boolean": true},
},
{
str: "is_null=null",
expect: map[string]interface{}{"is_null": nil},
err: false,
},
{
str: "name1,name2=",
err: true,
@ -270,6 +303,30 @@ func TestParseSet(t *testing.T) {
str: "nested[1][1]=1",
expect: map[string]interface{}{"nested": []interface{}{nil, []interface{}{nil, 1}}},
},
{
str: "name1.name2[0].foo=bar,name1.name2[1].foo=bar",
expect: map[string]interface{}{
"name1": map[string]interface{}{
"name2": []map[string]interface{}{{"foo": "bar"}, {"foo": "bar"}},
},
},
},
{
str: "name1.name2[1].foo=bar,name1.name2[0].foo=bar",
expect: map[string]interface{}{
"name1": map[string]interface{}{
"name2": []map[string]interface{}{{"foo": "bar"}, {"foo": "bar"}},
},
},
},
{
str: "name1.name2[1].foo=bar",
expect: map[string]interface{}{
"name1": map[string]interface{}{
"name2": []map[string]interface{}{nil, {"foo": "bar"}},
},
},
},
}
for _, tt := range tests {
@ -331,12 +388,13 @@ func TestParseInto(t *testing.T) {
"inner2": "value2",
},
}
input := "outer.inner1=value1,outer.inner3=value3"
input := "outer.inner1=value1,outer.inner3=value3,outer.inner4=4"
expect := map[string]interface{}{
"outer": map[string]interface{}{
"inner1": "value1",
"inner2": "value2",
"inner3": "value3",
"inner4": 4,
},
}
@ -357,6 +415,39 @@ func TestParseInto(t *testing.T) {
t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2)
}
}
func TestParseIntoString(t *testing.T) {
got := map[string]interface{}{
"outer": map[string]interface{}{
"inner1": "overwrite",
"inner2": "value2",
},
}
input := "outer.inner1=1,outer.inner3=3"
expect := map[string]interface{}{
"outer": map[string]interface{}{
"inner1": "1",
"inner2": "value2",
"inner3": "3",
},
}
if err := ParseIntoString(input, got); err != nil {
t.Fatal(err)
}
y1, err := yaml.Marshal(expect)
if err != nil {
t.Fatal(err)
}
y2, err := yaml.Marshal(got)
if err != nil {
t.Fatalf("Error serializing parsed value: %s", err)
}
if string(y1) != string(y2) {
t.Errorf("%s: Expected:\n%s\nGot:\n%s", input, y1, y2)
}
}
func TestParseIntoFile(t *testing.T) {
got := map[string]interface{}{}
@ -367,7 +458,7 @@ func TestParseIntoFile(t *testing.T) {
rs2v := func(rs []rune) (interface{}, error) {
v := string(rs)
if v != "path1" {
t.Errorf("%s: RunesValueReader: Expected value path1, got %s", input, v)
t.Errorf("%s: runesToVal: Expected value path1, got %s", input, v)
return "", nil
}
return "value1", nil

@ -78,13 +78,16 @@ verifySupported() {
# checkDesiredVersion checks if the desired version is available.
checkDesiredVersion() {
if [ "x$DESIRED_VERSION" == "x" ]; then
# FIXME(bacongobbler): hard code the desired version for the time being.
# A better fix would be to filter for Helm 2 release pages.
TAG="v2.16.1"
# Get tag from release URL
local latest_release_url="https://github.com/helm/helm/releases/latest"
if type "curl" > /dev/null; then
TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" )
elif type "wget" > /dev/null; then
TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$")
fi
# local latest_release_url="https://github.com/helm/helm/releases/latest"
# if type "curl" > /dev/null; then
# TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" )
# elif type "wget" > /dev/null; then
# TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$")
# fi
else
TAG=$DESIRED_VERSION
fi
@ -187,7 +190,7 @@ testVersion() {
help () {
echo "Accepted cli arguments are:"
echo -e "\t[--help|-h ] ->> prints this help"
echo -e "\t[--version|-v <desired_version>] . When not defined it defaults to latest"
echo -e "\t[--version|-v <desired_version>]"
echo -e "\te.g. --version v2.4.0 or -v latest"
echo -e "\t[--no-sudo] ->> install without sudo"
}

@ -0,0 +1,236 @@
#!/usr/bin/env bash
# Copyright The Helm Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# The install script is based off of the MIT-licensed script from glide,
# the package manager for Go: https://github.com/Masterminds/glide.sh/blob/master/get
PROJECT_NAME="helm"
: ${USE_SUDO:="true"}
: ${HELM_INSTALL_DIR:="/usr/local/bin"}
# initArch discovers the architecture for this system.
initArch() {
ARCH=$(uname -m)
case $ARCH in
armv5*) ARCH="armv5";;
armv6*) ARCH="armv6";;
armv7*) ARCH="arm";;
aarch64) ARCH="arm64";;
x86) ARCH="386";;
x86_64) ARCH="amd64";;
i686) ARCH="386";;
i386) ARCH="386";;
esac
}
# initOS discovers the operating system for this system.
initOS() {
OS=$(echo `uname`|tr '[:upper:]' '[:lower:]')
case "$OS" in
# Minimalist GNU for Windows
mingw*) OS='windows';;
esac
}
# runs the given command as root (detects if we are root already)
runAsRoot() {
local CMD="$*"
if [ $EUID -ne 0 -a $USE_SUDO = "true" ]; then
CMD="sudo $CMD"
fi
$CMD
}
# verifySupported checks that the os/arch combination is supported for
# binary builds.
verifySupported() {
local supported="darwin-386\ndarwin-amd64\nlinux-386\nlinux-amd64\nlinux-arm\nlinux-arm64\nlinux-ppc64le\nwindows-386\nwindows-amd64"
if ! echo "${supported}" | grep -q "${OS}-${ARCH}"; then
echo "No prebuilt binary for ${OS}-${ARCH}."
echo "To build from source, go to https://github.com/helm/helm"
exit 1
fi
if ! type "curl" > /dev/null && ! type "wget" > /dev/null; then
echo "Either curl or wget is required"
exit 1
fi
}
# checkDesiredVersion checks if the desired version is available.
checkDesiredVersion() {
if [ "x$DESIRED_VERSION" == "x" ]; then
# Get tag from release URL
local latest_release_url="https://github.com/helm/helm/releases/latest"
if type "curl" > /dev/null; then
TAG=$(curl -Ls -o /dev/null -w %{url_effective} $latest_release_url | grep -oE "[^/]+$" )
elif type "wget" > /dev/null; then
TAG=$(wget $latest_release_url --server-response -O /dev/null 2>&1 | awk '/^ Location: /{DEST=$2} END{ print DEST}' | grep -oE "[^/]+$")
fi
else
TAG=$DESIRED_VERSION
fi
}
# checkHelmInstalledVersion checks which version of helm is installed and
# if it needs to be changed.
checkHelmInstalledVersion() {
if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then
local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version --template="{{ .Version }}")
if [[ "$version" == "$TAG" ]]; then
echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
return 0
else
echo "Helm ${TAG} is available. Changing from version ${version}."
return 1
fi
else
return 1
fi
}
# downloadFile downloads the latest binary package and also the checksum
# for that binary.
downloadFile() {
HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz"
DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST"
CHECKSUM_URL="$DOWNLOAD_URL.sha256"
HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)"
HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST"
HELM_SUM_FILE="$HELM_TMP_ROOT/$HELM_DIST.sha256"
echo "Downloading $DOWNLOAD_URL"
if type "curl" > /dev/null; then
curl -SsL "$CHECKSUM_URL" -o "$HELM_SUM_FILE"
elif type "wget" > /dev/null; then
wget -q -O "$HELM_SUM_FILE" "$CHECKSUM_URL"
fi
if type "curl" > /dev/null; then
curl -SsL "$DOWNLOAD_URL" -o "$HELM_TMP_FILE"
elif type "wget" > /dev/null; then
wget -q -O "$HELM_TMP_FILE" "$DOWNLOAD_URL"
fi
}
# installFile verifies the SHA256 for the file, then unpacks and
# installs it.
installFile() {
HELM_TMP="$HELM_TMP_ROOT/$PROJECT_NAME"
local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}')
local expected_sum=$(cat ${HELM_SUM_FILE})
if [ "$sum" != "$expected_sum" ]; then
echo "SHA sum of ${HELM_TMP_FILE} does not match. Aborting."
exit 1
fi
mkdir -p "$HELM_TMP"
tar xf "$HELM_TMP_FILE" -C "$HELM_TMP"
HELM_TMP_BIN="$HELM_TMP/$OS-$ARCH/$PROJECT_NAME"
echo "Preparing to install $PROJECT_NAME into ${HELM_INSTALL_DIR}"
runAsRoot cp "$HELM_TMP_BIN" "$HELM_INSTALL_DIR"
echo "$PROJECT_NAME installed into $HELM_INSTALL_DIR/$PROJECT_NAME"
}
# fail_trap is executed if an error occurs.
fail_trap() {
result=$?
if [ "$result" != "0" ]; then
if [[ -n "$INPUT_ARGUMENTS" ]]; then
echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS"
help
else
echo "Failed to install $PROJECT_NAME"
fi
echo -e "\tFor support, go to https://github.com/helm/helm."
fi
cleanup
exit $result
}
# testVersion tests the installed client to make sure it is working.
testVersion() {
set +e
HELM="$(which $PROJECT_NAME)"
if [ "$?" = "1" ]; then
echo "$PROJECT_NAME not found. Is $HELM_INSTALL_DIR on your "'$PATH?'
exit 1
fi
set -e
}
# help provides possible cli installation arguments
help () {
echo "Accepted cli arguments are:"
echo -e "\t[--help|-h ] ->> prints this help"
echo -e "\t[--version|-v <desired_version>] . When not defined it fetches the latest release from GitHub"
echo -e "\te.g. --version v3.0.0 or -v canary"
echo -e "\t[--no-sudo] ->> install without sudo"
}
# cleanup temporary files to avoid https://github.com/helm/helm/issues/2977
cleanup() {
if [[ -d "${HELM_TMP_ROOT:-}" ]]; then
rm -rf "$HELM_TMP_ROOT"
fi
}
# Execution
#Stop execution on any error
trap "fail_trap" EXIT
set -e
# Parsing input arguments (if any)
export INPUT_ARGUMENTS="${@}"
set -u
while [[ $# -gt 0 ]]; do
case $1 in
'--version'|-v)
shift
if [[ $# -ne 0 ]]; then
export DESIRED_VERSION="${1}"
else
echo -e "Please provide the desired version. e.g. --version v3.0.0 or -v canary"
exit 0
fi
;;
'--no-sudo')
USE_SUDO="false"
;;
'--help'|-h)
help
exit 0
;;
*) exit 1
;;
esac
shift
done
set +u
initArch
initOS
verifySupported
checkDesiredVersion
if ! checkHelmInstalledVersion; then
downloadFile
installFile
fi
testVersion
cleanup

@ -27,14 +27,16 @@ find_files() {
\( -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
echo "Some source files are missing license headers."
printf '%s\n' "${failed_license_header[@]}"
exit 1
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
echo "Some source files are missing the copyright header."
printf '%s\n' "${failed_copyright_header[@]}"

Loading…
Cancel
Save