Merge remote-tracking branch 'upstream/master'

pull/6231/head
Eduard Laur 6 years ago
commit c1c2cc1ef9

@ -126,13 +126,6 @@ __helm_compgen() {
__helm_compopt() { __helm_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh true # don't do anything. Not supported by bashcompinit in zsh
} }
__helm_declare() {
if [ "$1" == "-F" ]; then
whence -w "$@"
else
builtin declare "$@"
fi
}
__helm_ltrim_colon_completions() __helm_ltrim_colon_completions()
{ {
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
@ -194,7 +187,7 @@ autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed # use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]' LWORD='[[:<:]]'
RWORD='[[:>:]]' RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then
LWORD='\<' LWORD='\<'
RWORD='\>' RWORD='\>'
fi fi
@ -210,7 +203,7 @@ __helm_convert_bash_to_zsh() {
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \ -e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \
-e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \ -e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \
-e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \ -e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \
-e "s/${LWORD}declare${RWORD}/__helm_declare/g" \ -e "s/${LWORD}declare${RWORD}/builtin declare/g" \
-e "s/\\\$(type${RWORD}/\$(__helm_type/g" \ -e "s/\\\$(type${RWORD}/\$(__helm_type/g" \
-e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \ -e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \
-e 's/FUNCNAME/funcstack/g' \ -e 's/FUNCNAME/funcstack/g' \

@ -106,7 +106,7 @@ func (c *createCmd) run() error {
if c.starter != "" { if c.starter != "" {
// Create from the starter // Create from the starter
lstarter := filepath.Join(c.home.Starters(), c.starter) lstarter := filepath.Join(c.home.Starters(), c.starter)
// If path is absolute, we dont want to prefix it with helm starters folder // If path is absolute, we don't want to prefix it with helm starters folder
if filepath.IsAbs(c.starter) { if filepath.IsAbs(c.starter) {
lstarter = c.starter lstarter = c.starter
} }

@ -41,7 +41,7 @@ import (
const ( const (
bashCompletionFunc = ` bashCompletionFunc = `
__helm_override_flag_list=(--kubeconfig --kube-context --host --tiller-namespace) __helm_override_flag_list=(--kubeconfig --kube-context --host --tiller-namespace --home)
__helm_override_flags() __helm_override_flags()
{ {
local ${__helm_override_flag_list[*]##*-} two_word_of of var local ${__helm_override_flag_list[*]##*-} two_word_of of var
@ -69,13 +69,46 @@ __helm_override_flags()
done done
} }
__helm_binary_name()
{
local helm_binary
helm_binary="${words[0]}"
__helm_debug "${FUNCNAME[0]}: helm_binary is ${helm_binary}"
echo ${helm_binary}
}
__helm_list_releases() __helm_list_releases()
{ {
__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 filter local out filter
# Use ^ to map from the start of the release name # Use ^ to map from the start of the release name
filter="^${words[c]}" filter="^${words[c]}"
if out=$(helm list $(__helm_override_flags) -a -q ${filter} 2>/dev/null); then # Use eval in case helm_binary_name or __helm_override_flags contains a variable (e.g., $HOME/bin/h2)
if out=$(eval $(__helm_binary_name) list $(__helm_override_flags) -a -q -m 1000 ${filter} 2>/dev/null); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__helm_list_repos()
{
__helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out oflags
oflags=$(__helm_override_flags)
__helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}"
# Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2)
if out=$(eval $(__helm_binary_name) repo list ${oflags} 2>/dev/null | tail +2 | cut -f1); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__helm_list_plugins()
{
__helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out oflags
oflags=$(__helm_override_flags)
__helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}"
# Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2)
if out=$(eval $(__helm_binary_name) plugin list ${oflags} 2>/dev/null | tail +2 | cut -f1); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) ) COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi fi
} }
@ -89,6 +122,14 @@ __helm_custom_func()
__helm_list_releases __helm_list_releases
return return
;; ;;
helm_repo_remove | helm_repo_update)
__helm_list_repos
return
;;
helm_plugin_remove | helm_plugin_update)
__helm_list_plugins
return
;;
*) *)
;; ;;
esac esac

@ -282,7 +282,7 @@ func (i *initCmd) run() error {
if err := i.ping(i.opts.SelectImage()); err != nil { if err := i.ping(i.opts.SelectImage()); err != nil {
return err return err
} }
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been upgraded to the current version.") fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been updated to", i.opts.SelectImage(), ".")
} else { } else {
debug("The error received while trying to init: %s", err) debug("The error received while trying to init: %s", err)
fmt.Fprintln(i.out, "Warning: Tiller is already installed in the cluster.\n"+ fmt.Fprintln(i.out, "Warning: Tiller is already installed in the cluster.\n"+

@ -28,8 +28,8 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
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/runtime" "k8s.io/apimachinery/pkg/runtime"
@ -83,7 +83,7 @@ func TestInitCmd_exists(t *testing.T) {
defer os.RemoveAll(home) defer os.RemoveAll(home)
var buf bytes.Buffer var buf bytes.Buffer
fc := fake.NewSimpleClientset(&v1beta1.Deployment{ fc := fake.NewSimpleClientset(&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: v1.NamespaceDefault, Namespace: v1.NamespaceDefault,
Name: "tiller-deploy", Name: "tiller-deploy",

@ -143,6 +143,7 @@ type installCmd struct {
certFile string certFile string
keyFile string keyFile string
caFile string caFile string
output string
} }
type valueFiles []string type valueFiles []string
@ -226,6 +227,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&inst.depUp, "dep-up", false, "Run helm dependency update before installing the chart") f.BoolVar(&inst.depUp, "dep-up", false, "Run helm dependency update before installing the chart")
f.BoolVar(&inst.subNotes, "render-subchart-notes", false, "Render subchart notes along with the parent") f.BoolVar(&inst.subNotes, "render-subchart-notes", false, "Render subchart notes along with the parent")
f.StringVar(&inst.description, "description", "", "Specify a description for the release") f.StringVar(&inst.description, "description", "", "Specify a description for the release")
bindOutputFlag(cmd, &inst.output)
// set defaults from environment // set defaults from environment
settings.InitTLS(f) settings.InitTLS(f)
@ -335,7 +337,10 @@ func (i *installCmd) run() error {
if rel == nil { if rel == nil {
return nil return nil
} }
if outputFormat(i.output) == outputTable {
i.printRelease(rel) i.printRelease(rel)
}
// If this is a dry run, we can't display status. // If this is a dry run, we can't display status.
if i.dryRun { if i.dryRun {
@ -351,8 +356,8 @@ func (i *installCmd) run() error {
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }
PrintStatus(i.out, status)
return nil return write(i.out, &statusWriter{status}, outputFormat(i.output))
} }
// Merges source and destination map, preferring values from the source map // Merges source and destination map, preferring values from the source map

@ -191,6 +191,22 @@ func TestInstall(t *testing.T) {
flags: []string{"--name-template", "{{UPPER \"foobar\"}}"}, flags: []string{"--name-template", "{{UPPER \"foobar\"}}"},
err: true, err: true,
}, },
// Install, using --output json
{
name: "install using output json",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name virgil --output json", " "),
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
expected: regexp.QuoteMeta(`{"name":"virgil","info":{"status":{"code":1},"first_deployed":{"seconds":242085845},"last_deployed":{"seconds":242085845},"Description":"Release mock"},"namespace":"default"}`),
},
// Install, using --output yaml
{
name: "install using output yaml",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name virgil --output yaml", " "),
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
expected: "info:\n Description: Release mock\n first_deployed:\n seconds: 242085845\n last_deployed:\n seconds: 242085845\n status:\n code: 1\nname: virgil\nnamespace: default\n",
},
} }
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command { runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {

@ -17,23 +17,22 @@ limitations under the License.
package installer // import "k8s.io/helm/cmd/helm/installer" package installer // import "k8s.io/helm/cmd/helm/installer"
import ( import (
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"strings" "strings"
"github.com/Masterminds/semver" "github.com/Masterminds/semver"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1" extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
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/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr" "k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes" "k8s.io/client-go/kubernetes"
appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1" corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
"k8s.io/helm/pkg/version"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/tiller/environment" "k8s.io/helm/pkg/tiller/environment"
@ -43,7 +42,7 @@ import (
// //
// Returns an error if the command failed. // Returns an error if the command failed.
func Install(client kubernetes.Interface, opts *Options) error { func Install(client kubernetes.Interface, opts *Options) error {
if err := createDeployment(client.ExtensionsV1beta1(), opts); err != nil { if err := createDeployment(client.AppsV1(), opts); err != nil {
return err return err
} }
if err := createService(client.CoreV1(), opts.Namespace); err != nil { if err := createService(client.CoreV1(), opts.Namespace); err != nil {
@ -61,51 +60,108 @@ func Install(client kubernetes.Interface, opts *Options) error {
// //
// Returns an error if the command failed. // Returns an error if the command failed.
func Upgrade(client kubernetes.Interface, opts *Options) error { func Upgrade(client kubernetes.Interface, opts *Options) error {
obj, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{}) appsobj, err := client.AppsV1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err != nil { if err == nil {
// Can happen in two cases:
// 1. helm init inserted an apps/v1 Deployment up front in Kubernetes
// 2. helm init inserted an extensions/v1beta1 Deployment against a K8s cluster already
// supporting apps/v1 Deployment. In such a case K8s is returning the apps/v1 object anyway.`
// (for the same reason "kubectl convert" is being deprecated)
return upgradeAppsTillerDeployment(client, opts, appsobj)
}
extensionsobj, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err == nil {
// User performed helm init against older version of kubernetes (Previous to 1.9)
return upgradeExtensionsTillerDeployment(client, opts, extensionsobj)
}
return err
}
func upgradeAppsTillerDeployment(client kubernetes.Interface, opts *Options, obj *appsv1.Deployment) error {
// Update the PodTemplateSpec section of the deployment
if err := updatePodTemplate(&obj.Spec.Template.Spec, opts); err != nil {
return err return err
} }
tillerImage := obj.Spec.Template.Spec.Containers[0].Image
if semverCompare(tillerImage) == -1 && !opts.ForceUpgrade { if _, err := client.AppsV1().Deployments(opts.Namespace).Update(obj); err != nil {
return errors.New("current Tiller version is newer, use --force-upgrade to downgrade") return err
} }
obj.Spec.Template.Spec.Containers[0].Image = opts.SelectImage()
obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy() // If the service does not exist that would mean we are upgrading from a Tiller version
obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount // that didn't deploy the service, so install it.
_, err := client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return createService(client.CoreV1(), opts.Namespace)
}
return err
}
func upgradeExtensionsTillerDeployment(client kubernetes.Interface, opts *Options, obj *extensionsv1beta1.Deployment) error {
// Update the PodTemplateSpec section of the deployment
if err := updatePodTemplate(&obj.Spec.Template.Spec, opts); err != nil {
return err
}
if _, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Update(obj); err != nil { if _, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Update(obj); err != nil {
return err return err
} }
// If the service does not exist that would mean we are upgrading from a Tiller version // If the service does not exist that would mean we are upgrading from a Tiller version
// that didn't deploy the service, so install it. // that didn't deploy the service, so install it.
_, err = client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{}) _, err := client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
if apierrors.IsNotFound(err) { if apierrors.IsNotFound(err) {
return createService(client.CoreV1(), opts.Namespace) return createService(client.CoreV1(), opts.Namespace)
} }
return err return err
} }
// semverCompare returns whether the client's version is older, equal or newer than the given image's version. func updatePodTemplate(podSpec *v1.PodSpec, opts *Options) error {
func semverCompare(image string) int { tillerImage := podSpec.Containers[0].Image
split := strings.Split(image, ":") clientImage := opts.SelectImage()
if len(split) < 2 {
// If we don't know the version, we consider the client version newer. if semverCompare(tillerImage, clientImage) == -1 && !opts.ForceUpgrade {
return 1 return fmt.Errorf("current Tiller version %s is newer than client version %s, use --force-upgrade to downgrade", tillerImage, clientImage)
}
podSpec.Containers[0].Image = clientImage
podSpec.Containers[0].ImagePullPolicy = opts.pullPolicy()
podSpec.ServiceAccountName = opts.ServiceAccount
return nil
} }
tillerVersion, err := semver.NewVersion(split[1])
// semverCompare returns whether the client's version is older, equal or newer than the given image's version.
func semverCompare(tillerImage, clientImage string) int {
tillerVersion, err := string2semver(tillerImage)
if err != nil { if err != nil {
// same thing with unparsable tiller versions (e.g. canary releases). // same thing with unparsable tiller versions (e.g. canary releases).
return 1 return 1
} }
clientVersion, err := semver.NewVersion(version.Version)
// clientVersion, err := semver.NewVersion(currentVersion)
clientVersion, err := string2semver(clientImage)
if err != nil { if err != nil {
// aaaaaand same thing with unparsable helm versions (e.g. canary releases). // aaaaaand same thing with unparsable helm versions (e.g. canary releases).
return 1 return 1
} }
return clientVersion.Compare(tillerVersion) return clientVersion.Compare(tillerVersion)
} }
func string2semver(image string) (*semver.Version, error) {
split := strings.Split(image, ":")
if len(split) < 2 {
// If we don't know the version, we consider the client version newer.
return nil, fmt.Errorf("no repository in image %s", image)
}
return semver.NewVersion(split[1])
}
// createDeployment creates the Tiller Deployment resource. // createDeployment creates the Tiller Deployment resource.
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error { func createDeployment(client appsv1client.DeploymentsGetter, opts *Options) error {
obj, err := generateDeployment(opts) obj, err := generateDeployment(opts)
if err != nil { if err != nil {
return err return err
@ -118,7 +174,7 @@ func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options)
// Deployment gets a deployment object that can be used to generate a manifest // Deployment gets a deployment object that can be used to generate a manifest
// as a string. This object should not be submitted directly to the Kubernetes // as a string. This object should not be submitted directly to the Kubernetes
// api // api
func Deployment(opts *Options) (*v1beta1.Deployment, error) { func Deployment(opts *Options) (*appsv1.Deployment, error) {
dep, err := generateDeployment(opts) dep, err := generateDeployment(opts)
if err != nil { if err != nil {
return nil, err return nil, err
@ -197,7 +253,7 @@ func parseNodeSelectorsInto(labels string, m map[string]string) error {
} }
return nil return nil
} }
func generateDeployment(opts *Options) (*v1beta1.Deployment, error) { func generateDeployment(opts *Options) (*appsv1.Deployment, error) {
labels := generateLabels(map[string]string{"name": "tiller"}) labels := generateLabels(map[string]string{"name": "tiller"})
nodeSelectors := map[string]string{} nodeSelectors := map[string]string{}
if len(opts.NodeSelectors) > 0 { if len(opts.NodeSelectors) > 0 {
@ -206,14 +262,17 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
return nil, err return nil, err
} }
} }
d := &v1beta1.Deployment{ d := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Namespace: opts.Namespace, Namespace: opts.Namespace,
Name: deploymentName, Name: deploymentName,
Labels: labels, Labels: labels,
}, },
Spec: v1beta1.DeploymentSpec{ Spec: appsv1.DeploymentSpec{
Replicas: opts.getReplicas(), Replicas: opts.getReplicas(),
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: v1.PodTemplateSpec{ Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Labels: labels, Labels: labels,
@ -297,7 +356,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
// merge them and convert back to Deployment // merge them and convert back to Deployment
if len(opts.Values) > 0 { if len(opts.Values) > 0 {
// base deployment struct // base deployment struct
var dd v1beta1.Deployment var dd appsv1.Deployment
// get YAML from original deployment // get YAML from original deployment
dy, err := yaml.Marshal(d) dy, err := yaml.Marshal(d)
if err != nil { if err != nil {

@ -24,8 +24,8 @@ import (
"testing" "testing"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake" "k8s.io/client-go/kubernetes/fake"
@ -192,7 +192,7 @@ func TestInstall(t *testing.T) {
fc := &fake.Clientset{} fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
l := obj.GetLabels() l := obj.GetLabels()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l) t.Errorf("expected labels = '', got '%s'", l)
@ -239,7 +239,7 @@ func TestInstallHA(t *testing.T) {
fc := &fake.Clientset{} fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
replicas := obj.Spec.Replicas replicas := obj.Spec.Replicas
if int(*replicas) != 2 { if int(*replicas) != 2 {
t.Errorf("expected replicas = 2, got '%d'", replicas) t.Errorf("expected replicas = 2, got '%d'", replicas)
@ -263,7 +263,7 @@ func TestInstall_WithTLS(t *testing.T) {
fc := &fake.Clientset{} fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
l := obj.GetLabels() l := obj.GetLabels()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) { if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l) t.Errorf("expected labels = '', got '%s'", l)
@ -331,7 +331,7 @@ func TestInstall_WithTLS(t *testing.T) {
func TestInstall_canary(t *testing.T) { func TestInstall_canary(t *testing.T) {
fc := &fake.Clientset{} fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != "gcr.io/kubernetes-helm/tiller:canary" { if i != "gcr.io/kubernetes-helm/tiller:canary" {
t.Errorf("expected canary image, got '%s'", i) t.Errorf("expected canary image, got '%s'", i)
@ -369,7 +369,7 @@ func TestUpgrade(t *testing.T) {
return true, existingDeployment, nil return true, existingDeployment, nil
}) })
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != image { if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i) t.Errorf("expected image = '%s', got '%s'", image, i)
@ -408,7 +408,7 @@ func TestUpgrade_serviceNotFound(t *testing.T) {
return true, existingDeployment, nil return true, existingDeployment, nil
}) })
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != image { if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i) t.Errorf("expected image = '%s', got '%s'", image, i)
@ -453,7 +453,7 @@ func TestUgrade_newerVersion(t *testing.T) {
return true, existingDeployment, nil return true, existingDeployment, nil
}) })
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != image { if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i) t.Errorf("expected image = '%s', got '%s'", image, i)
@ -513,7 +513,7 @@ func TestUpgrade_identical(t *testing.T) {
return true, existingDeployment, nil return true, existingDeployment, nil
}) })
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != image { if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i) t.Errorf("expected image = '%s', got '%s'", image, i)
@ -554,7 +554,7 @@ func TestUpgrade_canaryClient(t *testing.T) {
return true, existingDeployment, nil return true, existingDeployment, nil
}) })
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != image { if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i) t.Errorf("expected image = '%s', got '%s'", image, i)
@ -595,7 +595,7 @@ func TestUpgrade_canaryServer(t *testing.T) {
return true, existingDeployment, nil return true, existingDeployment, nil
}) })
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment) obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image i := obj.Spec.Template.Spec.Containers[0].Image
if i != image { if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i) t.Errorf("expected image = '%s', got '%s'", image, i)

@ -17,16 +17,31 @@ limitations under the License.
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"text/template" "text/template"
"time" "time"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/timeconv" "k8s.io/helm/pkg/timeconv"
) )
type outputFormat string
const (
outputFlag = "output"
outputTable outputFormat = "table"
outputJSON outputFormat = "json"
outputYAML outputFormat = "yaml"
)
var printReleaseTemplate = `REVISION: {{.Release.Version}} var printReleaseTemplate = `REVISION: {{.Release.Version}}
RELEASED: {{.ReleaseDate}} RELEASED: {{.ReleaseDate}}
CHART: {{.Release.Chart.Metadata.Name}}-{{.Release.Chart.Metadata.Version}} CHART: {{.Release.Chart.Metadata.Name}}-{{.Release.Chart.Metadata.Version}}
@ -80,3 +95,66 @@ func debug(format string, args ...interface{}) {
fmt.Printf(format, args...) fmt.Printf(format, args...)
} }
} }
// bindOutputFlag will add the output flag to the given command and bind the
// value to the given string pointer
func bindOutputFlag(cmd *cobra.Command, varRef *string) {
cmd.Flags().StringVarP(varRef, outputFlag, "o", string(outputTable), fmt.Sprintf("Prints the output in the specified format. Allowed values: %s, %s, %s", outputTable, outputJSON, outputYAML))
}
type outputWriter interface {
WriteTable(out io.Writer) error
WriteJSON(out io.Writer) error
WriteYAML(out io.Writer) error
}
func write(out io.Writer, ow outputWriter, format outputFormat) error {
switch format {
case outputTable:
return ow.WriteTable(out)
case outputJSON:
return ow.WriteJSON(out)
case outputYAML:
return ow.WriteYAML(out)
}
return fmt.Errorf("unsupported format %s", format)
}
// encodeJSON is a helper function to decorate any error message with a bit more
// context and avoid writing the same code over and over for printers
func encodeJSON(out io.Writer, obj interface{}) error {
enc := json.NewEncoder(out)
err := enc.Encode(obj)
if err != nil {
return fmt.Errorf("unable to write JSON output: %s", err)
}
return nil
}
// encodeYAML is a helper function to decorate any error message with a bit more
// context and avoid writing the same code over and over for printers
func encodeYAML(out io.Writer, obj interface{}) error {
raw, err := yaml.Marshal(obj)
if err != nil {
return fmt.Errorf("unable to write YAML output: %s", err)
}
// Append a newline, as with a JSON encoder
raw = append(raw, []byte("\n")...)
_, err = out.Write(raw)
if err != nil {
return fmt.Errorf("unable to write YAML output: %s", err)
}
return nil
}
// encodeTable is a helper function to decorate any error message with a bit
// more context and avoid writing the same code over and over for printers
func encodeTable(out io.Writer, table *uitable.Table) error {
raw := table.Bytes()
raw = append(raw, []byte("\n")...)
_, err := out.Write(raw)
if err != nil {
return fmt.Errorf("unable to write table output: %s", err)
}
return nil
}

@ -17,8 +17,6 @@ limitations under the License.
package main package main
import ( import (
"errors"
"fmt"
"io" "io"
"github.com/gosuri/uitable" "github.com/gosuri/uitable"
@ -31,6 +29,12 @@ import (
type repoListCmd struct { type repoListCmd struct {
out io.Writer out io.Writer
home helmpath.Home home helmpath.Home
output string
}
type repositoryElement struct {
Name string
URL string
} }
func newRepoListCmd(out io.Writer) *cobra.Command { func newRepoListCmd(out io.Writer) *cobra.Command {
@ -45,22 +49,56 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
}, },
} }
bindOutputFlag(cmd, &list.output)
return cmd return cmd
} }
func (a *repoListCmd) run() error { func (a *repoListCmd) run() error {
f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile()) repoFile, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
if err != nil { if err != nil {
return err return err
} }
if len(f.Repositories) == 0 {
return errors.New("no repositories to show") return write(a.out, &repoListWriter{repoFile.Repositories}, outputFormat(a.output))
}
//////////// Printer implementation below here
type repoListWriter struct {
repos []*repo.Entry
} }
func (r *repoListWriter) WriteTable(out io.Writer) error {
table := uitable.New() table := uitable.New()
table.AddRow("NAME", "URL") table.AddRow("NAME", "URL")
for _, re := range f.Repositories { for _, re := range r.repos {
table.AddRow(re.Name, re.URL) table.AddRow(re.Name, re.URL)
} }
fmt.Fprintln(a.out, table) return encodeTable(out, table)
}
func (r *repoListWriter) WriteJSON(out io.Writer) error {
return r.encodeByFormat(out, outputJSON)
}
func (r *repoListWriter) WriteYAML(out io.Writer) error {
return r.encodeByFormat(out, outputYAML)
}
func (r *repoListWriter) encodeByFormat(out io.Writer, format outputFormat) error {
var repolist []repositoryElement
for _, re := range r.repos {
repolist = append(repolist, repositoryElement{Name: re.Name, URL: re.URL})
}
switch format {
case outputJSON:
return encodeJSON(out, repolist)
case outputYAML:
return encodeYAML(out, repolist)
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil return nil
} }

@ -48,6 +48,14 @@ type searchCmd struct {
regexp bool regexp bool
version string version string
colWidth uint colWidth uint
output string
}
type chartElement struct {
Name string
Version string
AppVersion string
Description string
} }
func newSearchCmd(out io.Writer) *cobra.Command { func newSearchCmd(out io.Writer) *cobra.Command {
@ -68,6 +76,7 @@ func newSearchCmd(out io.Writer) *cobra.Command {
f.BoolVarP(&sc.versions, "versions", "l", false, "Show the long listing, with each version of each chart on its own line") f.BoolVarP(&sc.versions, "versions", "l", false, "Show the long listing, with each version of each chart on its own line")
f.StringVarP(&sc.version, "version", "v", "", "Search using semantic versioning constraints") f.StringVarP(&sc.version, "version", "v", "", "Search using semantic versioning constraints")
f.UintVar(&sc.colWidth, "col-width", 60, "Specifies the max column width of output") f.UintVar(&sc.colWidth, "col-width", 60, "Specifies the max column width of output")
bindOutputFlag(cmd, &sc.output)
return cmd return cmd
} }
@ -95,9 +104,7 @@ func (s *searchCmd) run(args []string) error {
return err return err
} }
fmt.Fprintln(s.out, s.formatSearchResults(data, s.colWidth)) return write(s.out, &searchWriter{data, s.colWidth}, outputFormat(s.output))
return nil
} }
func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, error) { func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, error) {
@ -128,19 +135,6 @@ func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, err
return data, nil return data, nil
} }
func (s *searchCmd) formatSearchResults(res []*search.Result, colWidth uint) string {
if len(res) == 0 {
return "No results found"
}
table := uitable.New()
table.MaxColWidth = colWidth
table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION")
for _, r := range res {
table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description)
}
return table.String()
}
func (s *searchCmd) buildIndex() (*search.Index, error) { func (s *searchCmd) buildIndex() (*search.Index, error) {
// Load the repositories.yaml // Load the repositories.yaml
rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile()) rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile())
@ -162,3 +156,53 @@ func (s *searchCmd) buildIndex() (*search.Index, error) {
} }
return i, nil return i, nil
} }
//////////// Printer implementation below here
type searchWriter struct {
results []*search.Result
columnWidth uint
}
func (r *searchWriter) WriteTable(out io.Writer) error {
if len(r.results) == 0 {
_, err := out.Write([]byte("No results found\n"))
if err != nil {
return fmt.Errorf("unable to write results: %s", err)
}
return nil
}
table := uitable.New()
table.MaxColWidth = r.columnWidth
table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION")
for _, r := range r.results {
table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description)
}
return encodeTable(out, table)
}
func (r *searchWriter) WriteJSON(out io.Writer) error {
return r.encodeByFormat(out, outputJSON)
}
func (r *searchWriter) WriteYAML(out io.Writer) error {
return r.encodeByFormat(out, outputYAML)
}
func (r *searchWriter) encodeByFormat(out io.Writer, format outputFormat) error {
var chartList []chartElement
for _, r := range r.results {
chartList = append(chartList, chartElement{r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description})
}
switch format {
case outputJSON:
return encodeJSON(out, chartList)
case outputYAML:
return encodeYAML(out, chartList)
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}

@ -55,7 +55,7 @@ type Index struct {
charts map[string]*repo.ChartVersion charts map[string]*repo.ChartVersion
} }
// NewIndex creats a new Index. // NewIndex creates a new Index.
func NewIndex() *Index { func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}} return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
} }

