Merge remote-tracking branch 'upstream/master'

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

@ -126,13 +126,6 @@ __helm_compgen() {
__helm_compopt() {
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()
{
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
@ -194,7 +187,7 @@ autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then
if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then
LWORD='\<'
RWORD='\>'
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}compgen${RWORD}/__helm_compgen/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/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \
-e 's/FUNCNAME/funcstack/g' \

@ -106,7 +106,7 @@ func (c *createCmd) run() error {
if c.starter != "" {
// Create from the 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) {
lstarter = c.starter
}

@ -41,7 +41,7 @@ import (
const (
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()
{
local ${__helm_override_flag_list[*]##*-} two_word_of of var
@ -69,13 +69,46 @@ __helm_override_flags()
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_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out filter
# Use ^ to map from the start of the release name
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" ) )
fi
}
@ -89,6 +122,14 @@ __helm_custom_func()
__helm_list_releases
return
;;
helm_repo_remove | helm_repo_update)
__helm_list_repos
return
;;
helm_plugin_remove | helm_plugin_update)
__helm_list_plugins
return
;;
*)
;;
esac

@ -282,7 +282,7 @@ func (i *initCmd) run() error {
if err := i.ping(i.opts.SelectImage()); err != nil {
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 {
debug("The error received while trying to init: %s", err)
fmt.Fprintln(i.out, "Warning: Tiller is already installed in the cluster.\n"+

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

@ -143,6 +143,7 @@ type installCmd struct {
certFile string
keyFile string
caFile string
output 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.subNotes, "render-subchart-notes", false, "Render subchart notes along with the parent")
f.StringVar(&inst.description, "description", "", "Specify a description for the release")
bindOutputFlag(cmd, &inst.output)
// set defaults from environment
settings.InitTLS(f)
@ -335,7 +337,10 @@ func (i *installCmd) run() error {
if rel == nil {
return nil
}
if outputFormat(i.output) == outputTable {
i.printRelease(rel)
}
// If this is a dry run, we can't display status.
if i.dryRun {
@ -351,8 +356,8 @@ func (i *installCmd) run() error {
if err != nil {
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

@ -191,6 +191,22 @@ func TestInstall(t *testing.T) {
flags: []string{"--name-template", "{{UPPER \"foobar\"}}"},
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 {

@ -17,23 +17,22 @@ limitations under the License.
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"errors"
"fmt"
"io/ioutil"
"strings"
"github.com/Masterminds/semver"
"github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
appsv1client "k8s.io/client-go/kubernetes/typed/apps/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/tiller/environment"
@ -43,7 +42,7 @@ import (
//
// Returns an error if the command failed.
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
}
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.
func Upgrade(client kubernetes.Interface, opts *Options) error {
obj, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err != nil {
appsobj, err := client.AppsV1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
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
}
tillerImage := obj.Spec.Template.Spec.Containers[0].Image
if semverCompare(tillerImage) == -1 && !opts.ForceUpgrade {
return errors.New("current Tiller version is newer, use --force-upgrade to downgrade")
if _, err := client.AppsV1().Deployments(opts.Namespace).Update(obj); err != nil {
return err
}
obj.Spec.Template.Spec.Containers[0].Image = opts.SelectImage()
obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy()
obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount
// 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.
_, 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 {
return err
}
// 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.
_, err = client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
_, err := client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return createService(client.CoreV1(), opts.Namespace)
}
return err
}
// semverCompare returns whether the client's version is older, equal or newer than the given image's version.
func semverCompare(image string) int {
split := strings.Split(image, ":")
if len(split) < 2 {
// If we don't know the version, we consider the client version newer.
return 1
func updatePodTemplate(podSpec *v1.PodSpec, opts *Options) error {
tillerImage := podSpec.Containers[0].Image
clientImage := opts.SelectImage()
if semverCompare(tillerImage, clientImage) == -1 && !opts.ForceUpgrade {
return fmt.Errorf("current Tiller version %s is newer than client version %s, use --force-upgrade to downgrade", tillerImage, clientImage)
}
tillerVersion, err := semver.NewVersion(split[1])
podSpec.Containers[0].Image = clientImage
podSpec.Containers[0].ImagePullPolicy = opts.pullPolicy()
podSpec.ServiceAccountName = opts.ServiceAccount
return nil
}
// 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 {
// same thing with unparsable tiller versions (e.g. canary releases).
return 1
}
clientVersion, err := semver.NewVersion(version.Version)
// clientVersion, err := semver.NewVersion(currentVersion)
clientVersion, err := string2semver(clientImage)
if err != nil {
// aaaaaand same thing with unparsable helm versions (e.g. canary releases).
return 1
}
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.
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
func createDeployment(client appsv1client.DeploymentsGetter, opts *Options) error {
obj, err := generateDeployment(opts)
if err != nil {
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
// as a string. This object should not be submitted directly to the Kubernetes
// api
func Deployment(opts *Options) (*v1beta1.Deployment, error) {
func Deployment(opts *Options) (*appsv1.Deployment, error) {
dep, err := generateDeployment(opts)
if err != nil {
return nil, err
@ -197,7 +253,7 @@ func parseNodeSelectorsInto(labels string, m map[string]string) error {
}
return nil
}
func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
func generateDeployment(opts *Options) (*appsv1.Deployment, error) {
labels := generateLabels(map[string]string{"name": "tiller"})
nodeSelectors := map[string]string{}
if len(opts.NodeSelectors) > 0 {
@ -206,14 +262,17 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
return nil, err
}
}
d := &v1beta1.Deployment{
d := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: opts.Namespace,
Name: deploymentName,
Labels: labels,
},
Spec: v1beta1.DeploymentSpec{
Spec: appsv1.DeploymentSpec{
Replicas: opts.getReplicas(),
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
@ -297,7 +356,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
// merge them and convert back to Deployment
if len(opts.Values) > 0 {
// base deployment struct
var dd v1beta1.Deployment
var dd appsv1.Deployment
// get YAML from original deployment
dy, err := yaml.Marshal(d)
if err != nil {

@ -24,8 +24,8 @@ import (
"testing"
"github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
@ -192,7 +192,7 @@ func TestInstall(t *testing.T) {
fc := &fake.Clientset{}
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()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l)
@ -239,7 +239,7 @@ func TestInstallHA(t *testing.T) {
fc := &fake.Clientset{}
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
if int(*replicas) != 2 {
t.Errorf("expected replicas = 2, got '%d'", replicas)
@ -263,7 +263,7 @@ func TestInstall_WithTLS(t *testing.T) {
fc := &fake.Clientset{}
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()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l)
@ -331,7 +331,7 @@ func TestInstall_WithTLS(t *testing.T) {
func TestInstall_canary(t *testing.T) {
fc := &fake.Clientset{}
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
if i != "gcr.io/kubernetes-helm/tiller:canary" {
t.Errorf("expected canary image, got '%s'", i)
@ -369,7 +369,7 @@ func TestUpgrade(t *testing.T) {
return true, existingDeployment, nil
})
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
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -408,7 +408,7 @@ func TestUpgrade_serviceNotFound(t *testing.T) {
return true, existingDeployment, nil
})
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
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -453,7 +453,7 @@ func TestUgrade_newerVersion(t *testing.T) {
return true, existingDeployment, nil
})
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
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -513,7 +513,7 @@ func TestUpgrade_identical(t *testing.T) {
return true, existingDeployment, nil
})
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
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -554,7 +554,7 @@ func TestUpgrade_canaryClient(t *testing.T) {
return true, existingDeployment, nil
})
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
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -595,7 +595,7 @@ func TestUpgrade_canaryServer(t *testing.T) {
return true, existingDeployment, nil
})
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
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)

@ -17,16 +17,31 @@ limitations under the License.
package main
import (
"encoding/json"
"fmt"
"io"
"text/template"
"time"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/release"
"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}}
RELEASED: {{.ReleaseDate}}
CHART: {{.Release.Chart.Metadata.Name}}-{{.Release.Chart.Metadata.Version}}
@ -80,3 +95,66 @@ func debug(format string, args ...interface{}) {
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
import (
"errors"
"fmt"
"io"
"github.com/gosuri/uitable"
@ -31,6 +29,12 @@ import (
type repoListCmd struct {
out io.Writer
home helmpath.Home
output string
}
type repositoryElement struct {
Name string
URL string
}
func newRepoListCmd(out io.Writer) *cobra.Command {
@ -45,22 +49,56 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
},
}
bindOutputFlag(cmd, &list.output)
return cmd
}
func (a *repoListCmd) run() error {
f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
repoFile, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
if err != nil {
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.AddRow("NAME", "URL")
for _, re := range f.Repositories {
for _, re := range r.repos {
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
}

@ -48,6 +48,14 @@ type searchCmd struct {
regexp bool
version string
colWidth uint
output string
}
type chartElement struct {
Name string
Version string
AppVersion string
Description string
}
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.StringVarP(&sc.version, "version", "v", "", "Search using semantic versioning constraints")
f.UintVar(&sc.colWidth, "col-width", 60, "Specifies the max column width of output")
bindOutputFlag(cmd, &sc.output)
return cmd
}
@ -95,9 +104,7 @@ func (s *searchCmd) run(args []string) error {
return err
}
fmt.Fprintln(s.out, s.formatSearchResults(data, s.colWidth))
return nil
return write(s.out, &searchWriter{data, s.colWidth}, outputFormat(s.output))
}
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
}
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) {
// Load the repositories.yaml
rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile())
@ -162,3 +156,53 @@ func (s *searchCmd) buildIndex() (*search.Index, error) {
}
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
}
// NewIndex creats a new Index.
// NewIndex creates a new Index.
func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
}

@ -18,6 +18,7 @@ package main
import (
"io"
"strings"
"testing"
"github.com/spf13/cobra"
@ -84,6 +85,30 @@ func TestSearchCmd(t *testing.T) {
flags: []string{"--regexp"},
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()

@ -17,13 +17,11 @@ limitations under the License.
package main
import (
"encoding/json"
"fmt"
"io"
"regexp"
"text/tabwriter"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/gosuri/uitable/util/strutil"
"github.com/spf13/cobra"
@ -79,7 +77,7 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
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
settings.InitTLS(f)
@ -93,27 +91,26 @@ func (s *statusCmd) run() error {
return prettyError(err)
}
switch 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
case "yaml":
data, err := yaml.Marshal(res)
if err != nil {
return fmt.Errorf("Failed to Marshal YAML output: %s", err)
}
s.out.Write(data)
return write(s.out, &statusWriter{res}, outputFormat(s.outfmt))
}
type statusWriter struct {
status *services.GetReleaseStatusResponse
}
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
}
}
func (s *statusWriter) WriteJSON(out io.Writer) error {
return encodeJSON(out, s.status)
}
return fmt.Errorf("Unknown output format %q", s.outfmt)
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

@ -91,6 +91,7 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
RunE: t.run,
}
cmd.SetOutput(out)
f := cmd.Flags()
f.BoolVar(&t.showNotes, "notes", false, "Show the computed NOTES.txt file as well")
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) {
continue
}
err = writeToFile(t.outputDir, m.Name, data)
err = writeToFile(t.outputDir, m.Name, data, t.out)
if err != nil {
return err
}
continue
}
fmt.Printf("---\n# Source: %s\n", m.Name)
fmt.Println(data)
fmt.Fprintf(t.out, "---\n# Source: %s\n", m.Name)
fmt.Fprintln(t.out, data)
}
return nil
}
// 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))
err := ensureDirectoryForFile(outfileName)
@ -275,7 +276,7 @@ func writeToFile(outputDir string, name string, data string) error {
return err
}
fmt.Printf("wrote %s\n", outfileName)
fmt.Fprintf(out, "wrote %s\n", outfileName)
return nil
}

@ -20,7 +20,6 @@ import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
@ -158,7 +157,7 @@ func TestTemplateCmd(t *testing.T) {
},
{
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"},
expectError: "is invalid",
},
@ -178,14 +177,9 @@ func TestTemplateCmd(t *testing.T) {
},
}
var buf bytes.Buffer
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// capture stdout
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// execute template command
out := bytes.NewBuffer(nil)
cmd := newTemplateCmd(out)
@ -206,14 +200,8 @@ func TestTemplateCmd(t *testing.T) {
} else if err != nil {
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
scanner := bufio.NewScanner(&b)
scanner := bufio.NewScanner(out)
next := false
lastKey := ""
m := map[string]string{}
@ -239,7 +227,6 @@ func TestTemplateCmd(t *testing.T) {
} else {
t.Errorf("could not find key %s", tt.expectKey)
}
buf.Reset()
})
}
}

@ -117,6 +117,7 @@ type upgradeCmd struct {
certFile string
keyFile string
caFile string
output string
}
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.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")
bindOutputFlag(cmd, &upgrade.output)
f.MarkDeprecated("disable-hooks", "Use --no-hooks instead")
@ -307,14 +309,14 @@ func (u *upgradeCmd) run() error {
printRelease(u.out, resp.Release)
}
if outputFormat(u.output) == outputTable {
fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release)
}
// Print the status like status command does
status, err := u.client.ReleaseStatus(u.release)
if err != nil {
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
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
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...
Starting synchronization
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
```yaml
{{ $root := . }}
{{ range $path, $bytes := .Files.Glob "foo/*" }}
{{ base $path }}: '{{ $root.Files.Get $path | b64enc }}'
{{ 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.
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
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.
In the top parent's values, all charts with tags can be enabled or disabled by
@ -272,7 +274,7 @@ dependencies:
- name: subchart1
repository: http://localhost:10191
version: 0.1.0
condition: subchart1.enabled,global.subchart1.enabled
condition: subchart1.enabled
tags:
- front-end
- subchart1
@ -280,11 +282,19 @@ dependencies:
- name: subchart2
repository: http://localhost:10191
version: 0.1.0
condition: subchart2.enabled,global.subchart2.enabled
condition: subchart2.enabled
tags:
- back-end
- subchart2
```
```yaml
# subchart2/requirements.yaml
dependencies:
- name: subsubchart
repository: http://localhost:10191
version: 0.1.0
condition: subsubchart.enabled
```
```yaml
@ -292,6 +302,9 @@ dependencies:
subchart1:
enabled: true
subchart2:
subsubchart:
enabled: false
tags:
front-end: false
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
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
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.
--no-crd-hook Prevent CRD hooks from running, but run other hooks
--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
--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
@ -130,4 +131,4 @@ helm install [CHART] [flags]
* [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
-o, --output string Prints the output in the specified format. Allowed values: table, json, yaml (default "table")
```
### 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
###### 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)
-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
-v, --version string Search using semantic versioning constraints
-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.
###### 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
-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
--tls Enable TLS for request
--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.
###### 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")
--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
-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
--recreate-pods Performs pods restart for the resource if applicable
--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.
###### 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
Requests if you can.
## MicroK8s
Helm can be enabled in [MicroK8s](https://microk8s.io) using the command: `microk8s.enable helm`
## 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
distributions:
- Arch Linux
- Ubuntu 16.04
- 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
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
updated: 2019-07-03T21:59:06.87934+02:00
hash: 13c07a8e64f0777d08cd03d5edba6f254621ec1ee8e3c7b3ef26efc682b643ce
updated: 2019-09-18T12:07:21.888497-04:00
imports:
- name: cloud.google.com/go
version: 0ebda48a7f143b1cce9eb37a8c1106ac762a3430
@ -121,7 +121,7 @@ imports:
- name: github.com/google/gofuzz
version: 24818f796faf91cd76ec7bddd72458fbced7a6c1
- name: github.com/google/uuid
version: 064e2069ce9c359c118179501254f67d7d37ba24
version: 0cd6bf5da1e1c83f8b45653022c74f71af0538a4
- name: github.com/googleapis/gnostic
version: 0c5108395e2debce0d731cf0287ddf7242066aba
subpackages:
@ -183,11 +183,11 @@ imports:
- name: github.com/Masterminds/goutils
version: 41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0
- name: github.com/Masterminds/semver
version: c7af12943936e8c39859482e61f0574c2fd7fc75
version: 805c489aa98f412e79eb308a37996bf9d8b1c91e
- name: github.com/Masterminds/sprig
version: 258b00ffa7318e8b109a141349980ffbd30a35db
version: 2691a9cba2adee8d9a60100a1bc49e770f97b7db
- name: github.com/Masterminds/vcs
version: 3084677c2c188840777bff30054f2b553729d329
version: f94282d8632a0620f79f0c6ff0e82604e8c5c85b
- name: github.com/mattn/go-runewidth
version: d6bea18f789704b5f83375793155289da36a3c7f
- name: github.com/matttproud/golang_protobuf_extensions
@ -247,7 +247,7 @@ imports:
subpackages:
- doc
- name: github.com/spf13/pflag
version: 298182f68c66c05229eb03ac171abe6e309ee79a
version: e8f29969b682c41a730f8f08b76033b120498464
- name: github.com/technosophos/moniker
version: a5dbd03a2245d554160e3ae6bfdcf969fe58b431
- name: golang.org/x/crypto

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

@ -17,7 +17,7 @@ limitations under the License.
/*Package chartutil contains tools for working with 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:

@ -124,7 +124,7 @@ func LoadRequirementsLock(c *chart.Chart) (*RequirementsLock, error) {
}
// 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 conds []string
if reqs == nil || len(reqs.Dependencies) == 0 {
@ -143,7 +143,7 @@ func ProcessRequirementsConditions(reqs *Requirements, cvals Values) {
for _, c := range conds {
if len(c) > 0 {
// retrieve value
vv, err := cvals.PathValue(c)
vv, err := cvals.PathValue(cpath + c)
if err == nil {
// if not bool, warn
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
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)
if err != nil {
// 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}
// flag dependencies as enabled/disabled
ProcessRequirementsTags(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals)
ProcessRequirementsConditions(reqs, cvals, path)
// make a map of charts to remove
rm := map[string]bool{}
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
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 nerr, ok := err.(ErrNoRequirementsFile); !ok && err != nil {
return nerr

@ -158,7 +158,7 @@ func TestRequirementsCombinedDisabledL2(t *testing.T) {
t.Fatalf("Failed to load testdata: %s", err)
}
// 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
e := []string{"parentchart", "subchart1", "subchart2", "subcharta", "subchartb", "subchartb"}
@ -176,6 +176,15 @@ func TestRequirementsCombinedDisabledL1(t *testing.T) {
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) {
out := []*chart.Chart{}

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

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

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

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

@ -205,6 +205,11 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
}
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.
i, err := repo.LoadIndexFile(c.HelmHome.CacheIndex(r.Config.Name))
if err != nil {

@ -233,7 +233,7 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error {
// Any failure to resolve/download a chart should fail:
// 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 {
saveError = fmt.Errorf("could not find %s: %s", churl, err)
break
@ -403,9 +403,17 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string,
}
}
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 {
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.
@ -424,6 +432,7 @@ repository, use "https://kubernetes-charts.storage.googleapis.com/" or "@stable"
}
return nil, errors.New(errorMessage)
}
return reposMap, nil
}
@ -433,8 +442,7 @@ func (m *Manager) UpdateRepositories() error {
if err != nil {
return err
}
repos := rf.Repositories
if len(repos) > 0 {
if repos := rf.Repositories; len(repos) > 0 {
// This prints warnings straight to out.
if err := m.parallelRepoUpdate(repos); err != nil {
return err
@ -475,7 +483,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
// repoURL is the repository to search
//
// 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 {
if urlutil.Equal(repoURL, cr.Config.URL) {
var entry repo.ChartVersions
@ -497,6 +505,10 @@ func findChartURL(name, version, repoURL string, repos map[string]*repo.ChartRep
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)
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/
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")
if !strings.HasPrefix(repo, "file://") {

@ -78,7 +78,7 @@ func TestFindChartURL(t *testing.T) {
version := "0.1.0"
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 {
t.Fatal(err)
}
@ -106,13 +106,6 @@ func TestGetRepoNames(t *testing.T) {
err bool
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",
req: []*chartutil.Dependency{
@ -128,6 +121,13 @@ func TestGetRepoNames(t *testing.T) {
err: true,
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",
req: []*chartutil.Dependency{

@ -267,7 +267,7 @@ func (e *Engine) renderWithReferences(tpls map[string]renderable, referenceTpls
rendered = make(map[string]string, len(files))
var buf bytes.Buffer
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.
if strings.HasPrefix(path.Base(file), "_") {
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.
// 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.
func All(settings environment.EnvSettings) Providers {
result := Providers{

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

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

@ -55,7 +55,7 @@ func TestBadChart(t *testing.T) {
}
}
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
}
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)
}
c, err := semver.NewConstraint("> 0")
c, err := semver.NewConstraint(">0.0.0-0")
if err != nil {
return err
}

@ -106,7 +106,7 @@ func TestValidateChartVersion(t *testing.T) {
ErrorMsg string
}{
{"", "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"},
{"-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)
}
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)
}

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

@ -122,7 +122,7 @@ func NewFromKeyring(keyringfile, id string) (*Signatory, error) {
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
// out.
var candidate *openpgp.Entity

@ -54,6 +54,6 @@ func TestFilterAll(t *testing.T) {
case r0.Version == 4:
t.Fatal("got release with status revision 4")
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.
func (r *RepoFile) Has(name string) bool {
for _, rf := range r.Repositories {
if rf.Name == name {
return true
_, ok := r.Get(name)
return ok
}
// 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.

@ -16,10 +16,12 @@ limitations under the License.
package repo
import "testing"
import "io/ioutil"
import "os"
import "strings"
import (
"io/ioutil"
"os"
"strings"
"testing"
)
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) {
sampleRepository := NewRepoFile()
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)
}
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 {
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,
},
{
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",
req: &chartutil.Requirements{

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

@ -126,7 +126,7 @@ func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream s
return nil
}
// partition packs releases into slices upto the capacity cap in bytes.
// partition packs releases into slices up to the capacity cap in bytes.
func (s *ReleaseServer) partition(rels []*release.Release, cap int) <-chan []*release.Release {
chunks := make(chan []*release.Release, 1)
go func() {

@ -94,7 +94,7 @@ checkDesiredVersion() {
# if it needs to be changed.
checkHelmInstalledVersion() {
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
echo "Helm ${version} is already ${DESIRED_VERSION:-latest}"
return 0

Loading…
Cancel
Save