From 8108062543e71f329d3ef35dd747787741cb745c Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 17 Mar 2017 13:56:42 -0600 Subject: [PATCH 01/48] docs(related): add "Deploying Kubernetes Applications with Helm" Closes #2097 --- docs/related.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/related.md b/docs/related.md index e2955498b..3ed3fa97c 100644 --- a/docs/related.md +++ b/docs/related.md @@ -8,6 +8,7 @@ or [pull request](https://github.com/kubernetes/helm/pulls). ## Article, Blogs, How-Tos, and Extra Documentation - [Using Helm to Deploy to Kubernetes](https://daemonza.github.io/2017/02/20/using-helm-to-deploy-to-kubernetes/) - [Honestbee's Helm Chart Conventions](https://gist.github.com/so0k/f927a4b60003cedd101a0911757c605a) +- [Deploying Kubernetes Applications with Helm](http://cloudacademy.com/blog/deploying-kubernetes-applications-with-helm/) - [Releasing backward-incompatible changes: Kubernetes, Jenkins, Prometheus Operator, Helm and Traefik](https://medium.com/@enxebre/releasing-backward-incompatible-changes-kubernetes-jenkins-plugin-prometheus-operator-helm-self-6263ca61a1b1#.e0c7elxhq) - [CI/CD with Kubernetes, Helm & Wercker ](http://www.slideshare.net/Diacode/cicd-with-kubernetes-helm-wercker-madscalability) - [The missing CI/CD Kubernetes component: Helm package manager](https://hackernoon.com/the-missing-ci-cd-kubernetes-component-helm-package-manager-1fe002aac680#.691sk2zhu) From 0d399baec2acda578a217d1aec8d7d707c71e44d Mon Sep 17 00:00:00 2001 From: Leszek Charkiewicz Date: Sun, 19 Mar 2017 23:11:28 +0100 Subject: [PATCH 02/48] Add checking installed helm version before downloading latest release This change prevents downloading the same version as already installed. --- scripts/get | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/scripts/get b/scripts/get index d5fb16ff2..73266f373 100755 --- a/scripts/get +++ b/scripts/get @@ -62,9 +62,8 @@ verifySupported() { fi } -# downloadFile downloads the latest binary package and also the checksum -# for that binary. -downloadFile() { +# checkLatestVersion checks the latest available version. +checkLatestVersion() { # Use the GitHub API to find the latest version for this project. local latest_url="https://api.github.com/repos/kubernetes/helm/releases/latest" if type "curl" > /dev/null; then @@ -72,7 +71,28 @@ downloadFile() { elif type "wget" > /dev/null; then TAG=$(wget -q -O - $latest_url | awk '/\"tag_name\":/{gsub( /[,\"]/,"", $2); print $2}') fi +} + +# checkHelmInstalledVersion checks which version of helm is installed and +# if it needs to be updated. +checkHelmInstalledVersion() { + if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then + local version=$(helm version | grep '^Client' | cut -d'"' -f2) + if [[ "$version" == "$TAG" ]]; then + echo "Helm ${version} is up-to-date." + return 0 + else + echo "Helm ${TAG} is available. Upgrading from version ${version}." + return 1 + fi + else + return 1 + fi +} +# downloadFile downloads the latest binary package and also the checksum +# for that binary. +downloadFile() { HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" DOWNLOAD_URL="https://kubernetes-helm.storage.googleapis.com/$HELM_DIST" CHECKSUM_URL="$DOWNLOAD_URL.sha256" @@ -140,6 +160,9 @@ set -e initArch initOS verifySupported -downloadFile -installFile +checkLatestVersion +if ! checkHelmInstalledVersion; then + downloadFile + installFile +fi testVersion From d2af40080601931de7db3f9d47ba3f8354e24efc Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 20 Mar 2017 15:27:28 -0700 Subject: [PATCH 03/48] run glide up A few bugs were fixed in ghodss/yaml since the last update. --- glide.lock | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/glide.lock b/glide.lock index 0fa856f87..9672f8c0d 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ hash: 0d1c5b7304a853820dcaa296d3aa1f5f3466a8491dcef80cbcaf43c954acb2a8 -updated: 2017-03-15T15:56:18.814305691-06:00 +updated: 2017-03-20T15:26:55.446524716-07:00 imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -99,7 +99,7 @@ imports: - name: github.com/facebookgo/symwalk version: 42004b9f322246749dd73ad71008b1f3160c0052 - name: github.com/ghodss/yaml - version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee + version: 04f313413ffd65ce25f2541bfd2b2ceec5c0908c - name: github.com/go-openapi/jsonpointer version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 - name: github.com/go-openapi/jsonreference @@ -186,7 +186,7 @@ imports: - name: github.com/Masterminds/sprig version: 23597e5f6ad0e4d590e71314bfd0251a4a3cf849 - name: github.com/mattn/go-runewidth - version: d6bea18f789704b5f83375793155289da36a3c7f + version: 14207d285c6c197daabb5c9793d63e7af9ab2d50 - name: github.com/mitchellh/go-wordwrap version: ad45545899c7b13c020ea92b2072220eefad42b8 - name: github.com/naoina/go-stringutil @@ -216,7 +216,7 @@ imports: - name: github.com/spf13/pflag version: 5ccb023bc27df288a957c5e994cd44fd19619465 - name: github.com/technosophos/moniker - version: 9f956786b91d9786ca11aa5be6104542fa911546 + version: ab470f5e105a44d0c87ea21bacd6a335c4816d83 - name: github.com/ugorji/go version: f1f1a805ed361a0e078bb537e4ea78cd37dcf065 subpackages: @@ -298,7 +298,7 @@ imports: - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 - version: a83829b6f1293c91addabc89d0571c246397bbf4 + version: a3f3340b5840cee44f372bddb5880fcbc419b46a - name: k8s.io/kubernetes version: 00a1fb254bd8e5235575fba1398b958943e39078 subpackages: @@ -510,3 +510,5 @@ testImports: version: e3a8ff8ce36581f87a15341206f205b1da467059 subpackages: - assert + - mock + - require From 27c3ff595a6ec7aad28bba27a37cf782b83528d7 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 20 Mar 2017 18:21:15 -0600 Subject: [PATCH 04/48] fix(tiller): fix helm status failure on missing resource This fixes a bug in which 'helm status' fails if any of the expected resources are missing in Kubernetes. Now it prints a list of missing resources at the end of the status report. Closes #2118 --- pkg/kube/client.go | 11 ++++++++++- pkg/kube/client_test.go | 44 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index a43627416..a4d1e086d 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -151,11 +151,14 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { if err != nil { return "", err } + missing := []string{} err = perform(c, namespace, infos, func(info *resource.Info) error { log.Printf("Doing get for: '%s'", info.Name) obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export) if err != nil { - return err + log.Printf("WARNING: Failed Get for resource %q: %s", info.Name, err) + missing = append(missing, fmt.Sprintf("%v\t\t%s", info.Mapping.Resource, info.Name)) + return nil } // We need to grab the ObjectReference so we can correctly group the objects. or, err := api.GetReference(obj) @@ -194,6 +197,12 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { return "", err } } + if len(missing) > 0 { + buf.WriteString("==> MISSING\nKIND\t\tNAME\n") + for _, s := range missing { + fmt.Fprintln(buf, s) + } + } return buf.String(), nil } diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 8a9980abb..02736f3bf 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -59,6 +59,7 @@ func newPodWithStatus(name string, status api.PodStatus, namespace string) api.P ObjectMeta: api.ObjectMeta{ Name: name, Namespace: ns, + SelfLink: "/api/v1/namespaces/default/pods/" + name, }, Spec: api.PodSpec{ Containers: []api.Container{{ @@ -279,6 +280,49 @@ func TestBuild(t *testing.T) { } } +func TestGet(t *testing.T) { + list := newPodList("starfish", "otter") + f, tf, _, ns := cmdtesting.NewAPIFactory() + tf.Client = &fake.RESTClient{ + NegotiatedSerializer: ns, + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + p, m := req.URL.Path, req.Method + //actions = append(actions, p+":"+m) + t.Logf("got request %s %s", p, m) + switch { + case p == "/namespaces/default/pods/starfish" && m == "GET": + return newResponse(404, notFoundBody()) + case p == "/namespaces/default/pods/otter" && m == "GET": + return newResponse(200, &list.Items[1]) + default: + t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path) + return nil, nil + } + }), + } + c := &Client{Factory: f} + + // Test Success + data := strings.NewReader("kind: Pod\napiVersion: v1\nmetadata:\n name: otter") + o, err := c.Get("default", data) + if err != nil { + t.Errorf("Expected missing results, got %q", err) + } + if !strings.Contains(o, "==> v1/Pod") && !strings.Contains(o, "otter") { + t.Errorf("Expected v1/Pod otter, got %s", o) + } + + // Test failure + data = strings.NewReader("kind: Pod\napiVersion: v1\nmetadata:\n name: starfish") + o, err = c.Get("default", data) + if err != nil { + t.Errorf("Expected missing results, got %q", err) + } + if !strings.Contains(o, "MISSING") && !strings.Contains(o, "pods\t\tstarfish") { + t.Errorf("Expected missing starfish, got %s", o) + } +} + func TestPerform(t *testing.T) { tests := []struct { name string From 41f727761556c0b37a0794c9317ca9bf60f2f98f Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 22 Mar 2017 13:08:34 -0700 Subject: [PATCH 05/48] fix(ci): disable gosimple gosimple seems to be eating all memory in CircleCI. Testing disabling the check to see if CI builds. --- scripts/validate-go.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/validate-go.sh b/scripts/validate-go.sh index 33a4c6633..2ecf5dfb3 100755 --- a/scripts/validate-go.sh +++ b/scripts/validate-go.sh @@ -30,7 +30,6 @@ gometalinter.v1 \ --enable deadcode \ --severity deadcode:error \ --enable gofmt \ - --enable gosimple \ --enable ineffassign \ --enable misspell \ --enable vet \ From ad614b91a53df2b5566a407760fc03c6cd14e248 Mon Sep 17 00:00:00 2001 From: fibonacci1729 Date: Mon, 20 Mar 2017 11:00:10 -0600 Subject: [PATCH 06/48] feat: helm support for TLS --- cmd/helm/helm.go | 8 ++ cmd/helm/init.go | 70 ++++++++++++--- cmd/helm/installer/install.go | 128 +++++++++++++++++++++------ cmd/helm/installer/install_test.go | 14 ++- cmd/helm/installer/options.go | 22 ++++- cmd/helm/installer/uninstall_test.go | 8 +- 6 files changed, 205 insertions(+), 45 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 64200adaf..9ade3e344 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -45,6 +45,14 @@ const ( tillerNamespaceEnvVar = "TILLER_NAMESPACE" ) +var ( + tlsCaCertFile string // path to TLS CA certificate file + tlsCertFile string // path to TLS certificate file + tlsKeyFile string // path to TLS key file + tlsVerify bool // enable TLS and verify remote certificates + tlsEnable bool // enable TLS +) + var ( helmHome string tillerHost string diff --git a/cmd/helm/init.go b/cmd/helm/init.go index f855abe9a..bf7d38f16 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -70,6 +70,7 @@ type initCmd struct { dryRun bool out io.Writer home helmpath.Home + opts installer.Options kubeClient internalclientset.Interface } @@ -99,25 +100,73 @@ func newInitCmd(out io.Writer) *cobra.Command { f.BoolVarP(&i.clientOnly, "client-only", "c", false, "if set does not install tiller") f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote") + // f.BoolVar(&tlsEnable, "tiller-tls", false, "install tiller with TLS enabled") + // f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "install tiller with TLS enabled and to verify remote certificates") + // f.StringVar(&tlsKeyFile, "tiller-tls-key", "", "path to TLS key file to install with tiller") + // f.StringVar(&tlsCertFile, "tiller-tls-cert", "", "path to TLS certificate file to install with tiller") + // f.StringVar(&tlsCaCertFile, "tls-ca-cert", "", "path to CA root certificate") + return cmd } +// tlsOptions sanitizes the tls flags as well as checks for the existence of required +// tls files indicated by those flags, if any. +func (i *initCmd) tlsOptions() error { + i.opts.EnableTLS = tlsEnable || tlsVerify + i.opts.VerifyTLS = tlsVerify + + if i.opts.EnableTLS { + missing := func(file string) bool { + _, err := os.Stat(file) + return os.IsNotExist(err) + } + if i.opts.TLSKeyFile = tlsKeyFile; i.opts.TLSKeyFile == "" || missing(i.opts.TLSKeyFile) { + return errors.New("missing required TLS key file") + } + if i.opts.TLSCertFile = tlsCertFile; i.opts.TLSCertFile == "" || missing(i.opts.TLSCertFile) { + return errors.New("missing required TLS certificate file") + } + if i.opts.VerifyTLS { + if i.opts.TLSCaCertFile = tlsCaCertFile; i.opts.TLSCaCertFile == "" || missing(i.opts.TLSCaCertFile) { + return errors.New("missing required TLS CA file") + } + } + } + return nil +} + // runInit initializes local config and installs tiller to Kubernetes Cluster func (i *initCmd) run() error { + if err := i.tlsOptions(); err != nil { + return err + } + i.opts.Namespace = i.namespace + i.opts.UseCanary = i.canary + i.opts.ImageSpec = i.image + if flagDebug { - dm, err := installer.DeploymentManifest(i.namespace, i.image, i.canary) - if err != nil { + var mfs string + var err error + + // write deployment manifest + if mfs, err = installer.DeploymentManifest(&i.opts); err != nil { return err } - fm := fmt.Sprintf("apiVersion: extensions/v1beta1\nkind: Deployment\n%s", dm) - fmt.Fprintln(i.out, fm) + fmt.Fprintln(i.out, fmt.Sprintf("apiVersion: extensions/v1beta1\nkind: Deployment\n%s", mfs)) - sm, err := installer.ServiceManifest(i.namespace) - if err != nil { + // write service manifest + if mfs, err = installer.ServiceManifest(i.namespace); err != nil { return err } - fm = fmt.Sprintf("apiVersion: v1\nkind: Service\n%s", sm) - fmt.Fprintln(i.out, fm) + fmt.Fprintln(i.out, fmt.Sprintf("apiVersion: v1\nkind: Service\n%s", mfs)) + + // write secret manifest + if i.opts.EnableTLS { + if mfs, err = installer.SecretManifest(&i.opts); err != nil { + return err + } + fmt.Fprintln(i.out, fmt.Sprintf("apiVersion: v1\nkind: Secret\n%s", mfs)) + } } if i.dryRun { @@ -143,13 +192,12 @@ func (i *initCmd) run() error { } i.kubeClient = c } - opts := &installer.Options{Namespace: i.namespace, ImageSpec: i.image, UseCanary: i.canary} - if err := installer.Install(i.kubeClient, opts); err != nil { + if err := installer.Install(i.kubeClient, &i.opts); err != nil { if !kerrors.IsAlreadyExists(err) { return fmt.Errorf("error installing: %s", err) } if i.upgrade { - if err := installer.Upgrade(i.kubeClient, opts); err != nil { + if err := installer.Upgrade(i.kubeClient, &i.opts); err != nil { return fmt.Errorf("error when upgrading: %s", err) } fmt.Fprintln(i.out, "\nTiller (the helm server side component) has been upgraded to the current version.") diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index 2ca06a9c1..338e31eca 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -17,7 +17,7 @@ limitations under the License. package installer // import "k8s.io/helm/cmd/helm/installer" import ( - "fmt" + "io/ioutil" "github.com/ghodss/yaml" @@ -28,22 +28,23 @@ import ( "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion" extensionsclient "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion" "k8s.io/kubernetes/pkg/util/intstr" - - "k8s.io/helm/pkg/version" ) -const defaultImage = "gcr.io/kubernetes-helm/tiller" - // Install uses kubernetes client to install tiller. // // Returns an error if the command failed. func Install(client internalclientset.Interface, opts *Options) error { - if err := createDeployment(client.Extensions(), opts.Namespace, opts.ImageSpec, opts.UseCanary); err != nil { + if err := createDeployment(client.Extensions(), opts); err != nil { return err } if err := createService(client.Core(), opts.Namespace); err != nil { return err } + if opts.tls() { + if err := createSecret(client.Core(), opts); err != nil { + return err + } + } return nil } @@ -55,7 +56,7 @@ func Upgrade(client internalclientset.Interface, opts *Options) error { if err != nil { return err } - obj.Spec.Template.Spec.Containers[0].Image = selectImage(opts.ImageSpec, opts.UseCanary) + obj.Spec.Template.Spec.Containers[0].Image = opts.selectImage() if _, err := client.Extensions().Deployments(opts.Namespace).Update(obj); err != nil { return err } @@ -73,15 +74,15 @@ func Upgrade(client internalclientset.Interface, opts *Options) error { } // createDeployment creates the Tiller deployment reource -func createDeployment(client extensionsclient.DeploymentsGetter, namespace, image string, canary bool) error { - obj := deployment(namespace, image, canary) +func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error { + obj := deployment(opts) _, err := client.Deployments(obj.Namespace).Create(obj) return err } // deployment gets the deployment object that installs Tiller. -func deployment(namespace, image string, canary bool) *extensions.Deployment { - return generateDeployment(namespace, selectImage(image, canary)) +func deployment(opts *Options) *extensions.Deployment { + return generateDeployment(opts) } // createService creates the Tiller service resource @@ -96,21 +97,10 @@ func service(namespace string) *api.Service { return generateService(namespace) } -func selectImage(image string, canary bool) string { - switch { - case canary: - image = defaultImage + ":canary" - case image == "": - image = fmt.Sprintf("%s:%s", defaultImage, version.Version) - } - return image -} - // DeploymentManifest gets the manifest (as a string) that describes the Tiller Deployment // resource. -func DeploymentManifest(namespace, image string, canary bool) (string, error) { - obj := deployment(namespace, image, canary) - +func DeploymentManifest(opts *Options) (string, error) { + obj := deployment(opts) buf, err := yaml.Marshal(obj) return string(buf), err } @@ -129,11 +119,11 @@ func generateLabels(labels map[string]string) map[string]string { return labels } -func generateDeployment(namespace, image string) *extensions.Deployment { +func generateDeployment(opts *Options) *extensions.Deployment { labels := generateLabels(map[string]string{"name": "tiller"}) d := &extensions.Deployment{ ObjectMeta: api.ObjectMeta{ - Namespace: namespace, + Namespace: opts.Namespace, Name: "tiller-deploy", Labels: labels, }, @@ -147,13 +137,13 @@ func generateDeployment(namespace, image string) *extensions.Deployment { Containers: []api.Container{ { Name: "tiller", - Image: image, + Image: opts.selectImage(), ImagePullPolicy: "IfNotPresent", Ports: []api.ContainerPort{ {ContainerPort: 44134, Name: "tiller"}, }, Env: []api.EnvVar{ - {Name: "TILLER_NAMESPACE", Value: namespace}, + {Name: "TILLER_NAMESPACE", Value: opts.Namespace}, }, LivenessProbe: &api.Probe{ Handler: api.Handler{ @@ -181,6 +171,37 @@ func generateDeployment(namespace, image string) *extensions.Deployment { }, }, } + + if opts.tls() { + const certsDir = "/etc/certs" + + var tlsVerify, tlsEnable = "", "1" + if opts.VerifyTLS { + tlsVerify = "1" + } + + // Mount secret to "/etc/certs" + d.Spec.Template.Spec.Containers[0].VolumeMounts = append(d.Spec.Template.Spec.Containers[0].VolumeMounts, api.VolumeMount{ + Name: "tiller-certs", + ReadOnly: true, + MountPath: certsDir, + }) + // Add environment variable required for enabling TLS + d.Spec.Template.Spec.Containers[0].Env = append(d.Spec.Template.Spec.Containers[0].Env, []api.EnvVar{ + {Name: "TILLER_TLS_VERIFY", Value: tlsVerify}, + {Name: "TILLER_TLS_ENABLE", Value: tlsEnable}, + {Name: "TILLER_TLS_CERTS", Value: certsDir}, + }...) + // Add secret volume to deployment + d.Spec.Template.Spec.Volumes = append(d.Spec.Template.Spec.Volumes, api.Volume{ + Name: "tiller-certs", + VolumeSource: api.VolumeSource{ + Secret: &api.SecretVolumeSource{ + SecretName: "tiller-secret", + }, + }, + }) + } return d } @@ -206,3 +227,54 @@ func generateService(namespace string) *api.Service { } return s } + +// SecretManifest gets the manifest (as a string) that describes the Tiller Secret resource. +func SecretManifest(opts *Options) (string, error) { + o, err := generateSecret(opts) + if err != nil { + return "", err + } + buf, err := yaml.Marshal(o) + return string(buf), err +} + +// createSecret creates the Tiller secret resource. +func createSecret(client internalversion.SecretsGetter, opts *Options) error { + o, err := generateSecret(opts) + if err != nil { + return err + } + _, err = client.Secrets(o.Namespace).Create(o) + return err +} + +// generateSecret builds the secret object that hold Tiller secrets. +func generateSecret(opts *Options) (*api.Secret, error) { + const secretName = "tiller-secret" + + labels := generateLabels(map[string]string{"name": "tiller"}) + secret := &api.Secret{ + Type: api.SecretTypeOpaque, + Data: make(map[string][]byte), + ObjectMeta: api.ObjectMeta{ + Name: secretName, + Labels: labels, + Namespace: opts.Namespace, + }, + } + var err error + if secret.Data["tls.key"], err = read(opts.TLSKeyFile); err != nil { + return nil, err + } + if secret.Data["tls.crt"], err = read(opts.TLSCertFile); err != nil { + return nil, err + } + if opts.VerifyTLS { + if secret.Data["ca.crt"], err = read(opts.TLSCaCertFile); err != nil { + return nil, err + } + } + return secret, nil +} + +func read(path string) (b []byte, err error) { return ioutil.ReadFile(path) } diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go index daa09b0d8..4d9a2cbca 100644 --- a/cmd/helm/installer/install_test.go +++ b/cmd/helm/installer/install_test.go @@ -45,7 +45,7 @@ func TestDeploymentManifest(t *testing.T) { } for _, tt := range tests { - o, err := DeploymentManifest(api.NamespaceDefault, tt.image, tt.canary) + o, err := DeploymentManifest(&Options{Namespace: api.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary}) if err != nil { t.Fatalf("%s: error %q", tt.name, err) } @@ -146,7 +146,11 @@ func TestInstall_canary(t *testing.T) { func TestUpgrade(t *testing.T) { image := "gcr.io/kubernetes-helm/tiller:v2.0.0" - existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false) + existingDeployment := deployment(&Options{ + Namespace: api.NamespaceDefault, + ImageSpec: "imageToReplace", + UseCanary: false, + }) existingService := service(api.NamespaceDefault) fc := &fake.Clientset{} @@ -178,7 +182,11 @@ func TestUpgrade(t *testing.T) { func TestUpgrade_serviceNotFound(t *testing.T) { image := "gcr.io/kubernetes-helm/tiller:v2.0.0" - existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false) + existingDeployment := deployment(&Options{ + Namespace: api.NamespaceDefault, + ImageSpec: "imageToReplace", + UseCanary: false, + }) fc := &fake.Clientset{} fc.AddReactor("get", "deployments", func(action testcore.Action) (bool, runtime.Object, error) { diff --git a/cmd/helm/installer/options.go b/cmd/helm/installer/options.go index 909b7e9e4..41f8aaf2a 100644 --- a/cmd/helm/installer/options.go +++ b/cmd/helm/installer/options.go @@ -16,6 +16,13 @@ limitations under the License. package installer // import "k8s.io/helm/cmd/helm/installer" +import ( + "fmt" + "k8s.io/helm/pkg/version" +) + +const defaultImage = "gcr.io/kubernetes-helm/tiller" + // Options control how to install tiller into a cluster, upgrade, and uninstall tiller from a cluster. type Options struct { // EnableTLS instructs tiller to serve with TLS enabled. @@ -43,7 +50,7 @@ type Options struct { // key tiller should use. // // Required and valid if and only if EnableTLS or VerifyTLS is set. - TLSKey string + TLSKeyFile string // TLSCertFile identifies the file containing the pem encoded TLS // certificate tiller should use. @@ -57,3 +64,16 @@ type Options struct { // Required and valid if and only if VerifyTLS is set. TLSCaCertFile string } + +func (opts *Options) selectImage() string { + switch { + case opts.UseCanary: + return defaultImage + ":canary" + case opts.ImageSpec == "": + return fmt.Sprintf("%s:%s", defaultImage, version.Version) + default: + return opts.ImageSpec + } +} + +func (opts *Options) tls() bool { return opts.EnableTLS || opts.VerifyTLS } diff --git a/cmd/helm/installer/uninstall_test.go b/cmd/helm/installer/uninstall_test.go index e0db07fe6..bda91ca1c 100644 --- a/cmd/helm/installer/uninstall_test.go +++ b/cmd/helm/installer/uninstall_test.go @@ -55,7 +55,11 @@ func (f *fakeReaperFactory) Reaper(mapping *meta.RESTMapping) (kubectl.Reaper, e func TestUninstall(t *testing.T) { existingService := service(api.NamespaceDefault) - existingDeployment := deployment(api.NamespaceDefault, "image", false) + existingDeployment := deployment(&Options{ + Namespace: api.NamespaceDefault, + ImageSpec: "image", + UseCanary: false, + }) fc := &fake.Clientset{} fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) { @@ -92,7 +96,7 @@ func TestUninstall(t *testing.T) { } func TestUninstall_serviceNotFound(t *testing.T) { - existingDeployment := deployment(api.NamespaceDefault, "imageToReplace", false) + existingDeployment := deployment(&Options{Namespace: api.NamespaceDefault, ImageSpec: "imageToReplace", UseCanary: false}) fc := &fake.Clientset{} fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) { From 7cffe38f84dbf809c226bbcd210ed51767fdc658 Mon Sep 17 00:00:00 2001 From: Derek Perkins Date: Thu, 23 Mar 2017 14:04:56 -0600 Subject: [PATCH 07/48] Add link to Quay App Registry --- docs/related.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/related.md b/docs/related.md index 3ed3fa97c..bca554c42 100644 --- a/docs/related.md +++ b/docs/related.md @@ -33,6 +33,7 @@ or [pull request](https://github.com/kubernetes/helm/pulls). Tools layered on top of Helm or Tiller. +- [Quay App Registry](https://coreos.com/blog/quay-application-registry-for-kubernetes.html) - Open Kubernetes application registry, including a Helm access client - [Chartify](https://github.com/appscode/chartify) - Generate Helm charts from existing Kubernetes resources. - [VIM-Kubernetes](https://github.com/andrewstuart/vim-kubernetes) - VIM plugin for Kubernetes and Helm - [Landscaper](https://github.com/Eneco/landscaper/) - "Landscaper takes a set of Helm Chart references with values (a desired state), and realizes this in a Kubernetes cluster." From cfd041eed56002475841aac89116390447722bd3 Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Tue, 21 Mar 2017 11:41:39 -0700 Subject: [PATCH 08/48] Added unit tests Added tests for: - [ ] pkg/version/versions.go - [ ] pkg/repo/repo.go --- pkg/repo/repo_test.go | 99 +++++++++++++++++++++++++++++++++++++ pkg/version/version_test.go | 47 ++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 pkg/version/version_test.go diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go index c0b0e6da7..6aee41faf 100644 --- a/pkg/repo/repo_test.go +++ b/pkg/repo/repo_test.go @@ -17,6 +17,8 @@ limitations under the License. package repo import "testing" +import "io/ioutil" +import "os" const testRepositoriesFile = "testdata/repositories.yaml" @@ -116,3 +118,100 @@ func TestNewPreV1RepositoriesFile(t *testing.T) { t.Errorf("expected the best charts ever. Got %#v", r.Repositories) } } + +func TestRemoveRepository(t *testing.T) { + sampleRepository := NewRepoFile() + sampleRepository.Add( + &Entry{ + Name: "stable", + URL: "https://example.com/stable/charts", + Cache: "stable-index.yaml", + }, + &Entry{ + Name: "incubator", + URL: "https://example.com/incubator", + Cache: "incubator-index.yaml", + }, + ) + + removeRepository := "stable" + found := sampleRepository.Remove(removeRepository) + if !found { + t.Errorf("expected repository %s not found", removeRepository) + } + + found = sampleRepository.Has(removeRepository) + if found { + t.Errorf("repository %s not deleted", removeRepository) + } +} + +func TestUpdateRepository(t *testing.T) { + sampleRepository := NewRepoFile() + sampleRepository.Add( + &Entry{ + Name: "stable", + URL: "https://example.com/stable/charts", + Cache: "stable-index.yaml", + }, + &Entry{ + Name: "incubator", + URL: "https://example.com/incubator", + Cache: "incubator-index.yaml", + }, + ) + newRepoName := "sample" + sampleRepository.Update(&Entry{Name: newRepoName, + URL: "https://example.com/sample", + Cache: "sample-index.yaml", + }) + + if !sampleRepository.Has(newRepoName) { + t.Errorf("expected repository %s not found", newRepoName) + } + repoCount := len(sampleRepository.Repositories) + + sampleRepository.Update(&Entry{Name: newRepoName, + URL: "https://example.com/sample", + Cache: "sample-index.yaml", + }) + + if repoCount != len(sampleRepository.Repositories) { + t.Errorf("invalid number of repositories found %d, expected number of repositories %d", len(sampleRepository.Repositories), repoCount) + } +} + +func TestWriteFile(t *testing.T) { + sampleRepository := NewRepoFile() + sampleRepository.Add( + &Entry{ + Name: "stable", + URL: "https://example.com/stable/charts", + Cache: "stable-index.yaml", + }, + &Entry{ + Name: "incubator", + URL: "https://example.com/incubator", + Cache: "incubator-index.yaml", + }, + ) + + repoFile, err := ioutil.TempFile("", "helm-repo") + if err != nil { + t.Errorf("failed to create test-file (%v)", err) + } + defer os.Remove(repoFile.Name()) + if err := sampleRepository.WriteFile(repoFile.Name(), 744); err != nil { + t.Errorf("failed to write file (%v)", err) + } + + repos, err := LoadRepositoriesFile(repoFile.Name()) + if err != nil { + t.Errorf("failed to load file (%v)", err) + } + for _, repo := range sampleRepository.Repositories { + if !repos.Has(repo.Name) { + t.Errorf("expected repository %s not found", repo.Name) + } + } +} diff --git a/pkg/version/version_test.go b/pkg/version/version_test.go new file mode 100644 index 000000000..e0e4cac0f --- /dev/null +++ b/pkg/version/version_test.go @@ -0,0 +1,47 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package version represents the current version of the project. +package version // import "k8s.io/helm/pkg/version" + +import "testing" +import "k8s.io/helm/pkg/proto/hapi/version" + +func TestGetVersionProto(t *testing.T) { + tests := []struct { + version string + buildMetadata string + gitCommit string + gitTreeState string + expected version.Version + }{ + {"", "", "", "", version.Version{SemVer: "", GitCommit: "", GitTreeState: ""}}, + {"v1.0.0", "", "", "", version.Version{SemVer: "v1.0.0", GitCommit: "", GitTreeState: ""}}, + {"v1.0.0", "79d5c5f7", "", "", version.Version{SemVer: "v1.0.0+79d5c5f7", GitCommit: "", GitTreeState: ""}}, + {"v1.0.0", "79d5c5f7", "0d399baec2acda578a217d1aec8d7d707c71e44d", "", version.Version{SemVer: "v1.0.0+79d5c5f7", GitCommit: "0d399baec2acda578a217d1aec8d7d707c71e44d", GitTreeState: ""}}, + {"v1.0.0", "79d5c5f7", "0d399baec2acda578a217d1aec8d7d707c71e44d", "clean", version.Version{SemVer: "v1.0.0+79d5c5f7", GitCommit: "0d399baec2acda578a217d1aec8d7d707c71e44d", GitTreeState: "clean"}}, + } + for _, tt := range tests { + Version = tt.version + BuildMetadata = tt.buildMetadata + GitCommit = tt.gitCommit + GitTreeState = tt.gitTreeState + if versionProto := GetVersionProto(); *versionProto != tt.expected { + t.Errorf("expected Semver(%s), GitCommit(%s) and GitTreeState(%s) to be %v", tt.expected, tt.gitCommit, tt.gitTreeState, *versionProto) + } + } + +} From f4f274c571453c1d0566cb39a1baac17ee32238c Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 23 Mar 2017 22:38:46 -0600 Subject: [PATCH 09/48] docs(related): Added podcast, plugin article Slightly reorganized and retitled to be accurate. --- docs/related.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/related.md b/docs/related.md index bca554c42..c2540b5f4 100644 --- a/docs/related.md +++ b/docs/related.md @@ -12,14 +12,16 @@ or [pull request](https://github.com/kubernetes/helm/pulls). - [Releasing backward-incompatible changes: Kubernetes, Jenkins, Prometheus Operator, Helm and Traefik](https://medium.com/@enxebre/releasing-backward-incompatible-changes-kubernetes-jenkins-plugin-prometheus-operator-helm-self-6263ca61a1b1#.e0c7elxhq) - [CI/CD with Kubernetes, Helm & Wercker ](http://www.slideshare.net/Diacode/cicd-with-kubernetes-helm-wercker-madscalability) - [The missing CI/CD Kubernetes component: Helm package manager](https://hackernoon.com/the-missing-ci-cd-kubernetes-component-helm-package-manager-1fe002aac680#.691sk2zhu) -- [CI/CD with Jenkins, Kubernetes, and Helm](https://www.youtube.com/watch?v=NVoln4HdZOY) - [The Workflow "Umbrella" Helm Chart](https://deis.com/blog/2017/workflow-chart-assembly) - [GitLab, Consumer Driven Contracts, Helm and Kubernetes](https://medium.com/@enxebre/gitlab-consumer-driven-contracts-helm-and-kubernetes-b7235a60a1cb#.xwp1y4tgi) - [Writing a Helm Chart](https://www.influxdata.com/packaged-kubernetes-deployments-writing-helm-chart/) +- [Creating a Helm Plugin in 3 Steps](http://technosophos.com/2017/03/21/creating-a-helm-plugin.html) -## Videos +## Video, Audio, and Podcast +- [CI/CD with Jenkins, Kubernetes, and Helm](https://www.youtube.com/watch?v=NVoln4HdZOY): AKA "The Infamous Croc Hunter Video". - [KubeCon2016: Delivering Kubernetes-Native Applications by Michelle Noorali](https://www.youtube.com/watch?v=zBc1goRfk3k&index=49&list=PLj6h78yzYM2PqgIGU1Qmi8nY7dqn9PCr4) +- [Helm with Michelle Noorali and Matthew Butcher](https://gcppodcast.com/post/episode-50-helm-with-michelle-noorali-and-matthew-butcher/): The official Google CloudPlatform Podcast interviews Michelle and Matt about Helm. ## Helm Plugins From a2ab1aaa09300186bd9d137e794c9b8a24313ff1 Mon Sep 17 00:00:00 2001 From: Louis Taylor Date: Tue, 21 Mar 2017 14:40:00 +0000 Subject: [PATCH 10/48] fix(helm): add --destination flag to 'helm package' This adds a flag similar to the --destination flag on 'helm fetch', allowing control over the destination for the packaged .tgz file. Closes #2117 --- cmd/helm/package.go | 36 +++++++++++++++++++++++------------- cmd/helm/package_test.go | 18 ++++++++++++++++++ 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index ac8ff9d8e..100a4663d 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -48,14 +48,16 @@ Versioned chart archives are used by Helm package repositories. ` type packageCmd struct { - save bool - sign bool - path string - key string - keyring string - version string - out io.Writer - home helmpath.Home + save bool + sign bool + path string + key string + keyring string + version string + destination string + + out io.Writer + home helmpath.Home } func newPackageCmd(out io.Writer) *cobra.Command { @@ -96,6 +98,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true") f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring") f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version") + f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.") return cmd } @@ -129,12 +132,19 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { checkDependencies(ch, reqs, p.out) } - // Save to the current working directory. - cwd, err := os.Getwd() - if err != nil { - return err + var dest string + if p.destination == "." { + // Save to the current working directory. + dest, err = os.Getwd() + if err != nil { + return err + } + } else { + // Otherwise save to set destination + dest = p.destination } - name, err := chartutil.Save(ch, cwd) + + name, err := chartutil.Save(ch, dest) if err == nil && flagDebug { fmt.Fprintf(p.out, "Saved %s to current directory\n", name) } diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index a9dc021a3..340da0fdb 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -94,6 +94,20 @@ func TestPackage(t *testing.T) { expect: "", hasfile: "alpine-0.1.0.tgz", }, + { + name: "package --destination toot", + args: []string{"testdata/testcharts/alpine"}, + flags: map[string]string{"destination": "toot"}, + expect: "", + hasfile: "toot/alpine-0.1.0.tgz", + }, + { + name: "package --destination does-not-exist", + args: []string{"testdata/testcharts/alpine"}, + flags: map[string]string{"destination": "does-not-exist"}, + expect: "stat does-not-exist: no such file or directory", + err: true, + }, { name: "package --sign --key=KEY --keyring=KEYRING testdata/testcharts/alpine", args: []string{"testdata/testcharts/alpine"}, @@ -124,6 +138,10 @@ func TestPackage(t *testing.T) { t.Fatal(err) } + if err := os.Mkdir("toot", 0777); err != nil { + t.Fatal(err) + } + ensureTestHome(helmpath.Home(tmp), t) oldhome := homePath() helmHome = tmp From 05d0fcb774d8134b49e60587f4dd7db98c8ef7b7 Mon Sep 17 00:00:00 2001 From: Jonathan Chauncey Date: Tue, 21 Mar 2017 15:21:24 -0400 Subject: [PATCH 11/48] feat(hooks): Adds weighted hooks closes #2136 * Adds new annotation `helm.sh/hookWeight` * Sorts executing hooks of similar kind in ascending order * There is no upper or lower bounds on the weights --- pkg/hooks/hooks.go | 3 ++ pkg/proto/hapi/release/hook.pb.go | 2 + pkg/tiller/hook_sorter.go | 53 ++++++++++++++++++++++ pkg/tiller/hook_sorter_test.go | 73 +++++++++++++++++++++++++++++++ pkg/tiller/hooks.go | 10 +++++ pkg/tiller/release_server.go | 14 +++--- 6 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 pkg/tiller/hook_sorter.go create mode 100644 pkg/tiller/hook_sorter_test.go diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index ee467ea27..d6966a87a 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -23,6 +23,9 @@ import ( // HookAnno is the label name for a hook const HookAnno = "helm.sh/hook" +// HookWeightAnno is the label name for a hook weight +const HookWeightAnno = "helm.sh/hookWeight" + // Types of hooks const ( PreInstall = "pre-install" diff --git a/pkg/proto/hapi/release/hook.pb.go b/pkg/proto/hapi/release/hook.pb.go index 956ca15f3..435d2c80e 100644 --- a/pkg/proto/hapi/release/hook.pb.go +++ b/pkg/proto/hapi/release/hook.pb.go @@ -100,6 +100,8 @@ type Hook struct { Events []Hook_Event `protobuf:"varint,5,rep,packed,name=events,enum=hapi.release.Hook_Event" json:"events,omitempty"` // LastRun indicates the date/time this was last run. LastRun *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=last_run,json=lastRun" json:"last_run,omitempty"` + // Weight indicates the sort order for execution among similar Hook types + Weight int `protobuf:"bytes,7,opt,name=weight" json:"weight,omitempty"` } func (m *Hook) Reset() { *m = Hook{} } diff --git a/pkg/tiller/hook_sorter.go b/pkg/tiller/hook_sorter.go new file mode 100644 index 000000000..42d546620 --- /dev/null +++ b/pkg/tiller/hook_sorter.go @@ -0,0 +1,53 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "sort" + + "k8s.io/helm/pkg/proto/hapi/release" +) + +// sortByHookWeight does an in-place sort of hooks by their supplied weight. +func sortByHookWeight(hooks []*release.Hook) []*release.Hook { + hs := newHookWeightSorter(hooks) + sort.Sort(hs) + return hs.hooks +} + +type hookWeightSorter struct { + hooks []*release.Hook +} + +func newHookWeightSorter(h []*release.Hook) *hookWeightSorter { + return &hookWeightSorter{ + hooks: h, + } +} + +func (hs *hookWeightSorter) Len() int { return len(hs.hooks) } + +func (hs *hookWeightSorter) Swap(i, j int) { + hs.hooks[i], hs.hooks[j] = hs.hooks[j], hs.hooks[i] +} + +func (hs *hookWeightSorter) Less(i, j int) bool { + if hs.hooks[i].Weight == hs.hooks[j].Weight { + return hs.hooks[i].Name < hs.hooks[j].Name + } + return hs.hooks[i].Weight < hs.hooks[j].Weight +} diff --git a/pkg/tiller/hook_sorter_test.go b/pkg/tiller/hook_sorter_test.go new file mode 100644 index 000000000..ac5b9bf8d --- /dev/null +++ b/pkg/tiller/hook_sorter_test.go @@ -0,0 +1,73 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "testing" + + "k8s.io/helm/pkg/proto/hapi/release" +) + +func TestHookSorter(t *testing.T) { + hooks := []*release.Hook{ + { + Name: "g", + Kind: "pre-install", + Weight: 99, + }, + { + Name: "f", + Kind: "pre-install", + Weight: 3, + }, + { + Name: "b", + Kind: "pre-install", + Weight: -3, + }, + { + Name: "e", + Kind: "pre-install", + Weight: 3, + }, + { + Name: "a", + Kind: "pre-install", + Weight: -10, + }, + { + Name: "c", + Kind: "pre-install", + Weight: 0, + }, + { + Name: "d", + Kind: "pre-install", + Weight: 3, + }, + } + + res := sortByHookWeight(hooks) + got := "" + expect := "abcdefg" + for _, r := range res { + got += r.Name + } + if got != expect { + t.Errorf("Expected %q, got %q", expect, got) + } +} diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 0b5510b8c..b6dfeb9cd 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -20,6 +20,7 @@ import ( "fmt" "log" "path" + "strconv" "strings" "github.com/ghodss/yaml" @@ -109,12 +110,21 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort generic = append(generic, manifest{name: n, content: c, head: &sh}) continue } + + hw := 0 + hws, _ := sh.Metadata.Annotations[hooks.HookWeightAnno] + hw, err = strconv.Atoi(hws) + if err != nil { + hw = 0 + } + h := &release.Hook{ Name: sh.Metadata.Name, Kind: sh.Kind, Path: n, Manifest: c, Events: []release.Hook_Event{}, + Weight: hw, } isHook := false diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 873d2335a..a73dd9a73 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -911,17 +911,18 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin } log.Printf("Executing %s hooks for %s", hook, name) + executingHooks := []*release.Hook{} for _, h := range hs { - found := false for _, e := range h.Events { if e == code { - found = true + executingHooks = append(executingHooks, h) } } - // If this doesn't implement the hook, skip it. - if !found { - continue - } + } + + executingHooks = sortByHookWeight(executingHooks) + + for _, h := range executingHooks { b := bytes.NewBufferString(h.Manifest) if err := kubeCli.Create(namespace, b, timeout, false); err != nil { @@ -937,6 +938,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin } h.LastRun = timeconv.Now() } + log.Printf("Hooks complete for %s %s", hook, name) return nil } From 91e7f6bfd69c4fde8cd382051c17486da304fc19 Mon Sep 17 00:00:00 2001 From: Jonathan Chauncey Date: Wed, 22 Mar 2017 14:57:09 -0400 Subject: [PATCH 12/48] docs(docs/charts_hooks.md): Add information about hook weights --- docs/charts_hooks.md | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index 56cecd788..9b64199e2 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -58,16 +58,18 @@ hooks, the lifecycle is altered like this: 1. User runs `helm install foo` 2. Chart is loaded into Tiller 3. After some verification, Tiller renders the `foo` templates -4. Tiller executes the `pre-install` hook (loading hook resources into +4. Tiller prepares to execute the `pre-install` hooks (loading hook resources into Kubernetes) -5. Tiller waits until the hook is "Ready" -6. Tiller loads the resulting resources into Kubernetes. Note that if the `--wait` +5. Tiller sorts hooks by weight (assigning a weight of 0 by default) and by name for those hooks with the same weight in ascending order. +6. Tiller then loads the hook with the lowest weight first (negative to positive) +7. Tiller waits until the hook is "Ready" +8. Tiller loads the resulting resources into Kubernetes. Note that if the `--wait` flag is set, Tiller will wait until all resources are in a ready state and will not run the `post-install` hook until they are ready. -7. Tiller executes the `post-install` hook (loading hook resources) -8. Tiller waits until the hook is "Ready" -9. Tiller returns the release name (and other data) to the client -10. The client exits +9. Tiller executes the `post-install` hook (loading hook resources) +10. Tiller waits until the hook is "Ready" +11. Tiller returns the release name (and other data) to the client +12. The client exits What does it mean to wait until a hook is ready? This depends on the resource declared in the hook. If the resources is a `Job` kind, Tiller @@ -114,6 +116,7 @@ metadata: # This is what defines this resource as a hook. Without this line, the # job is considered part of the release. "helm.sh/hook": post-install + "helm.sh/hookWeight": "-5" spec: template: metadata: @@ -154,3 +157,12 @@ When subcharts declare hooks, those are also evaluated. There is no way for a top-level chart to disable the hooks declared by subcharts. And again, there is no guaranteed ordering. +It is also possible to define a weight for a hook which will help build a deterministic executing order. Weights are defined using the following annotation: + +``` + annotations: + "helm.sh/hookWeight": "5" +``` + +Hook weights can be positive or negative numbers but must be represented as strings. When Tiller starts the execution cycle of hooks of a particular Kind it will sort those hooks in ascending order. + From 694402a7638cad445d5e118b512a9ddf34427ef8 Mon Sep 17 00:00:00 2001 From: Mario Lamontagne Date: Fri, 24 Mar 2017 15:23:54 -0400 Subject: [PATCH 13/48] Fix typo in chart guide --- docs/chart_template_guide/subcharts_and_globals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chart_template_guide/subcharts_and_globals.md b/docs/chart_template_guide/subcharts_and_globals.md index 059b0b601..69d828c11 100644 --- a/docs/chart_template_guide/subcharts_and_globals.md +++ b/docs/chart_template_guide/subcharts_and_globals.md @@ -137,7 +137,7 @@ data: salad: {{ .Values.global.salad }} ``` -`mysubchart/tempaltes/configmap.yaml`: +`mysubchart/templates/configmap.yaml`: ```yaml apiVersion: v1 From f4486d485855f389e8244ea56cc3f0ebd43e1e49 Mon Sep 17 00:00:00 2001 From: John Welsh Date: Sat, 25 Mar 2017 01:57:25 -0700 Subject: [PATCH 14/48] fix(helm): Don't assume index.yaml is sorted This change sorts the helm index file after downloading from a repo. Closes #2186 --- pkg/repo/index.go | 1 + pkg/repo/index_test.go | 13 +++++++ pkg/repo/testdata/local-index-unordered.yaml | 39 ++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 pkg/repo/testdata/local-index-unordered.yaml diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 148caac4a..1ba5500d6 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -243,6 +243,7 @@ func loadIndex(data []byte) (*IndexFile, error) { if err := yaml.Unmarshal(data, i); err != nil { return i, err } + i.SortEntries() if i.APIVersion == "" { // When we leave Beta, we should remove legacy support and just // return this error: diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index ccf6cfd3c..f96193176 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -29,6 +29,7 @@ import ( const ( testfile = "testdata/local-index.yaml" + unorderedTestfile = "testdata/local-index-unordered.yaml" testRepo = "test-repo" ) @@ -82,6 +83,18 @@ func TestLoadIndexFile(t *testing.T) { verifyLocalIndex(t, i) } +func TestLoadUnorderedIndex(t *testing.T) { + b, err := ioutil.ReadFile(unorderedTestfile) + if err != nil { + t.Fatal(err) + } + i, err := loadIndex(b) + if err != nil { + t.Fatal(err) + } + verifyLocalIndex(t, i) +} + func TestMerge(t *testing.T) { ind1 := NewIndexFile() ind1.Add(&chart.Metadata{ diff --git a/pkg/repo/testdata/local-index-unordered.yaml b/pkg/repo/testdata/local-index-unordered.yaml new file mode 100644 index 000000000..ec529f110 --- /dev/null +++ b/pkg/repo/testdata/local-index-unordered.yaml @@ -0,0 +1,39 @@ +apiVersion: v1 +entries: + nginx: + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz + name: nginx + description: string + version: 0.1.0 + home: https://github.com/something + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + - urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz + name: nginx + description: string + version: 0.2.0 + home: https://github.com/something/else + digest: "sha256:1234567890abcdef" + keywords: + - popular + - web server + - proxy + alpine: + - urls: + - https://kubernetes-charts.storage.googleapis.com/alpine-1.0.0.tgz + - http://storage2.googleapis.com/kubernetes-charts/alpine-1.0.0.tgz + name: alpine + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - linux + - alpine + - small + - sumtin + digest: "sha256:1234567890abcdef" From e42f39a1147a2965a19455ed60077f9a1f47c570 Mon Sep 17 00:00:00 2001 From: John Welsh Date: Sat, 25 Mar 2017 03:24:32 -0700 Subject: [PATCH 15/48] Forgot gofmt --- pkg/repo/index_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index f96193176..9eca22996 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -28,9 +28,9 @@ import ( ) const ( - testfile = "testdata/local-index.yaml" + testfile = "testdata/local-index.yaml" unorderedTestfile = "testdata/local-index-unordered.yaml" - testRepo = "test-repo" + testRepo = "test-repo" ) func TestIndexFile(t *testing.T) { From 611bba0f51770d9b6671183171505e68b801383d Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Sat, 25 Mar 2017 19:27:05 -0700 Subject: [PATCH 16/48] fix(helm): manifests string parsing works for newlines in the manifests Trying to fix #2158 --- pkg/releaseutil/manifest.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index b6bb87b0a..5cb1fe3e9 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -40,7 +40,9 @@ func SplitManifests(bigfile string) map[string]string { sep := "\n---\n" tpl := "manifest-%d" res := map[string]string{} - tmp := strings.Split(bigfile, sep) + // Making sure yaml formatting doesn't matter when generating manifest from string. + bigFileTmp := strings.TrimSpace(bigfile) + tmp := strings.Split(bigFileTmp, sep) for i, d := range tmp { res[fmt.Sprintf(tpl, i)] = d } From c17ce5f9c1bd5d32ccdd0e48f3607ffd41707f1e Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Sun, 26 Mar 2017 01:11:41 -0700 Subject: [PATCH 17/48] fix(tiller): adding kind to tiller client logs Closes #2162 --- pkg/kube/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index a43627416..b3795fb3c 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -152,7 +152,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { return "", err } err = perform(c, namespace, infos, func(info *resource.Info) error { - log.Printf("Doing get for: '%s'", info.Name) + log.Printf("Doing get for %s: '%s'", info.Mapping.GroupVersionKind.Kind, info.Name) obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export) if err != nil { return err @@ -390,7 +390,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return fmt.Errorf("failed to create patch: %s", err) } if patch == nil { - log.Printf("Looks like there are no changes for %s", target.Name) + log.Printf("Looks like there are no changes for %s %s", target.Mapping.GroupVersionKind.Kind, target.Name) // This needs to happen to make sure that tiller has the latest info from the API // Otherwise there will be no labels and other functions that use labels will panic if err := target.Get(); err != nil { From 837da9360edea2d53169b2199202ba7a8a10a298 Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Sun, 26 Mar 2017 23:26:00 -0700 Subject: [PATCH 18/48] fix(helm): using regexp to match whitespaces instead --- pkg/releaseutil/manifest.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index 5cb1fe3e9..f4545e5b5 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -18,6 +18,7 @@ package releaseutil import ( "fmt" + "regexp" "strings" ) @@ -37,12 +38,12 @@ func SplitManifests(bigfile string) map[string]string { // Basically, we're quickly splitting a stream of YAML documents into an // array of YAML docs. In the current implementation, the file name is just // a place holder, and doesn't have any further meaning. - sep := "\n---\n" + sep := regexp.MustCompile("(?:^|\\s*\n)---\\s*") tpl := "manifest-%d" res := map[string]string{} - // Making sure yaml formatting doesn't matter when generating manifest from string. + // Making sure YAML formatting doesn't matter when generating manifest from string. bigFileTmp := strings.TrimSpace(bigfile) - tmp := strings.Split(bigFileTmp, sep) + tmp := sep.Split(bigFileTmp, -1) for i, d := range tmp { res[fmt.Sprintf(tpl, i)] = d } From 73fd0e4557e6557121b573db51b731bc214966ce Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Mon, 27 Mar 2017 22:40:25 -0700 Subject: [PATCH 19/48] fix(tiller): now better formatting --- pkg/kube/client.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index b3795fb3c..9990cc86e 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -152,7 +152,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { return "", err } err = perform(c, namespace, infos, func(info *resource.Info) error { - log.Printf("Doing get for %s: '%s'", info.Mapping.GroupVersionKind.Kind, info.Name) + log.Printf("Doing get for %q: %q", info.Mapping.GroupVersionKind.Kind, info.Name) obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export) if err != nil { return err @@ -390,7 +390,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return fmt.Errorf("failed to create patch: %s", err) } if patch == nil { - log.Printf("Looks like there are no changes for %s %s", target.Mapping.GroupVersionKind.Kind, target.Name) + log.Printf("Looks like there are no changes for %q %q", target.Mapping.GroupVersionKind.Kind, target.Name) // This needs to happen to make sure that tiller has the latest info from the API // Otherwise there will be no labels and other functions that use labels will panic if err := target.Get(); err != nil { From 1cf197d69e824e2747d674b3e4e2e0c6708fbcfa Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Mon, 27 Mar 2017 22:48:01 -0700 Subject: [PATCH 20/48] fix(helm): fix comments and removed unwanted split --- pkg/releaseutil/manifest.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index f4545e5b5..e86e3892a 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -32,19 +32,19 @@ type SimpleHead struct { } `json:"metadata,omitempty"` } +var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") + // SplitManifests takes a string of manifest and returns a map contains individual manifests func SplitManifests(bigfile string) map[string]string { // This is not the best way of doing things, but it's how k8s itself does it. // Basically, we're quickly splitting a stream of YAML documents into an // array of YAML docs. In the current implementation, the file name is just // a place holder, and doesn't have any further meaning. - sep := regexp.MustCompile("(?:^|\\s*\n)---\\s*") tpl := "manifest-%d" res := map[string]string{} - // Making sure YAML formatting doesn't matter when generating manifest from string. - bigFileTmp := strings.TrimSpace(bigfile) - tmp := sep.Split(bigFileTmp, -1) - for i, d := range tmp { + // Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly. + docs := sep.Split(bigfile, -1) + for i, d := range docs { res[fmt.Sprintf(tpl, i)] = d } return res From 6aeadb272d53872f41b48c4b7aed9e58fafe6c56 Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Mon, 27 Mar 2017 22:52:58 -0700 Subject: [PATCH 21/48] fix tests --- pkg/releaseutil/manifest.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index e86e3892a..9a62efe85 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -19,7 +19,6 @@ package releaseutil import ( "fmt" "regexp" - "strings" ) // SimpleHead defines what the structure of the head of a manifest file From b208258f5304f8beb748f23e9f43129bb6ea3362 Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Tue, 28 Mar 2017 16:20:17 -0700 Subject: [PATCH 22/48] fix(helm): more quotation fixes --- pkg/kube/client.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 9990cc86e..788ddd754 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -152,7 +152,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { return "", err } err = perform(c, namespace, infos, func(info *resource.Info) error { - log.Printf("Doing get for %q: %q", info.Mapping.GroupVersionKind.Kind, info.Name) + log.Printf("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name) obj, err := resource.NewHelper(info.Client, info.Mapping).Get(info.Namespace, info.Name, info.Export) if err != nil { return err @@ -186,7 +186,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { } for _, o := range ot { if err := p.PrintObj(o, buf); err != nil { - log.Printf("failed to print object type '%s', object: '%s' :\n %v", t, o, err) + log.Printf("failed to print object type %d, object: %d :\n %v", t, o, err) return "", err } } @@ -233,17 +233,17 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader } kind := info.Mapping.GroupVersionKind.Kind - log.Printf("Created a new %s called %s\n", kind, info.Name) + log.Printf("Created a new %s called %d\n", kind, info.Name) return nil } originalInfo := original.Get(info) if originalInfo == nil { - return fmt.Errorf("no resource with the name %s found", info.Name) + return fmt.Errorf("no resource with the name %d found", info.Name) } if err := updateResource(c, info, originalInfo.Object, recreate); err != nil { - log.Printf("error updating the resource %s:\n\t %v", info.Name, err) + log.Printf("error updating the resource %d:\n\t %v", info.Name, err) updateErrors = append(updateErrors, err.Error()) } @@ -258,9 +258,9 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader } for _, info := range original.Difference(target) { - log.Printf("Deleting %s in %s...", info.Name, info.Namespace) + log.Printf("Deleting %d in %s...", info.Name, info.Namespace) if err := deleteResource(c, info); err != nil { - log.Printf("Failed to delete %s, err: %s", info.Name, err) + log.Printf("Failed to delete %d, err: %s", info.Name, err) } } if shouldWait { @@ -278,7 +278,7 @@ func (c *Client) Delete(namespace string, reader io.Reader) error { return err } return perform(c, namespace, infos, func(info *resource.Info) error { - log.Printf("Starting delete for %s %s", info.Name, info.Mapping.GroupVersionKind.Kind) + log.Printf("Starting delete for %d %s", info.Name, info.Mapping.GroupVersionKind.Kind) err := deleteResource(c, info) return skipIfNotFound(err) }) @@ -350,7 +350,7 @@ func deleteResource(c *Client, info *resource.Info) error { } return err } - log.Printf("Using reaper for deleting %s", info.Name) + log.Printf("Using reaper for deleting %d", info.Name) return reaper.Stop(info.Namespace, info.Name, 0, nil) } @@ -390,7 +390,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, return fmt.Errorf("failed to create patch: %s", err) } if patch == nil { - log.Printf("Looks like there are no changes for %q %q", target.Mapping.GroupVersionKind.Kind, target.Name) + log.Printf("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) // This needs to happen to make sure that tiller has the latest info from the API // Otherwise there will be no labels and other functions that use labels will panic if err := target.Get(); err != nil { From 76ebf20aac7b7939904130a3bf61b56e22cdf904 Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Tue, 28 Mar 2017 16:32:05 -0700 Subject: [PATCH 23/48] fix(helm): fix typo --- pkg/kube/client.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 788ddd754..ef8a88639 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -186,7 +186,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { } for _, o := range ot { if err := p.PrintObj(o, buf); err != nil { - log.Printf("failed to print object type %d, object: %d :\n %v", t, o, err) + log.Printf("failed to print object type %s, object: %q :\n %v", t, o, err) return "", err } } @@ -233,17 +233,17 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader } kind := info.Mapping.GroupVersionKind.Kind - log.Printf("Created a new %s called %d\n", kind, info.Name) + log.Printf("Created a new %s called %q\n", kind, info.Name) return nil } originalInfo := original.Get(info) if originalInfo == nil { - return fmt.Errorf("no resource with the name %d found", info.Name) + return fmt.Errorf("no resource with the name %q found", info.Name) } if err := updateResource(c, info, originalInfo.Object, recreate); err != nil { - log.Printf("error updating the resource %d:\n\t %v", info.Name, err) + log.Printf("error updating the resource %q:\n\t %v", info.Name, err) updateErrors = append(updateErrors, err.Error()) } @@ -258,9 +258,9 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader } for _, info := range original.Difference(target) { - log.Printf("Deleting %d in %s...", info.Name, info.Namespace) + log.Printf("Deleting %q in %s...", info.Name, info.Namespace) if err := deleteResource(c, info); err != nil { - log.Printf("Failed to delete %d, err: %s", info.Name, err) + log.Printf("Failed to delete %q, err: %s", info.Name, err) } } if shouldWait { @@ -278,7 +278,7 @@ func (c *Client) Delete(namespace string, reader io.Reader) error { return err } return perform(c, namespace, infos, func(info *resource.Info) error { - log.Printf("Starting delete for %d %s", info.Name, info.Mapping.GroupVersionKind.Kind) + log.Printf("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) err := deleteResource(c, info) return skipIfNotFound(err) }) @@ -350,7 +350,7 @@ func deleteResource(c *Client, info *resource.Info) error { } return err } - log.Printf("Using reaper for deleting %d", info.Name) + log.Printf("Using reaper for deleting %q", info.Name) return reaper.Stop(info.Namespace, info.Name, 0, nil) } From e6b79e138b1ec26c519b805fd9b100fe08128451 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Mon, 27 Mar 2017 17:31:01 -0700 Subject: [PATCH 24/48] fix(helm): local path in requirements.yaml relative to working dir closes bug: 2103 --- pkg/downloader/manager.go | 17 ++--------------- pkg/resolver/resolver.go | 33 ++++++++++++++++++++++++++------- pkg/resolver/resolver_test.go | 4 ++-- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index bf9b384fd..f8d8cac9c 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -325,14 +325,7 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, for _, dd := range deps { // if dep chart is from local path, verify the path is valid if strings.HasPrefix(dd.Repository, "file://") { - depPath, err := filepath.Abs(strings.TrimPrefix(dd.Repository, "file://")) - if err != nil { - return nil, err - } - - if _, err = os.Stat(depPath); os.IsNotExist(err) { - return nil, fmt.Errorf("directory %s not found", depPath) - } else if err != nil { + if _, err := resolver.GetLocalPath(dd.Repository, m.ChartPath); err != nil { return nil, err } @@ -537,17 +530,11 @@ func tarFromLocalDir(chartpath string, name string, repo string, version string) return "", fmt.Errorf("wrong format: chart %s repository %s", name, repo) } - origPath, err := filepath.Abs(strings.TrimPrefix(repo, "file://")) + origPath, err := resolver.GetLocalPath(repo, chartpath) if err != nil { return "", err } - if _, err = os.Stat(origPath); os.IsNotExist(err) { - return "", fmt.Errorf("directory %s not found: %s", origPath, err) - } else if err != nil { - return "", err - } - ch, err := chartutil.LoadDir(origPath) if err != nil { return "", err diff --git a/pkg/resolver/resolver.go b/pkg/resolver/resolver.go index 858ca272d..e8a599002 100644 --- a/pkg/resolver/resolver.go +++ b/pkg/resolver/resolver.go @@ -58,14 +58,8 @@ func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]st missing := []string{} for i, d := range reqs.Dependencies { if strings.HasPrefix(d.Repository, "file://") { - depPath, err := filepath.Abs(strings.TrimPrefix(d.Repository, "file://")) - if err != nil { - return nil, err - } - if _, err = os.Stat(depPath); os.IsNotExist(err) { - return nil, fmt.Errorf("directory %s not found", depPath) - } else if err != nil { + if _, err := GetLocalPath(d.Repository, r.chartpath); err != nil { return nil, err } @@ -136,3 +130,28 @@ func HashReq(req *chartutil.Requirements) (string, error) { s, err := provenance.Digest(bytes.NewBuffer(data)) return "sha256:" + s, err } + +// GetLocalPath generates absolute local path when use +// "file://" in repository of requirements +func GetLocalPath(repo string, chartpath string) (string, error) { + var depPath string + var err error + p := strings.TrimPrefix(repo, "file://") + + // root path is absolute + if strings.HasPrefix(p, "/") { + if depPath, err = filepath.Abs(p); err != nil { + return "", err + } + } else { + depPath = filepath.Join(chartpath, p) + } + + if _, err = os.Stat(depPath); os.IsNotExist(err) { + return "", fmt.Errorf("directory %s not found", depPath) + } else if err != nil { + return "", err + } + + return depPath, nil +} diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index 4a4f853b7..8d4b86019 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -81,12 +81,12 @@ func TestResolve(t *testing.T) { name: "repo from valid local path", req: &chartutil.Requirements{ Dependencies: []*chartutil.Dependency{ - {Name: "signtest", Repository: "file://../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, + {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, }, }, expect: &chartutil.RequirementsLock{ Dependencies: []*chartutil.Dependency{ - {Name: "signtest", Repository: "file://../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, + {Name: "signtest", Repository: "file://../../../../cmd/helm/testdata/testcharts/signtest", Version: "0.1.0"}, }, }, }, From 2dc69cfdb097f24434eae46a36744894e965e826 Mon Sep 17 00:00:00 2001 From: Gergo Huszty Date: Wed, 29 Mar 2017 14:27:23 +0000 Subject: [PATCH 25/48] able to build without being in a git repository --- versioning.mk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/versioning.mk b/versioning.mk index 4825c4e72..b336a7999 100644 --- a/versioning.mk +++ b/versioning.mk @@ -1,9 +1,9 @@ MUTABLE_VERSION ?= canary -GIT_COMMIT := $(shell git rev-parse HEAD) -GIT_SHA := $(shell git rev-parse --short HEAD) -GIT_TAG := $(shell git describe --tags --abbrev=0 2>/dev/null) -GIT_DIRTY = $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean") +GIT_COMMIT ?= $(shell git rev-parse HEAD) +GIT_SHA ?= $(shell git rev-parse --short HEAD) +GIT_TAG ?= $(shell git describe --tags --abbrev=0 2>/dev/null) +GIT_DIRTY ?= $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean") ifdef VERSION DOCKER_VERSION = $(VERSION) From 7ccfc6d7d48c874bb5bd97624372e70bbba656c8 Mon Sep 17 00:00:00 2001 From: "Steven E. Harris" Date: Wed, 29 Mar 2017 17:31:21 -0400 Subject: [PATCH 26/48] Sort dependent RBAC role and binding kinds During installation and uninstallation, handle the RBAC-related ClusterRole, ClusterRoleBinding, Role, and RoleBinding kinds in an order that respects their potential referential integrity, namely that ClusterRoleBindings can refer to ClusterRoles and ServiceAccounts, and RoleBindings can refer to ClusterRoles, Roles, and ServiceAccounts. Fixes #2199. --- pkg/tiller/kind_sorter.go | 44 +++++++++++++++++-- pkg/tiller/kind_sorter_test.go | 78 +++++++++++++++++++++++----------- 2 files changed, 94 insertions(+), 28 deletions(-) diff --git a/pkg/tiller/kind_sorter.go b/pkg/tiller/kind_sorter.go index ff991fc4a..bf40e7c6e 100644 --- a/pkg/tiller/kind_sorter.go +++ b/pkg/tiller/kind_sorter.go @@ -23,11 +23,47 @@ import ( // SortOrder is an ordering of Kinds. type SortOrder []string -// InstallOrder is the order in which manifests should be installed (by Kind) -var InstallOrder SortOrder = []string{"Namespace", "Secret", "ConfigMap", "PersistentVolume", "PersistentVolumeClaim", "ServiceAccount", "Service", "Pod", "ReplicationController", "Deployment", "DaemonSet", "Ingress", "Job"} +// InstallOrder is the order in which manifests should be installed (by Kind). +var InstallOrder SortOrder = []string{ + "Namespace", + "Secret", + "ConfigMap", + "PersistentVolume", + "PersistentVolumeClaim", + "ServiceAccount", + "ClusterRole", + "ClusterRoleBinding", + "Role", + "RoleBinding", + "Service", + "Pod", + "ReplicationController", + "Deployment", + "DaemonSet", + "Ingress", + "Job", +} -// UninstallOrder is the order in which manifests should be uninstalled (by Kind) -var UninstallOrder SortOrder = []string{"Service", "Pod", "ReplicationController", "Deployment", "DaemonSet", "ConfigMap", "Secret", "PersistentVolumeClaim", "PersistentVolume", "ServiceAccount", "Ingress", "Job", "Namespace"} +// UninstallOrder is the order in which manifests should be uninstalled (by Kind). +var UninstallOrder SortOrder = []string{ + "Service", + "Pod", + "ReplicationController", + "Deployment", + "DaemonSet", + "ConfigMap", + "Secret", + "PersistentVolumeClaim", + "PersistentVolume", + "RoleBinding", + "Role", + "ClusterRoleBinding", + "ClusterRole", + "ServiceAccount", + "Ingress", + "Job", + "Namespace", +} // sortByKind does an in-place sort of manifests by Kind. // diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index 212b8aefc..55fc10251 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -17,6 +17,7 @@ limitations under the License. package tiller import ( + "bytes" "testing" util "k8s.io/helm/pkg/releaseutil" @@ -27,48 +28,77 @@ func TestKindSorter(t *testing.T) { { name: "m", content: "", - head: &util.SimpleHead{Kind: "Deployment"}, + head: &util.SimpleHead{Kind: "ClusterRole"}, }, { - name: "l", + name: " ", content: "", - head: &util.SimpleHead{Kind: "Service"}, + head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, + }, + { + name: "e", + content: "", + head: &util.SimpleHead{Kind: "ConfigMap"}, + }, + { + name: "k", + content: "", + head: &util.SimpleHead{Kind: "Deployment"}, }, { name: "!", content: "", head: &util.SimpleHead{Kind: "HonkyTonkSet"}, }, + { + name: "s", + content: "", + head: &util.SimpleHead{Kind: "Job"}, + }, { name: "h", content: "", head: &util.SimpleHead{Kind: "Namespace"}, }, { - name: "e", + name: "w", content: "", - head: &util.SimpleHead{Kind: "ConfigMap"}, + head: &util.SimpleHead{Kind: "Role"}, + }, + { + name: "o", + content: "", + head: &util.SimpleHead{Kind: "RoleBinding"}, + }, + { + name: "r", + content: "", + head: &util.SimpleHead{Kind: "Service"}, + }, + { + name: "l", + content: "", + head: &util.SimpleHead{Kind: "ServiceAccount"}, }, } - res := sortByKind(manifests, InstallOrder) - got := "" - expect := "helm!" - for _, r := range res { - got += r.name - } - if got != expect { - t.Errorf("Expected %q, got %q", expect, got) - } - - expect = "lmeh!" - got = "" - res = sortByKind(manifests, UninstallOrder) - for _, r := range res { - got += r.name + for _, test := range []struct { + description string + order SortOrder + expected string + }{ + {"install", InstallOrder, "helm works!"}, + {"uninstall", UninstallOrder, "rkeow mlsh!"}, + } { + var buf bytes.Buffer + t.Run(test.description, func(t *testing.T) { + defer buf.Reset() + for _, r := range sortByKind(manifests, test.order) { + buf.WriteString(r.name) + } + if got := buf.String(); got != test.expected { + t.Errorf("Expected %q, got %q", test.expected, got) + } + }) } - if got != expect { - t.Errorf("Expected %q, got %q", expect, got) - } - } From 4178ec08ac57284075b3177522924b3f58fc4627 Mon Sep 17 00:00:00 2001 From: "Steven E. Harris" Date: Thu, 30 Mar 2017 12:04:59 -0400 Subject: [PATCH 27/48] Add more object kinds to sorted installation order The following kinds are newly addressed: CronJob LimitRange ReplicaSet ResourceQuota StatefulSet Furthermore, adjust the installation and uninstallation order to better respect likely dependencies. Bare pods remain a difficult one to place properly in both sequences. --- pkg/tiller/kind_sorter.go | 34 ++++++++++---- pkg/tiller/kind_sorter_test.go | 85 +++++++++++++++++++++++++++++----- 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/pkg/tiller/kind_sorter.go b/pkg/tiller/kind_sorter.go index bf40e7c6e..f08d13569 100644 --- a/pkg/tiller/kind_sorter.go +++ b/pkg/tiller/kind_sorter.go @@ -24,8 +24,12 @@ import ( type SortOrder []string // InstallOrder is the order in which manifests should be installed (by Kind). +// +// Those occurring earlier in the list get installed before those occurring later in the list. var InstallOrder SortOrder = []string{ "Namespace", + "ResourceQuota", + "LimitRange", "Secret", "ConfigMap", "PersistentVolume", @@ -36,32 +40,42 @@ var InstallOrder SortOrder = []string{ "Role", "RoleBinding", "Service", + "DaemonSet", "Pod", "ReplicationController", + "ReplicaSet", "Deployment", - "DaemonSet", - "Ingress", + "StatefulSet", "Job", + "CronJob", + "Ingress", } // UninstallOrder is the order in which manifests should be uninstalled (by Kind). +// +// Those occurring earlier in the list get uninstalled before those occurring later in the list. var UninstallOrder SortOrder = []string{ + "Ingress", "Service", - "Pod", - "ReplicationController", + "CronJob", + "Job", + "StatefulSet", "Deployment", + "ReplicaSet", + "ReplicationController", + "Pod", "DaemonSet", - "ConfigMap", - "Secret", - "PersistentVolumeClaim", - "PersistentVolume", "RoleBinding", "Role", "ClusterRoleBinding", "ClusterRole", "ServiceAccount", - "Ingress", - "Job", + "PersistentVolumeClaim", + "PersistentVolume", + "ConfigMap", + "Secret", + "LimitRange", + "ResourceQuota", "Namespace", } diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index 55fc10251..4fe4b09e6 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -26,12 +26,12 @@ import ( func TestKindSorter(t *testing.T) { manifests := []manifest{ { - name: "m", + name: "i", content: "", head: &util.SimpleHead{Kind: "ClusterRole"}, }, { - name: " ", + name: "j", content: "", head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, }, @@ -41,7 +41,17 @@ func TestKindSorter(t *testing.T) { head: &util.SimpleHead{Kind: "ConfigMap"}, }, { - name: "k", + name: "u", + content: "", + head: &util.SimpleHead{Kind: "CronJob"}, + }, + { + name: "n", + content: "", + head: &util.SimpleHead{Kind: "DaemonSet"}, + }, + { + name: "r", content: "", head: &util.SimpleHead{Kind: "Deployment"}, }, @@ -51,35 +61,85 @@ func TestKindSorter(t *testing.T) { head: &util.SimpleHead{Kind: "HonkyTonkSet"}, }, { - name: "s", + name: "v", + content: "", + head: &util.SimpleHead{Kind: "Ingress"}, + }, + { + name: "t", content: "", head: &util.SimpleHead{Kind: "Job"}, }, { - name: "h", + name: "c", + content: "", + head: &util.SimpleHead{Kind: "LimitRange"}, + }, + { + name: "a", content: "", head: &util.SimpleHead{Kind: "Namespace"}, }, { - name: "w", + name: "f", content: "", - head: &util.SimpleHead{Kind: "Role"}, + head: &util.SimpleHead{Kind: "PersistentVolume"}, + }, + { + name: "g", + content: "", + head: &util.SimpleHead{Kind: "PersistentVolumeClaim"}, }, { name: "o", content: "", + head: &util.SimpleHead{Kind: "Pod"}, + }, + { + name: "q", + content: "", + head: &util.SimpleHead{Kind: "ReplicaSet"}, + }, + { + name: "p", + content: "", + head: &util.SimpleHead{Kind: "ReplicationController"}, + }, + { + name: "b", + content: "", + head: &util.SimpleHead{Kind: "ResourceQuota"}, + }, + { + name: "k", + content: "", + head: &util.SimpleHead{Kind: "Role"}, + }, + { + name: "l", + content: "", head: &util.SimpleHead{Kind: "RoleBinding"}, }, { - name: "r", + name: "d", + content: "", + head: &util.SimpleHead{Kind: "Secret"}, + }, + { + name: "m", content: "", head: &util.SimpleHead{Kind: "Service"}, }, { - name: "l", + name: "h", content: "", head: &util.SimpleHead{Kind: "ServiceAccount"}, }, + { + name: "s", + content: "", + head: &util.SimpleHead{Kind: "StatefulSet"}, + }, } for _, test := range []struct { @@ -87,11 +147,14 @@ func TestKindSorter(t *testing.T) { order SortOrder expected string }{ - {"install", InstallOrder, "helm works!"}, - {"uninstall", UninstallOrder, "rkeow mlsh!"}, + {"install", InstallOrder, "abcdefghijklmnopqrstuv!"}, + {"uninstall", UninstallOrder, "vmutsrqponlkjihgfedcba!"}, } { var buf bytes.Buffer t.Run(test.description, func(t *testing.T) { + if got, want := len(test.expected), len(manifests); got != want { + t.Fatalf("Expected %d names in order, got %d", want, got) + } defer buf.Reset() for _, r := range sortByKind(manifests, test.order) { buf.WriteString(r.name) From a484d00e33039d91a42104060d5e59e623e1f9ae Mon Sep 17 00:00:00 2001 From: Sushil Kumar Date: Thu, 30 Mar 2017 13:27:04 -0700 Subject: [PATCH 28/48] Fixes TestInstallRelease_VerifyOptions & TestUpdateRelease_VerifyOptions Following tests were failing unnoticed - [ ] TestUpdateRelease_VerifyOptions - [ ] TestUpdateRelease_VerifyOptions Fixed those tests for the reason of failure. Also added error checking to rest of the tests in same test-file, so that new failures do not un-noticed. --- pkg/helm/helm_test.go | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/pkg/helm/helm_test.go b/pkg/helm/helm_test.go index 00db8b4e8..bcaeec2f0 100644 --- a/pkg/helm/helm_test.go +++ b/pkg/helm/helm_test.go @@ -87,7 +87,9 @@ func TestListReleases_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).ListReleases(ops...) + if _, err := NewClient(b4c).ListReleases(ops...); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } // Verify InstallOption's are applied to an InstallReleaseRequest correctly. @@ -99,6 +101,7 @@ func TestInstallRelease_VerifyOptions(t *testing.T) { var reuseName = true var dryRun = true var chartName = "alpine" + var chartPath = filepath.Join(chartsDir, chartName) var overrides = []byte("key1=value1,key2=value2") // Expected InstallReleaseRequest message @@ -133,7 +136,9 @@ func TestInstallRelease_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).InstallRelease(chartName, namespace, ops...) + if _, err := NewClient(b4c).InstallRelease(chartPath, namespace, ops...); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } // Verify DeleteOptions's are applied to an UninstallReleaseRequest correctly. @@ -168,13 +173,16 @@ func TestDeleteRelease_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).DeleteRelease(releaseName, ops...) + if _, err := NewClient(b4c).DeleteRelease(releaseName, ops...); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } // Verify UpdateOption's are applied to an UpdateReleaseRequest correctly. func TestUpdateRelease_VerifyOptions(t *testing.T) { // Options testdata var chartName = "alpine" + var chartPath = filepath.Join(chartsDir, chartName) var releaseName = "test" var disableHooks = true var overrides = []byte("key1=value1,key2=value2") @@ -208,7 +216,9 @@ func TestUpdateRelease_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).UpdateRelease(releaseName, chartName, ops...) + if _, err := NewClient(b4c).UpdateRelease(releaseName, chartPath, ops...); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } // Verify RollbackOption's are applied to a RollbackReleaseRequest correctly. @@ -246,7 +256,9 @@ func TestRollbackRelease_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).RollbackRelease(releaseName, ops...) + if _, err := NewClient(b4c).RollbackRelease(releaseName, ops...); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } // Verify StatusOption's are applied to a GetReleaseStatusRequest correctly. @@ -273,7 +285,9 @@ func TestReleaseStatus_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).ReleaseStatus(releaseName, StatusReleaseVersion(revision)) + if _, err := NewClient(b4c).ReleaseStatus(releaseName, StatusReleaseVersion(revision)); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } // Verify ContentOption's are applied to a GetReleaseContentRequest correctly. @@ -300,7 +314,9 @@ func TestReleaseContent_VerifyOptions(t *testing.T) { return errSkip }) - NewClient(b4c).ReleaseContent(releaseName, ContentReleaseVersion(revision)) + if _, err := NewClient(b4c).ReleaseContent(releaseName, ContentReleaseVersion(revision)); err != errSkip { + t.Fatalf("did not expect error but got (%v)\n``", err) + } } func assert(t *testing.T, expect, actual interface{}) { From 5734c2162e6960b015b23a443421b77b10b817ad Mon Sep 17 00:00:00 2001 From: David Wittman Date: Thu, 30 Mar 2017 17:10:20 -0500 Subject: [PATCH 29/48] Fix identation of `helm dep` help text The `repository` lines in the help text for `helm dep` were misaligned with the rest of the dependency block. --- cmd/helm/dependency.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/dependency.go b/cmd/helm/dependency.go index 44cb29e4d..9c51be600 100644 --- a/cmd/helm/dependency.go +++ b/cmd/helm/dependency.go @@ -47,10 +47,10 @@ For example, this requirements file declares two dependencies: dependencies: - name: nginx version: "1.2.3" - repository: "https://example.com/charts" + repository: "https://example.com/charts" - name: memcached version: "3.2.1" - repository: "https://another.example.com/charts" + repository: "https://another.example.com/charts" The 'name' should be the name of a chart, where that name must match the name in that chart's 'Chart.yaml' file. From da950c5c57dfe93dc04bd94696c2e96f1f2c4b85 Mon Sep 17 00:00:00 2001 From: "Steven E. Harris" Date: Thu, 30 Mar 2017 15:12:57 -0400 Subject: [PATCH 30/48] Separate manifests with document boundary markers In order to allow the stream emitted by "helm init --debug" to be fed back into "kubectl create/apply -f", use YAML starting and ending document boundary markers instead of blank lines to separate the individual manifests. --- cmd/helm/init.go | 51 ++++++++++++++++++++++++++++++++++--------- cmd/helm/init_test.go | 16 +++++++++----- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/cmd/helm/init.go b/cmd/helm/init.go index bf7d38f16..bc73a6a6c 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -145,27 +145,58 @@ func (i *initCmd) run() error { i.opts.ImageSpec = i.image if flagDebug { - var mfs string + writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error { + w := i.out + if !first { + // YAML starting document boundary marker + if _, err := fmt.Fprintln(w, "---"); err != nil { + return err + } + } + if _, err := fmt.Fprintln(w, "apiVersion:", apiVersion); err != nil { + return err + } + if _, err := fmt.Fprintln(w, "kind:", kind); err != nil { + return err + } + if _, err := fmt.Fprint(w, body); err != nil { + return err + } + if !last { + return nil + } + // YAML ending document boundary marker + _, err := fmt.Fprintln(w, "...") + return err + } + + var body string var err error - // write deployment manifest - if mfs, err = installer.DeploymentManifest(&i.opts); err != nil { + // write Deployment manifest + if body, err = installer.DeploymentManifest(&i.opts); err != nil { + return err + } + if err := writeYAMLManifest("extensions/v1beta1", "Deployment", body, true, false); err != nil { return err } - fmt.Fprintln(i.out, fmt.Sprintf("apiVersion: extensions/v1beta1\nkind: Deployment\n%s", mfs)) - // write service manifest - if mfs, err = installer.ServiceManifest(i.namespace); err != nil { + // write Service manifest + if body, err = installer.ServiceManifest(i.namespace); err != nil { + return err + } + if err := writeYAMLManifest("v1", "Service", body, false, !i.opts.EnableTLS); err != nil { return err } - fmt.Fprintln(i.out, fmt.Sprintf("apiVersion: v1\nkind: Service\n%s", mfs)) - // write secret manifest + // write Secret manifest if i.opts.EnableTLS { - if mfs, err = installer.SecretManifest(&i.opts); err != nil { + if body, err = installer.SecretManifest(&i.opts); err != nil { + return err + } + if err := writeYAMLManifest("v1", "Secret", body, false, true); err != nil { return err } - fmt.Fprintln(i.out, fmt.Sprintf("apiVersion: v1\nkind: Secret\n%s", mfs)) } } diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 00c754cfa..0ef111a72 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -156,13 +156,19 @@ func TestInitCmd_dryRun(t *testing.T) { if err := cmd.run(); err != nil { t.Fatal(err) } - if len(fc.Actions()) != 0 { - t.Error("expected no server calls") + if got := len(fc.Actions()); got != 0 { + t.Errorf("expected no server calls, got %d", got) } - var y map[string]interface{} - if err := yaml.Unmarshal(buf.Bytes(), &y); err != nil { - t.Errorf("Expected parseable YAML, got %q\n\t%s", buf.String(), err) + docs := bytes.Split(buf.Bytes(), []byte("\n---")) + if got, want := len(docs), 2; got != want { + t.Fatalf("Expected document count of %d, got %d", want, got) + } + for _, doc := range docs { + var y map[string]interface{} + if err := yaml.Unmarshal(doc, &y); err != nil { + t.Errorf("Expected parseable YAML, got %q\n\t%s", doc, err) + } } } From 0e81899f5f4f95fcef385bf84fee1edb288abaec Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 14 Mar 2017 11:04:33 -0700 Subject: [PATCH 31/48] WIP feat(helm): import child values to parent Implements a mechanism in requirements.yaml to allow the import and re-parenting of value table from child chart. Closes #1995 --- pkg/chartutil/requirements.go | 126 ++++++++++++++++++ pkg/chartutil/requirements_test.go | 57 ++++++++ .../subchart1/charts/subchartA/values.yaml | 12 +- .../subpop/charts/subchart1/requirements.yaml | 6 + .../subpop/charts/subchart1/values.yaml | 11 +- .../testdata/subpop/requirements.yaml | 5 + pkg/chartutil/testdata/subpop/values.yaml | 14 ++ pkg/helm/client.go | 8 ++ pkg/resolver/resolver_test.go | 2 +- 9 files changed, 232 insertions(+), 9 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 3a31042d6..326afb36c 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -62,6 +62,8 @@ type Dependency struct { Tags []string `json:"tags"` // Enabled bool determines if chart should be loaded Enabled bool `json:"enabled"` + // ImportValues holds the mapping of source values to parent key to be imported + ImportValues []interface{} `json:"import-values"` } // ErrNoRequirementsFile to detect error condition @@ -266,3 +268,127 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { return nil } + +// pathToMap creates a nested map given a YAML path in dot notation +func pathToMap(path string, data map[string]interface{}) map[string]interface{} { + ap := strings.Split(path, ".") + n := []map[string]interface{}{} + for _, v := range ap { + nm := make(map[string]interface{}) + nm[v] = make(map[string]interface{}) + n = append(n, nm) + } + for i, d := range n { + for k := range d { + z := i + 1 + if z == len(n) { + n[i][k] = data + break + } + n[i][k] = n[z] + } + } + + return n[0] +} + +// getParents returns a slice of parent charts in reverse order +func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { + if len(out) == 0 { + out = []*chart.Chart{} + out = append(out, c) + } + for _, ch := range c.Dependencies { + if len(ch.Dependencies) > 0 { + out = append(out, ch) + out = getParents(ch, out) + } + } + + return out +} + +// processImportValues merges values from child to parent based on ImportValues field +func processImportValues(c *chart.Chart, v *chart.Config) error { + reqs, err := LoadRequirements(c) + if err != nil { + log.Printf("Warning: ImportValues cannot load requirements for %s", c.Metadata.Name) + return nil + } + cvals, err := CoalesceValues(c, v) + nv := v.GetValues() + b := make(map[string]interface{}) + for kk, v3 := range nv { + b[kk] = v3 + } + for _, r := range reqs.Dependencies { + if len(r.ImportValues) > 0 { + var outiv []interface{} + for _, riv := range r.ImportValues { + switch tr := riv.(type) { + case map[string]interface{}: + if m, ok := riv.(map[string]interface{}); ok { + nm := make(map[string]string) + nm["child"] = m["child"].(string) + nm["parent"] = m["parent"].(string) + outiv = append(outiv, nm) + s := r.Name + "." + nm["child"] + vv, err := cvals.Table(s) + if err != nil { + log.Printf("Warning: ImportValues missing table %v", err) + continue + } + if nm["parent"] == "." { + coalesceTables(b, vv.AsMap()) + } else { + + vm := pathToMap(nm["parent"], vv.AsMap()) + coalesceTables(b, vm) + } + } + case string: + log.Printf("its a string %v", tr) + // todo validation + nm := make(map[string]string) + nm["child"] = riv.(string) + nm["parent"] = "." + outiv = append(outiv, nm) + s := r.Name + "." + nm["child"] + vv, err := cvals.Table(s) + if err != nil { + log.Printf("Warning: ImportValues missing table %v", err) + continue + } + coalesceTables(b, vv.AsMap()) + } + } + // set our formatted import values + r.ImportValues = outiv + } + } + + cv, err := coalesceValues(c, b) + if err != nil { + log.Fatalf("Error coalescing values for ImportValues %s", err) + } + y, err := yaml.Marshal(cv) + if err != nil { + log.Printf("Warning: ImportValues could not marshall %v", err) + } + bb := &chart.Config{Raw: string(y)} + v = bb + c.Values = bb + + return nil +} + +// ProcessRequirementsImportValues imports specified chart values from child to parent +func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error { + pc := getParents(c, nil) + for i := len(pc) - 1; i >= 0; i-- { + processImportValues(pc[i], v) + + } + + return nil +} diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index b9a5ae12a..21268df9d 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -18,6 +18,8 @@ import ( "sort" "testing" + "strconv" + "k8s.io/helm/pkg/proto/hapi/chart" ) @@ -206,3 +208,58 @@ func extractCharts(c *chart.Chart, out []*chart.Chart) []*chart.Chart { } return out } +func TestProcessRequirementsImportValues(t *testing.T) { + c, err := Load("testdata/subpop") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + + v := &chart.Config{Raw: ""} + + e := make(map[string]string) + e["imported-from-chart1.type"] = "ClusterIP" + e["imported-from-chart1.name"] = "nginx" + e["imported-from-chart1.externalPort"] = "80" + // this doesn't exist in imported table. it should merge and remain unchanged + e["imported-from-chart1.notimported1"] = "1" + e["imported-from-chartA-via-chart1.limits.cpu"] = "300m" + e["imported-from-chartA-via-chart1.limits.memory"] = "300Mi" + e["imported-from-chartA-via-chart1.limits.volume"] = "11" + e["imported-from-chartA-via-chart1.requests.truthiness"] = "0.01" + + verifyRequirementsImportValues(t, c, v, e) +} +func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Config, e map[string]string) { + + err := ProcessRequirementsImportValues(c, v) + if err != nil { + t.Errorf("Error processing import values requirements %v", err) + } + cv := c.GetValues() + cc, err := ReadValues([]byte(cv.Raw)) + if err != nil { + t.Errorf("Error reading import values %v", err) + } + for kk, vv := range e { + pv, err := cc.PathValue(kk) + if err != nil { + t.Fatalf("Error retrieving import values table %v %v", kk, err) + return + } + + switch pv.(type) { + case float64: + s := strconv.FormatFloat(pv.(float64), 'f', -1, 64) + if s != vv { + t.Errorf("Failed to match imported float value %v with expected %v", s, vv) + return + } + default: + if pv.(string) != vv { + t.Errorf("Failed to match imported string value %v with expected %v", pv, vv) + return + } + } + + } +} diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml index 5e5b21065..8c348e86c 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml @@ -1,6 +1,7 @@ # Default values for subchart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# subchartA replicaCount: 1 image: repository: nginx @@ -13,9 +14,12 @@ service: internalPort: 80 resources: limits: - cpu: 100m - memory: 128Mi + cpu: 300m + memory: 300Mi + plasticity: 1.7331 + volume: 11 requests: - cpu: 100m - memory: 128Mi + cpu: 350m + memory: 350Mi + truthiness: 0.01 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index 94d278234..5adf2f10a 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -6,6 +6,12 @@ dependencies: tags: - front-end - subcharta + import-values: + - child: resources.limits + parent: imported-from-chartA.limits + - child: resources.requests + parent: imported-from-chartA.requests + - name: subchartb repository: http://localhost:10191 version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 5e5b21065..4c5085d82 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -1,6 +1,7 @@ # Default values for subchart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. +# subchart1 replicaCount: 1 image: repository: nginx @@ -13,9 +14,11 @@ service: internalPort: 80 resources: limits: - cpu: 100m - memory: 128Mi + cpu: 200m + memory: 200Mi + plasticity: 0 requests: - cpu: 100m - memory: 128Mi + cpu: 250m + memory: 250Mi + truthiness: 200 diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index 9840e047d..19d320889 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -6,6 +6,11 @@ dependencies: tags: - front-end - subchart1 + import-values: + - child: service + parent: imported-from-chart1 + - child: imported-from-chartA + parent: imported-from-chartA-via-chart1 - name: subchart2 repository: http://localhost:10191 version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chartutil/testdata/subpop/values.yaml index 85fc7b49d..f03c449cf 100644 --- a/pkg/chartutil/testdata/subpop/values.yaml +++ b/pkg/chartutil/testdata/subpop/values.yaml @@ -1,6 +1,20 @@ # parent/values.yaml # switch-like +imported-from-chart1: + name: bathtubginx + type: None + externalPort: 25 + notimported1: 1 + +imported-from-chartA-via-chart1: + limits: + cpu: 100m + memory: 100Mi + notimported2: 100 + requests: + truthiness: 33.3 + tags: front-end: true back-end: false diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 2e773cca7..82c87e0da 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -97,6 +97,10 @@ func (h *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ... if err != nil { return nil, err } + err = chartutil.ProcessRequirementsImportValues(req.Chart, req.Values) + if err != nil { + return nil, err + } return h.install(ctx, req) } @@ -166,6 +170,10 @@ func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts if err != nil { return nil, err } + err = chartutil.ProcessRequirementsImportValues(req.Chart, req.Values) + if err != nil { + return nil, err + } return h.update(ctx, req) } diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index 8d4b86019..5f0811f20 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -141,7 +141,7 @@ func TestResolve(t *testing.T) { } func TestHashReq(t *testing.T) { - expect := "sha256:c8250374210bd909cef274be64f871bd4e376d4ecd34a1589b5abf90b68866ba" + expect := "sha256:1feffe2016ca113f64159d91c1f77d6a83bcd23510b171d9264741bf9d63f741" req := &chartutil.Requirements{ Dependencies: []*chartutil.Dependency{ {Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"}, From d1424f6c08ce1eb11c2958ca832e7d937d5c8fd9 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 14 Mar 2017 14:50:49 -0700 Subject: [PATCH 32/48] Handle missed error and make error messages unique --- pkg/chartutil/requirements.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 326afb36c..5ca475241 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -316,6 +316,9 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { return nil } cvals, err := CoalesceValues(c, v) + if err != nil { + log.Fatalf("Error coalescing values for ImportValues %s", err) + } nv := v.GetValues() b := make(map[string]interface{}) for kk, v3 := range nv { @@ -369,7 +372,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { cv, err := coalesceValues(c, b) if err != nil { - log.Fatalf("Error coalescing values for ImportValues %s", err) + log.Fatalf("Error coalescing processed values for ImportValues %s", err) } y, err := yaml.Marshal(cv) if err != nil { From 2bd4d1d003abadbe051eb1a72b0e621d8a509db4 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 14 Mar 2017 15:09:55 -0700 Subject: [PATCH 33/48] Cleanup old todo, unused log and value for ImportValues feature --- pkg/chartutil/requirements.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 5ca475241..3f5895c4d 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -328,7 +328,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { if len(r.ImportValues) > 0 { var outiv []interface{} for _, riv := range r.ImportValues { - switch tr := riv.(type) { + switch riv.(type) { case map[string]interface{}: if m, ok := riv.(map[string]interface{}); ok { nm := make(map[string]string) @@ -350,8 +350,6 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } } case string: - log.Printf("its a string %v", tr) - // todo validation nm := make(map[string]string) nm["child"] = riv.(string) nm["parent"] = "." From 007bb9dbae5f893f8a18ed4feee78edd8813a420 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Mon, 20 Mar 2017 10:40:40 -0700 Subject: [PATCH 34/48] Implement 'exports' convetion for simple list items --- pkg/chartutil/requirements.go | 2 +- pkg/chartutil/requirements_test.go | 12 ++++++++++++ .../charts/subchart1/charts/subchartB/values.yaml | 10 +++++++++- .../subpop/charts/subchart1/requirements.yaml | 3 +++ .../testdata/subpop/charts/subchart1/values.yaml | 3 +++ pkg/chartutil/testdata/subpop/requirements.yaml | 6 ++++++ 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 3f5895c4d..26ea5cf5b 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -351,7 +351,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } case string: nm := make(map[string]string) - nm["child"] = riv.(string) + nm["child"] = "exports." + riv.(string) nm["parent"] = "." outiv = append(outiv, nm) s := r.Name + "." + nm["child"] diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 21268df9d..59c4ecc11 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -226,6 +226,12 @@ func TestProcessRequirementsImportValues(t *testing.T) { e["imported-from-chartA-via-chart1.limits.memory"] = "300Mi" e["imported-from-chartA-via-chart1.limits.volume"] = "11" e["imported-from-chartA-via-chart1.requests.truthiness"] = "0.01" + // single list items (looks for exports. parent key) + e["imported-from-chartB-via-chart1.databint"] = "1" + e["imported-from-chartB-via-chart1.databstr"] = "x.y.z" + e["parent1c3"] = "true" + // checks that a chartb value was merged in with charta values + e["imported-from-chartA-via-chart1.resources.limits.shares"] = "100" verifyRequirementsImportValues(t, c, v, e) } @@ -254,6 +260,12 @@ func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Confi t.Errorf("Failed to match imported float value %v with expected %v", s, vv) return } + case bool: + b := strconv.FormatBool(pv.(bool)) + if b != vv { + t.Errorf("Failed to match imported bool value %v with expected %v", b, vv) + return + } default: if pv.(string) != vv { t.Errorf("Failed to match imported string value %v with expected %v", pv, vv) diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index 5e5b21065..5f473ef61 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -18,4 +18,12 @@ resources: requests: cpu: 100m memory: 128Mi - +exports: + convention1: + data: + databint: 1 + databstr: x.y.z + convention2: + resources: + limits: + shares: 100 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index 5adf2f10a..cb62b9f36 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -16,6 +16,9 @@ dependencies: repository: http://localhost:10191 version: 0.1.0 condition: subchartb.enabled + import-values: + - convention1 + - convention2 tags: - front-end - subchartb diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 4c5085d82..15ba88485 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -21,4 +21,7 @@ resources: cpu: 250m memory: 250Mi truthiness: 200 +exports: + convention3: + parent1c3: true diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index 19d320889..e299d057b 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -11,6 +11,12 @@ dependencies: parent: imported-from-chart1 - child: imported-from-chartA parent: imported-from-chartA-via-chart1 + - convention3 + # this merges chartb "shares" into charta "limits" + - child: resources.limits + parent: imported-from-chartA-via-chart1.resources.limits + - child: data + parent: imported-from-chartB-via-chart1 - name: subchart2 repository: http://localhost:10191 version: 0.1.0 From 4a5721fb360bb25c88163e880ca020f4e460a17a Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 21 Mar 2017 10:04:54 -0700 Subject: [PATCH 35/48] Fixup style and errors --- pkg/chartutil/requirements.go | 64 +++++++++++++++++------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 26ea5cf5b..189e2c647 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -269,15 +269,20 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { return nil } -// pathToMap creates a nested map given a YAML path in dot notation +// pathToMap creates a nested map given a YAML path in dot notation. func pathToMap(path string, data map[string]interface{}) map[string]interface{} { ap := strings.Split(path, ".") + if len(ap) == 0 { + return nil + } n := []map[string]interface{}{} + // created nested map for each key, adding to slice for _, v := range ap { nm := make(map[string]interface{}) nm[v] = make(map[string]interface{}) n = append(n, nm) } + // find the last key (map) and set our data for i, d := range n { for k := range d { z := i + 1 @@ -292,11 +297,10 @@ func pathToMap(path string, data map[string]interface{}) map[string]interface{} return n[0] } -// getParents returns a slice of parent charts in reverse order +// getParents returns a slice of parent charts in reverse order. func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { if len(out) == 0 { - out = []*chart.Chart{} - out = append(out, c) + out = []*chart.Chart{c} } for _, ch := range c.Dependencies { if len(ch.Dependencies) > 0 { @@ -308,16 +312,15 @@ func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { return out } -// processImportValues merges values from child to parent based on ImportValues field +// processImportValues merges values from child to parent based on ImportValues field. func processImportValues(c *chart.Chart, v *chart.Config) error { reqs, err := LoadRequirements(c) if err != nil { - log.Printf("Warning: ImportValues cannot load requirements for %s", c.Metadata.Name) - return nil + return err } cvals, err := CoalesceValues(c, v) if err != nil { - log.Fatalf("Error coalescing values for ImportValues %s", err) + return err } nv := v.GetValues() b := make(map[string]interface{}) @@ -328,30 +331,29 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { if len(r.ImportValues) > 0 { var outiv []interface{} for _, riv := range r.ImportValues { - switch riv.(type) { + switch iv := riv.(type) { case map[string]interface{}: - if m, ok := riv.(map[string]interface{}); ok { - nm := make(map[string]string) - nm["child"] = m["child"].(string) - nm["parent"] = m["parent"].(string) - outiv = append(outiv, nm) - s := r.Name + "." + nm["child"] - vv, err := cvals.Table(s) - if err != nil { - log.Printf("Warning: ImportValues missing table %v", err) - continue - } - if nm["parent"] == "." { - coalesceTables(b, vv.AsMap()) - } else { + nm := map[string]string{ + "child": iv["child"].(string), + "parent": iv["parent"].(string), + } + outiv = append(outiv, nm) + s := r.Name + "." + nm["child"] + vv, err := cvals.Table(s) + if err != nil { + log.Printf("Warning: ImportValues missing table %v", err) + continue + } + if nm["parent"] == "." { + coalesceTables(b, vv.AsMap()) + } else { - vm := pathToMap(nm["parent"], vv.AsMap()) - coalesceTables(b, vm) - } + vm := pathToMap(nm["parent"], vv.AsMap()) + coalesceTables(b, vm) } case string: nm := make(map[string]string) - nm["child"] = "exports." + riv.(string) + nm["child"] = "exports." + iv nm["parent"] = "." outiv = append(outiv, nm) s := r.Name + "." + nm["child"] @@ -367,14 +369,13 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { r.ImportValues = outiv } } - cv, err := coalesceValues(c, b) if err != nil { - log.Fatalf("Error coalescing processed values for ImportValues %s", err) + return err } y, err := yaml.Marshal(cv) if err != nil { - log.Printf("Warning: ImportValues could not marshall %v", err) + return err } bb := &chart.Config{Raw: string(y)} v = bb @@ -383,12 +384,11 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { return nil } -// ProcessRequirementsImportValues imports specified chart values from child to parent +// ProcessRequirementsImportValues imports specified chart values from child to parent. func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error { pc := getParents(c, nil) for i := len(pc) - 1; i >= 0; i-- { processImportValues(pc[i], v) - } return nil From 7ea4d8c7c460eae18bf74c682832acfb009445ce Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Mon, 27 Mar 2017 18:21:26 -0700 Subject: [PATCH 36/48] Refactor so parent's values win --- docs/charts.md | 92 +++++++++++++++++++ pkg/chartutil/requirements.go | 37 ++++---- pkg/chartutil/requirements_test.go | 74 ++++++++++++--- .../subchart1/charts/subchartA/values.yaml | 22 ++--- .../subchart1/charts/subchartB/values.yaml | 48 ++++++---- .../subpop/charts/subchart1/requirements.yaml | 20 ++-- .../subpop/charts/subchart1/values.yaml | 62 +++++++++---- .../testdata/subpop/requirements.yaml | 25 +++-- pkg/chartutil/testdata/subpop/values.yaml | 48 +++++++--- 9 files changed, 312 insertions(+), 116 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index b7d97cc0b..677e9e66d 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -302,6 +302,98 @@ helm install --set tags.front-end=true --set subchart2.enabled=false * The `tags:` key in values must be a top level key. Globals and nested `tags:` tables are not currently supported. +#### Importing Child Values via requirements.yaml + +In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be shared +as common defaults. The values to be imported can be specified in the parent chart's requirements.yaml +using a YAML list format that contains the source of the values to be imported (child) and the +destination path in the parent chart's values (parent). + +The optional `import-values` in the example below instructs Helm to take any values found at `child:` +path and copy them to the path at `parent:` + +```` +# parent's requirements.yaml +dependencies: + - name: subchart1 + repository: http://localhost:10191 + version: 0.1.0 + ... + import-values: + - child: default.data + parent: myimports +```` +In the above example, values found at `default.data` in the subchart1's values will be imported +to the `myimports` key in the parent chart's values as detailed below: + +```` +# parent's values + +myimports: + myint: 0 + mybool: false + mystring: "helm rocks" + + +```` +```` +# subchart1's values.yaml + +default: + data: + myint: 999 + mybool: true + +```` +The parent chart's resulting values would be: + +```` +# parent's final values + +myimports: + myint: 999 + mybool: true + mystring: "helm rocks" + +```` + +The parent's final values now contains the `myint` and `mybool` fields imported from subchart1. + +##### Using the exports convention + +If a values.yaml contains an `exports` field at the root, it's contents may be imported +directly into the parent's values by using a simple string format as in the example below: + +```` +# parent's requirements.yaml + ... + import-values: + - data +```` +```` +# child values + +... +exports: + data: + myint:99 +```` + +Since we are using the simple string `data` in our import list, Helm looks in the the `exports` +field of the child chart for `data` key and imports its contents. + +The final parent values would contain our exported field. + +```` +# parent's values +... +myint: 99 + +```` + +Please note the parent key `data` is not contained in the parent's final values. If +you need to specify the parent key, use the 'child/parent' format. + ## Templates and Values Helm Chart templates are written in the diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 189e2c647..31e4d255a 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -271,6 +271,9 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { // pathToMap creates a nested map given a YAML path in dot notation. func pathToMap(path string, data map[string]interface{}) map[string]interface{} { + if path == "." { + return data + } ap := strings.Split(path, ".") if len(ap) == 0 { return nil @@ -318,15 +321,18 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { if err != nil { return err } + // combine chart values and its dependencies' values cvals, err := CoalesceValues(c, v) if err != nil { return err } nv := v.GetValues() b := make(map[string]interface{}) - for kk, v3 := range nv { - b[kk] = v3 + // convert values to map + for kk, vvv := range nv { + b[kk] = vvv } + // import values from each dependency if specified in import-values for _, r := range reqs.Dependencies { if len(r.ImportValues) > 0 { var outiv []interface{} @@ -339,47 +345,40 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } outiv = append(outiv, nm) s := r.Name + "." + nm["child"] + // get child table vv, err := cvals.Table(s) if err != nil { log.Printf("Warning: ImportValues missing table %v", err) continue } - if nm["parent"] == "." { - coalesceTables(b, vv.AsMap()) - } else { - - vm := pathToMap(nm["parent"], vv.AsMap()) - coalesceTables(b, vm) - } + // create value map from child to be merged into parent + vm := pathToMap(nm["parent"], vv.AsMap()) + b = coalesceTables(cvals, vm) case string: nm := make(map[string]string) nm["child"] = "exports." + iv nm["parent"] = "." outiv = append(outiv, nm) s := r.Name + "." + nm["child"] - vv, err := cvals.Table(s) + vm, err := cvals.Table(s) if err != nil { log.Printf("Warning: ImportValues missing table %v", err) continue } - coalesceTables(b, vv.AsMap()) + b = coalesceTables(b, vm.AsMap()) } } // set our formatted import values r.ImportValues = outiv } } - cv, err := coalesceValues(c, b) - if err != nil { - return err - } - y, err := yaml.Marshal(cv) + b = coalesceTables(b, cvals) + y, err := yaml.Marshal(b) if err != nil { return err } - bb := &chart.Config{Raw: string(y)} - v = bb - c.Values = bb + // set the new values + c.Values.Raw = string(y) return nil } diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index 59c4ecc11..c92c9f052 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -217,21 +217,65 @@ func TestProcessRequirementsImportValues(t *testing.T) { v := &chart.Config{Raw: ""} e := make(map[string]string) - e["imported-from-chart1.type"] = "ClusterIP" - e["imported-from-chart1.name"] = "nginx" - e["imported-from-chart1.externalPort"] = "80" - // this doesn't exist in imported table. it should merge and remain unchanged - e["imported-from-chart1.notimported1"] = "1" - e["imported-from-chartA-via-chart1.limits.cpu"] = "300m" - e["imported-from-chartA-via-chart1.limits.memory"] = "300Mi" - e["imported-from-chartA-via-chart1.limits.volume"] = "11" - e["imported-from-chartA-via-chart1.requests.truthiness"] = "0.01" - // single list items (looks for exports. parent key) - e["imported-from-chartB-via-chart1.databint"] = "1" - e["imported-from-chartB-via-chart1.databstr"] = "x.y.z" - e["parent1c3"] = "true" - // checks that a chartb value was merged in with charta values - e["imported-from-chartA-via-chart1.resources.limits.shares"] = "100" + + e["imported-chart1.SC1bool"] = "true" + e["imported-chart1.SC1float"] = "3.14" + e["imported-chart1.SC1int"] = "100" + e["imported-chart1.SC1string"] = "dollywood" + e["imported-chart1.SC1extra1"] = "11" + e["imported-chart1.SPextra1"] = "helm rocks" + e["imported-chart1.SC1extra1"] = "11" + + e["imported-chartA.SCAbool"] = "false" + e["imported-chartA.SCAfloat"] = "3.1" + e["imported-chartA.SCAint"] = "55" + e["imported-chartA.SCAstring"] = "jabba" + e["imported-chartA.SPextra3"] = "1.337" + e["imported-chartA.SC1extra2"] = "1.337" + e["imported-chartA.SCAnested1.SCAnested2"] = "true" + + e["imported-chartA-B.SCAbool"] = "false" + e["imported-chartA-B.SCAfloat"] = "3.1" + e["imported-chartA-B.SCAint"] = "55" + e["imported-chartA-B.SCAstring"] = "jabba" + + e["imported-chartA-B.SCBbool"] = "true" + e["imported-chartA-B.SCBfloat"] = "7.77" + e["imported-chartA-B.SCBint"] = "33" + e["imported-chartA-B.SCBstring"] = "boba" + e["imported-chartA-B.SPextra5"] = "k8s" + e["imported-chartA-B.SC1extra5"] = "tiller" + + e["overridden-chart1.SC1bool"] = "false" + e["overridden-chart1.SC1float"] = "3.141592" + e["overridden-chart1.SC1int"] = "99" + e["overridden-chart1.SC1string"] = "pollywog" + e["overridden-chart1.SPextra2"] = "42" + + e["overridden-chartA.SCAbool"] = "true" + e["overridden-chartA.SCAfloat"] = "41.3" + e["overridden-chartA.SCAint"] = "808" + e["overridden-chartA.SCAstring"] = "jaberwocky" + e["overridden-chartA.SPextra4"] = "true" + + e["overridden-chartA-B.SCAbool"] = "true" + e["overridden-chartA-B.SCAfloat"] = "41.3" + e["overridden-chartA-B.SCAint"] = "808" + e["overridden-chartA-B.SCAstring"] = "jaberwocky" + e["overridden-chartA-B.SCBbool"] = "false" + e["overridden-chartA-B.SCBfloat"] = "1.99" + e["overridden-chartA-B.SCBint"] = "77" + e["overridden-chartA-B.SCBstring"] = "jango" + e["overridden-chartA-B.SPextra6"] = "111" + e["overridden-chartA-B.SCAextra1"] = "23" + e["overridden-chartA-B.SCBextra1"] = "13" + e["overridden-chartA-B.SC1extra6"] = "77" + + // `exports` style + e["SCBexported1B"] = "1965" + e["SC1extra7"] = "true" + e["SCBexported2A"] = "blaster" + e["global.SC1exported2.all.SC1exported3"] = "SC1expstr" verifyRequirementsImportValues(t, c, v, e) } diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml index 8c348e86c..712b3a2fa 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml @@ -2,24 +2,16 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. # subchartA -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 -resources: - limits: - cpu: 300m - memory: 300Mi - plasticity: 1.7331 - volume: 11 - requests: - cpu: 350m - memory: 350Mi - truthiness: 0.01 +SCAdata: + SCAbool: false + SCAfloat: 3.1 + SCAint: 55 + SCAstring: "jabba" + SCAnested1: + SCAnested2: true diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index 5f473ef61..aba524d97 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -1,29 +1,37 @@ # Default values for subchart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 -resources: - limits: - cpu: 100m - memory: 128Mi - requests: - cpu: 100m - memory: 128Mi + +SCBdata: + SCBbool: true + SCBfloat: 7.77 + SCBint: 33 + SCBstring: "boba" + exports: - convention1: - data: - databint: 1 - databstr: x.y.z - convention2: - resources: - limits: - shares: 100 + SCBexported1: + SCBexported1A: + SCBexported1B: 1965 + + SCBexported2: + SCBexported2A: "blaster" + +global: + kolla: + nova: + api: + all: + port: 8774 + metadata: + all: + port: 8775 +test: + dummy: 1 + + + diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml index cb62b9f36..abfe85e76 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/requirements.yaml @@ -7,18 +7,26 @@ dependencies: - front-end - subcharta import-values: - - child: resources.limits - parent: imported-from-chartA.limits - - child: resources.requests - parent: imported-from-chartA.requests + - child: SCAdata + parent: imported-chartA + - child: SCAdata + parent: overridden-chartA + - child: SCAdata + parent: imported-chartA-B - name: subchartb repository: http://localhost:10191 version: 0.1.0 condition: subchartb.enabled import-values: - - convention1 - - convention2 + - child: SCBdata + parent: imported-chartB + - child: SCBdata + parent: imported-chartA-B + - child: exports.SCBexported2 + parent: exports.SCBexported2 + - SCBexported1 + tags: - front-end - subchartb diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 15ba88485..4b5bd6bd9 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -2,26 +2,54 @@ # This is a YAML-formatted file. # Declare variables to be passed into your templates. # subchart1 -replicaCount: 1 -image: - repository: nginx - tag: stable - pullPolicy: IfNotPresent service: name: nginx type: ClusterIP externalPort: 80 internalPort: 80 -resources: - limits: - cpu: 200m - memory: 200Mi - plasticity: 0 - requests: - cpu: 250m - memory: 250Mi - truthiness: 200 -exports: - convention3: - parent1c3: true + +SC1data: + SC1bool: true + SC1float: 3.14 + SC1int: 100 + SC1string: "dollywood" + SC1extra1: 11 + +imported-chartA: + SC1extra2: 1.337 + +overridden-chartA: + SCAbool: true + SCAfloat: 3.14 + SCAint: 100 + SCAstring: "jabathehut" + SC1extra3: true + +imported-chartA-B: + SC1extra5: "tiller" + +overridden-chartA-B: + SCAbool: true + SCAfloat: 3.33 + SCAint: 555 + SCAstring: "wormwood" + SCAextra1: 23 + + SCBbool: true + SCBfloat: 0.25 + SCBint: 98 + SCBstring: "murkwood" + SCBextra1: 13 + + SC1extra6: 77 + +SCBexported1A: + SC1extra7: true + +exports: + SC1exported1: + global: + SC1exported2: + all: + SC1exported3: "SC1expstr" \ No newline at end of file diff --git a/pkg/chartutil/testdata/subpop/requirements.yaml b/pkg/chartutil/testdata/subpop/requirements.yaml index e299d057b..a8eb0aace 100644 --- a/pkg/chartutil/testdata/subpop/requirements.yaml +++ b/pkg/chartutil/testdata/subpop/requirements.yaml @@ -7,16 +7,21 @@ dependencies: - front-end - subchart1 import-values: - - child: service - parent: imported-from-chart1 - - child: imported-from-chartA - parent: imported-from-chartA-via-chart1 - - convention3 - # this merges chartb "shares" into charta "limits" - - child: resources.limits - parent: imported-from-chartA-via-chart1.resources.limits - - child: data - parent: imported-from-chartB-via-chart1 + - child: SC1data + parent: imported-chart1 + - child: SC1data + parent: overridden-chart1 + - child: imported-chartA + parent: imported-chartA + - child: imported-chartA-B + parent: imported-chartA-B + - child: overridden-chartA-B + parent: overridden-chartA-B + - child: SCBexported1A + parent: . + - SCBexported2 + - SC1exported1 + - name: subchart2 repository: http://localhost:10191 version: 0.1.0 diff --git a/pkg/chartutil/testdata/subpop/values.yaml b/pkg/chartutil/testdata/subpop/values.yaml index f03c449cf..55e872d41 100644 --- a/pkg/chartutil/testdata/subpop/values.yaml +++ b/pkg/chartutil/testdata/subpop/values.yaml @@ -1,19 +1,39 @@ # parent/values.yaml -# switch-like -imported-from-chart1: - name: bathtubginx - type: None - externalPort: 25 - notimported1: 1 - -imported-from-chartA-via-chart1: - limits: - cpu: 100m - memory: 100Mi - notimported2: 100 - requests: - truthiness: 33.3 +imported-chart1: + SPextra1: "helm rocks" + +overridden-chart1: + SC1bool: false + SC1float: 3.141592 + SC1int: 99 + SC1string: "pollywog" + SPextra2: 42 + + +imported-chartA: + SPextra3: 1.337 + +overridden-chartA: + SCAbool: true + SCAfloat: 41.3 + SCAint: 808 + SCAstring: "jaberwocky" + SPextra4: true + +imported-chartA-B: + SPextra5: "k8s" + +overridden-chartA-B: + SCAbool: true + SCAfloat: 41.3 + SCAint: 808 + SCAstring: "jaberwocky" + SCBbool: false + SCBfloat: 1.99 + SCBint: 77 + SCBstring: "jango" + SPextra6: 111 tags: front-end: true From 1a8e728ed9a7a29345df856ccc879c2fd4d4868a Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Wed, 29 Mar 2017 08:58:29 -0700 Subject: [PATCH 37/48] Update docs with details about exports --- docs/charts.md | 96 ++++++++++++++++++++++++++++---------------------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index 677e9e66d..638b12387 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -304,13 +304,58 @@ helm install --set tags.front-end=true --set subchart2.enabled=false #### Importing Child Values via requirements.yaml -In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be shared -as common defaults. The values to be imported can be specified in the parent chart's requirements.yaml -using a YAML list format that contains the source of the values to be imported (child) and the -destination path in the parent chart's values (parent). +In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be +shared as common defaults. An additional benefit of using the `exports` format is that it will enable future +tooling to introspect user settable values. -The optional `import-values` in the example below instructs Helm to take any values found at `child:` -path and copy them to the path at `parent:` +The keys containing the values to be imported can be specified in the parent chart's requirements.yaml using +a YAML list. Each item in the list is a key which is imported from the child chart's `exports` field. + +To import values not contained in the `exports` key, use the [child/parent](#using-the-child/parent-format) format. + +##### Using the exports format + +If a child chart's values.yaml contains an `exports` field at the root, it's contents may be imported +directly into the parent's values by specifying the keys to import as in the example below: + +```` +# parent's requirements.yaml + ... + import-values: + - data +```` +```` +# child's values.yaml + +... +exports: + data: + myint: 99 +```` + +Since we are specifying the key `data` in our import list, Helm looks in the the `exports` field of the child +chart for `data` key and imports its contents. + +The final parent values would contain our exported field: + +```` +# parent's values +... +myint: 99 + +```` + +Please note the parent key `data` is not contained in the parent's final values. If you need to specify the +parent key, use the 'child/parent' format. + +##### Using the child/parent format + +To access values that are not contained in the `exports` key of the child chart's values, you will need to +specify the source key of the values to be imported (`child`) and the destination path in the parent chart's +values (`parent`). + +The `import-values` in the example below instructs Helm to take any values found at `child:` path and copy them +to the parent's values at the path specified in `parent:` ```` # parent's requirements.yaml @@ -332,7 +377,7 @@ to the `myimports` key in the parent chart's values as detailed below: myimports: myint: 0 mybool: false - mystring: "helm rocks" + mystring: "helm rocks!" ```` @@ -353,47 +398,12 @@ The parent chart's resulting values would be: myimports: myint: 999 mybool: true - mystring: "helm rocks" + mystring: "helm rocks!" ```` The parent's final values now contains the `myint` and `mybool` fields imported from subchart1. -##### Using the exports convention - -If a values.yaml contains an `exports` field at the root, it's contents may be imported -directly into the parent's values by using a simple string format as in the example below: - -```` -# parent's requirements.yaml - ... - import-values: - - data -```` -```` -# child values - -... -exports: - data: - myint:99 -```` - -Since we are using the simple string `data` in our import list, Helm looks in the the `exports` -field of the child chart for `data` key and imports its contents. - -The final parent values would contain our exported field. - -```` -# parent's values -... -myint: 99 - -```` - -Please note the parent key `data` is not contained in the parent's final values. If -you need to specify the parent key, use the 'child/parent' format. - ## Templates and Values Helm Chart templates are written in the From 3bf143f05223018eec79ca7ea9d91cf3fa559ec8 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 31 Mar 2017 10:18:29 -0700 Subject: [PATCH 38/48] Fix codefences and nits in charts.md. Correct whitespace in charts. Add clarity to description of ImportValues requirements field. --- docs/charts.md | 65 +++++++++---------- pkg/chartutil/requirements.go | 19 ++++-- .../subchart1/charts/subchartB/values.yaml | 18 +++-- .../subpop/charts/subchart1/values.yaml | 10 +-- 4 files changed, 57 insertions(+), 55 deletions(-) diff --git a/docs/charts.md b/docs/charts.md index 638b12387..414f80bf7 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -306,44 +306,44 @@ helm install --set tags.front-end=true --set subchart2.enabled=false In some cases it is desirable to allow a child chart's values to propagate to the parent chart and be shared as common defaults. An additional benefit of using the `exports` format is that it will enable future -tooling to introspect user settable values. +tooling to introspect user-settable values. -The keys containing the values to be imported can be specified in the parent chart's requirements.yaml using -a YAML list. Each item in the list is a key which is imported from the child chart's `exports` field. +The keys containing the values to be imported can be specified in the parent chart's `requirements.yaml` file +using a YAML list. Each item in the list is a key which is imported from the child chart's `exports` field. To import values not contained in the `exports` key, use the [child/parent](#using-the-child/parent-format) format. +Examples of both formats are described below. ##### Using the exports format -If a child chart's values.yaml contains an `exports` field at the root, it's contents may be imported +If a child chart's `values.yaml` file contains an `exports` field at the root, its contents may be imported directly into the parent's values by specifying the keys to import as in the example below: -```` -# parent's requirements.yaml +```yaml +# parent's requirements.yaml file ... import-values: - data -```` -```` -# child's values.yaml - +``` +```yaml +# child's values.yaml file ... exports: data: myint: 99 -```` +``` Since we are specifying the key `data` in our import list, Helm looks in the the `exports` field of the child chart for `data` key and imports its contents. The final parent values would contain our exported field: -```` -# parent's values +```yaml +# parent's values file ... myint: 99 -```` +``` Please note the parent key `data` is not contained in the parent's final values. If you need to specify the parent key, use the 'child/parent' format. @@ -357,42 +357,41 @@ values (`parent`). The `import-values` in the example below instructs Helm to take any values found at `child:` path and copy them to the parent's values at the path specified in `parent:` -```` -# parent's requirements.yaml +```yaml +# parent's requirements.yaml file dependencies: - - name: subchart1 - repository: http://localhost:10191 - version: 0.1.0 - ... - import-values: - - child: default.data - parent: myimports -```` + - name: subchart1 + repository: http://localhost:10191 + version: 0.1.0 + ... + import-values: + - child: default.data + parent: myimports +``` In the above example, values found at `default.data` in the subchart1's values will be imported to the `myimports` key in the parent chart's values as detailed below: -```` -# parent's values +```yaml +# parent's values.yaml file myimports: myint: 0 mybool: false mystring: "helm rocks!" - -```` -```` -# subchart1's values.yaml +``` +```yaml +# subchart1's values.yaml file default: data: myint: 999 mybool: true -```` +``` The parent chart's resulting values would be: -```` +```yaml # parent's final values myimports: @@ -400,7 +399,7 @@ myimports: mybool: true mystring: "helm rocks!" -```` +``` The parent's final values now contains the `myint` and `mybool` fields imported from subchart1. diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 31e4d255a..54088f98e 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -62,7 +62,8 @@ type Dependency struct { Tags []string `json:"tags"` // Enabled bool determines if chart should be loaded Enabled bool `json:"enabled"` - // ImportValues holds the mapping of source values to parent key to be imported + // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a + // string or pair of child/parent sublist items. ImportValues []interface{} `json:"import-values"` } @@ -315,7 +316,7 @@ func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { return out } -// processImportValues merges values from child to parent based on ImportValues field. +// processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. func processImportValues(c *chart.Chart, v *chart.Config) error { reqs, err := LoadRequirements(c) if err != nil { @@ -327,7 +328,7 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { return err } nv := v.GetValues() - b := make(map[string]interface{}) + b := make(map[string]interface{}, len(nv)) // convert values to map for kk, vvv := range nv { b[kk] = vvv @@ -348,21 +349,25 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { // get child table vv, err := cvals.Table(s) if err != nil { - log.Printf("Warning: ImportValues missing table %v", err) + log.Printf("Warning: ImportValues missing table: %v", err) continue } // create value map from child to be merged into parent vm := pathToMap(nm["parent"], vv.AsMap()) b = coalesceTables(cvals, vm) case string: - nm := make(map[string]string) + nm := map[string]string{ + "child": "exports." + iv, + "parent": ".", + } + /*nm := make(map[string]string) nm["child"] = "exports." + iv - nm["parent"] = "." + nm["parent"] = "."*/ outiv = append(outiv, nm) s := r.Name + "." + nm["child"] vm, err := cvals.Table(s) if err != nil { - log.Printf("Warning: ImportValues missing table %v", err) + log.Printf("Warning: ImportValues missing table: %v", err) continue } b = coalesceTables(b, vm.AsMap()) diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index aba524d97..6d9f9c677 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -22,16 +22,14 @@ exports: SCBexported2A: "blaster" global: - kolla: - nova: - api: - all: - port: 8774 - metadata: - all: - port: 8775 -test: - dummy: 1 + kolla: + nova: + api: + all: + port: 8774 + metadata: + all: + port: 8775 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml index 4b5bd6bd9..72d3fa5c8 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/values.yaml @@ -48,8 +48,8 @@ SCBexported1A: SC1extra7: true exports: - SC1exported1: - global: - SC1exported2: - all: - SC1exported3: "SC1expstr" \ No newline at end of file + SC1exported1: + global: + SC1exported2: + all: + SC1exported3: "SC1expstr" \ No newline at end of file From 31e57d89212d54d621d30d7908a85064d5b27fb0 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 31 Mar 2017 10:25:59 -0700 Subject: [PATCH 39/48] Remove commented code --- pkg/chartutil/requirements.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 54088f98e..53e28e788 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -360,9 +360,6 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { "child": "exports." + iv, "parent": ".", } - /*nm := make(map[string]string) - nm["child"] = "exports." + iv - nm["parent"] = "."*/ outiv = append(outiv, nm) s := r.Name + "." + nm["child"] vm, err := cvals.Table(s) From 75ea56641318bd8f47cce9f1687c69efeb3068f4 Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 31 Mar 2017 10:46:50 -0700 Subject: [PATCH 40/48] Correct indention of YAML field in subchartB --- .../subpop/charts/subchart1/charts/subchartB/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml index 6d9f9c677..774fdd75c 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartB/values.yaml @@ -19,7 +19,7 @@ exports: SCBexported1B: 1965 SCBexported2: - SCBexported2A: "blaster" + SCBexported2A: "blaster" global: kolla: From 68d400cd382d93d1132b1ec8641761c8c1cae221 Mon Sep 17 00:00:00 2001 From: Anubhav Mishra Date: Sat, 1 Apr 2017 17:54:50 -0700 Subject: [PATCH 41/48] Update comment. --- pkg/releaseutil/manifest.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index 9a62efe85..aad1641d7 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -35,7 +35,6 @@ var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") // SplitManifests takes a string of manifest and returns a map contains individual manifests func SplitManifests(bigfile string) map[string]string { - // This is not the best way of doing things, but it's how k8s itself does it. // Basically, we're quickly splitting a stream of YAML documents into an // array of YAML docs. In the current implementation, the file name is just // a place holder, and doesn't have any further meaning. From c010da4d9310a067dc45bf8cc2adb8c2d1533a92 Mon Sep 17 00:00:00 2001 From: Qin Wang Date: Thu, 23 Mar 2017 13:14:34 -0700 Subject: [PATCH 42/48] feat(helm): add support for repo alias support repo alias in requirements.yaml The syntax should be like: "alias:reponame" or "@reponame". closes: #1985 --- docs/chart_best_practices/requirements.md | 2 ++ docs/helm/helm_dependency.md | 17 ++++++++++++++--- pkg/downloader/manager.go | 21 +++++++++++++++++---- pkg/downloader/manager_test.go | 14 ++++++++++++++ pkg/resolver/resolver.go | 6 +----- pkg/resolver/resolver_test.go | 7 ++++++- 6 files changed, 54 insertions(+), 13 deletions(-) diff --git a/docs/chart_best_practices/requirements.md b/docs/chart_best_practices/requirements.md index ce99649f9..0bbdfdfd7 100644 --- a/docs/chart_best_practices/requirements.md +++ b/docs/chart_best_practices/requirements.md @@ -16,6 +16,8 @@ This will match version 1.2.0 and any patches to that release (1.2.1, 1.2.999, a Where possible, use `https://` repository URLs, followed by `http://` URLs. +If the repository has been added to the repository index file, the repository name can be used as an alias of URL. Use `alias:` or `@` followed by repository names. + File URLs (`file://...`) are considered a "special case" for charts that are assembled by a fixed deployment pipeline. Charts that use `file://` in a `requirements.yaml` file are not allowed in the official Helm repository. ## Conditions and Tags diff --git a/docs/helm/helm_dependency.md b/docs/helm/helm_dependency.md index 08cf5d7c2..c2a46e39e 100644 --- a/docs/helm/helm_dependency.md +++ b/docs/helm/helm_dependency.md @@ -36,8 +36,20 @@ The 'version' field should contain a semantic version or version range. The 'repository' URL should point to a Chart Repository. Helm expects that by appending '/index.yaml' to the URL, it should be able to retrieve the chart -repository's index. Note: 'repository' cannot be a repository alias. It must be -a URL. +repository's index. + +A repository can also be represented by a repository name defined in the index file +in lieu of a repository URL. If a repository alias is used, it is expected to start with +'alias:' or '@', followed by a repository name. For example, + # requirements.yaml + dependencies: + - name: nginx + version: "1.2.3" + repository: "alias:stable" + +Note: In the above example, if the '@' syntax is used, the repository alias '@stable' +must be quoted, as YAML requires to use quotes if the value includes a special character +like '@'. Starting from 2.2.0, repository can be defined as the path to the directory of the dependency charts stored locally. The path should start with a prefix of @@ -53,7 +65,6 @@ If the dependency chart is retrieved locally, it is not required to have the repository added to helm by "helm add repo". Version matching is also supported for this case. - ### Options inherited from parent commands ``` diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index bf9b384fd..e64150e00 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -124,6 +124,13 @@ func (m *Manager) Update() error { } return err } + + // Hash requirements.yaml + hash, err := resolver.HashReq(req) + if err != nil { + return err + } + // Check that all of the repos we're dependent on actually exist and // the repo index names. repoNames, err := m.getRepoNames(req.Dependencies) @@ -140,7 +147,7 @@ func (m *Manager) Update() error { // Now we need to find out which version of a chart best satisfies the // requirements the requirements.yaml - lock, err := m.resolve(req, repoNames) + lock, err := m.resolve(req, repoNames, hash) if err != nil { return err } @@ -172,9 +179,9 @@ func (m *Manager) loadChartDir() (*chart.Chart, error) { // resolve takes a list of requirements and translates them into an exact version to download. // // This returns a lock file, which has all of the requirements normalized to a specific version. -func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]string) (*chartutil.RequirementsLock, error) { +func (m *Manager) resolve(req *chartutil.Requirements, repoNames map[string]string, hash string) (*chartutil.RequirementsLock, error) { res := resolver.New(m.ChartPath, m.HelmHome) - return res.Resolve(req, repoNames) + return res.Resolve(req, repoNames, hash) } // downloadAll takes a list of dependencies and downloads them into charts/ @@ -346,7 +353,13 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string, found := false for _, repo := range repos { - if urlutil.Equal(repo.URL, dd.Repository) { + if (strings.HasPrefix(dd.Repository, "@") && strings.TrimPrefix(dd.Repository, "@") == repo.Name) || + (strings.HasPrefix(dd.Repository, "alias:") && strings.TrimPrefix(dd.Repository, "alias:") == repo.Name) { + found = true + dd.Repository = repo.URL + reposMap[dd.Name] = repo.Name + break + } else if urlutil.Equal(repo.URL, dd.Repository) { found = true reposMap[dd.Name] = repo.Name break diff --git a/pkg/downloader/manager_test.go b/pkg/downloader/manager_test.go index 2035676c4..683e80e00 100644 --- a/pkg/downloader/manager_test.go +++ b/pkg/downloader/manager_test.go @@ -120,6 +120,20 @@ func TestGetRepoNames(t *testing.T) { }, expect: map[string]string{"local-dep": "file://./testdata/signtest"}, }, + { + name: "repo alias (alias:)", + req: []*chartutil.Dependency{ + {Name: "oedipus-rex", Repository: "alias:testing"}, + }, + expect: map[string]string{"oedipus-rex": "testing"}, + }, + { + name: "repo alias (@)", + req: []*chartutil.Dependency{ + {Name: "oedipus-rex", Repository: "@testing"}, + }, + expect: map[string]string{"oedipus-rex": "testing"}, + }, } for _, tt := range tests { diff --git a/pkg/resolver/resolver.go b/pkg/resolver/resolver.go index 858ca272d..1db18e842 100644 --- a/pkg/resolver/resolver.go +++ b/pkg/resolver/resolver.go @@ -47,11 +47,7 @@ func New(chartpath string, helmhome helmpath.Home) *Resolver { } // Resolve resolves dependencies and returns a lock file with the resolution. -func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]string) (*chartutil.RequirementsLock, error) { - d, err := HashReq(reqs) - if err != nil { - return nil, err - } +func (r *Resolver) Resolve(reqs *chartutil.Requirements, repoNames map[string]string, d string) (*chartutil.RequirementsLock, error) { // Now we clone the dependencies, locking as we go. locked := make([]*chartutil.Dependency, len(reqs.Dependencies)) diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index 4a4f853b7..57725355e 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -104,7 +104,12 @@ func TestResolve(t *testing.T) { repoNames := map[string]string{"alpine": "kubernetes-charts", "redis": "kubernetes-charts"} r := New("testdata/chartpath", "testdata/helmhome") for _, tt := range tests { - l, err := r.Resolve(tt.req, repoNames) + hash, err := HashReq(tt.req) + if err != nil { + t.Fatal(err) + } + + l, err := r.Resolve(tt.req, repoNames, hash) if err != nil { if tt.err { continue From b9ef8dbe563e6626c047d1cf91b1367ac7f087c2 Mon Sep 17 00:00:00 2001 From: Jonathan Chauncey Date: Mon, 3 Apr 2017 11:25:39 -0400 Subject: [PATCH 43/48] fix(hooks): Change annotation from hookWeight to hook-weight --- pkg/hooks/hooks.go | 2 +- pkg/tiller/hooks.go | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index d6966a87a..e1fccb416 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -24,7 +24,7 @@ import ( const HookAnno = "helm.sh/hook" // HookWeightAnno is the label name for a hook weight -const HookWeightAnno = "helm.sh/hookWeight" +const HookWeightAnno = "helm.sh/hook-weight" // Types of hooks const ( diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index b6dfeb9cd..3833ba9ea 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -111,9 +111,8 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort continue } - hw := 0 hws, _ := sh.Metadata.Annotations[hooks.HookWeightAnno] - hw, err = strconv.Atoi(hws) + hw, err := strconv.Atoi(hws) if err != nil { hw = 0 } From ba6c55c987aa76b6c6f63cefdacaebbe037106a5 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 20 Mar 2017 13:05:12 -0600 Subject: [PATCH 44/48] fix(helm): add 'skip-refresh' flag to 'helm init' This exposes the skip-refresh flag to helm init to make it possible to initialize Helm without fetching the index.yaml of the stable repo. This mirrors the behavior of 'helm dep up's skip-refresh flag. Closes #2127 --- cmd/helm/init.go | 34 ++++++++++++++++++++-------------- cmd/helm/init_test.go | 5 ++++- pkg/repo/chartrepo.go | 1 - 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/cmd/helm/init.go b/cmd/helm/init.go index bf7d38f16..6acf2b96c 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -62,16 +62,17 @@ const ( ) type initCmd struct { - image string - clientOnly bool - canary bool - upgrade bool - namespace string - dryRun bool - out io.Writer - home helmpath.Home - opts installer.Options - kubeClient internalclientset.Interface + image string + clientOnly bool + canary bool + upgrade bool + namespace string + dryRun bool + skipRefresh bool + out io.Writer + home helmpath.Home + opts installer.Options + kubeClient internalclientset.Interface } func newInitCmd(out io.Writer) *cobra.Command { @@ -99,6 +100,7 @@ func newInitCmd(out io.Writer) *cobra.Command { f.BoolVar(&i.upgrade, "upgrade", false, "upgrade if tiller is already installed") f.BoolVarP(&i.clientOnly, "client-only", "c", false, "if set does not install tiller") f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote") + f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache") // f.BoolVar(&tlsEnable, "tiller-tls", false, "install tiller with TLS enabled") // f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "install tiller with TLS enabled and to verify remote certificates") @@ -176,7 +178,7 @@ func (i *initCmd) run() error { if err := ensureDirectories(i.home, i.out); err != nil { return err } - if err := ensureDefaultRepos(i.home, i.out); err != nil { + if err := ensureDefaultRepos(i.home, i.out, i.skipRefresh); err != nil { return err } if err := ensureRepoFileFormat(i.home.RepositoryFile(), i.out); err != nil { @@ -242,12 +244,12 @@ func ensureDirectories(home helmpath.Home, out io.Writer) error { return nil } -func ensureDefaultRepos(home helmpath.Home, out io.Writer) error { +func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) error { repoFile := home.RepositoryFile() if fi, err := os.Stat(repoFile); err != nil { fmt.Fprintf(out, "Creating %s \n", repoFile) f := repo.NewRepoFile() - sr, err := initStableRepo(home.CacheIndex(stableRepository)) + sr, err := initStableRepo(home.CacheIndex(stableRepository), skipRefresh) if err != nil { return err } @@ -266,7 +268,7 @@ func ensureDefaultRepos(home helmpath.Home, out io.Writer) error { return nil } -func initStableRepo(cacheFile string) (*repo.Entry, error) { +func initStableRepo(cacheFile string, skipRefresh bool) (*repo.Entry, error) { c := repo.Entry{ Name: stableRepository, URL: stableRepositoryURL, @@ -277,6 +279,10 @@ func initStableRepo(cacheFile string) (*repo.Entry, error) { return nil, err } + if skipRefresh { + return &c, nil + } + // In this case, the cacheFile is always absolute. So passing empty string // is safe. if err := r.DownloadIndexFile(""); err != nil { diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 00c754cfa..c7e351f67 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -179,7 +179,10 @@ func TestEnsureHome(t *testing.T) { if err := ensureDirectories(hh, b); err != nil { t.Error(err) } - if err := ensureDefaultRepos(hh, b); err != nil { + if err := ensureDefaultRepos(hh, b, false); err != nil { + t.Error(err) + } + if err := ensureDefaultRepos(hh, b, true); err != nil { t.Error(err) } if err := ensureRepoFileFormat(hh.RepositoryFile(), b); err != nil { diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index 4a0751f57..601d36ead 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -161,7 +161,6 @@ func (r *ChartRepository) DownloadIndexFile(cachePath string) error { if !filepath.IsAbs(cp) { cp = filepath.Join(cachePath, cp) } - println("Writing to", cp) return ioutil.WriteFile(cp, index, 0644) } From daa39c2689c90453db5ea9799e1cd7b012cdeb72 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 17 Mar 2017 21:34:33 -0600 Subject: [PATCH 45/48] feat(helm): add --reuse-values flag to upgrade This makes it possible to re-use the existing values on an upgrade, merging in any new values set by `-f` or `--set`. Closes #1876 --- _proto/hapi/services/tiller.proto | 3 + cmd/helm/upgrade.go | 3 + cmd/helm/upgrade_test.go | 7 ++ pkg/helm/client.go | 1 + pkg/helm/option.go | 9 ++ pkg/proto/hapi/services/tiller.pb.go | 152 ++++++++++++++------------- pkg/tiller/release_server.go | 31 +++++- pkg/tiller/release_server_test.go | 67 +++++++++++- 8 files changed, 194 insertions(+), 79 deletions(-) diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index f47a47f14..f16d68238 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -203,6 +203,9 @@ message UpdateReleaseRequest { // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state // before marking the release as successful. It will wait for as long as timeout bool wait = 9; + // ReuseValues will cause Tiller to reuse the values from the last release. + // This is ignored if reset_values is set. + bool reuse_values = 10; } // UpdateReleaseResponse is the response to an update request. diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 5261efb06..e50ec73d3 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -72,6 +72,7 @@ type upgradeCmd struct { version string timeout int64 resetValues bool + reuseValues bool wait bool } @@ -114,6 +115,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used") f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual kubernetes operation (like Jobs for hooks)") f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") + f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored.") f.BoolVar(&upgrade.wait, "wait", false, "if set, will wait until all Pods, PVCs, and Services are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.MarkDeprecated("disable-hooks", "use --no-hooks instead") @@ -177,6 +179,7 @@ func (u *upgradeCmd) run() error { helm.UpgradeDisableHooks(u.disableHooks), helm.UpgradeTimeout(u.timeout), helm.ResetValues(u.resetValues), + helm.ReuseValues(u.reuseValues), helm.UpgradeWait(u.wait)) if err != nil { return fmt.Errorf("UPGRADE FAILED: %v", prettyError(err)) diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 2ae4abb25..a913016e2 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -109,6 +109,13 @@ func TestUpgradeCmd(t *testing.T) { resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 4, chart: ch2}), expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", }, + { + name: "upgrade a release with --reuse-values", + args: []string{"funny-bunny", chartPath}, + flags: []string{"--reuse-values", "true"}, + resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 5, chart: ch2}), + expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n", + }, { name: "install a release with 'upgrade --install'", args: []string{"zany-bunny", chartPath}, diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 2e773cca7..d66895596 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -155,6 +155,7 @@ func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts req.DisableHooks = h.opts.disableHooks req.Recreate = h.opts.recreate req.ResetValues = h.opts.resetValues + req.ReuseValues = h.opts.reuseValues ctx := NewContext() if h.opts.before != nil { diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 3853133ef..9dadafd11 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -66,6 +66,8 @@ type options struct { histReq rls.GetHistoryRequest // resetValues instructs Tiller to reset values to their defaults. resetValues bool + // reuseValues instructs Tiller to reuse the values from the last release. + reuseValues bool // release test options are applied directly to the test release history request testReq rls.TestReleaseRequest } @@ -323,6 +325,13 @@ func ResetValues(reset bool) UpdateOption { } } +// ReuseValues will (if true) trigger resetting the values to their original state. +func ReuseValues(reuse bool) UpdateOption { + return func(opts *options) { + opts.reuseValues = reuse + } +} + // UpgradeRecreate will (if true) recreate pods after upgrade. func UpgradeRecreate(recreate bool) UpdateOption { return func(opts *options) { diff --git a/pkg/proto/hapi/services/tiller.pb.go b/pkg/proto/hapi/services/tiller.pb.go index 4e772dd0d..8d2873741 100644 --- a/pkg/proto/hapi/services/tiller.pb.go +++ b/pkg/proto/hapi/services/tiller.pb.go @@ -259,6 +259,9 @@ type UpdateReleaseRequest struct { // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state // before marking the release as successful. It will wait for as long as timeout Wait bool `protobuf:"varint,9,opt,name=wait" json:"wait,omitempty"` + // ReuseValues will cause Tiller to reuse the values from the last release. + // This is ignored if reset_values is set. + ReuseValues bool `protobuf:"varint,10,opt,name=reuse_values,json=reuseValues" json:"reuse_values,omitempty"` } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } @@ -996,78 +999,79 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1162 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x6e, 0xe3, 0xc4, - 0x17, 0xaf, 0xe3, 0x7c, 0x9e, 0x76, 0xfb, 0x4f, 0xa7, 0x5f, 0xae, 0xf5, 0x07, 0x15, 0x23, 0x68, - 0x76, 0x61, 0x53, 0x08, 0x57, 0x48, 0x08, 0xa9, 0xdb, 0x8d, 0xda, 0x42, 0xe9, 0x4a, 0xce, 0x76, - 0x91, 0x10, 0x22, 0x72, 0x93, 0x49, 0x6b, 0xd6, 0xf1, 0x04, 0xcf, 0xb8, 0x6c, 0x6f, 0xb9, 0xe3, - 0x51, 0xe0, 0x29, 0x78, 0x02, 0x5e, 0x80, 0x97, 0x41, 0x9e, 0x0f, 0xd7, 0xe3, 0xda, 0xad, 0xe9, - 0x4d, 0x3c, 0x33, 0xe7, 0xcc, 0xf9, 0xf8, 0x9d, 0x33, 0xbf, 0x99, 0x80, 0x7d, 0xe5, 0x2d, 0xfc, - 0x7d, 0x8a, 0xa3, 0x6b, 0x7f, 0x82, 0xe9, 0x3e, 0xf3, 0x83, 0x00, 0x47, 0xfd, 0x45, 0x44, 0x18, - 0x41, 0x1b, 0x89, 0xac, 0xaf, 0x64, 0x7d, 0x21, 0xb3, 0xb7, 0xf8, 0x8e, 0xc9, 0x95, 0x17, 0x31, - 0xf1, 0x2b, 0xb4, 0xed, 0xed, 0xec, 0x3a, 0x09, 0x67, 0xfe, 0xa5, 0x14, 0x08, 0x17, 0x11, 0x0e, - 0xb0, 0x47, 0xb1, 0xfa, 0x6a, 0x9b, 0x94, 0xcc, 0x0f, 0x67, 0x44, 0x0a, 0x76, 0x34, 0x01, 0x65, - 0x1e, 0x8b, 0xa9, 0x66, 0xef, 0x1a, 0x47, 0xd4, 0x27, 0xa1, 0xfa, 0x0a, 0x99, 0xf3, 0x57, 0x0d, - 0xd6, 0x4f, 0x7d, 0xca, 0x5c, 0xb1, 0x91, 0xba, 0xf8, 0x97, 0x18, 0x53, 0x86, 0x36, 0xa0, 0x11, - 0xf8, 0x73, 0x9f, 0x59, 0xc6, 0xae, 0xd1, 0x33, 0x5d, 0x31, 0x41, 0x5b, 0xd0, 0x24, 0xb3, 0x19, - 0xc5, 0xcc, 0xaa, 0xed, 0x1a, 0xbd, 0x8e, 0x2b, 0x67, 0xe8, 0x6b, 0x68, 0x51, 0x12, 0xb1, 0xf1, - 0xc5, 0x8d, 0x65, 0xee, 0x1a, 0xbd, 0xd5, 0xc1, 0x47, 0xfd, 0x22, 0x28, 0xfa, 0x89, 0xa7, 0x11, - 0x89, 0x58, 0x3f, 0xf9, 0x79, 0x71, 0xe3, 0x36, 0x29, 0xff, 0x26, 0x76, 0x67, 0x7e, 0xc0, 0x70, - 0x64, 0xd5, 0x85, 0x5d, 0x31, 0x43, 0x47, 0x00, 0xdc, 0x2e, 0x89, 0xa6, 0x38, 0xb2, 0x1a, 0xdc, - 0x74, 0xaf, 0x82, 0xe9, 0x57, 0x89, 0xbe, 0xdb, 0xa1, 0x6a, 0x88, 0xbe, 0x82, 0x15, 0x01, 0xc9, - 0x78, 0x42, 0xa6, 0x98, 0x5a, 0xcd, 0x5d, 0xb3, 0xb7, 0x3a, 0xd8, 0x11, 0xa6, 0x14, 0xc2, 0x23, - 0x01, 0xda, 0x21, 0x99, 0x62, 0x77, 0x59, 0xa8, 0x27, 0x63, 0x8a, 0xfe, 0x0f, 0x9d, 0xd0, 0x9b, - 0x63, 0xba, 0xf0, 0x26, 0xd8, 0x6a, 0xf1, 0x08, 0x6f, 0x17, 0x9c, 0x9f, 0xa0, 0xad, 0x9c, 0x3b, - 0x03, 0x68, 0x8a, 0xd4, 0xd0, 0x32, 0xb4, 0xce, 0xcf, 0xbe, 0x3d, 0x7b, 0xf5, 0xfd, 0x59, 0x77, - 0x09, 0xb5, 0xa1, 0x7e, 0x76, 0xf0, 0xdd, 0xb0, 0x6b, 0xa0, 0x35, 0x78, 0x72, 0x7a, 0x30, 0x7a, - 0x3d, 0x76, 0x87, 0xa7, 0xc3, 0x83, 0xd1, 0xf0, 0x65, 0xb7, 0xe6, 0xbc, 0x0f, 0x9d, 0x34, 0x66, - 0xd4, 0x02, 0xf3, 0x60, 0x74, 0x28, 0xb6, 0xbc, 0x1c, 0x8e, 0x0e, 0xbb, 0x86, 0xf3, 0xbb, 0x01, - 0x1b, 0x7a, 0x89, 0xe8, 0x82, 0x84, 0x14, 0x27, 0x35, 0x9a, 0x90, 0x38, 0x4c, 0x6b, 0xc4, 0x27, - 0x08, 0x41, 0x3d, 0xc4, 0xef, 0x54, 0x85, 0xf8, 0x38, 0xd1, 0x64, 0x84, 0x79, 0x01, 0xaf, 0x8e, - 0xe9, 0x8a, 0x09, 0xfa, 0x1c, 0xda, 0x32, 0x75, 0x6a, 0xd5, 0x77, 0xcd, 0xde, 0xf2, 0x60, 0x53, - 0x07, 0x44, 0x7a, 0x74, 0x53, 0x35, 0xe7, 0x08, 0xb6, 0x8f, 0xb0, 0x8a, 0x44, 0xe0, 0xa5, 0x3a, - 0x26, 0xf1, 0xeb, 0xcd, 0x31, 0x0f, 0x26, 0xf1, 0xeb, 0xcd, 0x31, 0xb2, 0xa0, 0x25, 0xdb, 0x8d, - 0x87, 0xd3, 0x70, 0xd5, 0xd4, 0x61, 0x60, 0xdd, 0x35, 0x24, 0xf3, 0x2a, 0xb2, 0xf4, 0x31, 0xd4, - 0x93, 0x66, 0xe7, 0x66, 0x96, 0x07, 0x48, 0x8f, 0xf3, 0x24, 0x9c, 0x11, 0x97, 0xcb, 0xf5, 0x52, - 0x99, 0xf9, 0x52, 0x1d, 0x67, 0xbd, 0x1e, 0x92, 0x90, 0xe1, 0x90, 0x3d, 0x2e, 0xfe, 0x53, 0xd8, - 0x29, 0xb0, 0x24, 0x13, 0xd8, 0x87, 0x96, 0x0c, 0x8d, 0x5b, 0x2b, 0xc5, 0x55, 0x69, 0x39, 0x7f, - 0xd6, 0x60, 0xe3, 0x7c, 0x31, 0xf5, 0x18, 0x56, 0xa2, 0x7b, 0x82, 0xda, 0x83, 0x06, 0x27, 0x0d, - 0x89, 0xc5, 0x9a, 0xb0, 0x2d, 0x98, 0xe5, 0x30, 0xf9, 0x75, 0x85, 0x1c, 0x3d, 0x83, 0xe6, 0xb5, - 0x17, 0xc4, 0x98, 0x72, 0x20, 0x52, 0xd4, 0xa4, 0x26, 0x67, 0x1c, 0x57, 0x6a, 0xa0, 0x6d, 0x68, - 0x4d, 0xa3, 0x9b, 0x71, 0x14, 0x87, 0xfc, 0x08, 0xb6, 0xdd, 0xe6, 0x34, 0xba, 0x71, 0xe3, 0x10, - 0x7d, 0x08, 0x4f, 0xa6, 0x3e, 0xf5, 0x2e, 0x02, 0x3c, 0xbe, 0x22, 0xe4, 0x2d, 0xe5, 0xa7, 0xb0, - 0xed, 0xae, 0xc8, 0xc5, 0xe3, 0x64, 0x0d, 0xd9, 0x49, 0x27, 0x4d, 0x22, 0xec, 0x31, 0x6c, 0x35, - 0xb9, 0x3c, 0x9d, 0x27, 0x18, 0x32, 0x7f, 0x8e, 0x49, 0xcc, 0xf8, 0xd1, 0x31, 0x5d, 0x35, 0x45, - 0x1f, 0xc0, 0x4a, 0x84, 0x29, 0x66, 0x63, 0x19, 0x65, 0x9b, 0xef, 0x5c, 0xe6, 0x6b, 0x6f, 0x44, - 0x58, 0x08, 0xea, 0xbf, 0x7a, 0x3e, 0xb3, 0x3a, 0x5c, 0xc4, 0xc7, 0xce, 0x31, 0x6c, 0xe6, 0xb0, - 0x7a, 0x2c, 0xec, 0x7f, 0x1b, 0xb0, 0xe5, 0x92, 0x20, 0xb8, 0xf0, 0x26, 0x6f, 0x2b, 0x00, 0x9f, - 0xc1, 0xa8, 0x76, 0x3f, 0x46, 0x66, 0x01, 0x46, 0x99, 0x5e, 0xaa, 0x6b, 0xbd, 0xa4, 0xa1, 0xd7, - 0x28, 0x47, 0xaf, 0xa9, 0xa3, 0xa7, 0xa0, 0x69, 0x65, 0xa0, 0xf9, 0x06, 0xb6, 0xef, 0xe4, 0xf3, - 0x58, 0x70, 0xfe, 0xa8, 0xc1, 0xe6, 0x49, 0x48, 0x99, 0x17, 0x04, 0x39, 0x6c, 0xd2, 0x06, 0x34, - 0x2a, 0x37, 0x60, 0xed, 0xbf, 0x34, 0xa0, 0xa9, 0x81, 0xab, 0x2a, 0x51, 0xcf, 0x54, 0xa2, 0x52, - 0x53, 0x6a, 0x54, 0xd0, 0xcc, 0x51, 0x01, 0x7a, 0x0f, 0x20, 0xc2, 0x31, 0xc5, 0x63, 0x6e, 0x5c, - 0x80, 0xd8, 0xe1, 0x2b, 0x67, 0xf2, 0xe4, 0x2b, 0xdc, 0xdb, 0xc5, 0xb8, 0x67, 0x5b, 0xf2, 0x04, - 0xb6, 0xf2, 0x50, 0x3d, 0x16, 0xf6, 0xdf, 0x0c, 0xd8, 0x3e, 0x0f, 0xfd, 0x42, 0xe0, 0x8b, 0x9a, - 0xf2, 0x0e, 0x14, 0xb5, 0x02, 0x28, 0x36, 0xa0, 0xb1, 0x88, 0xa3, 0x4b, 0x2c, 0xa1, 0x15, 0x93, - 0x6c, 0x8e, 0x75, 0x2d, 0x47, 0x67, 0x0c, 0xd6, 0xdd, 0x18, 0x1e, 0x99, 0x51, 0x12, 0x75, 0x4a, - 0xdd, 0x1d, 0x41, 0xd3, 0xce, 0x3a, 0xac, 0x1d, 0x61, 0xf6, 0x46, 0x1c, 0x00, 0x99, 0x9e, 0x33, - 0x04, 0x94, 0x5d, 0xbc, 0xf5, 0x27, 0x97, 0x74, 0x7f, 0xea, 0x1d, 0xa3, 0xf4, 0x95, 0x96, 0xf3, - 0x25, 0xb7, 0x7d, 0xec, 0x53, 0x46, 0xa2, 0x9b, 0xfb, 0xa0, 0xeb, 0x82, 0x39, 0xf7, 0xde, 0x49, - 0x66, 0x4f, 0x86, 0xce, 0x11, 0x8f, 0x20, 0xdd, 0x2a, 0x23, 0xc8, 0xde, 0x93, 0x46, 0xb5, 0x7b, - 0xf2, 0x47, 0x40, 0xaf, 0x71, 0x7a, 0x65, 0x3f, 0x70, 0xc5, 0xa8, 0x22, 0xd4, 0xf4, 0x46, 0xb3, - 0xa0, 0x35, 0x09, 0xb0, 0x17, 0xc6, 0x0b, 0x59, 0x36, 0x35, 0x75, 0xf6, 0x60, 0x5d, 0xb3, 0x2e, - 0xe3, 0x4c, 0xf2, 0xa1, 0x97, 0xd2, 0x7a, 0x32, 0x1c, 0xfc, 0xd3, 0x86, 0x55, 0x75, 0xc7, 0x8a, - 0xf7, 0x12, 0xf2, 0x61, 0x25, 0xfb, 0x98, 0x40, 0x4f, 0xcb, 0x9f, 0x53, 0xb9, 0x37, 0xa1, 0xfd, - 0xac, 0x8a, 0xaa, 0x88, 0xc5, 0x59, 0xfa, 0xcc, 0x40, 0x14, 0xba, 0xf9, 0x3b, 0x1e, 0x3d, 0x2f, - 0xb6, 0x51, 0xf2, 0xa8, 0xb0, 0xfb, 0x55, 0xd5, 0x95, 0x5b, 0x74, 0xcd, 0xab, 0xaf, 0x5f, 0xcc, - 0xe8, 0x41, 0x33, 0xfa, 0x5b, 0xc0, 0xde, 0xaf, 0xac, 0x9f, 0xfa, 0xfd, 0x19, 0x9e, 0x68, 0xb7, - 0x12, 0x2a, 0x41, 0xab, 0xe8, 0x9a, 0xb7, 0x3f, 0xa9, 0xa4, 0x9b, 0xfa, 0x9a, 0xc3, 0xaa, 0x4e, - 0x37, 0xa8, 0xc4, 0x40, 0x21, 0x7f, 0xdb, 0x9f, 0x56, 0x53, 0x4e, 0xdd, 0x51, 0xe8, 0xe6, 0xd9, - 0xa0, 0xac, 0x8e, 0x25, 0xcc, 0x55, 0x56, 0xc7, 0x32, 0x92, 0x71, 0x96, 0x90, 0x07, 0x70, 0x4b, - 0x06, 0x68, 0xaf, 0xb4, 0x20, 0x3a, 0x87, 0xd8, 0xbd, 0x87, 0x15, 0x53, 0x17, 0x0b, 0xf8, 0x5f, - 0xee, 0xb6, 0x44, 0x25, 0xd0, 0x14, 0x3f, 0x12, 0xec, 0xe7, 0x15, 0xb5, 0x73, 0x49, 0x49, 0x7e, - 0xb9, 0x27, 0x29, 0x9d, 0xbc, 0xee, 0x49, 0x2a, 0x47, 0x55, 0xce, 0x12, 0xf2, 0x61, 0xd5, 0x8d, - 0x43, 0xe9, 0x3a, 0x61, 0x09, 0x54, 0xb2, 0xfb, 0x2e, 0x3f, 0xd9, 0x4f, 0x2b, 0x68, 0xde, 0x9e, - 0xef, 0x17, 0xf0, 0x43, 0x5b, 0xa9, 0x5e, 0x34, 0xf9, 0xdf, 0xc9, 0x2f, 0xfe, 0x0d, 0x00, 0x00, - 0xff, 0xff, 0xf9, 0x32, 0x44, 0xf7, 0x1f, 0x0f, 0x00, 0x00, + // 1170 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x57, 0xdb, 0x6e, 0xe3, 0x44, + 0x18, 0xae, 0xe3, 0x1c, 0xff, 0x1e, 0x48, 0xa7, 0x27, 0xd7, 0x02, 0x54, 0x8c, 0xa0, 0xd9, 0x85, + 0x4d, 0x21, 0x5c, 0x21, 0x21, 0xa4, 0x6e, 0x37, 0x6a, 0x0b, 0xa5, 0x2b, 0x39, 0xdb, 0x45, 0x42, + 0x88, 0xc8, 0x4d, 0x26, 0xad, 0x59, 0xc7, 0x13, 0x3c, 0xe3, 0xb2, 0xbd, 0xe5, 0x8e, 0x47, 0xe1, + 0x2d, 0x78, 0x01, 0x78, 0x01, 0x5e, 0x06, 0x79, 0x0e, 0x6e, 0xc6, 0xb5, 0x5b, 0x6f, 0x6e, 0x62, + 0xcf, 0xfc, 0xe7, 0xef, 0xff, 0xfd, 0xcd, 0x04, 0xec, 0x6b, 0x6f, 0xe6, 0x1f, 0x50, 0x1c, 0xdd, + 0xf8, 0x23, 0x4c, 0x0f, 0x98, 0x1f, 0x04, 0x38, 0xea, 0xce, 0x22, 0xc2, 0x08, 0xda, 0x4c, 0x64, + 0x5d, 0x25, 0xeb, 0x0a, 0x99, 0xbd, 0xcd, 0x2d, 0x46, 0xd7, 0x5e, 0xc4, 0xc4, 0xaf, 0xd0, 0xb6, + 0x77, 0xe6, 0xf7, 0x49, 0x38, 0xf1, 0xaf, 0xa4, 0x40, 0x84, 0x88, 0x70, 0x80, 0x3d, 0x8a, 0xd5, + 0x53, 0x33, 0x52, 0x32, 0x3f, 0x9c, 0x10, 0x29, 0xd8, 0xd5, 0x04, 0x94, 0x79, 0x2c, 0xa6, 0x9a, + 0xbf, 0x1b, 0x1c, 0x51, 0x9f, 0x84, 0xea, 0x29, 0x64, 0xce, 0xdf, 0x15, 0xd8, 0x38, 0xf3, 0x29, + 0x73, 0x85, 0x21, 0x75, 0xf1, 0x6f, 0x31, 0xa6, 0x0c, 0x6d, 0x42, 0x2d, 0xf0, 0xa7, 0x3e, 0xb3, + 0x8c, 0x3d, 0xa3, 0x63, 0xba, 0x62, 0x81, 0xb6, 0xa1, 0x4e, 0x26, 0x13, 0x8a, 0x99, 0x55, 0xd9, + 0x33, 0x3a, 0x2d, 0x57, 0xae, 0xd0, 0xb7, 0xd0, 0xa0, 0x24, 0x62, 0xc3, 0xcb, 0x5b, 0xcb, 0xdc, + 0x33, 0x3a, 0x6b, 0xbd, 0x4f, 0xba, 0x79, 0x50, 0x74, 0x93, 0x48, 0x03, 0x12, 0xb1, 0x6e, 0xf2, + 0xf3, 0xfc, 0xd6, 0xad, 0x53, 0xfe, 0x4c, 0xfc, 0x4e, 0xfc, 0x80, 0xe1, 0xc8, 0xaa, 0x0a, 0xbf, + 0x62, 0x85, 0x8e, 0x01, 0xb8, 0x5f, 0x12, 0x8d, 0x71, 0x64, 0xd5, 0xb8, 0xeb, 0x4e, 0x09, 0xd7, + 0x2f, 0x13, 0x7d, 0xb7, 0x45, 0xd5, 0x2b, 0xfa, 0x06, 0x56, 0x04, 0x24, 0xc3, 0x11, 0x19, 0x63, + 0x6a, 0xd5, 0xf7, 0xcc, 0xce, 0x5a, 0x6f, 0x57, 0xb8, 0x52, 0x08, 0x0f, 0x04, 0x68, 0x47, 0x64, + 0x8c, 0xdd, 0x65, 0xa1, 0x9e, 0xbc, 0x53, 0xf4, 0x3e, 0xb4, 0x42, 0x6f, 0x8a, 0xe9, 0xcc, 0x1b, + 0x61, 0xab, 0xc1, 0x33, 0xbc, 0xdb, 0x70, 0x7e, 0x81, 0xa6, 0x0a, 0xee, 0xf4, 0xa0, 0x2e, 0x4a, + 0x43, 0xcb, 0xd0, 0xb8, 0x38, 0xff, 0xfe, 0xfc, 0xe5, 0x8f, 0xe7, 0xed, 0x25, 0xd4, 0x84, 0xea, + 0xf9, 0xe1, 0x0f, 0xfd, 0xb6, 0x81, 0xd6, 0x61, 0xf5, 0xec, 0x70, 0xf0, 0x6a, 0xe8, 0xf6, 0xcf, + 0xfa, 0x87, 0x83, 0xfe, 0x8b, 0x76, 0xc5, 0xf9, 0x10, 0x5a, 0x69, 0xce, 0xa8, 0x01, 0xe6, 0xe1, + 0xe0, 0x48, 0x98, 0xbc, 0xe8, 0x0f, 0x8e, 0xda, 0x86, 0xf3, 0xa7, 0x01, 0x9b, 0x7a, 0x8b, 0xe8, + 0x8c, 0x84, 0x14, 0x27, 0x3d, 0x1a, 0x91, 0x38, 0x4c, 0x7b, 0xc4, 0x17, 0x08, 0x41, 0x35, 0xc4, + 0x6f, 0x55, 0x87, 0xf8, 0x7b, 0xa2, 0xc9, 0x08, 0xf3, 0x02, 0xde, 0x1d, 0xd3, 0x15, 0x0b, 0xf4, + 0x25, 0x34, 0x65, 0xe9, 0xd4, 0xaa, 0xee, 0x99, 0x9d, 0xe5, 0xde, 0x96, 0x0e, 0x88, 0x8c, 0xe8, + 0xa6, 0x6a, 0xce, 0x31, 0xec, 0x1c, 0x63, 0x95, 0x89, 0xc0, 0x4b, 0x4d, 0x4c, 0x12, 0xd7, 0x9b, + 0x62, 0x9e, 0x4c, 0x12, 0xd7, 0x9b, 0x62, 0x64, 0x41, 0x43, 0x8e, 0x1b, 0x4f, 0xa7, 0xe6, 0xaa, + 0xa5, 0xc3, 0xc0, 0xba, 0xef, 0x48, 0xd6, 0x95, 0xe7, 0xe9, 0x53, 0xa8, 0x26, 0xc3, 0xce, 0xdd, + 0x2c, 0xf7, 0x90, 0x9e, 0xe7, 0x69, 0x38, 0x21, 0x2e, 0x97, 0xeb, 0xad, 0x32, 0xb3, 0xad, 0x3a, + 0x99, 0x8f, 0x7a, 0x44, 0x42, 0x86, 0x43, 0xb6, 0x58, 0xfe, 0x67, 0xb0, 0x9b, 0xe3, 0x49, 0x16, + 0x70, 0x00, 0x0d, 0x99, 0x1a, 0xf7, 0x56, 0x88, 0xab, 0xd2, 0x72, 0xfe, 0xa9, 0xc0, 0xe6, 0xc5, + 0x6c, 0xec, 0x31, 0xac, 0x44, 0x0f, 0x24, 0xb5, 0x0f, 0x35, 0x4e, 0x1a, 0x12, 0x8b, 0x75, 0xe1, + 0x5b, 0x30, 0xcb, 0x51, 0xf2, 0xeb, 0x0a, 0x39, 0x7a, 0x0a, 0xf5, 0x1b, 0x2f, 0x88, 0x31, 0xe5, + 0x40, 0xa4, 0xa8, 0x49, 0x4d, 0xce, 0x38, 0xae, 0xd4, 0x40, 0x3b, 0xd0, 0x18, 0x47, 0xb7, 0xc3, + 0x28, 0x0e, 0xf9, 0x27, 0xd8, 0x74, 0xeb, 0xe3, 0xe8, 0xd6, 0x8d, 0x43, 0xf4, 0x31, 0xac, 0x8e, + 0x7d, 0xea, 0x5d, 0x06, 0x78, 0x78, 0x4d, 0xc8, 0x1b, 0xca, 0xbf, 0xc2, 0xa6, 0xbb, 0x22, 0x37, + 0x4f, 0x92, 0x3d, 0x64, 0x27, 0x93, 0x34, 0x8a, 0xb0, 0xc7, 0xb0, 0x55, 0xe7, 0xf2, 0x74, 0x9d, + 0x60, 0xc8, 0xfc, 0x29, 0x26, 0x31, 0xe3, 0x9f, 0x8e, 0xe9, 0xaa, 0x25, 0xfa, 0x08, 0x56, 0x22, + 0x4c, 0x31, 0x1b, 0xca, 0x2c, 0x9b, 0xdc, 0x72, 0x99, 0xef, 0xbd, 0x16, 0x69, 0x21, 0xa8, 0xfe, + 0xee, 0xf9, 0xcc, 0x6a, 0x71, 0x11, 0x7f, 0x17, 0x66, 0x31, 0xc5, 0xca, 0x0c, 0x94, 0x59, 0x4c, + 0xb1, 0x30, 0x73, 0x4e, 0x60, 0x2b, 0x03, 0xe7, 0xa2, 0x9d, 0xf9, 0xd7, 0x80, 0x6d, 0x97, 0x04, + 0xc1, 0xa5, 0x37, 0x7a, 0x53, 0xa2, 0x37, 0x73, 0x30, 0x56, 0x1e, 0x86, 0xd1, 0xcc, 0x81, 0x71, + 0x6e, 0xdc, 0xaa, 0xda, 0xb8, 0x69, 0x00, 0xd7, 0x8a, 0x01, 0xae, 0xeb, 0x00, 0x2b, 0xf4, 0x1a, + 0x77, 0xe8, 0x39, 0xdf, 0xc1, 0xce, 0xbd, 0x7a, 0x16, 0x05, 0xe7, 0xaf, 0x0a, 0x6c, 0x9d, 0x86, + 0x94, 0x79, 0x41, 0x90, 0xc1, 0x26, 0x9d, 0x51, 0xa3, 0xf4, 0x8c, 0x56, 0xde, 0x65, 0x46, 0x4d, + 0x0d, 0x5c, 0xd5, 0x89, 0xea, 0x5c, 0x27, 0x4a, 0xcd, 0xad, 0xc6, 0x16, 0xf5, 0x0c, 0x5b, 0xa0, + 0x0f, 0x00, 0xc4, 0xa0, 0x71, 0xe7, 0x02, 0xc4, 0x16, 0xdf, 0x39, 0x97, 0xe4, 0xa0, 0x70, 0x6f, + 0xe6, 0xe3, 0x3e, 0x37, 0xb5, 0xce, 0x29, 0x6c, 0x67, 0xa1, 0x5a, 0x14, 0xf6, 0x3f, 0x0c, 0xd8, + 0xb9, 0x08, 0xfd, 0x5c, 0xe0, 0xf3, 0x86, 0xf2, 0x1e, 0x14, 0x95, 0x1c, 0x28, 0x36, 0xa1, 0x36, + 0x8b, 0xa3, 0x2b, 0x2c, 0xa1, 0x15, 0x8b, 0xf9, 0x1a, 0xab, 0x5a, 0x8d, 0xce, 0x10, 0xac, 0xfb, + 0x39, 0x2c, 0x58, 0x51, 0x92, 0x75, 0xca, 0xee, 0x2d, 0xc1, 0xe4, 0xce, 0x06, 0xac, 0x1f, 0x63, + 0xf6, 0x5a, 0x7c, 0x00, 0xb2, 0x3c, 0xa7, 0x0f, 0x68, 0x7e, 0xf3, 0x2e, 0x9e, 0xdc, 0xd2, 0xe3, + 0xa9, 0xab, 0x8e, 0xd2, 0x57, 0x5a, 0xce, 0xd7, 0xdc, 0xf7, 0x89, 0x4f, 0x19, 0x89, 0x6e, 0x1f, + 0x82, 0xae, 0x0d, 0xe6, 0xd4, 0x7b, 0x2b, 0xc9, 0x3f, 0x79, 0x75, 0x8e, 0x79, 0x06, 0xa9, 0xa9, + 0xcc, 0x60, 0xfe, 0x28, 0x35, 0xca, 0x1d, 0xa5, 0x3f, 0x03, 0x7a, 0x85, 0xd3, 0x53, 0xfd, 0x91, + 0x53, 0x48, 0x35, 0xa1, 0xa2, 0x0f, 0x9a, 0x05, 0x8d, 0x51, 0x80, 0xbd, 0x30, 0x9e, 0xc9, 0xb6, + 0xa9, 0xa5, 0xb3, 0x0f, 0x1b, 0x9a, 0x77, 0x99, 0x67, 0x52, 0x0f, 0xbd, 0x92, 0xde, 0x93, 0xd7, + 0xde, 0x7f, 0x4d, 0x58, 0x53, 0xc7, 0xb0, 0xb8, 0x52, 0x21, 0x1f, 0x56, 0xe6, 0xef, 0x1b, 0xe8, + 0x49, 0xf1, 0x8d, 0x2b, 0x73, 0x6d, 0xb4, 0x9f, 0x96, 0x51, 0x15, 0xb9, 0x38, 0x4b, 0x5f, 0x18, + 0x88, 0x42, 0x3b, 0x7b, 0x0d, 0x40, 0xcf, 0xf2, 0x7d, 0x14, 0xdc, 0x3b, 0xec, 0x6e, 0x59, 0x75, + 0x15, 0x16, 0xdd, 0xf0, 0xee, 0xeb, 0x67, 0x37, 0x7a, 0xd4, 0x8d, 0x7e, 0x5d, 0xb0, 0x0f, 0x4a, + 0xeb, 0xa7, 0x71, 0x7f, 0x85, 0x55, 0xed, 0x54, 0x42, 0x05, 0x68, 0xe5, 0xdd, 0x04, 0xec, 0xcf, + 0x4a, 0xe9, 0xa6, 0xb1, 0xa6, 0xb0, 0xa6, 0xd3, 0x0d, 0x2a, 0x70, 0x90, 0xcb, 0xdf, 0xf6, 0xe7, + 0xe5, 0x94, 0xd3, 0x70, 0x14, 0xda, 0x59, 0x36, 0x28, 0xea, 0x63, 0x01, 0x73, 0x15, 0xf5, 0xb1, + 0x88, 0x64, 0x9c, 0x25, 0xe4, 0x01, 0xdc, 0x91, 0x01, 0xda, 0x2f, 0x6c, 0x88, 0xce, 0x21, 0x76, + 0xe7, 0x71, 0xc5, 0x34, 0xc4, 0x0c, 0xde, 0xcb, 0x9c, 0x96, 0xa8, 0x00, 0x9a, 0xfc, 0x4b, 0x82, + 0xfd, 0xac, 0xa4, 0x76, 0xa6, 0x28, 0xc9, 0x2f, 0x0f, 0x14, 0xa5, 0x93, 0xd7, 0x03, 0x45, 0x65, + 0xa8, 0xca, 0x59, 0x42, 0x3e, 0xac, 0xb9, 0x71, 0x28, 0x43, 0x27, 0x2c, 0x81, 0x0a, 0xac, 0xef, + 0xf3, 0x93, 0xfd, 0xa4, 0x84, 0xe6, 0xdd, 0xf7, 0xfd, 0x1c, 0x7e, 0x6a, 0x2a, 0xd5, 0xcb, 0x3a, + 0xff, 0xc7, 0xf9, 0xd5, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x30, 0x80, 0xed, 0x18, 0x42, 0x0f, + 0x00, 0x00, } diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 873d2335a..a8d5594c3 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -354,13 +354,33 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R // // This is skipped if the req.ResetValues flag is set, in which case the // request values are not altered. -func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current *release.Release) { +func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current *release.Release) error { if req.ResetValues { // If ResetValues is set, we comletely ignore current.Config. log.Print("Reset values to the chart's original version.") - return + return nil } - // If req.Values is empty, but current. config is not, copy current into the + + // If the ReuseValues flag is set, we always copy the old values over the new config's values. + if req.ReuseValues { + log.Print("Reusing the old release's values") + + // We have to regenerate the old coalesced values: + oldVals, err := chartutil.CoalesceValues(current.Chart, current.Config) + if err != nil { + err := fmt.Errorf("failed to rebuild old values: %s", err) + log.Print(err) + return err + } + nv, err := oldVals.YAML() + if err != nil { + return err + } + req.Chart.Values = &chart.Config{Raw: nv} + return nil + } + + // If req.Values is empty, but current.Config is not, copy current into the // request. if (req.Values == nil || req.Values.Raw == "" || req.Values.Raw == "{}\n") && current.Config != nil && @@ -369,6 +389,7 @@ func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current log.Printf("Copying values from %s (v%d) to new release.", current.Name, current.Version) req.Values = current.Config } + return nil } // prepareUpdate builds an updated release for an update operation. @@ -388,7 +409,9 @@ func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*rele } // If new values were not supplied in the upgrade, re-use the existing values. - s.reuseValues(req, currentRelease) + if err := s.reuseValues(req, currentRelease); err != nil { + return nil, nil, err + } // Increment revision count. This is passed to templates, and also stored on // the release object. diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index 185ef05e7..d4e47a07f 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -717,7 +717,7 @@ func TestUpdateRelease(t *testing.T) { t.Errorf("Expected description %q, got %q", edesc, got) } } -func TestUpdateReleaseResetValues(t *testing.T) { +func TestUpdateRelease_ResetValues(t *testing.T) { c := helm.NewContext() rs := rsFixture() rel := releaseStub() @@ -744,6 +744,71 @@ func TestUpdateReleaseResetValues(t *testing.T) { } } +func TestUpdateRelease_ReuseValues(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + // Since reuseValues is set, this should get ignored. + Values: &chart.Config{Raw: "foo: bar\n"}, + }, + Values: &chart.Config{Raw: "name2: val2"}, + ReuseValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been overwritten with the old value. + expect := "name: value\n" + if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { + t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) + } + // This should have the newly-passed overrides. + expect = "name2: val2" + if res.Release.Config != nil && res.Release.Config.Raw != expect { + t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) + } +} + +func TestUpdateRelease_ResetReuseValues(t *testing.T) { + // This verifies that when both reset and reuse are set, reset wins. + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + ResetValues: true, + ReuseValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been unset. Config: &chart.Config{Raw: `name: value`}, + if res.Release.Config != nil && res.Release.Config.Raw != "" { + t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) + } +} + func TestUpdateReleaseFailure(t *testing.T) { c := helm.NewContext() rs := rsFixture() From fe57500930e8c797fe12dffa5364afab534ac959 Mon Sep 17 00:00:00 2001 From: Jonathan Chauncey Date: Mon, 3 Apr 2017 16:18:54 -0400 Subject: [PATCH 46/48] docs(chart_hooks.md): Rename annotation from hookWeight to hook-weight --- docs/charts_hooks.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index 9b64199e2..a5babc481 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -116,7 +116,7 @@ metadata: # This is what defines this resource as a hook. Without this line, the # job is considered part of the release. "helm.sh/hook": post-install - "helm.sh/hookWeight": "-5" + "helm.sh/hook-weight": "-5" spec: template: metadata: @@ -161,7 +161,7 @@ It is also possible to define a weight for a hook which will help build a determ ``` annotations: - "helm.sh/hookWeight": "5" + "helm.sh/hook-weight": "5" ``` Hook weights can be positive or negative numbers but must be represented as strings. When Tiller starts the execution cycle of hooks of a particular Kind it will sort those hooks in ascending order. From 8928a10071aa32d3bdbe4f5a9e7e91e7e8a4e035 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 3 Apr 2017 17:07:28 -0600 Subject: [PATCH 47/48] fix(glide.yaml): update SemVer to 1.2.3 There was a bug in SemVer 1.2.2 that miscalculated a couple of comparison patterns. --- glide.lock | 18 ++++++++---------- glide.yaml | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/glide.lock b/glide.lock index 9672f8c0d..3ea2cc617 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: 0d1c5b7304a853820dcaa296d3aa1f5f3466a8491dcef80cbcaf43c954acb2a8 -updated: 2017-03-20T15:26:55.446524716-07:00 +hash: df0fa621e6a6f80dbfeb815d9d8aa308c50346a9821e401b19b6f10782da3774 +updated: 2017-04-03T17:00:07.670429885-06:00 imports: - name: cloud.google.com/go version: 3b1ae45394a234c385be014e9a488f2bb6eef821 @@ -99,7 +99,7 @@ imports: - name: github.com/facebookgo/symwalk version: 42004b9f322246749dd73ad71008b1f3160c0052 - name: github.com/ghodss/yaml - version: 04f313413ffd65ce25f2541bfd2b2ceec5c0908c + version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee - name: github.com/go-openapi/jsonpointer version: 46af16f9f7b149af66e5d1bd010e3574dc06de98 - name: github.com/go-openapi/jsonreference @@ -182,11 +182,11 @@ imports: - jlexer - jwriter - name: github.com/Masterminds/semver - version: 59c29afe1a994eacb71c833025ca7acf874bb1da + version: 3f0ab6d4ab4bed1c61caf056b63a6e62190c7801 - name: github.com/Masterminds/sprig version: 23597e5f6ad0e4d590e71314bfd0251a4a3cf849 - name: github.com/mattn/go-runewidth - version: 14207d285c6c197daabb5c9793d63e7af9ab2d50 + version: d6bea18f789704b5f83375793155289da36a3c7f - name: github.com/mitchellh/go-wordwrap version: ad45545899c7b13c020ea92b2072220eefad42b8 - name: github.com/naoina/go-stringutil @@ -216,7 +216,7 @@ imports: - name: github.com/spf13/pflag version: 5ccb023bc27df288a957c5e994cd44fd19619465 - name: github.com/technosophos/moniker - version: ab470f5e105a44d0c87ea21bacd6a335c4816d83 + version: 9f956786b91d9786ca11aa5be6104542fa911546 - name: github.com/ugorji/go version: f1f1a805ed361a0e078bb537e4ea78cd37dcf065 subpackages: @@ -298,9 +298,9 @@ imports: - name: gopkg.in/inf.v0 version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 - name: gopkg.in/yaml.v2 - version: a3f3340b5840cee44f372bddb5880fcbc419b46a + version: a83829b6f1293c91addabc89d0571c246397bbf4 - name: k8s.io/kubernetes - version: 00a1fb254bd8e5235575fba1398b958943e39078 + version: ea8f6637b639246faa14a8d5c6f864100fcb77a9 subpackages: - cmd/kubeadm/app/apis/kubeadm - cmd/kubeadm/app/apis/kubeadm/install @@ -510,5 +510,3 @@ testImports: version: e3a8ff8ce36581f87a15341206f205b1da467059 subpackages: - assert - - mock - - require diff --git a/glide.yaml b/glide.yaml index f8a7af4f4..a703bff5b 100644 --- a/glide.yaml +++ b/glide.yaml @@ -12,7 +12,7 @@ import: version: ^2.10 - package: github.com/ghodss/yaml - package: github.com/Masterminds/semver - version: ~1.2.2 + version: ~1.2.3 - package: github.com/technosophos/moniker - package: github.com/golang/protobuf version: df1d3ca07d2d07bba352d5b73c4313b4e2a6203e From e4d39fd8c8fd5597611aaf22ead21cce6f538798 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Tue, 4 Apr 2017 01:00:38 -0700 Subject: [PATCH 48/48] fix(*): add missing proto for weight hook --- _proto/hapi/release/hook.proto | 2 ++ pkg/proto/hapi/release/hook.pb.go | 53 ++++++++++++++++--------------- pkg/tiller/hooks.go | 2 +- 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/_proto/hapi/release/hook.proto b/_proto/hapi/release/hook.proto index 413dcfb08..2237883ca 100644 --- a/_proto/hapi/release/hook.proto +++ b/_proto/hapi/release/hook.proto @@ -46,4 +46,6 @@ message Hook { repeated Event events = 5; // LastRun indicates the date/time this was last run. google.protobuf.Timestamp last_run = 6; + // Weight indicates the sort order for execution among similar Hook type + int32 weight = 7; } diff --git a/pkg/proto/hapi/release/hook.pb.go b/pkg/proto/hapi/release/hook.pb.go index 435d2c80e..3a1391745 100644 --- a/pkg/proto/hapi/release/hook.pb.go +++ b/pkg/proto/hapi/release/hook.pb.go @@ -100,8 +100,8 @@ type Hook struct { Events []Hook_Event `protobuf:"varint,5,rep,packed,name=events,enum=hapi.release.Hook_Event" json:"events,omitempty"` // LastRun indicates the date/time this was last run. LastRun *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=last_run,json=lastRun" json:"last_run,omitempty"` - // Weight indicates the sort order for execution among similar Hook types - Weight int `protobuf:"bytes,7,opt,name=weight" json:"weight,omitempty"` + // Weight indicates the sort order for execution among similar Hook type + Weight int32 `protobuf:"varint,7,opt,name=weight" json:"weight,omitempty"` } func (m *Hook) Reset() { *m = Hook{} } @@ -124,28 +124,29 @@ func init() { func init() { proto.RegisterFile("hapi/release/hook.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 354 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xdd, 0x6e, 0xa2, 0x40, - 0x18, 0x86, 0x17, 0x41, 0xd0, 0xd1, 0x75, 0x67, 0x27, 0x9b, 0xec, 0xc4, 0x93, 0x35, 0x1e, 0x79, - 0x34, 0x6c, 0x6c, 0x7a, 0x01, 0xa8, 0xd3, 0xd6, 0x48, 0xd0, 0x0c, 0x90, 0x26, 0x3d, 0x21, 0x98, - 0x8e, 0x4a, 0x14, 0x86, 0x08, 0xf6, 0x72, 0x7a, 0x55, 0xbd, 0xa0, 0x66, 0x86, 0x9f, 0x34, 0xe9, - 0xd9, 0xc7, 0xf3, 0x3e, 0x7c, 0x33, 0xef, 0x80, 0xbf, 0xa7, 0x38, 0x4f, 0xec, 0x2b, 0xbf, 0xf0, - 0xb8, 0xe0, 0xf6, 0x49, 0x88, 0x33, 0xc9, 0xaf, 0xa2, 0x14, 0x68, 0x28, 0x03, 0x52, 0x07, 0xe3, - 0x7f, 0x47, 0x21, 0x8e, 0x17, 0x6e, 0xab, 0x6c, 0x7f, 0x3b, 0xd8, 0x65, 0x92, 0xf2, 0xa2, 0x8c, - 0xd3, 0xbc, 0xd2, 0xa7, 0xef, 0x3a, 0x30, 0x9e, 0x84, 0x38, 0x23, 0x04, 0x8c, 0x2c, 0x4e, 0x39, - 0xd6, 0x26, 0xda, 0xac, 0xcf, 0xd4, 0x2c, 0xd9, 0x39, 0xc9, 0x5e, 0x71, 0xa7, 0x62, 0x72, 0x96, - 0x2c, 0x8f, 0xcb, 0x13, 0xd6, 0x2b, 0x26, 0x67, 0x34, 0x06, 0xbd, 0x34, 0xce, 0x92, 0x03, 0x2f, - 0x4a, 0x6c, 0x28, 0xde, 0x7e, 0xa3, 0xff, 0xc0, 0xe4, 0x6f, 0x3c, 0x2b, 0x0b, 0xdc, 0x9d, 0xe8, - 0xb3, 0xd1, 0x1c, 0x93, 0xaf, 0x17, 0x24, 0xf2, 0x6c, 0x42, 0xa5, 0xc0, 0x6a, 0x0f, 0xdd, 0x83, - 0xde, 0x25, 0x2e, 0xca, 0xe8, 0x7a, 0xcb, 0xb0, 0x39, 0xd1, 0x66, 0x83, 0xf9, 0x98, 0x54, 0x35, - 0x48, 0x53, 0x83, 0x04, 0x4d, 0x0d, 0x66, 0x49, 0x97, 0xdd, 0xb2, 0xe9, 0x87, 0x06, 0xba, 0x6a, - 0x11, 0x1a, 0x00, 0x2b, 0xf4, 0x36, 0xde, 0xf6, 0xd9, 0x83, 0x3f, 0xd0, 0x2f, 0x30, 0xd8, 0x31, - 0x1a, 0xad, 0x3d, 0x3f, 0x70, 0x5c, 0x17, 0x6a, 0x08, 0x82, 0xe1, 0x6e, 0xeb, 0x07, 0x2d, 0xe9, - 0xa0, 0x11, 0x00, 0x52, 0x59, 0x51, 0x97, 0x06, 0x14, 0xea, 0xea, 0x17, 0x69, 0xd4, 0xc0, 0x68, - 0x76, 0x84, 0xbb, 0x47, 0xe6, 0xac, 0x28, 0xec, 0xb6, 0x3b, 0x1a, 0x62, 0x2a, 0xc2, 0x68, 0xc4, - 0xb6, 0xae, 0xbb, 0x70, 0x96, 0x1b, 0x68, 0xa1, 0xdf, 0xe0, 0xa7, 0x72, 0x5a, 0xd4, 0x43, 0x18, - 0xfc, 0x61, 0xd4, 0xa5, 0x8e, 0x4f, 0xa3, 0x80, 0xfa, 0x41, 0xe4, 0x87, 0xcb, 0x25, 0xf5, 0x7d, - 0xd8, 0xff, 0x96, 0x3c, 0x38, 0x6b, 0x37, 0x64, 0x14, 0x82, 0x45, 0xff, 0xc5, 0xaa, 0xdf, 0x6a, - 0x6f, 0xaa, 0xfa, 0x77, 0x9f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x69, 0x41, 0x62, 0x57, 0xfc, 0x01, - 0x00, 0x00, + // 371 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0xdf, 0x8e, 0x93, 0x40, + 0x14, 0x87, 0x65, 0x5b, 0xa0, 0x3d, 0x5d, 0xd7, 0x71, 0x62, 0x74, 0xd2, 0x1b, 0xc9, 0x5e, 0x71, + 0x35, 0x98, 0x35, 0x3e, 0x00, 0xdb, 0x1d, 0x75, 0xb3, 0x84, 0x36, 0x03, 0xc4, 0xc4, 0x1b, 0xc2, + 0xc6, 0x69, 0x21, 0x2d, 0x0c, 0x29, 0x53, 0x7d, 0x33, 0x9f, 0xc4, 0x07, 0x32, 0x33, 0xfc, 0x89, + 0x89, 0x77, 0x67, 0xbe, 0xdf, 0xc7, 0x39, 0x9c, 0x03, 0xef, 0xca, 0xa2, 0xad, 0x82, 0xb3, 0x38, + 0x89, 0xa2, 0x13, 0x41, 0x29, 0xe5, 0x91, 0xb6, 0x67, 0xa9, 0x24, 0xbe, 0xd6, 0x01, 0x1d, 0x82, + 0xf5, 0xfb, 0x83, 0x94, 0x87, 0x93, 0x08, 0x4c, 0xf6, 0x7c, 0xd9, 0x07, 0xaa, 0xaa, 0x45, 0xa7, + 0x8a, 0xba, 0xed, 0xf5, 0xdb, 0xdf, 0x33, 0x98, 0x7f, 0x95, 0xf2, 0x88, 0x31, 0xcc, 0x9b, 0xa2, + 0x16, 0xc4, 0xf2, 0x2c, 0x7f, 0xc9, 0x4d, 0xad, 0xd9, 0xb1, 0x6a, 0x7e, 0x90, 0xab, 0x9e, 0xe9, + 0x5a, 0xb3, 0xb6, 0x50, 0x25, 0x99, 0xf5, 0x4c, 0xd7, 0x78, 0x0d, 0x8b, 0xba, 0x68, 0xaa, 0xbd, + 0xe8, 0x14, 0x99, 0x1b, 0x3e, 0xbd, 0xf1, 0x07, 0x70, 0xc4, 0x4f, 0xd1, 0xa8, 0x8e, 0xd8, 0xde, + 0xcc, 0xbf, 0xb9, 0x23, 0xf4, 0xdf, 0x1f, 0xa4, 0x7a, 0x36, 0x65, 0x5a, 0xe0, 0x83, 0x87, 0x3f, + 0xc1, 0xe2, 0x54, 0x74, 0x2a, 0x3f, 0x5f, 0x1a, 0xe2, 0x78, 0x96, 0xbf, 0xba, 0x5b, 0xd3, 0x7e, + 0x0d, 0x3a, 0xae, 0x41, 0xd3, 0x71, 0x0d, 0xee, 0x6a, 0x97, 0x5f, 0x1a, 0xfc, 0x16, 0x9c, 0x5f, + 0xa2, 0x3a, 0x94, 0x8a, 0xb8, 0x9e, 0xe5, 0xdb, 0x7c, 0x78, 0xdd, 0xfe, 0xb1, 0xc0, 0x36, 0x03, + 0xf0, 0x0a, 0xdc, 0x2c, 0x7e, 0x8a, 0xb7, 0xdf, 0x62, 0xf4, 0x02, 0xbf, 0x82, 0xd5, 0x8e, 0xb3, + 0xfc, 0x31, 0x4e, 0xd2, 0x30, 0x8a, 0x90, 0x85, 0x11, 0x5c, 0xef, 0xb6, 0x49, 0x3a, 0x91, 0x2b, + 0x7c, 0x03, 0xa0, 0x95, 0x07, 0x16, 0xb1, 0x94, 0xa1, 0x99, 0xf9, 0x44, 0x1b, 0x03, 0x98, 0x8f, + 0x3d, 0xb2, 0xdd, 0x17, 0x1e, 0x3e, 0x30, 0x64, 0x4f, 0x3d, 0x46, 0xe2, 0x18, 0xc2, 0x59, 0xce, + 0xb7, 0x51, 0x74, 0x1f, 0x6e, 0x9e, 0x90, 0x8b, 0x5f, 0xc3, 0x4b, 0xe3, 0x4c, 0x68, 0x81, 0x09, + 0xbc, 0xe1, 0x2c, 0x62, 0x61, 0xc2, 0xf2, 0x94, 0x25, 0x69, 0x9e, 0x64, 0x9b, 0x0d, 0x4b, 0x12, + 0xb4, 0xfc, 0x2f, 0xf9, 0x1c, 0x3e, 0x46, 0x19, 0x67, 0x08, 0xee, 0x97, 0xdf, 0xdd, 0xe1, 0x86, + 0xcf, 0x8e, 0x39, 0xcb, 0xc7, 0xbf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x82, 0x3c, 0x7a, 0x0e, 0x14, + 0x02, 0x00, 0x00, } diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 3833ba9ea..b26243479 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -123,7 +123,7 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort Path: n, Manifest: c, Events: []release.Hook_Event{}, - Weight: hw, + Weight: int32(hw), } isHook := false