@ -18,6 +18,7 @@ package main
import ( import (
"io" "io"
"strings"
"testing" "testing"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -84,6 +85,30 @@ func TestSearchCmd(t *testing.T) {
flags: []string{"--regexp"}, flags: []string{"--regexp"},
err: true, err: true,
}, },
{
name: "search for 'maria', expect one match output json",
args: []string{"maria"},
flags: strings.Split("--output json", " "),
expected: `[{"Name":"testing/mariadb","Version":"0.3.0","Appversion":"","Description":"Chart for MariaDB"}]`,
},
{
name: "search for 'alpine', expect two matches output json",
args: []string{"alpine"},
flags: strings.Split("--output json", " "),
expected: `[{"Name":"testing/alpine","Version":"0.2.0","Appversion":"2.3.4","Description":"Deploy a basic Alpine Linux pod"}]`,
},
{
name: "search for 'maria', expect one match output yaml",
args: []string{"maria"},
flags: strings.Split("--output yaml", " "),
expected: "- AppVersion: \"\"\n Description: Chart for MariaDB\n Name: testing/mariadb\n Version: 0.3.0\n\n",
},
{
name: "search for 'alpine', expect two matches output yaml",
args: []string{"alpine"},
flags: strings.Split("--output yaml", " "),
expected: "- AppVersion: 2.3.4\n Description: Deploy a basic Alpine Linux pod\n Name: testing/alpine\n Version: 0.2.0\n\n",
},
} }
cleanup := resetEnv() cleanup := resetEnv()

