diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 996f6d224..aee515e37 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -199,7 +199,7 @@ $ git pull upstream master ```sh $ git checkout -b new-feature ``` -6. Make any change on the branch `new-feature` then build and test your codes. +6. Make any change on the branch `new-feature` then build and test your codes. 7. Include in what will be committed. ```sh $ git add diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index 039dcbe5f..2b44dca5e 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -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' \ diff --git a/cmd/helm/create.go b/cmd/helm/create.go index da91291fd..8ecffdba6 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -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 } diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 2f394968f..f3b8fc215 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -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 diff --git a/cmd/helm/init.go b/cmd/helm/init.go index f02700508..4dcb434f3 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -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"+ diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index b58303f42..f9b3fcd48 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -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", diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 23f307564..117c7ba5b 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -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 } - i.printRelease(rel) + + 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 diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 24a5abe68..e00c33a81 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -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 { diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index 42c7132db..504b0183d 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -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 { diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go index 1c7063450..3673712b2 100644 --- a/cmd/helm/installer/install_test.go +++ b/cmd/helm/installer/install_test.go @@ -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) diff --git a/cmd/helm/printer.go b/cmd/helm/printer.go index 2f42bdab0..1c89c04ef 100644 --- a/cmd/helm/printer.go +++ b/cmd/helm/printer.go @@ -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 +} diff --git a/cmd/helm/repo_list.go b/cmd/helm/repo_list.go index 5983bca97..a65b81908 100644 --- a/cmd/helm/repo_list.go +++ b/cmd/helm/repo_list.go @@ -17,8 +17,6 @@ limitations under the License. package main import ( - "errors" - "fmt" "io" "github.com/gosuri/uitable" @@ -29,8 +27,14 @@ import ( ) type repoListCmd struct { - out io.Writer - home helmpath.Home + 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 } diff --git a/cmd/helm/search.go b/cmd/helm/search.go index 99ffafbd3..b55997ec8 100644 --- a/cmd/helm/search.go +++ b/cmd/helm/search.go @@ -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 +} diff --git a/cmd/helm/search/search.go b/cmd/helm/search/search.go index a9d0616e9..2fd6b4581 100644 --- a/cmd/helm/search/search.go +++ b/cmd/helm/search/search.go @@ -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{}} } diff --git a/cmd/helm/search_test.go b/cmd/helm/search_test.go index 233f94572..12824407c 100644 --- a/cmd/helm/search_test.go +++ b/cmd/helm/search_test.go @@ -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() diff --git a/cmd/helm/status.go b/cmd/helm/status.go index dac91916b..23120980a 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -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 nil - } + 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 diff --git a/cmd/helm/template.go b/cmd/helm/template.go index cef55b4f2..bc9fba9c1 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -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 to / -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 } diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 3c5026b08..10c836a0d 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -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[]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() }) } } diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index aa4bebeef..a105820a6 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -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) } - fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.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)) } diff --git a/docs/chart_repository_sync_example.md b/docs/chart_repository_sync_example.md index 931275431..91215cb25 100644 --- a/docs/chart_repository_sync_example.md +++ b/docs/chart_repository_sync_example.md @@ -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]... diff --git a/docs/chart_template_guide/accessing_files.md b/docs/chart_template_guide/accessing_files.md index e1a6932cc..cb33b3808 100644 --- a/docs/chart_template_guide/accessing_files.md +++ b/docs/chart_template_guide/accessing_files.md @@ -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 }} diff --git a/docs/charts.md b/docs/charts.md index 9affe50cb..0936a720f 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -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, -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. +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. 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. diff --git a/docs/helm/helm_install.md b/docs/helm/helm_install.md index d988dfcdd..d44f9ded5 100644 --- a/docs/helm/helm_install.md +++ b/docs/helm/helm_install.md @@ -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 diff --git a/docs/helm/helm_repo_list.md b/docs/helm/helm_repo_list.md index 9a544a6ba..3ff2cbaf0 100644 --- a/docs/helm/helm_repo_list.md +++ b/docs/helm/helm_repo_list.md @@ -13,7 +13,8 @@ helm repo list [flags] ### Options ``` - -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 @@ -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 diff --git a/docs/helm/helm_search.md b/docs/helm/helm_search.md index b1a89c4f9..558cadfee 100644 --- a/docs/helm/helm_search.md +++ b/docs/helm/helm_search.md @@ -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 diff --git a/docs/helm/helm_status.md b/docs/helm/helm_status.md index 38e774b8f..91c3a1427 100644 --- a/docs/helm/helm_status.md +++ b/docs/helm/helm_status.md @@ -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 diff --git a/docs/helm/helm_upgrade.md b/docs/helm/helm_upgrade.md index 0aa52565b..35888d568 100644 --- a/docs/helm/helm_upgrade.md +++ b/docs/helm/helm_upgrade.md @@ -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 diff --git a/docs/kubernetes_distros.md b/docs/kubernetes_distros.md index 52a5e1acc..a670100ea 100644 --- a/docs/kubernetes_distros.md +++ b/docs/kubernetes_distros.md @@ -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. diff --git a/glide.lock b/glide.lock index 92e7c50dd..d81ca12ca 100644 --- a/glide.lock +++ b/glide.lock @@ -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 diff --git a/glide.yaml b/glide.yaml index 96401aa8c..565682dad 100644 --- a/glide.yaml +++ b/glide.yaml @@ -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 diff --git a/pkg/chartutil/doc.go b/pkg/chartutil/doc.go index a4f6d4515..ad2224f94 100644 --- a/pkg/chartutil/doc.go +++ b/pkg/chartutil/doc.go @@ -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: diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index ac2649d7c..4c9713233 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -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 diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 6a41635d2..17a3b4780 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -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{} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index abfe85e76..d9383dc4f 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -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 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart2/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart2/requirements.yaml index 1f0023a08..d65d73dcd 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart2/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart2/requirements.yaml @@ -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 diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index a8eb0aace..a6ee20f07 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -29,3 +29,9 @@ dependencies: tags: - back-end - subchart2 + + - name: subchart2 + alias: subchart2alias + repository: http://localhost:10191 + version: 0.1.0 + condition: subchart2alias.enabled diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chartutil/testdata/subpop/values.yaml index 55e872d41..68eb1323c 100644 --- a/pkg/chartutil/testdata/subpop/values.yaml +++ b/pkg/chartutil/testdata/subpop/values.yaml @@ -39,3 +39,5 @@ tags: front-end: true back-end: false +subchart2alias: + enabled: false \ No newline at end of file diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 563188ec3..c2e9f6dc1 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -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 { diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index cca198916..23a5b587e 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -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://") { diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index cb588394a..ef8b95071 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -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{ diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index 9a155de2e..b4b6475c9 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -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 diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index c595fec69..062c7269e 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -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{ diff --git a/pkg/getter/testdata/repository/cache/stable-index.yaml b/pkg/getter/testdata/repository/cache/stable-index.yaml index d4f883a25..40d2d04b5 100644 --- a/pkg/getter/testdata/repository/cache/stable-index.yaml +++ b/pkg/getter/testdata/repository/cache/stable-index.yaml @@ -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 diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index 51fa6a6df..b5a756aac 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -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 { diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index 7204f36b9..0514f7f6d 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -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") { diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index 8f6c16d94..d851e73ab 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -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 } diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index a44129acf..4ec091fa7 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -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) } diff --git a/pkg/lint/rules/testdata/badchartfile/Chart.yaml b/pkg/lint/rules/testdata/badchartfile/Chart.yaml index dbb4a1501..c14ed7763 100644 --- a/pkg/lint/rules/testdata/badchartfile/Chart.yaml +++ b/pkg/lint/rules/testdata/badchartfile/Chart.yaml @@ -1,3 +1,3 @@ description: A Helm chart for Kubernetes -version: 0.0.0 +version: 0.0.0.0 home: "" diff --git a/pkg/provenance/sign.go b/pkg/provenance/sign.go index 26ae26031..d0e4d06c7 100644 --- a/pkg/provenance/sign.go +++ b/pkg/provenance/sign.go @@ -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 diff --git a/pkg/releaseutil/filter_test.go b/pkg/releaseutil/filter_test.go index 802b1db7a..4ec81a8da 100644 --- a/pkg/releaseutil/filter_test.go +++ b/pkg/releaseutil/filter_test.go @@ -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") } } diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index fa550357a..80166feea 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -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. diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go index 264e9bc3c..cf435d8c1 100644 --- a/pkg/repo/repo_test.go +++ b/pkg/repo/repo_test.go @@ -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( diff --git a/pkg/resolver/resolver.go b/pkg/resolver/resolver.go index 8177df2d3..516e9260f 100644 --- a/pkg/resolver/resolver.go +++ b/pkg/resolver/resolver.go @@ -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) } diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index 689ffbc32..f35e051fa 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -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{ diff --git a/pkg/tiller/release_install_test.go b/pkg/tiller/release_install_test.go index 78d00ce66..0d985b2e5 100644 --- a/pkg/tiller/release_install_test.go +++ b/pkg/tiller/release_install_test.go @@ -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 { diff --git a/pkg/tiller/release_list.go b/pkg/tiller/release_list.go index 6d62c7bc4..cd3b63856 100644 --- a/pkg/tiller/release_list.go +++ b/pkg/tiller/release_list.go @@ -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() { diff --git a/scripts/get b/scripts/get index ea2056ca6..3f645f807 100755 --- a/scripts/get +++ b/scripts/get @@ -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