@ -17,13 +17,11 @@ limitations under the License.
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"io" "io"
"regexp" "regexp"
"text/tabwriter" "text/tabwriter"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable" "github.com/gosuri/uitable"
"github.com/gosuri/uitable/util/strutil" "github.com/gosuri/uitable/util/strutil"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -79,7 +77,7 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags() f := cmd.Flags()
settings.AddFlagsTLS(f) settings.AddFlagsTLS(f)
f.Int32Var(&status.version, "revision", 0, "If set, display the status of the named release with revision") f.Int32Var(&status.version, "revision", 0, "If set, display the status of the named release with revision")
f.StringVarP(&status.outfmt, "output", "o", "", "Output the status in the specified format (json or yaml)") bindOutputFlag(cmd, &status.outfmt)
// set defaults from environment // set defaults from environment
settings.InitTLS(f) settings.InitTLS(f)
@ -93,27 +91,26 @@ func (s *statusCmd) run() error {
return prettyError(err) return prettyError(err)
} }
switch s.outfmt { return write(s.out, &statusWriter{res}, outputFormat(s.outfmt))
case "":
PrintStatus(s.out, res)
return nil
case "json":
data, err := json.Marshal(res)
if err != nil {
return fmt.Errorf("Failed to Marshal JSON output: %s", err)
} }
s.out.Write(data)
return nil type statusWriter struct {
case "yaml": status *services.GetReleaseStatusResponse
data, err := yaml.Marshal(res)
if err != nil {
return fmt.Errorf("Failed to Marshal YAML output: %s", err)
} }
s.out.Write(data)
func (s *statusWriter) WriteTable(out io.Writer) error {
PrintStatus(out, s.status)
// There is no error handling here due to backwards compatibility with
// PrintStatus
return nil return nil
} }
return fmt.Errorf("Unknown output format %q", s.outfmt) func (s *statusWriter) WriteJSON(out io.Writer) error {
return encodeJSON(out, s.status)
}
func (s *statusWriter) WriteYAML(out io.Writer) error {
return encodeYAML(out, s.status)
} }
// PrintStatus prints out the status of a release. Shared because also used by // PrintStatus prints out the status of a release. Shared because also used by

@ -91,6 +91,7 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
RunE: t.run, RunE: t.run,
} }
cmd.SetOutput(out)
f := cmd.Flags() f := cmd.Flags()
f.BoolVar(&t.showNotes, "notes", false, "Show the computed NOTES.txt file as well") f.BoolVar(&t.showNotes, "notes", false, "Show the computed NOTES.txt file as well")
f.StringVarP(&t.releaseName, "name", "n", "release-name", "Release name") f.StringVarP(&t.releaseName, "name", "n", "release-name", "Release name")
@ -241,20 +242,20 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
if whitespaceRegex.MatchString(data) { if whitespaceRegex.MatchString(data) {
continue continue
} }
err = writeToFile(t.outputDir, m.Name, data) err = writeToFile(t.outputDir, m.Name, data, t.out)
if err != nil { if err != nil {
return err return err
} }
continue continue
} }
fmt.Printf("---\n# Source: %s\n", m.Name) fmt.Fprintf(t.out, "---\n# Source: %s\n", m.Name)
fmt.Println(data) fmt.Fprintln(t.out, data)
} }
return nil return nil
} }
// write the <data> to <output-dir>/<name> // write the <data> to <output-dir>/<name>
func writeToFile(outputDir string, name string, data string) error { func writeToFile(outputDir string, name string, data string, out io.Writer) error {
outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator)) outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
err := ensureDirectoryForFile(outfileName) err := ensureDirectoryForFile(outfileName)
@ -275,7 +276,7 @@ func writeToFile(outputDir string, name string, data string) error {
return err return err
} }
fmt.Printf("wrote %s\n", outfileName) fmt.Fprintf(out, "wrote %s\n", outfileName)
return nil return nil
} }

@ -20,7 +20,6 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"io"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -158,7 +157,7 @@ func TestTemplateCmd(t *testing.T) {
}, },
{ {
name: "check_invalid_name_template", name: "check_invalid_name_template",
desc: "verify the relase name generate by template is invalid", desc: "verify the release name generate by template is invalid",
args: []string{subchart1ChartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"}, args: []string{subchart1ChartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"},
expectError: "is invalid", expectError: "is invalid",
}, },
@ -178,14 +177,9 @@ func TestTemplateCmd(t *testing.T) {
}, },
} }
var buf bytes.Buffer
for _, tt := range tests { for _, tt := range tests {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
// capture stdout
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// execute template command // execute template command
out := bytes.NewBuffer(nil) out := bytes.NewBuffer(nil)
cmd := newTemplateCmd(out) cmd := newTemplateCmd(out)
@ -206,14 +200,8 @@ func TestTemplateCmd(t *testing.T) {
} else if err != nil { } else if err != nil {
t.Errorf("expected no error, got %v", err) t.Errorf("expected no error, got %v", err)
} }
// restore stdout
w.Close()
os.Stdout = old
var b bytes.Buffer
io.Copy(&b, r)
r.Close()
// scan yaml into map[<path>]yaml // scan yaml into map[<path>]yaml
scanner := bufio.NewScanner(&b) scanner := bufio.NewScanner(out)
next := false next := false
lastKey := "" lastKey := ""
m := map[string]string{} m := map[string]string{}
@ -239,7 +227,6 @@ func TestTemplateCmd(t *testing.T) {
} else { } else {
t.Errorf("could not find key %s", tt.expectKey) t.Errorf("could not find key %s", tt.expectKey)
} }
buf.Reset()
}) })
} }
} }

@ -117,6 +117,7 @@ type upgradeCmd struct {
certFile string certFile string
keyFile string keyFile string
caFile string caFile string
output string
} }
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -181,6 +182,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "Render subchart notes along with parent") f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "Render subchart notes along with parent")
f.StringVar(&upgrade.description, "description", "", "Specify the description to use for the upgrade, rather than the default") f.StringVar(&upgrade.description, "description", "", "Specify the description to use for the upgrade, rather than the default")
f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "Allow deletion of new resources created in this upgrade when upgrade failed") f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "Allow deletion of new resources created in this upgrade when upgrade failed")
bindOutputFlag(cmd, &upgrade.output)
f.MarkDeprecated("disable-hooks", "Use --no-hooks instead") f.MarkDeprecated("disable-hooks", "Use --no-hooks instead")
@ -307,14 +309,14 @@ func (u *upgradeCmd) run() error {
printRelease(u.out, resp.Release) printRelease(u.out, resp.Release)
} }
if outputFormat(u.output) == outputTable {
fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release) fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release)
}
// Print the status like status command does // Print the status like status command does
status, err := u.client.ReleaseStatus(u.release) status, err := u.client.ReleaseStatus(u.release)
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }
PrintStatus(u.out, status)
return nil return write(u.out, &statusWriter{status}, outputFormat(u.output))
} }

@ -38,7 +38,7 @@ Building synchronization state...
Starting synchronization Starting synchronization
Would copy file://fantastic-charts/alpine-0.1.0.tgz to gs://fantastic-charts/alpine-0.1.0.tgz Would copy file://fantastic-charts/alpine-0.1.0.tgz to gs://fantastic-charts/alpine-0.1.0.tgz
Would copy file://fantastic-charts/index.yaml to gs://fantastic-charts/index.yaml Would copy file://fantastic-charts/index.yaml to gs://fantastic-charts/index.yaml
Are you sure you would like to continue with these changes?? [y/N]} y Are you sure you would like to continue with these changes? [y/N]} y
Building synchronization state... Building synchronization state...
Starting synchronization Starting synchronization
Copying file://fantastic-charts/alpine-0.1.0.tgz [Content-Type=application/x-tar]... Copying file://fantastic-charts/alpine-0.1.0.tgz [Content-Type=application/x-tar]...

@ -129,6 +129,7 @@ You have multiple options with Globs:
Or Or
```yaml ```yaml
{{ $root := . }}
{{ range $path, $bytes := .Files.Glob "foo/*" }} {{ range $path, $bytes := .Files.Glob "foo/*" }}
{{ base $path }}: '{{ $root.Files.Get $path | b64enc }}' {{ base $path }}: '{{ $root.Files.Get $path | b64enc }}'
{{ end }} {{ end }}

@ -258,9 +258,11 @@ All charts are loaded by default. If `tags` or `condition` fields are present,
they will be evaluated and used to control loading for the chart(s) they are applied to. they will be evaluated and used to control loading for the chart(s) they are applied to.
Condition - The condition field holds one or more YAML paths (delimited by commas). Condition - The condition field holds one or more YAML paths (delimited by commas).
If this path exists in the top parent's values and resolves to a boolean value, If this path exists in the parent's values and resolves to a boolean value,
the chart will be enabled or disabled based on that boolean value. Only the first the chart will be enabled or disabled based on that boolean value. Only the first
valid path found in the list is evaluated and if no paths exist then the condition has no effect. valid path found in the list is evaluated and if no paths exist then the condition
has no effect. For multiple level dependencies the condition is prependend by the
path to the parent chart.
Tags - The tags field is a YAML list of labels to associate with this chart. Tags - The tags field is a YAML list of labels to associate with this chart.
In the top parent's values, all charts with tags can be enabled or disabled by In the top parent's values, all charts with tags can be enabled or disabled by
@ -272,7 +274,7 @@ dependencies:
- name: subchart1 - name: subchart1
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchart1.enabled,global.subchart1.enabled condition: subchart1.enabled
tags: tags:
- front-end - front-end
- subchart1 - subchart1
@ -280,11 +282,19 @@ dependencies:
- name: subchart2 - name: subchart2
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchart2.enabled,global.subchart2.enabled condition: subchart2.enabled
tags: tags:
- back-end - back-end
- subchart2 - subchart2
```
```yaml
# subchart2/requirements.yaml
dependencies:
- name: subsubchart
repository: http://localhost:10191
version: 0.1.0
condition: subsubchart.enabled
``` ```
```yaml ```yaml
@ -292,6 +302,9 @@ dependencies:
subchart1: subchart1:
enabled: true enabled: true
subchart2:
subsubchart:
enabled: false
tags: tags:
front-end: false front-end: false
back-end: true back-end: true
@ -305,6 +318,9 @@ Since `subchart2` is tagged with `back-end` and that tag evaluates to `true`, `s
enabled. Also note that although `subchart2` has a condition specified in `requirements.yaml`, there enabled. Also note that although `subchart2` has a condition specified in `requirements.yaml`, there
is no corresponding path and value in the parent's values so that condition has no effect. is no corresponding path and value in the parent's values so that condition has no effect.
`subsubchart` is disabled by default but can be enabled by setting `subchart2.subsubchart.enabled=true`.
Hint: disabling `subchart2` via tag will also disable all sub-charts (even if overriding the value `subchart2.subsubchart.enabled=true`).
##### Using the CLI with Tags and Conditions ##### Using the CLI with Tags and Conditions
The `--set` parameter can be used as usual to alter tag and condition values. The `--set` parameter can be used as usual to alter tag and condition values.

@ -93,6 +93,7 @@ helm install [CHART] [flags]
--namespace string Namespace to install the release into. Defaults to the current kube config namespace. --namespace string Namespace to install the release into. Defaults to the current kube config namespace.
--no-crd-hook Prevent CRD hooks from running, but run other hooks --no-crd-hook Prevent CRD hooks from running, but run other hooks
--no-hooks Prevent hooks from running during install --no-hooks Prevent hooks from running during install
-o, --output string Prints the output in the specified format. Allowed values: table, json, yaml (default "table")
--password string Chart repository password where to locate the requested chart --password string Chart repository password where to locate the requested chart
--render-subchart-notes Render subchart notes along with the parent --render-subchart-notes Render subchart notes along with the parent
--replace Re-use the given name, even if that name is already used. This is unsafe in production --replace Re-use the given name, even if that name is already used. This is unsafe in production
@ -130,4 +131,4 @@ helm install [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019 ###### Auto generated by spf13/cobra on 24-Sep-2019

@ -14,6 +14,7 @@ helm repo list [flags]
``` ```
-h, --help help for list -h, --help help for list
-o, --output string Prints the output in the specified format. Allowed values: table, json, yaml (default "table")
``` ```
### Options inherited from parent commands ### Options inherited from parent commands
@ -32,4 +33,4 @@ helm repo list [flags]
* [helm repo](helm_repo.md) - Add, list, remove, update, and index chart repositories * [helm repo](helm_repo.md) - Add, list, remove, update, and index chart repositories
###### Auto generated by spf13/cobra on 16-May-2019 ###### Auto generated by spf13/cobra on 24-Sep-2019

@ -20,6 +20,7 @@ helm search [keyword] [flags]
``` ```
--col-width uint Specifies the max column width of output (default 60) --col-width uint Specifies the max column width of output (default 60)
-h, --help help for search -h, --help help for search
-o, --output string Prints the output in the specified format. Allowed values: table, json, yaml (default "table")
-r, --regexp Use regular expressions for searching -r, --regexp Use regular expressions for searching
-v, --version string Search using semantic versioning constraints -v, --version string Search using semantic versioning constraints
-l, --versions Show the long listing, with each version of each chart on its own line -l, --versions Show the long listing, with each version of each chart on its own line
@ -41,4 +42,4 @@ helm search [keyword] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019 ###### Auto generated by spf13/cobra on 24-Sep-2019

@ -23,7 +23,7 @@ helm status [flags] RELEASE_NAME
``` ```
-h, --help help for status -h, --help help for status
-o, --output string Output the status in the specified format (json or yaml) -o, --output string Prints the output in the specified format. Allowed values: table, json, yaml (default "table")
--revision int32 If set, display the status of the named release with revision --revision int32 If set, display the status of the named release with revision
--tls Enable TLS for request --tls Enable TLS for request
--tls-ca-cert string Path to TLS CA certificate file (default "$HELM_HOME/ca.pem") --tls-ca-cert string Path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
@ -49,4 +49,4 @@ helm status [flags] RELEASE_NAME
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019 ###### Auto generated by spf13/cobra on 6-Sep-2019

@ -79,6 +79,7 @@ helm upgrade [RELEASE] [CHART] [flags]
--keyring string Path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg") --keyring string Path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg")
--namespace string Namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace --namespace string Namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace
--no-hooks Disable pre/post upgrade hooks --no-hooks Disable pre/post upgrade hooks
-o, --output string Prints the output in the specified format. Allowed values: table, json, yaml (default "table")
--password string Chart repository password where to locate the requested chart --password string Chart repository password where to locate the requested chart
--recreate-pods Performs pods restart for the resource if applicable --recreate-pods Performs pods restart for the resource if applicable
--render-subchart-notes Render subchart notes along with parent --render-subchart-notes Render subchart notes along with parent
@ -118,4 +119,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes. * [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 16-May-2019 ###### Auto generated by spf13/cobra on 24-Sep-2019

@ -6,6 +6,10 @@ environments.
We are trying to add more details to this document. Please contribute via Pull We are trying to add more details to this document. Please contribute via Pull
Requests if you can. Requests if you can.
## MicroK8s
Helm can be enabled in [MicroK8s](https://microk8s.io) using the command: `microk8s.enable helm`
## MiniKube ## MiniKube
Helm is tested and known to work with [minikube](https://github.com/kubernetes/minikube). Helm is tested and known to work with [minikube](https://github.com/kubernetes/minikube).
@ -31,6 +35,7 @@ Helm works with [Azure Kubernetes Service](https://docs.microsoft.com/en-us/azur
Kubernetes bootstrapped with `kubeadm` is known to work on the following Linux Kubernetes bootstrapped with `kubeadm` is known to work on the following Linux
distributions: distributions:
- Arch Linux
- Ubuntu 16.04 - Ubuntu 16.04
- Fedora release 25 - Fedora release 25
@ -53,3 +58,11 @@ Helm Client and Helm Server (Tiller) are pre-installed with [Platform9 Managed K
Helm (both client and server) has been tested and is working on Mesospheres DC/OS 1.11 Kubernetes platform, and requires Helm (both client and server) has been tested and is working on Mesospheres DC/OS 1.11 Kubernetes platform, and requires
no additional configuration. no additional configuration.
## Kubermatic
Helm works in user clusters that are created by Kubermatic without caveats. Since seed cluster can be setup up in different ways Helm support depends on them.
## KubeOne
Helm works in clusters that are set up by KubeOne without caveats.

14
glide.lock generated

@ -1,5 +1,5 @@
hash: 277f7be1b21149bc06b361fa61c0a2ff81a7f00c59a6e35c21b6516f6ddfd9f7 hash: 13c07a8e64f0777d08cd03d5edba6f254621ec1ee8e3c7b3ef26efc682b643ce
updated: 2019-07-03T21:59:06.87934+02:00 updated: 2019-09-18T12:07:21.888497-04:00
imports: imports:
- name: cloud.google.com/go - name: cloud.google.com/go
version: 0ebda48a7f143b1cce9eb37a8c1106ac762a3430 version: 0ebda48a7f143b1cce9eb37a8c1106ac762a3430
@ -121,7 +121,7 @@ imports:
- name: github.com/google/gofuzz - name: github.com/google/gofuzz
version: 24818f796faf91cd76ec7bddd72458fbced7a6c1 version: 24818f796faf91cd76ec7bddd72458fbced7a6c1
- name: github.com/google/uuid - name: github.com/google/uuid
version: 064e2069ce9c359c118179501254f67d7d37ba24 version: 0cd6bf5da1e1c83f8b45653022c74f71af0538a4
- name: github.com/googleapis/gnostic - name: github.com/googleapis/gnostic
version: 0c5108395e2debce0d731cf0287ddf7242066aba version: 0c5108395e2debce0d731cf0287ddf7242066aba
subpackages: subpackages:
@ -183,11 +183,11 @@ imports:
- name: github.com/Masterminds/goutils - name: github.com/Masterminds/goutils
version: 41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0 version: 41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0
- name: github.com/Masterminds/semver - name: github.com/Masterminds/semver
version: c7af12943936e8c39859482e61f0574c2fd7fc75 version: 805c489aa98f412e79eb308a37996bf9d8b1c91e
- name: github.com/Masterminds/sprig - name: github.com/Masterminds/sprig
version: 258b00ffa7318e8b109a141349980ffbd30a35db version: 2691a9cba2adee8d9a60100a1bc49e770f97b7db
- name: github.com/Masterminds/vcs - name: github.com/Masterminds/vcs
version: 3084677c2c188840777bff30054f2b553729d329 version: f94282d8632a0620f79f0c6ff0e82604e8c5c85b
- name: github.com/mattn/go-runewidth - name: github.com/mattn/go-runewidth
version: d6bea18f789704b5f83375793155289da36a3c7f version: d6bea18f789704b5f83375793155289da36a3c7f
- name: github.com/matttproud/golang_protobuf_extensions - name: github.com/matttproud/golang_protobuf_extensions
@ -247,7 +247,7 @@ imports:
subpackages: subpackages:
- doc - doc
- name: github.com/spf13/pflag - name: github.com/spf13/pflag
version: 298182f68c66c05229eb03ac171abe6e309ee79a version: e8f29969b682c41a730f8f08b76033b120498464
- name: github.com/technosophos/moniker - name: github.com/technosophos/moniker
version: a5dbd03a2245d554160e3ae6bfdcf969fe58b431 version: a5dbd03a2245d554160e3ae6bfdcf969fe58b431
- name: golang.org/x/crypto - name: golang.org/x/crypto

@ -21,8 +21,9 @@ import:
- package: github.com/Masterminds/sprig - package: github.com/Masterminds/sprig
version: ^2.20.0 version: ^2.20.0
- package: github.com/ghodss/yaml - package: github.com/ghodss/yaml
version: c7ce16629ff4cd059ed96ed06419dd3856fd3577
- package: github.com/Masterminds/semver - package: github.com/Masterminds/semver
version: ~1.4.2 version: ^1.4.2
- package: github.com/technosophos/moniker - package: github.com/technosophos/moniker
version: ~0.2 version: ~0.2
- package: github.com/golang/protobuf - package: github.com/golang/protobuf

@ -17,7 +17,7 @@ limitations under the License.
/*Package chartutil contains tools for working with charts. /*Package chartutil contains tools for working with charts.
Charts are described in the protocol buffer definition (pkg/proto/hapi/charts). Charts are described in the protocol buffer definition (pkg/proto/hapi/charts).
This packe provides utilities for serializing and deserializing charts. This package provides utilities for serializing and deserializing charts.
A chart can be represented on the file system in one of two ways: A chart can be represented on the file system in one of two ways:

@ -124,7 +124,7 @@ func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
} }
// ProcessRequirementsConditions disables charts based on condition path value in values // ProcessRequirementsConditions disables charts based on condition path value in values
func ProcessRequirementsConditions(reqs *Requirements, cvals Values) { func ProcessRequirementsConditions(reqs *Requirements, cvals Values, cpath string) {
var cond string var cond string
var conds []string var conds []string
if reqs == nil || len(reqs.Dependencies) == 0 { if reqs == nil || len(reqs.Dependencies) == 0 {
@ -143,7 +143,7 @@ func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
for _, c := range conds { for _, c := range conds {
if len(c) > 0 { if len(c) > 0 {
// retrieve value // retrieve value
vv, err := cvals.PathValue(c) vv, err := cvals.PathValue(cpath + c)
if err == nil { if err == nil {
// if not bool, warn // if not bool, warn
if bv, ok := vv.(bool); ok { if bv, ok := vv.(bool); ok {
@ -247,6 +247,10 @@ func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Ch
// ProcessRequirementsEnabled removes disabled charts from dependencies // ProcessRequirementsEnabled removes disabled charts from dependencies
func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
return doProcessRequirementsEnabled(c, v, "")
}
func doProcessRequirementsEnabled(c *chart.Chart, v *chart.Config, path string) error {
reqs, err := LoadRequirements(c) reqs, err := LoadRequirements(c)
if err != nil { if err != nil {
// if not just missing requirements file, return error // if not just missing requirements file, return error
@ -303,7 +307,7 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
cc := chart.Config{Raw: yvals} cc := chart.Config{Raw: yvals}
// flag dependencies as enabled/disabled // flag dependencies as enabled/disabled
ProcessRequirementsTags(reqs, cvals) ProcessRequirementsTags(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals) ProcessRequirementsConditions(reqs, cvals, path)
// make a map of charts to remove // make a map of charts to remove
rm := map[string]bool{} rm := map[string]bool{}
for _, r := range reqs.Dependencies { for _, r := range reqs.Dependencies {
@ -323,7 +327,8 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error {
} }
// recursively call self to process sub dependencies // recursively call self to process sub dependencies
for _, t := range cd { for _, t := range cd {
err := ProcessRequirementsEnabled(t, &cc) subpath := path + t.Metadata.Name + "."
err := doProcessRequirementsEnabled(t, &cc, subpath)
// if its not just missing requirements file, return error // if its not just missing requirements file, return error
if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil { if nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
return nerr return nerr

@ -158,7 +158,7 @@ func TestRequirementsCombinedDisabledL2(t *testing.T) {
t.Fatalf("Failed to load testdata: %s", err) t.Fatalf("Failed to load testdata: %s", err)
} }
// tags enabling a parent/child group with condition disabling one child // tags enabling a parent/child group with condition disabling one child
v := &chart.Config{Raw: "subchartc:\n enabled: false\ntags:\n back-end: true\n"} v := &chart.Config{Raw: "subchart2:\n subchartc:\n enabled: false\ntags:\n back-end: true\n"}
// expected charts including duplicates in alphanumeric order // expected charts including duplicates in alphanumeric order
e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"} e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}
@ -176,6 +176,15 @@ func TestRequirementsCombinedDisabledL1(t *testing.T) {
verifyRequirementsEnabled(t, c, v, e) verifyRequirementsEnabled(t, c, v, e)
} }
func TestRequirementsAliasCondition(t *testing.T) {
c, err := Load("testdata/subpop")
if err != nil {
t.Fatalf("Failed to load testdata: %s", err)
}
v := &chart.Config{Raw: "subchart1:\n enabled: false\nsubchart2alias:\n enabled: true\n subchartb:\n enabled: true\n"}
e := []string{"parentchart", "subchart2alias", "subchartb"}
verifyRequirementsEnabled(t, c, v, e)
}
func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v *chart.Config, e []string) { func verifyRequirementsEnabled(t *testing.T, c *chart.Chart, v *chart.Config, e []string) {
out := []*chart.Chart{} out := []*chart.Chart{}

@ -2,7 +2,7 @@ dependencies:
- name: subcharta - name: subcharta
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subcharta.enabled,subchart1.subcharta.enabled condition: subcharta.enabled
tags: tags:
- front-end - front-end
- subcharta - subcharta

@ -2,7 +2,7 @@ dependencies:
- name: subchartb - name: subchartb
repository: http://localhost:10191 repository: http://localhost:10191
version: 0.1.0 version: 0.1.0
condition: subchartb.enabled,subchart2.subchartb.enabled condition: subchartb.enabled
tags: tags:
- back-end - back-end
- subchartb - subchartb

@ -29,3 +29,9 @@ dependencies:
tags: tags:
- back-end - back-end
- subchart2 - subchart2
- name: subchart2
alias: subchart2alias
repository: http://localhost:10191
version: 0.1.0
condition: subchart2alias.enabled

@ -39,3 +39,5 @@ tags:
front-end: true front-end: true
back-end: false back-end: false
subchart2alias:
enabled: false

@ -205,6 +205,11 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
} }
c.setCredentials(r) c.setCredentials(r)
// Skip if dependency not contain name
if len(r.Config.Name) == 0 {
return u, r.Client, nil
}
// 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.
i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name)) i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name))
if err != nil { if err != nil {

@ -233,7 +233,7 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
// Any failure to resolve/download a chart should fail: // Any failure to resolve/download a chart should fail:
// https://github.com/kubernetes/helm/issues/1439 // https://github.com/kubernetes/helm/issues/1439
churl, username, password, err := findChartURL(dep.Name, dep.Version, dep.Repository, repos) churl, username, password, err := m.findChartURL(dep.Name, dep.Version, dep.Repository, repos)
if err != nil { if err != nil {
saveError = fmt.Errorf("could not find %s: %s", churl, err) saveError = fmt.Errorf("could not find %s: %s", churl, err)
break break
@ -403,9 +403,17 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string,
} }
} }
if !found { if !found {
missing = append(missing, dd.Repository) repository := dd.Repository
// Add if URL
_, err := url.ParseRequestURI(repository)
if err == nil {
reposMap[repository] = repository
continue
} }
missing = append(missing, repository)
} }
}
if len(missing) > 0 { if len(missing) > 0 {
errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", ")) errorMessage := fmt.Sprintf("no repository definition for %s. Please add them via 'helm repo add'", strings.Join(missing, ", "))
// It is common for people to try to enter "stable" as a repository instead of the actual URL. // It is common for people to try to enter "stable" as a repository instead of the actual URL.
@ -424,6 +432,7 @@ repository, use "https://kubernetes-charts.storage.googleapis.com/" or "@stable"
} }
return nil, errors.New(errorMessage) return nil, errors.New(errorMessage)
} }
return reposMap, nil return reposMap, nil
} }
@ -433,8 +442,7 @@ func (m *Manager) UpdateRepositories() error {
if err != nil { if err != nil {
return err return err
} }
repos := rf.Repositories if repos := rf.Repositories; len(repos) > 0 {
if len(repos) > 0 {
// This prints warnings straight to out. // This prints warnings straight to out.
if err := m.parallelRepoUpdate(repos); err != nil { if err := m.parallelRepoUpdate(repos); err != nil {
return err return err
@ -475,7 +483,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
// repoURL is the repository to search // repoURL is the repository to search
// //
// If it finds a URL that is "relative", it will prepend the repoURL. // If it finds a URL that is "relative", it will prepend the repoURL.
func findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, err error) { func (m *Manager) findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRepository) (url, username, password string, err error) {
for _, cr := range repos { for _, cr := range repos {
if urlutil.Equal(repoURL, cr.Config.URL) { if urlutil.Equal(repoURL, cr.Config.URL) {
var entry repo.ChartVersions var entry repo.ChartVersions
@ -497,6 +505,10 @@ func findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRep
return return
} }
} }
url, err = repo.FindChartInRepoURL(repoURL, name, version, "", "", "", m.Getters)
if err == nil {
return
}
err = fmt.Errorf("chart %s not found in %s", name, repoURL) err = fmt.Errorf("chart %s not found in %s", name, repoURL)
return return
} }
@ -603,7 +615,7 @@ func writeLock(chartpath string, lock *chartutil.RequirementsLock) error {
} }
// tarFromLocalDir archive a dep chart from local directory and save it into charts/ // tarFromLocalDir archive a dep chart from local directory and save it into charts/
func tarFromLocalDir(chartpath string, name string, repo string, version string) (string, error) { func tarFromLocalDir(chartpath, name, repo, version string) (string, error) {
destPath := filepath.Join(chartpath, "charts") destPath := filepath.Join(chartpath, "charts")
if !strings.HasPrefix(repo, "file://") { if !strings.HasPrefix(repo, "file://") {

@ -78,7 +78,7 @@ func TestFindChartURL(t *testing.T) {
version := "0.1.0" version := "0.1.0"
repoURL := "http://example.com/charts" repoURL := "http://example.com/charts"
churl, username, password, err := findChartURL(name, version, repoURL, repos) churl, username, password, err := m.findChartURL(name, version, repoURL, repos)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -106,13 +106,6 @@ func TestGetRepoNames(t *testing.T) {
err bool err bool
expectedErr string expectedErr string
}{ }{
{
name: "no repo definition failure",
req: []*chartutil.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com/test"},
},
err: true,
},
{ {
name: "no repo definition failure -- stable repo", name: "no repo definition failure -- stable repo",
req: []*chartutil.Dependency{ req: []*chartutil.Dependency{
@ -128,6 +121,13 @@ func TestGetRepoNames(t *testing.T) {
err: true, err: true,
expectedErr: "no 'repository' field specified for dependency: \"dependency-missing-repository-field\"", expectedErr: "no 'repository' field specified for dependency: \"dependency-missing-repository-field\"",
}, },
{
name: "dependency repository is url but not exist in repos",
req: []*chartutil.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com/test"},
},
expect: map[string]string{"http://example.com/test": "http://example.com/test"},
},
{ {
name: "no repo definition failure", name: "no repo definition failure",
req: []*chartutil.Dependency{ req: []*chartutil.Dependency{

@ -267,7 +267,7 @@ func (e *Engine) renderWithReferences(tpls map[string]renderable, referenceTpls
rendered = make(map[string]string, len(files)) rendered = make(map[string]string, len(files))
var buf bytes.Buffer var buf bytes.Buffer
for _, file := range files { for _, file := range files {
// Don't render partials. We don't care out the direct output of partials. // Don't render partials. We don't care about the direct output of partials.
// They are only included from other templates. // They are only included from other templates.
if strings.HasPrefix(path.Base(file), "_") { if strings.HasPrefix(path.Base(file), "_") {
continue continue

@ -68,7 +68,7 @@ func (p Providers) ByScheme(scheme string) (Constructor, error) {
} }
// All finds all of the registered getters as a list of Provider instances. // All finds all of the registered getters as a list of Provider instances.
// Currently the build-in http/https getter and the discovered // Currently the built-in http/https getter and the discovered
// plugins with downloader notations are collected. // plugins with downloader notations are collected.
func All(settings environment.EnvSettings) Providers { func All(settings environment.EnvSettings) Providers {
result := Providers{ result := Providers{

@ -412,7 +412,7 @@ entries:
companies. All data is stored in plain text files, so no database is required. companies. All data is stored in plain text files, so no database is required.
digest: 5cfff9542341a391abf9029dd9b42e7c44813c520ef0301ce62e9c08586ceca2 digest: 5cfff9542341a391abf9029dd9b42e7c44813c520ef0301ce62e9c08586ceca2
engine: gotpl engine: gotpl
home: http://www.dokuwiki.org/ home: https://www.dokuwiki.org/
icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png
keywords: keywords:
- dokuwiki - dokuwiki
@ -433,7 +433,7 @@ entries:
companies. All data is stored in plain text files, so no database is required. companies. All data is stored in plain text files, so no database is required.
digest: 3c46f9d9196bbf975711b2bb7c889fd3df1976cc57c3c120c7374d721da0e240 digest: 3c46f9d9196bbf975711b2bb7c889fd3df1976cc57c3c120c7374d721da0e240
engine: gotpl engine: gotpl
home: http://www.dokuwiki.org/ home: https://www.dokuwiki.org/
icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png
keywords: keywords:
- dokuwiki - dokuwiki
@ -454,7 +454,7 @@ entries:
companies. All data is stored in plain text files, so no database is required. companies. All data is stored in plain text files, so no database is required.
digest: f533bc20e08179a49cca77b175f897087dc3f2c57e6c89ecbd7264ab5975d66a digest: f533bc20e08179a49cca77b175f897087dc3f2c57e6c89ecbd7264ab5975d66a
engine: gotpl engine: gotpl
home: http://www.dokuwiki.org/ home: https://www.dokuwiki.org/
icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png
keywords: keywords:
- dokuwiki - dokuwiki
@ -475,7 +475,7 @@ entries:
companies. All data is stored in plain text files, so no database is required. companies. All data is stored in plain text files, so no database is required.
digest: 34a926398cfafbf426ff468167ef49577252e260ebce5df33380e6e67b79fe59 digest: 34a926398cfafbf426ff468167ef49577252e260ebce5df33380e6e67b79fe59
engine: gotpl engine: gotpl
home: http://www.dokuwiki.org/ home: https://www.dokuwiki.org/
icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png
keywords: keywords:
- dokuwiki - dokuwiki
@ -496,7 +496,7 @@ entries:
companies. All data is stored in plain text files, so no database is required. companies. All data is stored in plain text files, so no database is required.
digest: 6825fbacb709cf05901985561be10ba9473a379488d99b71d1590d33f5a81374 digest: 6825fbacb709cf05901985561be10ba9473a379488d99b71d1590d33f5a81374
engine: gotpl engine: gotpl
home: http://www.dokuwiki.org/ home: https://www.dokuwiki.org/
icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png
keywords: keywords:
- dokuwiki - dokuwiki
@ -516,7 +516,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: db95c255b19164c5051eb75a6860f3775a1011399a62b27e474cd9ebee0cb578 digest: db95c255b19164c5051eb75a6860f3775a1011399a62b27e474cd9ebee0cb578
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -539,7 +539,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 84c13154a9aeb7215dc0d98e9825207207e69ca870f3d54b273bfc2d34699f61 digest: 84c13154a9aeb7215dc0d98e9825207207e69ca870f3d54b273bfc2d34699f61
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -562,7 +562,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 17d0bfdcdf5a1a650941343c76b6b928d76d3332fece127c502e91f9597f419e digest: 17d0bfdcdf5a1a650941343c76b6b928d76d3332fece127c502e91f9597f419e
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -585,7 +585,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 317674c89762e0b54156b734203ee93638dd7a25df35120c5cab45546814d89b digest: 317674c89762e0b54156b734203ee93638dd7a25df35120c5cab45546814d89b
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -608,7 +608,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 24c4f187b50c0e961cc2cacf6e6b2ce6d6b225c73637c578e001bebd9b3f5d48 digest: 24c4f187b50c0e961cc2cacf6e6b2ce6d6b225c73637c578e001bebd9b3f5d48
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -631,7 +631,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 7fcea4684a3d520454aeaa10beb9f9b1789c09c097680fc484954084f283feb3 digest: 7fcea4684a3d520454aeaa10beb9f9b1789c09c097680fc484954084f283feb3
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -654,7 +654,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: adb23bc71125b9691b407a47dadf4298de3516805218813b56067967e39db7d8 digest: adb23bc71125b9691b407a47dadf4298de3516805218813b56067967e39db7d8
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png
keywords: keywords:
- drupal - drupal
@ -677,7 +677,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 5de529e25767e8a37b8d6f413daa0fe99f5c304e48ddcfa8adb4d8c7a0496aa7 digest: 5de529e25767e8a37b8d6f413daa0fe99f5c304e48ddcfa8adb4d8c7a0496aa7
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -699,7 +699,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: a35dbf9d470972cc2461de3e0a8fcf2fec8d0adc04f5a0f1e924505f22c714d7 digest: a35dbf9d470972cc2461de3e0a8fcf2fec8d0adc04f5a0f1e924505f22c714d7
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -721,7 +721,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: a62d686d6bd47643dfa489e395dda89286954f785123a43a88db7ef34f3ea48d digest: a62d686d6bd47643dfa489e395dda89286954f785123a43a88db7ef34f3ea48d
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -743,7 +743,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 2c189424bda94eeebb7e6370e96884f09bdfa81498cb93ac4723d24c92a3938e digest: 2c189424bda94eeebb7e6370e96884f09bdfa81498cb93ac4723d24c92a3938e
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -765,7 +765,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 3596f47c5dcaa7a975d1c4cb7bf7ef6790c9ad8dda41a5a329e30c1ea8a40d11 digest: 3596f47c5dcaa7a975d1c4cb7bf7ef6790c9ad8dda41a5a329e30c1ea8a40d11
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -787,7 +787,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 78b2bb3717be63dccb02ea06b711ca7cf7869848b296b880099c6264e86d86d3 digest: 78b2bb3717be63dccb02ea06b711ca7cf7869848b296b880099c6264e86d86d3
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -809,7 +809,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 5508b29e20a5d609f76319869774f008dcc4bed13bbbc7ed40546bc9af8c7cd7 digest: 5508b29e20a5d609f76319869774f008dcc4bed13bbbc7ed40546bc9af8c7cd7
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -831,7 +831,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 023a282c93f8411fb81bb4fff7820c1337aad0586ccf7dae55bdbed515ad8b05 digest: 023a282c93f8411fb81bb4fff7820c1337aad0586ccf7dae55bdbed515ad8b05
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -853,7 +853,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 9bdaa53f7a9e82c9b32c7ac9b34b84fd142671732a54423a2dcdc893c4162801 digest: 9bdaa53f7a9e82c9b32c7ac9b34b84fd142671732a54423a2dcdc893c4162801
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -875,7 +875,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 25650526abc1036398dbb314d77a0062cbb644b2c5791a258fb863fdaad5093d digest: 25650526abc1036398dbb314d77a0062cbb644b2c5791a258fb863fdaad5093d
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -897,7 +897,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: 13d5d32d316c08359221d230004dd2adc0152362e87abcc0d61ea191241fa69f digest: 13d5d32d316c08359221d230004dd2adc0152362e87abcc0d61ea191241fa69f
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -919,7 +919,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: b3f09ecd191f8c06275c96d9af4d77a97c94355525864201e9baf151b17bd5a7 digest: b3f09ecd191f8c06275c96d9af4d77a97c94355525864201e9baf151b17bd5a7
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -941,7 +941,7 @@ entries:
description: One of the most versatile open source content management systems. description: One of the most versatile open source content management systems.
digest: c56fc55b93b0dead65af7b81bbd54befd5115860698ca475baa5acb178a12e5a digest: c56fc55b93b0dead65af7b81bbd54befd5115860698ca475baa5acb178a12e5a
engine: gotpl engine: gotpl
home: http://www.drupal.org/ home: https://www.drupal.org/
keywords: keywords:
- drupal - drupal
- cms - cms
@ -1154,7 +1154,7 @@ entries:
stories with the world stories with the world
digest: 91d195c99e00b2801eafef5c23fcf9ced218bb29c7097f08139e2bdc80e38a0f digest: 91d195c99e00b2801eafef5c23fcf9ced218bb29c7097f08139e2bdc80e38a0f
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png
keywords: keywords:
- ghost - ghost
@ -1178,7 +1178,7 @@ entries:
stories with the world stories with the world
digest: 6342a95aeef40690430c2e80b167fbb116a632746cdca49cfac4cbd535eadb22 digest: 6342a95aeef40690430c2e80b167fbb116a632746cdca49cfac4cbd535eadb22
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png
keywords: keywords:
- ghost - ghost
@ -1202,7 +1202,7 @@ entries:
stories with the world stories with the world
digest: 8998a9a4e75e777edb6f06c05b45d461daebba09021161af3bef523efd193b15 digest: 8998a9a4e75e777edb6f06c05b45d461daebba09021161af3bef523efd193b15
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png
keywords: keywords:
- ghost - ghost
@ -1226,7 +1226,7 @@ entries:
stories with the world stories with the world
digest: e44c9a53355086ded1832f769dca515b863337ad118ba618ef97f37b3ef84030 digest: e44c9a53355086ded1832f769dca515b863337ad118ba618ef97f37b3ef84030
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png
keywords: keywords:
- ghost - ghost
@ -1250,7 +1250,7 @@ entries:
stories with the world stories with the world
digest: b0c94a93c88fde68bb4fc78e92691d46cd2eb4d32cbac011e034565fbd35d46b digest: b0c94a93c88fde68bb4fc78e92691d46cd2eb4d32cbac011e034565fbd35d46b
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
keywords: keywords:
- ghost - ghost
- blog - blog
@ -1273,7 +1273,7 @@ entries:
stories with the world stories with the world
digest: 791ccb42b62d56d50c72b37db3282eb3f2af75d667a25542d76c7991004eb822 digest: 791ccb42b62d56d50c72b37db3282eb3f2af75d667a25542d76c7991004eb822
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
keywords: keywords:
- ghost - ghost
- blog - blog
@ -1296,7 +1296,7 @@ entries:
stories with the world stories with the world
digest: 331a2b145bfdb39b626313cda7dc539f32dbda5149893957589c5406317fca53 digest: 331a2b145bfdb39b626313cda7dc539f32dbda5149893957589c5406317fca53
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
keywords: keywords:
- ghost - ghost
- blog - blog
@ -1319,7 +1319,7 @@ entries:
stories with the world stories with the world
digest: 804227af037082a0f5c3c579feb9e24eb3682449e78876971c93a109bc716f40 digest: 804227af037082a0f5c3c579feb9e24eb3682449e78876971c93a109bc716f40
engine: gotpl engine: gotpl
home: http://www.ghost.org/ home: https://www.ghost.org/
keywords: keywords:
- ghost - ghost
- blog - blog
@ -1758,7 +1758,7 @@ entries:
visualization, and a dashboard feature for compiling multiple custom views visualization, and a dashboard feature for compiling multiple custom views
digest: 8a649026f55b2fa1e743c93fd331e127e66b49c4d7f20116a2bb06e5937f4828 digest: 8a649026f55b2fa1e743c93fd331e127e66b49c4d7f20116a2bb06e5937f4828
engine: gotpl engine: gotpl
home: http://community.jaspersoft.com/project/jasperreports-server home: https://community.jaspersoft.com/project/jasperreports-server
icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png
keywords: keywords:
- business intelligence - business intelligence
@ -1782,7 +1782,7 @@ entries:
visualization, and a dashboard feature for compiling multiple custom views visualization, and a dashboard feature for compiling multiple custom views
digest: d4a62f7ace55256852e5c650a56ccf671633c4f223180d304cfb03b9cd7993aa digest: d4a62f7ace55256852e5c650a56ccf671633c4f223180d304cfb03b9cd7993aa
engine: gotpl engine: gotpl
home: http://community.jaspersoft.com/project/jasperreports-server home: https://community.jaspersoft.com/project/jasperreports-server
icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png
keywords: keywords:
- business intelligence - business intelligence
@ -1806,7 +1806,7 @@ entries:
visualization, and a dashboard feature for compiling multiple custom views visualization, and a dashboard feature for compiling multiple custom views
digest: 99af0fca7ef1c475b239f2c8fc2dee6b040ea76b3c30bba1431f358df873aa49 digest: 99af0fca7ef1c475b239f2c8fc2dee6b040ea76b3c30bba1431f358df873aa49
engine: gotpl engine: gotpl
home: http://community.jaspersoft.com/project/jasperreports-server home: https://community.jaspersoft.com/project/jasperreports-server
icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png
keywords: keywords:
- business intelligence - business intelligence
@ -1830,7 +1830,7 @@ entries:
visualization, and a dashboard feature for compiling multiple custom views visualization, and a dashboard feature for compiling multiple custom views
digest: f01e53d1b89c4fb1fcd9702cd5f4e48d77607aed65f249e1f94b8b21f7eef3f4 digest: f01e53d1b89c4fb1fcd9702cd5f4e48d77607aed65f249e1f94b8b21f7eef3f4
engine: gotpl engine: gotpl
home: http://community.jaspersoft.com/project/jasperreports-server home: https://community.jaspersoft.com/project/jasperreports-server
icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png
keywords: keywords:
- business intelligence - business intelligence
@ -1854,7 +1854,7 @@ entries:
visualization, and a dashboard feature for compiling multiple custom views visualization, and a dashboard feature for compiling multiple custom views
digest: 5cc4af8c88691d7030602c97be2ccbc125ef11129b361da0aa236a127c31b965 digest: 5cc4af8c88691d7030602c97be2ccbc125ef11129b361da0aa236a127c31b965
engine: gotpl engine: gotpl
home: http://community.jaspersoft.com/project/jasperreports-server home: https://community.jaspersoft.com/project/jasperreports-server
icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png
keywords: keywords:
- business intelligence - business intelligence
@ -2265,7 +2265,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: 6f9934487533f325515f4877b3af1306c87d64bf3ece9d4bd875289cd73b340d digest: 6f9934487533f325515f4877b3af1306c87d64bf3ece9d4bd875289cd73b340d
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png
keywords: keywords:
- joomla - joomla
@ -2286,7 +2286,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: f9dedab2fc2dbf170cf45b2c230baa6d20aad9a6f8ccfcb09c459602fc5213dc digest: f9dedab2fc2dbf170cf45b2c230baa6d20aad9a6f8ccfcb09c459602fc5213dc
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png
keywords: keywords:
- joomla - joomla
@ -2307,7 +2307,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: 1e067e459873ae832d54ff516a3420f7f0e16ecd8f72f4c4f02be22e47702077 digest: 1e067e459873ae832d54ff516a3420f7f0e16ecd8f72f4c4f02be22e47702077
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png
keywords: keywords:
- joomla - joomla
@ -2328,7 +2328,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: 9a99b15e83e18955eb364985cd545659f1176ef203ac730876dfe39499edfb18 digest: 9a99b15e83e18955eb364985cd545659f1176ef203ac730876dfe39499edfb18
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png
keywords: keywords:
- joomla - joomla
@ -2349,7 +2349,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: 07c3a16eb674ffc74fe5b2b16191b8bb24c63bdae9bce9710bda1999920c46fc digest: 07c3a16eb674ffc74fe5b2b16191b8bb24c63bdae9bce9710bda1999920c46fc
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
keywords: keywords:
- joomla - joomla
- cms - cms
@ -2369,7 +2369,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: 95fbe272015941544609eee90b3bffd5172bfdec10be13636510caa8478a879e digest: 95fbe272015941544609eee90b3bffd5172bfdec10be13636510caa8478a879e
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
keywords: keywords:
- joomla - joomla
- cms - cms
@ -2389,7 +2389,7 @@ entries:
description: PHP content management system (CMS) for publishing web content description: PHP content management system (CMS) for publishing web content
digest: 0a01ea051ec15274932c8d82076c1a9fd62584b0fb916a81372319bef223c20e digest: 0a01ea051ec15274932c8d82076c1a9fd62584b0fb916a81372319bef223c20e
engine: gotpl engine: gotpl
home: http://www.joomla.org/ home: https://www.joomla.org/
keywords: keywords:
- joomla - joomla
- cms - cms
@ -2771,7 +2771,7 @@ entries:
- created: 2017-04-28T00:18:30.087097677Z - created: 2017-04-28T00:18:30.087097677Z
description: A modern load testing framework description: A modern load testing framework
digest: eb91b0e3c0b618cf5ad0f24d2685fe4086bc6f497685e58ad8a64032c4e82b7a digest: eb91b0e3c0b618cf5ad0f24d2685fe4086bc6f497685e58ad8a64032c4e82b7a
home: http://locust.io home: https://locust.io
icon: https://pbs.twimg.com/profile_images/1867636195/locust-logo-orignal.png icon: https://pbs.twimg.com/profile_images/1867636195/locust-logo-orignal.png
maintainers: maintainers:
- email: vincent.drl@gmail.com - email: vincent.drl@gmail.com
@ -3351,7 +3351,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: 0e51822c5547895109a5b41ce426c77f62d0434b40f3021afee8471ab976a6f5 digest: 0e51822c5547895109a5b41ce426c77f62d0434b40f3021afee8471ab976a6f5
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png
keywords: keywords:
- mediawiki - mediawiki
@ -3372,7 +3372,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: 0e419c2c5d87997f94a32da6597af3f3b52120dc1ec682dcbb6b238fb4825e06 digest: 0e419c2c5d87997f94a32da6597af3f3b52120dc1ec682dcbb6b238fb4825e06
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png
keywords: keywords:
- mediawiki - mediawiki
@ -3393,7 +3393,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: 6f4dde26737f7f1aa63ffda6c259ce388e3a3509225f90f334bfc3f0f7617bc1 digest: 6f4dde26737f7f1aa63ffda6c259ce388e3a3509225f90f334bfc3f0f7617bc1
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png
keywords: keywords:
- mediawiki - mediawiki
@ -3414,7 +3414,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: 0ba52b8c4c9e0bee3eb76fe625d2dc88729a1cdf41ace9d13cd4abc5b477cfb8 digest: 0ba52b8c4c9e0bee3eb76fe625d2dc88729a1cdf41ace9d13cd4abc5b477cfb8
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
keywords: keywords:
- mediawiki - mediawiki
- wiki - wiki
@ -3434,7 +3434,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: f49df3e17f97b238743aad0376eb9db7e4a9bca3829a3a65d7bbb349344a73be digest: f49df3e17f97b238743aad0376eb9db7e4a9bca3829a3a65d7bbb349344a73be
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
keywords: keywords:
- mediawiki - mediawiki
- wiki - wiki
@ -3454,7 +3454,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: 339a90050d5cf4216140409349a356aa7cd8dc95e2cbdca06e4fdd11e87aa963 digest: 339a90050d5cf4216140409349a356aa7cd8dc95e2cbdca06e4fdd11e87aa963
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
keywords: keywords:
- mediawiki - mediawiki
- wiki - wiki
@ -3474,7 +3474,7 @@ entries:
that uses PHP to process and display data stored in a database. that uses PHP to process and display data stored in a database.
digest: 9617f13f51f5bb016a072f2a026c627420721a1c5b7cd22f32d6cd0c90f34eda digest: 9617f13f51f5bb016a072f2a026c627420721a1c5b7cd22f32d6cd0c90f34eda
engine: gotpl engine: gotpl
home: http://www.mediawiki.org/ home: https://www.mediawiki.org/
keywords: keywords:
- mediawiki - mediawiki
- wiki - wiki
@ -3495,7 +3495,7 @@ entries:
system. system.
digest: 36ceb2767094598171b2851ecda54bd43d862b9b81aa4b294f3d8c8d59ddd79c digest: 36ceb2767094598171b2851ecda54bd43d862b9b81aa4b294f3d8c8d59ddd79c
engine: gotpl engine: gotpl
home: http://memcached.org/ home: https://memcached.org/
icon: https://upload.wikimedia.org/wikipedia/en/thumb/2/27/Memcached.svg/1024px-Memcached.svg.png icon: https://upload.wikimedia.org/wikipedia/en/thumb/2/27/Memcached.svg/1024px-Memcached.svg.png
keywords: keywords:
- memcached - memcached
@ -3513,7 +3513,7 @@ entries:
description: Chart for Memcached description: Chart for Memcached
digest: 2b918dd8129a9d706e58b3de459004e3367c05a162d3e3cdb031cb6818d5f820 digest: 2b918dd8129a9d706e58b3de459004e3367c05a162d3e3cdb031cb6818d5f820
engine: gotpl engine: gotpl
home: http://memcached.org/ home: https://memcached.org/
keywords: keywords:
- memcached - memcached
- cache - cache
@ -3911,7 +3911,7 @@ entries:
learning environments learning environments
digest: 386bff8ce61cf61961daf8ed6d68a76cd3a360560a08c1fca80bcbd897f11270 digest: 386bff8ce61cf61961daf8ed6d68a76cd3a360560a08c1fca80bcbd897f11270
engine: gotpl engine: gotpl
home: http://www.moodle.org/ home: https://www.moodle.org/
icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png
keywords: keywords:
- moodle - moodle
@ -3932,7 +3932,7 @@ entries:
learning environments learning environments
digest: bd85420a7cefd82e9d96088591601f832ecc60016d6389dbcde51a2050327a66 digest: bd85420a7cefd82e9d96088591601f832ecc60016d6389dbcde51a2050327a66
engine: gotpl engine: gotpl
home: http://www.moodle.org/ home: https://www.moodle.org/
icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png
keywords: keywords:
- moodle - moodle
@ -3953,7 +3953,7 @@ entries:
learning environments learning environments
digest: 8656c544a71fa8cc4ac23380e999e072740ec8e481a22aff86517d8362e70121 digest: 8656c544a71fa8cc4ac23380e999e072740ec8e481a22aff86517d8362e70121
engine: gotpl engine: gotpl
home: http://www.moodle.org/ home: https://www.moodle.org/
icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png
keywords: keywords:
- moodle - moodle

@ -71,6 +71,10 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
if err != nil { if err != nil {
return false, err return false, err
} }
// If paused deployment will never be ready
if currentDeployment.Spec.Paused {
continue
}
// Find RS associated with deployment // Find RS associated with deployment
newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
if err != nil || newReplicaSet == nil { if err != nil || newReplicaSet == nil {
@ -86,6 +90,10 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
if err != nil { if err != nil {
return false, err return false, err
} }
// If paused deployment will never be ready
if currentDeployment.Spec.Paused {
continue
}
// Find RS associated with deployment // Find RS associated with deployment
newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
if err != nil || newReplicaSet == nil { if err != nil || newReplicaSet == nil {
@ -101,6 +109,10 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
if err != nil { if err != nil {
return false, err return false, err
} }
// If paused deployment will never be ready
if currentDeployment.Spec.Paused {
continue
}
// Find RS associated with deployment // Find RS associated with deployment
newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
if err != nil || newReplicaSet == nil { if err != nil || newReplicaSet == nil {
@ -116,6 +128,10 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error {
if err != nil { if err != nil {
return false, err return false, err
} }
// If paused deployment will never be ready
if currentDeployment.Spec.Paused {
continue
}
// Find RS associated with deployment // Find RS associated with deployment
newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1()) newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, kcs.AppsV1())
if err != nil || newReplicaSet == nil { if err != nil || newReplicaSet == nil {

@ -55,7 +55,7 @@ func TestBadChart(t *testing.T) {
} }
} }
if msg.Severity == support.ErrorSev { if msg.Severity == support.ErrorSev {
if strings.Contains(msg.Err.Error(), "version 0.0.0 is less than or equal to 0") { if strings.Contains(msg.Err.Error(), "version '0.0.0.0' is not a valid SemVer") {
e = true e = true
} }
if strings.Contains(msg.Err.Error(), "name is required") { if strings.Contains(msg.Err.Error(), "name is required") {

@ -120,7 +120,7 @@ func validateChartVersion(cf *chart.Metadata) error {
return fmt.Errorf("version '%s' is not a valid SemVer", cf.Version) return fmt.Errorf("version '%s' is not a valid SemVer", cf.Version)
} }
c, err := semver.NewConstraint("> 0") c, err := semver.NewConstraint(">0.0.0-0")
if err != nil { if err != nil {
return err return err
} }

@ -106,7 +106,7 @@ func TestValidateChartVersion(t *testing.T) {
ErrorMsg string ErrorMsg string
}{ }{
{"", "version is required"}, {"", "version is required"},
{"0", "0 is less than or equal to 0"}, {"1.2.3.4", "version '1.2.3.4' is not a valid SemVer"},
{"waps", "'waps' is not a valid SemVer"}, {"waps", "'waps' is not a valid SemVer"},
{"-3", "'-3' is not a valid SemVer"}, {"-3", "'-3' is not a valid SemVer"},
} }
@ -252,7 +252,7 @@ func TestChartfile(t *testing.T) {
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 is less than or equal to 0") { if !strings.Contains(msgs[3].Err.Error(), "version '0.0.0.0' is not a valid SemVer") {
t.Errorf("Unexpected message 3: %s", msgs[2].Err) t.Errorf("Unexpected message 3: %s", msgs[2].Err)
} }

@ -1,3 +1,3 @@
description: A Helm chart for Kubernetes description: A Helm chart for Kubernetes
version: 0.0.0 version: 0.0.0.0
home: "" home: ""

@ -122,7 +122,7 @@ func NewFromKeyring(keyringfile, id string) (*Signatory, error) {
return s, nil return s, nil
} }
// We're gonna go all GnuPG on this and look for a string that _contains_. If // We're going to go all GnuPG on this and look for a string that _contains_. If
// two or more keys contain the string and none are a direct match, we error // two or more keys contain the string and none are a direct match, we error
// out. // out.
var candidate *openpgp.Entity var candidate *openpgp.Entity

@ -54,6 +54,6 @@ func TestFilterAll(t *testing.T) {
case r0.Version == 4: case r0.Version == 4:
t.Fatal("got release with status revision 4") t.Fatal("got release with status revision 4")
case r0.Info.Status.Code == rspb.Status_DELETED: case r0.Info.Status.Code == rspb.Status_DELETED:
t.Fatal("got release with status DELTED") t.Fatal("got release with status DELETED")
} }
} }

@ -117,12 +117,18 @@ func (r *RepoFile) Update(re ...*Entry) {
// Has returns true if the given name is already a repository name. // Has returns true if the given name is already a repository name.
func (r *RepoFile) Has(name string) bool { func (r *RepoFile) Has(name string) bool {
for _, rf := range r.Repositories { _, ok := r.Get(name)
if rf.Name == name { return ok
return true }
// Get returns entry by the given name if it exists.
func (r *RepoFile) Get(name string) (*Entry, bool) {
for _, entry := range r.Repositories {
if entry.Name == name {
return entry, true
} }
} }
return false return nil, false
} }
// Remove removes the entry from the list of repositories. // Remove removes the entry from the list of repositories.

@ -16,10 +16,12 @@ limitations under the License.
package repo package repo
import "testing" import (
import "io/ioutil" "io/ioutil"
import "os" "os"
import "strings" "strings"
"testing"
)
const testRepositoriesFile = "testdata/repositories.yaml" const testRepositoriesFile = "testdata/repositories.yaml"
@ -120,6 +122,43 @@ func TestNewPreV1RepositoriesFile(t *testing.T) {
} }
} }
func TestRepoFile_Get(t *testing.T) {
repo := NewRepoFile()
repo.Add(
&Entry{
Name: "first",
URL: "https://example.com/first",
Cache: "first-index.yaml",
},
&Entry{
Name: "second",
URL: "https://example.com/second",
Cache: "second-index.yaml",
},
&Entry{
Name: "third",
URL: "https://example.com/third",
Cache: "third-index.yaml",
},
&Entry{
Name: "fourth",
URL: "https://example.com/fourth",
Cache: "fourth-index.yaml",
},
)
name := "second"
entry, ok := repo.Get(name)
if !ok {
t.Fatalf("Expected repo entry %q to be found", name)
}
if entry.URL != "https://example.com/second" {
t.Fatalf("Expected repo URL to be %q but got %q", "https://example.com/second", entry.URL)
}
}
func TestRemoveRepository(t *testing.T) { func TestRemoveRepository(t *testing.T) {
sampleRepository := NewRepoFile() sampleRepository := NewRepoFile()
sampleRepository.Add( sampleRepository.Add(

@ -71,7 +71,18 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st
return nil, fmt.Errorf("dependency %q has an invalid version/constraint format: %s", d.Name, err) return nil, fmt.Errorf("dependency %q has an invalid version/constraint format: %s", d.Name, err)
} }
repoIndex, err := repo.LoadIndexFile(r.helmhome.CacheIndex(repoNames[d.Name])) // repo does not exist in cache but has url info
cacheRepoName := repoNames[d.Name]
if cacheRepoName == "" && d.Repository != "" {
locked[i] = &chartutil.Dependency{
Name: d.Name,
Repository: d.Repository,
Version: d.Version,
}
continue
}
repoIndex, err := repo.LoadIndexFile(r.helmhome.CacheIndex(cacheRepoName))
if err != nil { if err != nil {
return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err) return nil, fmt.Errorf("no cached repo found. (try 'helm repo update'). %s", err)
} }

@ -37,15 +37,6 @@ func TestResolve(t *testing.T) {
}, },
err: true, err: true,
}, },
{
name: "cache index failure",
req: &chartutil.Requirements{
Dependencies: []*chartutil.Dependency{
{Name: "oedipus-rex", Repository: "http://example.com", Version: "1.0.0"},
},
},
err: true,
},
{ {
name: "chart not found failure", name: "chart not found failure",
req: &chartutil.Requirements{ req: &chartutil.Requirements{

@ -502,7 +502,7 @@ func TestInstallRelease_KubeVersion(t *testing.T) {
rs := rsFixture() rs := rsFixture()
req := installRequest( req := installRequest(
withChart(withKube(">=0.0.0")), withChart(withKube(">=0.0.0-0")),
) )
_, err := rs.InstallRelease(c, req) _, err := rs.InstallRelease(c, req)
if err != nil { if err != nil {

@ -94,7 +94,7 @@ checkDesiredVersion() {
# if it needs to be changed. # if it needs to be changed.
checkHelmInstalledVersion() { checkHelmInstalledVersion() {
if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then
local version=$(helm version -c | grep '^Client' | cut -d'"' -f2) local version=$("${HELM_INSTALL_DIR}/${PROJECT_NAME}" version -c | grep '^Client' | cut -d'"' -f2)
if [[ "$version" == "$TAG" ]]; then if [[ "$version" == "$TAG" ]]; then
echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
return 0 return 0

Loading…
Cancel
Save