From 787d326c7a0c78c6c5306bc37838865c573ffdf0 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Sat, 30 Jul 2016 23:55:58 -0700 Subject: [PATCH 01/22] chore(*): upgrade dependencies --- glide.lock | 154 +++++++++++++++++++++++++++-------------------------- glide.yaml | 9 +++- 2 files changed, 86 insertions(+), 77 deletions(-) diff --git a/glide.lock b/glide.lock index 84dbc29a6..ba8502298 100644 --- a/glide.lock +++ b/glide.lock @@ -1,10 +1,10 @@ -hash: 141ef5b9c491c91b026ab4007e48502c9a6df9f173c40e1406233dd44f065190 -updated: 2016-07-05T16:51:52.631048739-07:00 +hash: d3f3df18316dca3703f5d073e8f9b1e6bfdb27e8d7fc9c5d742afeddebb022db +updated: 2016-07-30T23:52:42.581826208-07:00 imports: - name: github.com/aokoli/goutils version: 9c37978a95bd5c709a15883b6242714ea6709e64 - name: github.com/asaskevich/govalidator - version: df81827fdd59d8b4fb93d8910b286ab7a3919520 + version: 7664702784775e51966f0885f5cd27435916517b - name: github.com/beorn7/perks version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 subpackages: @@ -133,7 +133,7 @@ imports: - ptypes/any - ptypes/timestamp - name: github.com/google/cadvisor - version: 4dbefc9b671b81257973a33211fb12370c1a526e + version: c2ea32971ae033041f0fb0f309b1dee94fd1d55f subpackages: - api - cache/memory @@ -267,15 +267,15 @@ imports: - name: google.golang.org/appengine version: 12d5545dc1cfa6047a286d5e853841b6471f4c19 subpackages: + - urlfetch - internal + - internal/urlfetch - internal/app_identity + - internal/modules - internal/base - internal/datastore - internal/log - - internal/modules - internal/remote_api - - urlfetch - - internal/urlfetch - name: google.golang.org/cloud version: eb47ba841d53d93506cfbfbc03927daf9cc48f88 subpackages: @@ -297,40 +297,67 @@ imports: - name: gopkg.in/yaml.v2 version: a83829b6f1293c91addabc89d0571c246397bbf4 - name: k8s.io/kubernetes - version: 283137936a498aed572ee22af6774b6fb6e9fd94 + version: e7f022c926583ed8e755a52f23abc4cf8b532d12 subpackages: - pkg/api - pkg/api/meta + - pkg/api/error - pkg/client/restclient - pkg/client/unversioned + - pkg/apis/batch - pkg/client/unversioned/clientcmd - pkg/client/unversioned/fake - pkg/client/unversioned/portforward - pkg/client/unversioned/remotecommand + - pkg/kubectl - pkg/kubectl/cmd/util - pkg/kubectl/resource - pkg/labels + - pkg/runtime + - pkg/watch - pkg/api/errors + - pkg/client/unversioned/testclient - pkg/api/meta/metatypes - pkg/api/resource - pkg/api/unversioned - pkg/auth/user - pkg/conversion - pkg/fields - - pkg/runtime - pkg/runtime/serializer - pkg/types - pkg/util - pkg/util/intstr - pkg/util/rand - pkg/util/sets + - pkg/api/install + - pkg/apimachinery/registered + - pkg/apis/apps + - pkg/apis/apps/install + - pkg/apis/authentication.k8s.io/install + - pkg/apis/authorization/install + - pkg/apis/autoscaling + - pkg/apis/autoscaling/install + - pkg/apis/batch/install + - pkg/apis/batch/v2alpha1 + - pkg/apis/componentconfig/install + - pkg/apis/extensions + - pkg/apis/extensions/install + - pkg/apis/policy + - pkg/apis/policy/install + - pkg/apis/rbac + - pkg/apis/rbac/install + - pkg/client/typed/discovery + - pkg/util/net + - pkg/util/wait + - pkg/version + - plugin/pkg/client/auth + - pkg/util/validation + - pkg/util/validation/field - pkg/client/unversioned/auth - pkg/client/unversioned/clientcmd/api - pkg/client/unversioned/clientcmd/api/latest - pkg/util/errors - pkg/util/homedir - - pkg/util/validation - - pkg/util/validation/field - pkg/kubelet/server/portforward - pkg/util/httpstream - pkg/util/runtime @@ -339,42 +366,53 @@ imports: - pkg/util/httpstream/spdy - federation/apis/federation - federation/client/clientset_generated/federation_internalclientset - - pkg/api/service + - pkg/api/annotations + - pkg/api/util + - pkg/api/v1 - pkg/api/validation - - pkg/apimachinery - - pkg/apimachinery/registered - - pkg/apis/apps - - pkg/apis/autoscaling - - pkg/apis/batch - - pkg/apis/extensions - - pkg/apis/policy - - pkg/apis/rbac - - pkg/client/typed/discovery + - pkg/apis/batch/v1 + - pkg/client/clientset_generated/internalclientset - pkg/client/unversioned/adapters/internalclientset + - pkg/credentialprovider + - pkg/fieldpath + - pkg/kubelet/qos/util + - pkg/util/deployment + - pkg/util/integer + - pkg/util/jsonpath + - pkg/util/slice + - pkg/api/service + - pkg/apimachinery - pkg/controller - - pkg/kubectl - pkg/registry/thirdpartyresourcedata - pkg/runtime/serializer/json - pkg/util/flag - pkg/util/strategicpatch - - pkg/watch - pkg/util/yaml - - pkg/api/testapi - - third_party/forked/reflect - pkg/conversion/queryparams - pkg/util/json + - pkg/api/testapi + - third_party/forked/reflect - pkg/runtime/serializer/protobuf - pkg/runtime/serializer/recognizer - pkg/runtime/serializer/versioning - - pkg/util/wait - - pkg/api/v1 + - pkg/watch/versioned + - pkg/apis/apps/v1alpha1 + - pkg/apis/authentication.k8s.io + - pkg/apis/authentication.k8s.io/v1beta1 + - pkg/apis/authorization + - pkg/apis/authorization/v1beta1 + - pkg/apis/autoscaling/v1 + - pkg/apis/componentconfig + - pkg/apis/componentconfig/v1alpha1 + - pkg/apis/extensions/v1beta1 + - pkg/apis/policy/v1alpha1 + - pkg/apis/rbac/v1alpha1 - pkg/client/metrics - pkg/runtime/serializer/streaming - pkg/util/crypto - pkg/util/flowcontrol - - pkg/util/net - - pkg/version - - pkg/watch/versioned + - plugin/pkg/client/auth/gcp + - plugin/pkg/client/auth/oidc - pkg/client/unversioned/clientcmd/api/v1 - pkg/httplog - pkg/util/wsstream @@ -382,71 +420,35 @@ imports: - federation/apis/federation/install - federation/client/clientset_generated/federation_internalclientset/typed/core/unversioned - federation/client/clientset_generated/federation_internalclientset/typed/federation/unversioned - - pkg/util/net/sets + - pkg/util/parsers - pkg/api/endpoints - pkg/api/pod - pkg/api/unversioned/validation - - pkg/api/util - pkg/capabilities - - pkg/api/install - - pkg/apis/apps/install - - pkg/apis/authentication.k8s.io/install - - pkg/apis/authorization/install - - pkg/apis/autoscaling/install - - pkg/apis/batch/install - - pkg/apis/batch/v2alpha1 - - pkg/apis/componentconfig/install - - pkg/apis/extensions/install - - pkg/apis/policy/install - - pkg/apis/rbac/install - - plugin/pkg/client/auth - - pkg/client/clientset_generated/internalclientset + - pkg/client/clientset_generated/internalclientset/typed/autoscaling/unversioned - pkg/client/clientset_generated/internalclientset/typed/batch/unversioned - pkg/client/clientset_generated/internalclientset/typed/core/unversioned - pkg/client/clientset_generated/internalclientset/typed/extensions/unversioned + - pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned + - pkg/util/labels + - pkg/util/pod + - pkg/util/replicaset + - third_party/golang/template + - pkg/util/net/sets - pkg/client/cache - pkg/client/record - pkg/controller/framework - pkg/util/hash - - pkg/util/integer - - pkg/api/annotations - - pkg/apis/batch/v1 - - pkg/credentialprovider - - pkg/fieldpath - - pkg/kubelet/qos/util - - pkg/util/deployment - - pkg/util/jsonpath - - pkg/util/slice - pkg/api/rest - - pkg/apis/extensions/v1beta1 - pkg/apis/extensions/validation - pkg/registry/generic - pkg/util/framer - third_party/forked/json - - pkg/util/parsers + - pkg/kubelet/qos + - pkg/master/ports - federation/apis/federation/v1beta1 - - pkg/apis/apps/v1alpha1 - - pkg/apis/authentication.k8s.io - - pkg/apis/authentication.k8s.io/v1beta1 - - pkg/apis/authorization - - pkg/apis/authorization/v1beta1 - - pkg/apis/autoscaling/v1 - - pkg/apis/componentconfig - - pkg/apis/componentconfig/v1alpha1 - - pkg/apis/policy/v1alpha1 - - pkg/apis/rbac/v1alpha1 - - plugin/pkg/client/auth/gcp - - plugin/pkg/client/auth/oidc - - pkg/client/clientset_generated/internalclientset/typed/autoscaling/unversioned - - pkg/client/clientset_generated/internalclientset/typed/rbac/unversioned - - pkg/util/labels - - pkg/util/pod - - pkg/util/replicaset - - third_party/golang/template - pkg/security/podsecuritypolicy/util - pkg/storage - - pkg/kubelet/qos - - pkg/master/ports - name: speter.net/go/exp/math/dec/inf version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 repo: https://github.com/go-inf/inf.git diff --git a/glide.yaml b/glide.yaml index 79020c590..2b32855e8 100644 --- a/glide.yaml +++ b/glide.yaml @@ -22,21 +22,28 @@ import: - package: google.golang.org/grpc version: dec33edc378cf4971a2741cfd86ed70a644d6ba3 - package: k8s.io/kubernetes - version: v1.3.0 + version: ~1.3 subpackages: - pkg/api - pkg/api/meta + - pkg/api/error - pkg/client/restclient - pkg/client/unversioned + - pkg/apis/batch - pkg/client/unversioned/clientcmd - pkg/client/unversioned/fake - pkg/client/unversioned/portforward - pkg/client/unversioned/remotecommand + - pkg/kubectl - pkg/kubectl/cmd/util - pkg/kubectl/resource - pkg/labels + - pkg/runtime + - pkg/watch - package: github.com/gosuri/uitable - package: speter.net/go/exp/math/dec/inf + version: ^0.9.0 repo: https://github.com/go-inf/inf.git vcs: git - package: github.com/asaskevich/govalidator + version: ^4.0.0 From 64831314a7a79f6ae7e4590ca02c1ae26777c514 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 1 Aug 2016 12:57:33 -0600 Subject: [PATCH 02/22] docs(charts): describe {{.Files}} --- docs/charts.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/charts.md b/docs/charts.md index 7ac3dd2c6..03eecba04 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -202,6 +202,11 @@ sensitive_. - `Chart`: The contents of the `Chart.yaml`. Thus, the chart version is obtainable as `Chart.Version` and the maintainers are in `Chart.Maintainers`. +- `Files`: A map-like object containing all non-special files in the chart. This + will not give you access to templates, but will give you access to additional + files that are present. Files can be accessed using `{{index .Files "file.name"}}` + or using the `{{.Files.Get name}}` or `{{.Files.GetString name}}` functions. Note that + file data is returned as a `[]byte` unless `{{.Files.GetString}}` is used. **NOTE:** Any unknown Chart.yaml fields will be dropped. They will not be accessible inside of the `Chart` object. Thus, Chart.yaml cannot be From 45931118924ca4a3df01bbb0da3b0e74719abc10 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 1 Aug 2016 13:25:11 -0600 Subject: [PATCH 03/22] fix(helm): rename --reuse-name to --replace Closes #1010 --- cmd/helm/install.go | 6 +++--- cmd/helm/install_test.go | 4 ++-- cmd/tiller/release_server.go | 4 +++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index e80527b48..9d7e84a72 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -59,7 +59,7 @@ type installCmd struct { chartPath string dryRun bool disableHooks bool - reuseName bool + replace bool out io.Writer client helm.Interface values *values @@ -98,7 +98,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f.StringVar(&inst.namespace, "namespace", "default", "the namespace to install the release into") f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install") f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install") - f.BoolVar(&inst.reuseName, "reuse-name", false, "force Tiller to re-use the given name, even if that name is already used. This is unsafe in production") + f.BoolVar(&inst.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") return cmd } @@ -119,7 +119,7 @@ func (i *installCmd) run() error { helm.ValueOverrides(rawVals), helm.ReleaseName(i.name), helm.InstallDryRun(i.dryRun), - helm.InstallReuseName(i.reuseName), + helm.InstallReuseName(i.replace), helm.InstallDisableHooks(i.disableHooks)) if err != nil { return prettyError(err) diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 41663a337..9db48cb47 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -59,9 +59,9 @@ func TestInstall(t *testing.T) { }, // Install, re-use name { - name: "install and reuse name", + name: "install and replace release", args: []string{"testdata/testcharts/alpine"}, - flags: strings.Split("--name aeneas --reuse-name", " "), + flags: strings.Split("--name aeneas --replace", " "), expected: "aeneas", resp: releaseMock(&releaseOptions{name: "aeneas"}), }, diff --git a/cmd/tiller/release_server.go b/cmd/tiller/release_server.go index 42494cff6..061a76c4b 100644 --- a/cmd/tiller/release_server.go +++ b/cmd/tiller/release_server.go @@ -240,10 +240,12 @@ func (s *releaseServer) uniqName(start string, reuse bool) (string, error) { if start != "" { if rel, err := s.env.Releases.Read(start); err == storage.ErrNotFound { return start, nil - } else if reuse && rel.Info.Status.Code == release.Status_DELETED { + } else if st := rel.Info.Status.Code; reuse && (st == release.Status_DELETED || st == release.Status_FAILED) { // Allowe re-use of names if the previous release is marked deleted. log.Printf("reusing name %q", start) return start, nil + } else if reuse { + return "", errors.New("cannot re-use a name that is still in use") } return "", fmt.Errorf("a release named %q already exists", start) From 39ba728b6fec8fafa23ff27bca073aab363ab613 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 1 Aug 2016 13:00:02 -0700 Subject: [PATCH 04/22] ref(cmd): refactor init command --- cmd/helm/helm.go | 7 +++--- cmd/helm/init.go | 63 +++++++++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index c47a13e6a..ad1049267 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -84,12 +84,13 @@ func newRootCmd(out io.Writer) *cobra.Command { cmd.AddCommand( newCreateCmd(out), + newDeleteCmd(nil, out), newGetCmd(nil, out), + newInitCmd(out), + newInspectCmd(nil, out), + newInstallCmd(nil, out), newListCmd(nil, out), newStatusCmd(nil, out), - newInstallCmd(nil, out), - newDeleteCmd(nil, out), - newInspectCmd(nil, out), newUpgradeCmd(nil, out), ) return cmd diff --git a/cmd/helm/init.go b/cmd/helm/init.go index 565022a89..218527295 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -19,6 +19,7 @@ package main import ( "errors" "fmt" + "io" "os" "github.com/spf13/cobra" @@ -32,58 +33,54 @@ Kubernetes Cluster and sets up local configuration in $HELM_HOME (default: ~/.he ` var ( - tillerImg string - clientOnly bool defaultRepository = "kubernetes-charts" defaultRepositoryURL = "http://storage.googleapis.com/kubernetes-charts" ) -func init() { - f := initCmd.Flags() - f.StringVarP(&tillerImg, "tiller-image", "i", "", "override tiller image") - f.BoolVarP(&clientOnly, "client-only", "c", false, "If set does not install tiller") - RootCommand.AddCommand(initCmd) +type initCmd struct { + image string + clientOnly bool + out io.Writer } -var initCmd = &cobra.Command{ - Use: "init", - Short: "initialize Helm on both client and server", - Long: initDesc, - RunE: runInit, +func newInitCmd(out io.Writer) *cobra.Command { + i := &initCmd{ + out: out, + } + cmd := &cobra.Command{ + Use: "init", + Short: "initialize Helm on both client and server", + Long: initDesc, + RunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 0 { + return errors.New("This command does not accept arguments") + } + return i.run() + }, + } + cmd.Flags().StringVarP(&i.image, "tiller-image", "i", "", "override tiller image") + cmd.Flags().BoolVarP(&i.clientOnly, "client-only", "c", false, "If set does not install tiller") + return cmd } // runInit initializes local config and installs tiller to Kubernetes Cluster -func runInit(cmd *cobra.Command, args []string) error { - if len(args) != 0 { - return errors.New("This command does not accept arguments. \n") - } - +func (i *initCmd) run() error { if err := ensureHome(); err != nil { return err } - if !clientOnly { - if err := installTiller(); err != nil { - return err + if !i.clientOnly { + if err := client.Install(tillerNamespace, i.image, flagDebug); err != nil { + return fmt.Errorf("error installing: %s", err) } + fmt.Fprintln(i.out, "\nTiller (the helm server side component) has been installed into your Kubernetes Cluster.") } else { - fmt.Println("Not installing tiller due to 'client-only' flag having been set") + fmt.Fprintln(i.out, "Not installing tiller due to 'client-only' flag having been set") } - - fmt.Println("Happy Helming!") - return nil -} - -func installTiller() error { - if err := client.Install(tillerNamespace, tillerImg, flagDebug); err != nil { - return fmt.Errorf("error installing: %s", err) - } - fmt.Println("\nTiller (the helm server side component) has been installed into your Kubernetes Cluster.") - + fmt.Fprintln(i.out, "Happy Helming!") return nil } -// requireHome checks to see if $HELM_HOME exists, and returns an error if it does not. func requireHome() error { dirs := []string{homePath(), repositoryDirectory(), cacheDirectory(), localRepoDirectory()} for _, d := range dirs { From 9718c9e7c8525222de752afcd29432ceff9d42c9 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 29 Jul 2016 17:06:09 -0600 Subject: [PATCH 05/22] feat(engine): add 'partial' function This adds a context-aware template function called 'partial' that will allow rendering other templates in a chart into a string value, which can then be piped to other functions. Usage is something like '{{partial 'path/to/template.yaml' | indent 2}}' This might be a bad idea. Closes #1005 --- pkg/engine/engine.go | 27 ++++++++++++++++++++++++++- pkg/engine/engine_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index cb88088e6..73b8dabad 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -88,6 +88,28 @@ type renderable struct { vals chartutil.Values } +// alterFuncMap takes the Engine's FuncMap and adds context-specific functions. +// +// The resulting FuncMap is only valid for the passed-in template. +func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { + // Clone the func map because we are adding context-specific functions. + var funcMap template.FuncMap = map[string]interface{}{} + for k, v := range e.FuncMap { + funcMap[k] = v + } + + // Add the 'include' function here so we can close over t. + funcMap["include"] = func(name string, data interface{}) string { + buf := bytes.NewBuffer(nil) + if err := t.ExecuteTemplate(buf, name, data); err != nil { + buf.WriteString(err.Error()) + } + return buf.String() + } + + return funcMap +} + // render takes a map of templates/values and renders them. func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { // Basically, what we do here is start with an empty parent template and then @@ -105,10 +127,13 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { // but will still emit for others. We mitigate that later. t.Option("missingkey=zero") } + + funcMap := e.alterFuncMap(t) + files := []string{} for fname, r := range tpls { log.Printf("Preparing template %s", fname) - t = t.New(fname).Funcs(e.FuncMap) + t = t.New(fname).Funcs(funcMap) if _, err := t.Parse(r.tpl); err != nil { return map[string]string{}, fmt.Errorf("parse error in %q: %s", fname, err) } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index 03ee083dc..f680d2053 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -358,3 +358,33 @@ func TestRenderBuiltinValues(t *testing.T) { } } + +func TestAlterFuncMap(t *testing.T) { + c := &chart.Chart{ + Metadata: &chart.Metadata{Name: "conrad"}, + Templates: []*chart.Template{ + {Name: "quote", Data: []byte(`{{include "conrad/_partial" . | indent 2}} dead.`)}, + {Name: "_partial", Data: []byte(`{{.Release.Name}} - he`)}, + }, + Values: &chart.Config{Raw: ``}, + Dependencies: []*chart.Chart{}, + } + + v := chartutil.Values{ + "Values": &chart.Config{Raw: ""}, + "Chart": c.Metadata, + "Release": chartutil.Values{ + "Name": "Mistah Kurtz", + }, + } + + out, err := New().Render(c, v) + if err != nil { + t.Fatal(err) + } + + expect := " Mistah Kurtz - he dead." + if got := out["conrad/quote"]; got != expect { + t.Errorf("Expected %q, got %q (%v)", expect, got, out) + } +} From 01a598f53110a3a6b55701b73f075000a75c0823 Mon Sep 17 00:00:00 2001 From: vaikas-google Date: Mon, 1 Aug 2016 19:19:27 -0700 Subject: [PATCH 06/22] Add name-template flag for installation that allows the user to specify a template used for release name generation --- cmd/helm/install.go | 29 ++++++++++++++ cmd/helm/install_test.go | 84 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index e80527b48..e71a3ed41 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -17,6 +17,7 @@ limitations under the License. package main import ( + "bytes" "fmt" "io" "io/ioutil" @@ -24,6 +25,9 @@ import ( "path/filepath" "strings" + "html/template" + + "github.com/Masterminds/sprig" "github.com/ghodss/yaml" "github.com/spf13/cobra" @@ -63,6 +67,7 @@ type installCmd struct { out io.Writer client helm.Interface values *values + nameTemplate string } func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { @@ -100,6 +105,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&inst.reuseName, "reuse-name", false, "force Tiller to re-use the given name, even if that name is already used. This is unsafe in production") f.Var(inst.values, "set", "set values on the command line. Separate values with commas: key1=val1,key2=val2") + f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release") return cmd } @@ -113,6 +119,16 @@ func (i *installCmd) run() error { return err } + // If template is specified, try to run the template. + if i.nameTemplate != "" { + i.name, err = generateName(i.nameTemplate) + if err != nil { + return err + } + // Print the final name so the user knows what the final name of the release is. + fmt.Printf("final name: %s\n", i.name) + } + res, err := i.client.InstallRelease( i.chartPath, i.namespace, @@ -249,3 +265,16 @@ func locateChartPath(name string) (string, error) { return name, fmt.Errorf("file %q not found", origname) } + +func generateName(nameTemplate string) (string, error) { + t, err := template.New("name-template").Funcs(sprig.FuncMap()).Parse(nameTemplate) + if err != nil { + return "", err + } + var b bytes.Buffer + err = t.Execute(&b, nil) + if err != nil { + return "", err + } + return b.String(), nil +} diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 41663a337..24b05df4d 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "regexp" "strings" "testing" @@ -65,6 +66,14 @@ func TestInstall(t *testing.T) { expected: "aeneas", resp: releaseMock(&releaseOptions{name: "aeneas"}), }, + // Install, using the name-template + { + name: "install with name-template", + args: []string{"testdata/testcharts/alpine"}, + flags: []string{"--name-template", "{{upper \"foobar\"}}"}, + expected: "FOOBAR", + resp: releaseMock(&releaseOptions{name: "FOOBAR"}), + }, } runReleaseCases(t, tests, func(c *fakeReleaseClient, out io.Writer) *cobra.Command { @@ -113,3 +122,78 @@ sailor: sinbad t.Errorf("Expected String() to be \n%s\nGot\n%s\n", y, out) } } + +type nameTemplateTestCase struct { + tpl string + expected string + expectedErrorStr string +} + +func TestNameTemplate(t *testing.T) { + testCases := []nameTemplateTestCase{ + // Just a straight up nop please + { + tpl: "foobar", + expected: "foobar", + expectedErrorStr: "", + }, + // Random numbers at the end for fun & profit + { + tpl: "foobar-{{randNumeric 6}}", + expected: "foobar-[0-9]{6}$", + expectedErrorStr: "", + }, + // Random numbers in the middle for fun & profit + { + tpl: "foobar-{{randNumeric 4}}-baz", + expected: "foobar-[0-9]{4}-baz$", + expectedErrorStr: "", + }, + // No such function + { + tpl: "foobar-{{randInt}}", + expected: "", + expectedErrorStr: "function \"randInt\" not defined", + }, + // Invalid template + { + tpl: "foobar-{{", + expected: "", + expectedErrorStr: "unexpected unclosed action", + }, + } + + for _, tc := range testCases { + + n, err := generateName(tc.tpl) + if err != nil { + if tc.expectedErrorStr == "" { + t.Errorf("Was not expecting error, but got: %v", err) + continue + } + re, compErr := regexp.Compile(tc.expectedErrorStr) + if compErr != nil { + t.Errorf("Expected error string failed to compile: %v", compErr) + continue + } + if !re.MatchString(err.Error()) { + t.Errorf("Error didn't match for %s expected %s but got %v", tc.tpl, tc.expectedErrorStr, err) + continue + } + } + if err == nil && tc.expectedErrorStr != "" { + t.Errorf("Was expecting error %s but didn't get an error back", tc.expectedErrorStr) + } + + if tc.expected != "" { + re, err := regexp.Compile(tc.expected) + if err != nil { + t.Errorf("Expected string failed to compile: %v", err) + continue + } + if !re.MatchString(n) { + t.Errorf("Returned name didn't match for %s expected %s but got %s", tc.tpl, tc.expected, n) + } + } + } +} From 49644678689532062b16249c308f8f2113126b48 Mon Sep 17 00:00:00 2001 From: Phillip Wittrock Date: Tue, 2 Aug 2016 13:04:22 -0700 Subject: [PATCH 07/22] Swtich from rc to deployment for running tiller --- pkg/client/install.go | 12 +++--------- pkg/kube/tunnel.go | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/pkg/client/install.go b/pkg/client/install.go index 1ffbb5b77..0b7a3b1fc 100644 --- a/pkg/client/install.go +++ b/pkg/client/install.go @@ -66,19 +66,13 @@ func Install(namespace, image string, verbose bool) error { // InstallYAML is the installation YAML for DM. const InstallYAML = ` --- -apiVersion: v1 -kind: ReplicationController +apiVersion: extensions/v1beta1 +kind: Deployment metadata: - labels: - app: helm - name: tiller - name: tiller-rc + name: tiller-deploy namespace: {{ .Namespace }} spec: replicas: 1 - selector: - app: helm - name: tiller template: metadata: labels: diff --git a/pkg/kube/tunnel.go b/pkg/kube/tunnel.go index ff3b76b42..66406d512 100644 --- a/pkg/kube/tunnel.go +++ b/pkg/kube/tunnel.go @@ -51,7 +51,7 @@ func (c *Client) ForwardPort(namespace, podName string, remote int) (*Tunnel, er } // Build a url to the portforward endpoing - // example: http://localhost:8080/api/v1/namespaces/helm/pods/tiller-rc-9itlq/portforward + // example: http://localhost:8080/api/v1/namespaces/helm/pods/tiller-deploy-9itlq/portforward u := client.RESTClient.Post(). Resource("pods"). Namespace(namespace). From d42dbd353897cdfb3c400e45142ef2e791a7b840 Mon Sep 17 00:00:00 2001 From: vaikas-google Date: Tue, 2 Aug 2016 14:15:35 -0700 Subject: [PATCH 08/22] address code review comments. use text/template instead of html/template --- cmd/helm/install.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index e71a3ed41..bec47781d 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -25,7 +25,7 @@ import ( "path/filepath" "strings" - "html/template" + "text/template" "github.com/Masterminds/sprig" "github.com/ghodss/yaml" @@ -267,7 +267,7 @@ func locateChartPath(name string) (string, error) { } func generateName(nameTemplate string) (string, error) { - t, err := template.New("name-template").Funcs(sprig.FuncMap()).Parse(nameTemplate) + t, err := template.New("name-template").Funcs(sprig.TxtFuncMap()).Parse(nameTemplate) if err != nil { return "", err } From 107821f9ec28ba75fe5b6ac3fb35b542720a8127 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Wed, 3 Aug 2016 18:02:42 +0430 Subject: [PATCH 09/22] Add a strict flag to helm lint --- cmd/helm/lint.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/helm/lint.go b/cmd/helm/lint.go index d3e496cda..75a0f3082 100644 --- a/cmd/helm/lint.go +++ b/cmd/helm/lint.go @@ -47,7 +47,10 @@ var lintCommand = &cobra.Command{ RunE: lintCmd, } +var flagStrict bool + func init() { + lintCommand.Flags().BoolVarP(&flagStrict, "strict", "", false, "fail on lint warnings") RootCommand.AddCommand(lintCommand) } @@ -59,6 +62,13 @@ func lintCmd(cmd *cobra.Command, args []string) error { paths = args } + var lowestTolerance int + if flagStrict { + lowestTolerance = support.WarningSev + } else { + lowestTolerance = support.ErrorSev + } + var total int var failures int for _, path := range paths { @@ -77,7 +87,7 @@ func lintCmd(cmd *cobra.Command, args []string) error { } total = total + 1 - if linter.HighestSeverity >= support.ErrorSev { + if linter.HighestSeverity >= lowestTolerance { failures = failures + 1 } } From b7945d05c4373576b8274069d3dc3ce9b39e46d1 Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Wed, 3 Aug 2016 13:01:13 -0600 Subject: [PATCH 10/22] ref(templates): change GetString to Get This changes "pkg/chartutil".Files.Get to return a string, removes "pkg/chartutil".Files.GetString, and adds "pkg/chartutil".Files.GetBytes. Closes #1020 --- pkg/chartutil/files.go | 19 ++++++++++++------- pkg/chartutil/files_test.go | 4 ++-- pkg/engine/engine_test.go | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/pkg/chartutil/files.go b/pkg/chartutil/files.go index 45598de3a..89120a42f 100644 --- a/pkg/chartutil/files.go +++ b/pkg/chartutil/files.go @@ -32,11 +32,14 @@ func NewFiles(from []*any.Any) Files { return files } -// Get a file by path. +// GetBytes gets a file by path. +// +// The returned data is raw. In a template context, this is identical to calling +// {{index .Files $path}}. // // This is intended to be accessed from within a template, so a missed key returns // an empty []byte. -func (f Files) Get(name string) []byte { +func (f Files) GetBytes(name string) []byte { v, ok := f[name] if !ok { return []byte{} @@ -44,10 +47,12 @@ func (f Files) Get(name string) []byte { return v } -// GetString returns a string representation of the given file. +// Get returns a string representation of the given file. +// +// Fetch the contents of a file as a string. It is designed to be called in a +// template. // -// This is a convenience for the otherwise cumbersome template logic -// for '{{.Files.Get "foo" | printf "%s"}}'. -func (f Files) GetString(name string) string { - return string(f.Get(name)) +// {{.Files.Get "foo"}} +func (f Files) Get(name string) string { + return string(f.GetBytes(name)) } diff --git a/pkg/chartutil/files_test.go b/pkg/chartutil/files_test.go index 97eb4ee05..5e162f19d 100644 --- a/pkg/chartutil/files_test.go +++ b/pkg/chartutil/files_test.go @@ -43,10 +43,10 @@ func TestNewFiles(t *testing.T) { } for i, f := range cases { - if got := string(files.Get(f.path)); got != f.data { + if got := string(files.GetBytes(f.path)); got != f.data { t.Errorf("%d: expected %q, got %q", i, f.data, got) } - if got := files.GetString(f.path); got != f.data { + if got := files.Get(f.path); got != f.data { t.Errorf("%d: expected %q, got %q", i, f.data, got) } } diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index f680d2053..ec19f8ded 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -312,7 +312,7 @@ func TestRenderBuiltinValues(t *testing.T) { Metadata: &chart.Metadata{Name: "Latium"}, Templates: []*chart.Template{ {Name: "Lavinia", Data: []byte(`{{.Template.Name}}{{.Chart.Name}}{{.Release.Name}}`)}, - {Name: "From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.GetString "book/title.txt"}}`)}, + {Name: "From", Data: []byte(`{{.Files.author | printf "%s"}} {{.Files.Get "book/title.txt"}}`)}, }, Values: &chart.Config{Raw: ``}, Dependencies: []*chart.Chart{}, From c0de9b4adab4767772f80d8b826d61f5417209b5 Mon Sep 17 00:00:00 2001 From: Yusuke Kuoka Date: Thu, 4 Aug 2016 09:45:12 +0900 Subject: [PATCH 11/22] Check for the tgz extension while collecting chart paths --- pkg/repo/repo.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index f8b4267f4..c4ed13c52 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -98,8 +98,7 @@ func LoadChartRepository(dir, url string) (*ChartRepository, error) { return nil } r.IndexFile = i - } else { - // TODO: check for tgz extension + } else if strings.HasSuffix(f.Name(), ".tgz") { r.ChartPaths = append(r.ChartPaths, path) } } From b009bf7dc4ea62962f239643be591d931eac7b1a Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Thu, 4 Aug 2016 15:53:41 -0600 Subject: [PATCH 12/22] fix(helm): fix inspect command to not panic Helm can now inspect a chart if the values.yaml file is not present or is empty. --- cmd/helm/inspect.go | 9 +++---- cmd/helm/inspect_test.go | 13 ++++++++++ .../testdata/testcharts/novals/Chart.yaml | 6 +++++ cmd/helm/testdata/testcharts/novals/README.md | 13 ++++++++++ .../novals/templates/alpine-pod.yaml | 26 +++++++++++++++++++ 5 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 cmd/helm/testdata/testcharts/novals/Chart.yaml create mode 100644 cmd/helm/testdata/testcharts/novals/README.md create mode 100644 cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml diff --git a/cmd/helm/inspect.go b/cmd/helm/inspect.go index 2e43f0e7a..8d9948b6f 100644 --- a/cmd/helm/inspect.go +++ b/cmd/helm/inspect.go @@ -130,11 +130,10 @@ func (i *inspectCmd) run() error { fmt.Fprintln(i.out, string(cf)) } - if i.output == both { - fmt.Fprintln(i.out, "---") - } - - if i.output == valuesOnly || i.output == both { + if (i.output == valuesOnly || i.output == both) && chrt.Values != nil { + if i.output == both { + fmt.Fprintln(i.out, "---") + } fmt.Fprintln(i.out, chrt.Values.Raw) } diff --git a/cmd/helm/inspect_test.go b/cmd/helm/inspect_test.go index 24c4a895e..bbd9c6199 100644 --- a/cmd/helm/inspect_test.go +++ b/cmd/helm/inspect_test.go @@ -61,4 +61,17 @@ func TestInspect(t *testing.T) { t.Errorf("Expected\n%q\nGot\n%q\n", expect[i], got) } } + + // Regression tests for missing values. See issue #1024. + b.Reset() + insp = &inspectCmd{ + chartpath: "testdata/testcharts/novals", + output: "values", + out: b, + } + insp.run() + if b.Len() != 0 { + t.Errorf("expected empty values buffer, got %q", b.String()) + } + } diff --git a/cmd/helm/testdata/testcharts/novals/Chart.yaml b/cmd/helm/testdata/testcharts/novals/Chart.yaml new file mode 100644 index 000000000..ce1a81da6 --- /dev/null +++ b/cmd/helm/testdata/testcharts/novals/Chart.yaml @@ -0,0 +1,6 @@ +description: Deploy a basic Alpine Linux pod +home: https://k8s.io/helm +name: novals +sources: +- https://github.com/kubernetes/helm +version: 0.2.0 diff --git a/cmd/helm/testdata/testcharts/novals/README.md b/cmd/helm/testdata/testcharts/novals/README.md new file mode 100644 index 000000000..3c32de5db --- /dev/null +++ b/cmd/helm/testdata/testcharts/novals/README.md @@ -0,0 +1,13 @@ +#Alpine: A simple Helm chart + +Run a single pod of Alpine Linux. + +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.yaml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml new file mode 100644 index 000000000..c15ab8efc --- /dev/null +++ b/cmd/helm/testdata/testcharts/novals/templates/alpine-pod.yaml @@ -0,0 +1,26 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{.Release.Name}}-{{.Values.Name}}" + labels: + # The "heritage" label is used to track which tool deployed a given chart. + # It is useful for admins who want to see what releases a particular tool + # is responsible for. + heritage: {{.Release.Service | quote }} + # The "release" convention makes it easy to tie a release to all of the + # Kubernetes resources that were created as part of that release. + release: {{.Release.Name | quote }} + # This makes it easy to audit chart usage. + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + annotations: + "helm.sh/created": {{.Release.Time.Seconds | quote }} +spec: + # This shows how to use a simple value. This will look for a passed-in value + # called restartPolicy. If it is not found, it will use the default value. + # {{default "Never" .restartPolicy}} is a slightly optimized version of the + # more conventional syntax: {{.restartPolicy | default "Never"}} + restartPolicy: {{default "Never" .Values.restartPolicy}} + containers: + - name: waiter + image: "alpine:3.3" + command: ["/bin/sleep","9000"] From f600b30c7ac71d7d5bae47fb9322b4c2ab0eb18b Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Fri, 5 Aug 2016 09:56:02 -0600 Subject: [PATCH 13/22] feat(kube): add update logic to kube client This commit adds an Update function to the client. The Update function takes in the modified manifests and the original manifests. It then iterates through the modified objects, creates objects not found in kubernetes, and updates objects that exists but have been modified. Finally, it iterates through the original resources and checks to see if they have been deleted in the modified configuration and then proceeds to delete them. #690 --- cmd/tiller/environment/environment.go | 15 ++ cmd/tiller/environment/environment_test.go | 3 + pkg/kube/client.go | 157 +++++++++++++++++++++ pkg/kube/client_test.go | 90 ++++++++++++ 4 files changed, 265 insertions(+) diff --git a/cmd/tiller/environment/environment.go b/cmd/tiller/environment/environment.go index 9a966ca8a..2a9d85370 100644 --- a/cmd/tiller/environment/environment.go +++ b/cmd/tiller/environment/environment.go @@ -161,6 +161,15 @@ type KubeClient interface { // For all other kinds, it means the kind was created or modified without // error. WatchUntilReady(namespace string, reader io.Reader) error + + // Update updates one or more resources or creates the resource + // if it doesn't exist + // + // namespace must contain a valid existing namespace + // + // reader must contain a YAML stream (one or more YAML documents separated + // by "\n---\n"). + Update(namespace string, originalReader, modifiedReader io.Reader) error } // PrintingKubeClient implements KubeClient, but simply prints the reader to @@ -189,6 +198,12 @@ func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader) error { return err } +// Update implements KubeClient Update. +func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader) error { + _, err := io.Copy(p.Out, modifiedReader) + return err +} + // Environment provides the context for executing a client request. // // All services in a context are concurrency safe. diff --git a/cmd/tiller/environment/environment_test.go b/cmd/tiller/environment/environment_test.go index cfcbc8ca4..ff82e2cc1 100644 --- a/cmd/tiller/environment/environment_test.go +++ b/cmd/tiller/environment/environment_test.go @@ -83,6 +83,9 @@ func (k *mockKubeClient) Create(ns string, r io.Reader) error { func (k *mockKubeClient) Delete(ns string, r io.Reader) error { return nil } +func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader) error { + return nil +} func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader) error { return nil } diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 4e0c51908..5fde74344 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -20,15 +20,21 @@ import ( "fmt" "io" "log" + "reflect" "time" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/errors" + "k8s.io/kubernetes/pkg/api/unversioned" + "k8s.io/kubernetes/pkg/apimachinery/registered" "k8s.io/kubernetes/pkg/apis/batch" "k8s.io/kubernetes/pkg/client/unversioned/clientcmd" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" + "k8s.io/kubernetes/pkg/util/strategicpatch" + "k8s.io/kubernetes/pkg/util/yaml" "k8s.io/kubernetes/pkg/watch" ) @@ -57,6 +63,77 @@ func (c *Client) Create(namespace string, reader io.Reader) error { return perform(c, namespace, reader, createResource) } +// Update reads in the current configuration and a modified configuration from io.reader +// and creates resources that don't already exists, updates resources that have been modified +// and deletes resources from the current configuration that are not present in the +// modified configuration +// +// Namespace will set the namespaces +func (c *Client) Update(namespace string, currentReader, modifiedReader io.Reader) error { + current := c.NewBuilder(includeThirdPartyAPIs). + ContinueOnError(). + NamespaceParam(namespace). + DefaultNamespace(). + Stream(currentReader, ""). + Flatten(). + Do() + + modified := c.NewBuilder(includeThirdPartyAPIs). + ContinueOnError(). + NamespaceParam(namespace). + DefaultNamespace(). + Stream(modifiedReader, ""). + Flatten(). + Do() + + currentInfos, err := current.Infos() + if err != nil { + return err + } + + modifiedInfos := []*resource.Info{} + + modified.Visit(func(info *resource.Info, err error) error { + modifiedInfos = append(modifiedInfos, info) + if err != nil { + return err + } + resourceName := info.Name + + helper := resource.NewHelper(info.Client, info.Mapping) + if _, err := helper.Get(info.Namespace, resourceName, info.Export); err != nil { + if !errors.IsNotFound(err) { + return fmt.Errorf("Could not get information about the resource: err: %s", err) + } + + // Since the resource does not exist, create it. + if err := createResource(info); err != nil { + return err + } + + kind := info.Mapping.GroupVersionKind.Kind + log.Printf("Created a new %s called %s\n", kind, resourceName) + return nil + } + + currentObj, err := getCurrentObject(resourceName, currentInfos) + if err != nil { + return err + } + + if err := updateResource(info, currentObj); err != nil { + log.Printf("error updating the resource %s:\n\t %v", resourceName, err) + return err + } + + return err + }) + + deleteUnwantedResources(currentInfos, modifiedInfos) + + return nil +} + // Delete deletes kubernetes resources from an io.reader // // Namespace will set the namespace @@ -136,6 +213,52 @@ func createResource(info *resource.Info) error { return err } +func deleteResource(info *resource.Info) error { + return resource.NewHelper(info.Client, info.Mapping).Delete(info.Namespace, info.Name) +} + +func updateResource(modified *resource.Info, currentObj runtime.Object) error { + + encoder := api.Codecs.LegacyCodec(registered.EnabledVersions()...) + originalSerialization, err := runtime.Encode(encoder, currentObj) + if err != nil { + return err + } + + editedSerialization, err := runtime.Encode(encoder, modified.Object) + if err != nil { + return err + } + + originalJS, err := yaml.ToJSON(originalSerialization) + if err != nil { + return err + } + + editedJS, err := yaml.ToJSON(editedSerialization) + if err != nil { + return err + } + + if reflect.DeepEqual(originalJS, editedJS) { + return fmt.Errorf("Looks like there are no changes for %s", modified.Name) + } + + patch, err := strategicpatch.CreateStrategicMergePatch(originalJS, editedJS, currentObj) + if err != nil { + return err + } + + // send patch to server + helper := resource.NewHelper(modified.Client, modified.Mapping) + _, err = helper.Patch(modified.Namespace, modified.Name, api.StrategicMergePatchType, patch) + if err != nil { + return err + } + + return nil +} + func watchUntilReady(info *resource.Info) error { w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) if err != nil { @@ -213,3 +336,37 @@ func (c *Client) ensureNamespace(namespace string) error { } return nil } + +func deleteUnwantedResources(currentInfos, modifiedInfos []*resource.Info) { + for _, cInfo := range currentInfos { + found := false + for _, m := range modifiedInfos { + if m.Name == cInfo.Name { + found = true + } + } + if !found { + log.Printf("Deleting %s...", cInfo.Name) + if err := deleteResource(cInfo); err != nil { + log.Printf("Failed to delete %s, err: %s", cInfo.Name, err) + } + } + } +} + +func getCurrentObject(targetName string, infos []*resource.Info) (runtime.Object, error) { + var curr *resource.Info + for _, currInfo := range infos { + if currInfo.Name == targetName { + curr = currInfo + } + } + + if curr == nil { + return nil, fmt.Errorf("No resource with the name %s found.", targetName) + } + + encoder := api.Codecs.LegacyCodec(registered.EnabledVersions()...) + defaultVersion := unversioned.GroupVersion{} + return resource.AsVersionedObject([]*resource.Info{curr}, false, defaultVersion, encoder) +} diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 5ef8b913e..c59cc37b0 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -17,15 +17,55 @@ limitations under the License. package kube import ( + "bytes" + "encoding/json" "io" + "io/ioutil" + "net/http" "strings" "testing" "k8s.io/kubernetes/pkg/api/meta" + "k8s.io/kubernetes/pkg/api/testapi" + "k8s.io/kubernetes/pkg/api/unversioned" + api "k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/client/unversioned/fake" "k8s.io/kubernetes/pkg/kubectl/resource" + "k8s.io/kubernetes/pkg/runtime" ) +func TestUpdateResource(t *testing.T) { + + tests := []struct { + name string + namespace string + modified *resource.Info + currentObj runtime.Object + err bool + errMessage string + }{ + { + name: "no changes when updating resources", + modified: createFakeInfo("nginx", nil), + currentObj: createFakePod("nginx", nil), + err: true, + errMessage: "Looks like there are no changes for nginx", + }, + //{ + //name: "valid update input", + //modified: createFakeInfo("nginx", map[string]string{"app": "nginx"}), + //currentObj: createFakePod("nginx", nil), + //}, + } + + for _, tt := range tests { + err := updateResource(tt.modified, tt.currentObj) + if err != nil && err.Error() != tt.errMessage { + t.Errorf("%q. expected error message: %v, got %v", tt.name, tt.errMessage, err) + } + } +} + func TestPerform(t *testing.T) { tests := []struct { name string @@ -214,3 +254,53 @@ spec: ports: - containerPort: 80 ` + +func createFakePod(name string, labels map[string]string) runtime.Object { + objectMeta := createObjectMeta(name, labels) + + object := &api.Pod{ + ObjectMeta: objectMeta, + } + + return object +} + +func createFakeInfo(name string, labels map[string]string) *resource.Info { + pod := createFakePod(name, labels) + marshaledObj, _ := json.Marshal(pod) + + mapping := &meta.RESTMapping{ + Resource: name, + Scope: meta.RESTScopeNamespace, + GroupVersionKind: unversioned.GroupVersionKind{ + Kind: "Pod", + Version: "v1", + }} + + client := &fake.RESTClient{ + Codec: testapi.Default.Codec(), + Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) { + header := http.Header{} + header.Set("Content-Type", runtime.ContentTypeJSON) + return &http.Response{ + StatusCode: 200, + Header: header, + Body: ioutil.NopCloser(bytes.NewReader(marshaledObj)), + }, nil + })} + info := resource.NewInfo(client, mapping, "default", "nginx", false) + + info.Object = pod + + return info +} + +func createObjectMeta(name string, labels map[string]string) api.ObjectMeta { + objectMeta := api.ObjectMeta{Name: name, Namespace: "default"} + + if labels != nil { + objectMeta.Labels = labels + } + + return objectMeta +} From 2e95230b304eed9016c7473af684c13f565a96da Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Fri, 5 Aug 2016 15:50:02 -0600 Subject: [PATCH 14/22] fix(helm): fix helmignore evaluation of dirs This adds a few extra settings to the default .helmignore file. In doing this, I found a bug that some directory patterns are not evaluated correctly. Fixed that and added tests. Closes #989 Closes #1027 --- pkg/chartutil/create.go | 18 +++++++++++++++++- pkg/chartutil/load.go | 5 +++++ pkg/chartutil/load_test.go | 5 +++-- pkg/chartutil/testdata/frobnitz-1.2.3.tgz | Bin 3333 -> 3775 bytes pkg/chartutil/testdata/frobnitz/.helmignore | 1 + .../frobnitz/charts/mariner-4.3.2.tgz | Bin 896 -> 936 bytes pkg/chartutil/testdata/frobnitz/ignore/me.txt | 0 pkg/chartutil/testdata/genfrob.sh | 2 +- .../mariner/charts/albatross-0.1.0.tgz | Bin 322 -> 347 bytes pkg/ignore/rules.go | 5 +++-- 10 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 pkg/chartutil/testdata/frobnitz/.helmignore create mode 100644 pkg/chartutil/testdata/frobnitz/ignore/me.txt diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index f2b4c300e..8d0990e06 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -48,7 +48,23 @@ const defaultIgnore = `# Patterns to ignore when building packages. # This supports shell glob matching, relative path matching, and # negation (prefixed with !). Only one pattern per line. .DS_Store -.git +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj ` // Create creates a new chart in a directory. diff --git a/pkg/chartutil/load.go b/pkg/chartutil/load.go index 296b11231..911d883d2 100644 --- a/pkg/chartutil/load.go +++ b/pkg/chartutil/load.go @@ -218,6 +218,11 @@ func LoadDir(dir string) (*chart.Chart, error) { return err } if fi.IsDir() { + // Directory-based ignore rules should involve skipping the entire + // contents of that directory. + if rules.Ignore(n, fi) { + return filepath.SkipDir + } return nil } diff --git a/pkg/chartutil/load_test.go b/pkg/chartutil/load_test.go index fa94acbc3..822e8d078 100644 --- a/pkg/chartutil/load_test.go +++ b/pkg/chartutil/load_test.go @@ -49,8 +49,9 @@ func verifyChart(t *testing.T, c *chart.Chart) { t.Errorf("Expected 1 template, got %d", len(c.Templates)) } - if len(c.Files) != 5 { - t.Errorf("Expected 5 extra files, got %d", len(c.Files)) + numfiles := 6 + if len(c.Files) != numfiles { + t.Errorf("Expected %d extra files, got %d", numfiles, len(c.Files)) for _, n := range c.Files { t.Logf("\t%s", n.TypeUrl) } diff --git a/pkg/chartutil/testdata/frobnitz-1.2.3.tgz b/pkg/chartutil/testdata/frobnitz-1.2.3.tgz index 31c855223046ed3bba9abb2a7ca707195ad6daa4..50d1ef01484094a445c867e612e363f4f527d604 100644 GIT binary patch literal 3775 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 P_}Ta$fGYPr0C)fZ@}|9H diff --git a/pkg/chartutil/testdata/frobnitz/.helmignore b/pkg/chartutil/testdata/frobnitz/.helmignore new file mode 100644 index 000000000..9973a57b8 --- /dev/null +++ b/pkg/chartutil/testdata/frobnitz/.helmignore @@ -0,0 +1 @@ +ignore/ diff --git a/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/frobnitz/charts/mariner-4.3.2.tgz index 90c7979afe88fb36ef00f6fe09041217b874323f..27bd51f654f64fa50e431c35bd69c9e7d509a5c1 100644 GIT binary patch literal 936 zcmV;Z16TYXiwFR;38hy61MQZ3XcI{s$Abq|bQ#8S10ibbrZ?k1_k zXBC@>IG+S&LS|=X=llEq=BGeS@*&M;i6b;k+XVrHTQSnoEFWp30EV@*0?!E?ZwEBP zvkY$mLOeIsMnK0~>z@4gE!i=PZEk_^o`%Pp-cYD#!t%QDDtSx=WM%SW$)+YnztOmmSC#XP0Koo^$Oi5CpXQi%^FPZNAL9Q+5bN+* z<%!jP4&JN(q3ncMQ*}LqrWlH*uxIYKuhE|W1&%ZQFEA`^)cvOajqxJ!eo;buAkj#pY4J zRW3cd=Rx(Xie(4tnqJ??UpgV@*otkA|NkzXJn!(F?cLI+9N3(9sASmk%(Uv&=Qhn; z*Hua{TlVjRyE|&{IQLyp_Ad0SdFEKsypqlim0n#uyjUy?Hrvm&P8z#sZPw|cs$$Bz zt)$n`;=#DSYWv7lIiaFZ=K0IzJ%V7`mBPNYExVov%SYDy{;*%|quEK*TJ~4&u3xgY ztoCG2ap#dg`3Akd;MPiRHOkrxUX(w1l`^?26LkCz=E*6)+yx5Z4xMotT3Xf*>ew^&cJiepsS;Z`L z_@72+Q}(f6Z(gaHp$yxS8GD@j)#gsN&QsGZL%x@w1OkCTAP@)y0)apv5C{Z93~vC_ KC$O{tC;$M@pyPM| literal 896 zcmV-`1AqJyFIqY%^yyyBQQ+^g%6q>LQwJ^q?dW1r=f!(cXlzOnT`iX_i51-kZHxmX%8G zN_c*=@XfGi&N*}b-%Vs?-oeQRO#s1h+(gsBYZ;%#Ny=ya0D?4;G(|AD(FkyYB5|Vz z(1G047%l~}vbq(Kh0yGR7CR^ZcAPrQX8X%L{_ps1{}V*N{_E_l46!^`6lQ)6^*k7g z`m_E7Ve;xv8BHdNrd0hIf>QOzXMQUandAC9SdmKrelO5kImIILPRL7+1ds@lIY9&# zk7odzB!d)}nUftH

cVCoA4T6lTOotzGiW5)-4H?ax6>ld<^ss;mU23`{lu|A>E@ zM)^NDjOhwzk!wN1=2}}bpS^Xzz=9m7DF`5We|ARp+ zSkKv5mjHp!1H6Ua?-ZCe7VNO|3Q&J6$V}Xls#pK@9292-HeTQ`V29ADBp3`?JhZ#a zm_-r|P!a{bz&f(LcUsQ^7J-#H;Au5{eg>Q@FDn2_!0fqK6m>q{p+4qGE*X4JMV|G2 z{Yi}XHPl|rJQMT3YAl>x60E8NK<7+kgUR9_HxfVRf09ry6#v0se1wlz9);T6;FtcN z70fJ@B}LKW7=hs!%sTk*YfKjZG*0^KKS`L>T!53f+5n*V4+c?%i!`mVyD~H(T`g_d z4?tvS_r7*rWq9$m9?iY@JIg=lQf#F>Q*5bP7yuR{ue|&jvL{j_ZM*ZlT z(S|2EX$^ae@9r+U^I8|#W=mW0rfC=5GZWvUM=y3U z{hbTyi&ES3lk-Z*?w5mQ#0Gc8aDP#prf+mb)zI~{uR`@o*M z%7&edw#^PUw8u^~=X1(x-;Xn!Wk0%GbA5)&B41Dtb&pGEL74_#ol>prTw=m`( zZL@bK9qp@cfp6q5bA|kGVa)$vTxZ)U9snQJf0Fvu`%medQ2%dX$UhbD7&<%4vW@eV tab?Ds>0<3e$rj$(uw88|syhGx0000000000006*m_5v>lStDd~pD z<)$?0d@wFfr8XgCUC2x8ZuX1aisF-TuIm`vJ7LEBA4}sx`fBQG?svrRp@jH2f5zF6 zzu0Llq=@+|p?4cX=bp$d|2+T3)OJtCU68z3pS$BJ9NT734&=#JHOc#(L5kLq=Pt9Z zvBCP}Y@HuE(Uhe$DcgRW?Kb)A1;LL@=YL3fkuvbz`=2pJrK0`$k0k*2{~D(JgRM`M ziAQ_(E%1x{C0EFQ4O9MSQ+3ua@dNNx{imG2`~Oq%0rh_kWB##t2iI8?Z~y=R00000 U000000D$}T2EDQLp8zNT06#;d{Qv*} diff --git a/pkg/ignore/rules.go b/pkg/ignore/rules.go index 0251268ba..f5b08a4ee 100644 --- a/pkg/ignore/rules.go +++ b/pkg/ignore/rules.go @@ -65,7 +65,6 @@ func Parse(file io.Reader) (*Rules, error) { if err := s.Err(); err != nil { return r, err } - return r, nil } @@ -97,8 +96,10 @@ func (r *Rules) Ignore(path string, fi os.FileInfo) bool { continue } + // If the rule is looking for directories, and this is not a directory, + // skip it. if p.mustDir && !fi.IsDir() { - return false + continue } if p.match(path, fi) { return true From 1b31382d9cf97f4a5e11821519cbc73de1bffeee Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Fri, 5 Aug 2016 11:15:37 -0600 Subject: [PATCH 15/22] chore(glide): add kube pkgs to glide.yaml * Thanks for the reminder adam :) --- glide.yaml | 4 ++++ pkg/kube/client.go | 3 +-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/glide.yaml b/glide.yaml index 2b32855e8..03917acf4 100644 --- a/glide.yaml +++ b/glide.yaml @@ -27,6 +27,8 @@ import: - pkg/api - pkg/api/meta - pkg/api/error + - pkg/api/unversioned + - pkg/apimachinery/registered - pkg/client/restclient - pkg/client/unversioned - pkg/apis/batch @@ -40,6 +42,8 @@ import: - pkg/labels - pkg/runtime - pkg/watch + - pkg/util/strategicpatch + - pkg/util/yaml - package: github.com/gosuri/uitable - package: speter.net/go/exp/math/dec/inf version: ^0.9.0 diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 5fde74344..da073d173 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -251,8 +251,7 @@ func updateResource(modified *resource.Info, currentObj runtime.Object) error { // send patch to server helper := resource.NewHelper(modified.Client, modified.Mapping) - _, err = helper.Patch(modified.Namespace, modified.Name, api.StrategicMergePatchType, patch) - if err != nil { + if _, err = helper.Patch(modified.Namespace, modified.Name, api.StrategicMergePatchType, patch); err != nil { return err } From 1e7c814e8a5a1ee2afaa5de6fa6e3488d30289d5 Mon Sep 17 00:00:00 2001 From: joe2far Date: Mon, 8 Aug 2016 17:49:50 +0100 Subject: [PATCH 16/22] Serve charts from specified local repository path --- cmd/helm/serve.go | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/cmd/helm/serve.go b/cmd/helm/serve.go index ae60833ab..3fca0bd8a 100644 --- a/cmd/helm/serve.go +++ b/cmd/helm/serve.go @@ -17,17 +17,19 @@ limitations under the License. package main import ( + "os" + "path/filepath" + "github.com/spf13/cobra" "k8s.io/helm/pkg/repo" ) -var serveDesc = `This command starts a local chart repository server that serves the charts saved in your $HELM_HOME/local/ directory.` - -//TODO: add repoPath flag to be passed in in case you want -// to serve charts from a different local dir +var serveDesc = `This command starts a local chart repository server that serves charts from a local directory.` +var repoPath string func init() { + serveCmd.Flags().StringVar(&repoPath, "repo-path", localRepoDirectory(), "The local directory path from which to serve charts.") RootCommand.AddCommand(serveCmd) } @@ -35,9 +37,19 @@ var serveCmd = &cobra.Command{ Use: "serve", Short: "start a local http web server", Long: serveDesc, - Run: serve, + RunE: serve, } -func serve(cmd *cobra.Command, args []string) { - repo.StartLocalRepo(localRepoDirectory()) +func serve(cmd *cobra.Command, args []string) error { + + repoPath, err := filepath.Abs(repoPath) + if err != nil { + return err + } + if _, err := os.Stat(repoPath); os.IsNotExist(err) { + return err + } + + repo.StartLocalRepo(repoPath) + return nil } From 9465ce979b1c03f8e2470688328e2e2b4314418c Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Sun, 7 Aug 2016 21:29:05 -0600 Subject: [PATCH 17/22] chore(helm): add options to update request --- pkg/helm/option.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 98a6efc19..efd4c390e 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -238,8 +238,15 @@ func (o *options) rpcDeleteRelease(rlsName string, rlc rls.ReleaseServiceClient, // Executes tiller.UpdateRelease RPC. func (o *options) rpcUpdateRelease(rlsName string, chr *cpb.Chart, rlc rls.ReleaseServiceClient, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { //TODO: handle dryRun + for _, opt := range opts { + opt(o) + } + + o.updateReq.Chart = chr + o.updateReq.DryRun = o.dryRun + o.updateReq.Name = rlsName - return rlc.UpdateRelease(context.TODO(), &rls.UpdateReleaseRequest{Name: rlsName, Chart: chr}) + return rlc.UpdateRelease(context.TODO(), &o.updateReq) } // Executes tiller.GetReleaseStatus RPC. From 8be3a34ac62a45600913bbc90a02b7ab87051021 Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Mon, 8 Aug 2016 10:27:46 -0600 Subject: [PATCH 18/22] feat(tiller): add update logic to release server --- cmd/tiller/release_server.go | 152 ++++++++++++++++++++---------- cmd/tiller/release_server_test.go | 58 +++++++++++- 2 files changed, 157 insertions(+), 53 deletions(-) diff --git a/cmd/tiller/release_server.go b/cmd/tiller/release_server.go index 061a76c4b..afe13376e 100644 --- a/cmd/tiller/release_server.go +++ b/cmd/tiller/release_server.go @@ -24,7 +24,6 @@ import ( "regexp" "sort" - "github.com/Masterminds/semver" "github.com/ghodss/yaml" "github.com/technosophos/moniker" ctx "golang.org/x/net/context" @@ -171,65 +170,107 @@ func (s *releaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleas } func (s *releaseServer) UpdateRelease(c ctx.Context, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { - rel, err := s.prepareUpdate(req) + currentRelease, updatedRelease, err := s.prepareUpdate(req) if err != nil { return nil, err } - // TODO: perform update + res, err := s.performUpdate(currentRelease, updatedRelease, req) + if err != nil { + return nil, err + } + + if err := s.env.Releases.Update(updatedRelease); err != nil { + return nil, err + } + + return res, nil +} + +func (s *releaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { + res := &services.UpdateReleaseResponse{Release: updatedRelease} + + if req.DryRun { + log.Printf("Dry run for %s", updatedRelease.Name) + return res, nil + } + + // pre-ugrade hooks + //if !req.DisableHooks { + //if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, preUpgrade); err != nil { + //return res, err + //} + //} + + kubeCli := s.env.KubeClient + original := bytes.NewBufferString(originalRelease.Manifest) + modified := bytes.NewBufferString(updatedRelease.Manifest) + if err := kubeCli.Update(updatedRelease.Namespace, original, modified); err != nil { + return nil, fmt.Errorf("Update of %s failed: %s", updatedRelease.Name, err) + } + + // post-upgrade hooks + //if !req.DisableHooks { + //if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, postUpgrade); err != nil { + //return res, err + //} + //} - return &services.UpdateReleaseResponse{Release: rel}, nil + updatedRelease.Info.Status.Code = release.Status_DEPLOYED + + return res, nil } -// prepareUpdate builds a release for an update operation. -func (s *releaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, error) { +// prepareUpdate builds an updated release for an update operation. +func (s *releaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) { if req.Name == "" { - return nil, errMissingRelease + return nil, nil, errMissingRelease } if req.Chart == nil { - return nil, errMissingChart + return nil, nil, errMissingChart } // finds the non-deleted release with the given name - rel, err := s.env.Releases.Read(req.Name) + currentRelease, err := s.env.Releases.Read(req.Name) if err != nil { - return nil, err + return nil, nil, err } - //validate chart name is same as previous release - givenChart := req.Chart.Metadata.Name - releasedChart := rel.Chart.Metadata.Name - if givenChart != releasedChart { - return nil, fmt.Errorf("Given chart, %s, does not match chart originally released, %s", givenChart, releasedChart) + ts := timeconv.Now() + options := chartutil.ReleaseOptions{ + Name: req.Name, + Time: ts, + Namespace: currentRelease.Namespace, } - // validate new chart version is higher than old - - givenChartVersion := req.Chart.Metadata.Version - releasedChartVersion := rel.Chart.Metadata.Version - c, err := semver.NewConstraint("> " + releasedChartVersion) + valuesToRender, err := chartutil.ToRenderValues(req.Chart, req.Values, options) if err != nil { - return nil, err + return nil, nil, err } - v, err := semver.NewVersion(givenChartVersion) + hooks, manifestDoc, err := s.renderResources(req.Chart, valuesToRender) if err != nil { - return nil, err - } - - if a := c.Check(v); !a { - return nil, fmt.Errorf("Given chart (%s-%v) must be a higher version than released chart (%s-%v)", givenChart, givenChartVersion, releasedChart, releasedChartVersion) + return nil, nil, err } // Store an updated release. updatedRelease := &release.Release{ - Name: req.Name, - Chart: req.Chart, - Config: req.Values, - Version: rel.Version + 1, + Name: req.Name, + Namespace: currentRelease.Namespace, + Chart: req.Chart, + Config: req.Values, + Info: &release.Info{ + FirstDeployed: currentRelease.Info.FirstDeployed, + LastDeployed: ts, + Status: &release.Status{Code: release.Status_UNKNOWN}, + }, + Version: currentRelease.Version + 1, + Manifest: manifestDoc.String(), + Hooks: hooks, } - return updatedRelease, nil + + return currentRelease, updatedRelease, nil } func (s *releaseServer) uniqName(start string, reuse bool) (string, error) { @@ -308,12 +349,36 @@ func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re return nil, err } - renderer := s.engine(req.Chart) - files, err := renderer.Render(req.Chart, valuesToRender) + hooks, manifestDoc, err := s.renderResources(req.Chart, valuesToRender) if err != nil { return nil, err } + // Store a release. + rel := &release.Release{ + Name: name, + Namespace: req.Namespace, + Chart: req.Chart, + Config: req.Values, + Info: &release.Info{ + FirstDeployed: ts, + LastDeployed: ts, + Status: &release.Status{Code: release.Status_UNKNOWN}, + }, + Manifest: manifestDoc.String(), + Hooks: hooks, + Version: 1, + } + return rel, nil +} + +func (s *releaseServer) renderResources(ch *chart.Chart, values chartutil.Values) ([]*release.Hook, *bytes.Buffer, error) { + renderer := s.engine(ch) + files, err := renderer.Render(ch, values) + if err != nil { + return nil, nil, err + } + // Sort hooks, manifests, and partials. Only hooks and manifests are returned, // as partials are not used after renderer.Render. Empty manifests are also // removed here. @@ -321,7 +386,7 @@ func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re if err != nil { // By catching parse errors here, we can prevent bogus releases from going // to Kubernetes. - return nil, err + return nil, nil, err } // Aggregate all valid manifests into one big doc. @@ -331,22 +396,7 @@ func (s *releaseServer) prepareRelease(req *services.InstallReleaseRequest) (*re b.WriteString(file) } - // Store a release. - rel := &release.Release{ - Name: name, - Namespace: req.Namespace, - Chart: req.Chart, - Config: req.Values, - Info: &release.Info{ - FirstDeployed: ts, - LastDeployed: ts, - Status: &release.Status{Code: release.Status_UNKNOWN}, - }, - Manifest: b.String(), - Hooks: hooks, - Version: 1, - } - return rel, nil + return hooks, b, nil } // validateYAML checks to see if YAML is well-formed. diff --git a/cmd/tiller/release_server_test.go b/cmd/tiller/release_server_test.go index 18c36bd78..a6e6c2fa8 100644 --- a/cmd/tiller/release_server_test.go +++ b/cmd/tiller/release_server_test.go @@ -79,8 +79,9 @@ func releaseStub() *release.Release { LastDeployed: &date, Status: &release.Status{Code: release.Status_DEPLOYED}, }, - Chart: chartStub(), - Config: &chart.Config{Raw: `name = "value"`}, + Chart: chartStub(), + Config: &chart.Config{Raw: `name = "value"`}, + Version: 1, Hooks: []*release.Hook{ { Name: "test-cm", @@ -289,6 +290,59 @@ func TestInstallReleaseReuseName(t *testing.T) { } } +func TestUpdateRelease(t *testing.T) { + c := context.Background() + 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: "hello", Data: []byte("hello: world")}, + }, + }, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Errorf("Failed updated: %s", err) + } + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + + if res.Release.Name != rel.Name { + t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) + } + + if res.Release.Namespace != rel.Namespace { + t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) + } + + updated, err := rs.env.Releases.Read(res.Release.Name) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if len(updated.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(updated.Manifest, "---\n# Source: hello/hello\nhello: world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if res.Release.Version != 2 { + t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) + } +} + func TestUninstallRelease(t *testing.T) { c := context.Background() rs := rsFixture() From 70b9f11a03252bc698d560e293156bc71c484ab9 Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Mon, 8 Aug 2016 11:46:29 -0600 Subject: [PATCH 19/22] feat(*): add disable hooks flag to `helm upgrade` --- _proto/hapi/services/tiller.proto | 3 + cmd/helm/upgrade.go | 16 +++-- pkg/helm/option.go | 7 ++ pkg/proto/hapi/services/tiller.pb.go | 102 ++++++++++++++------------- 4 files changed, 71 insertions(+), 57 deletions(-) diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index 95f926430..ba3252e3c 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -162,6 +162,9 @@ message UpdateReleaseRequest { hapi.chart.Config values = 3; // dry_run, if true, will run through the release logic, but neither create bool dry_run = 4; + + // DisableHooks causes the server to skip running any hooks for the upgrade. + bool disable_hooks = 5; } // UpdateReleaseResponse is the response to an update request. diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index c4f75310f..7328281aa 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -34,12 +34,13 @@ argument can be a relative path to a packaged or unpackaged chart. ` type upgradeCmd struct { - release string - chart string - out io.Writer - client helm.Interface - dryRun bool - valuesFile string + release string + chart string + out io.Writer + client helm.Interface + dryRun bool + disableHooks bool + valuesFile string } func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { @@ -70,6 +71,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f := cmd.Flags() f.StringVarP(&upgrade.valuesFile, "values", "f", "", "path to a values YAML file") f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade") + f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks") return cmd } @@ -88,7 +90,7 @@ func (u *upgradeCmd) run() error { } } - _, err = u.client.UpdateRelease(u.release, chartPath, helm.UpdateValueOverrides(rawVals), helm.UpgradeDryRun(u.dryRun)) + _, err = u.client.UpdateRelease(u.release, chartPath, helm.UpdateValueOverrides(rawVals), helm.UpgradeDryRun(u.dryRun), helm.UpgradeDisableHooks(u.disableHooks)) if err != nil { return prettyError(err) } diff --git a/pkg/helm/option.go b/pkg/helm/option.go index efd4c390e..80db158a5 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -143,6 +143,13 @@ func DeleteDryRun(dry bool) DeleteOption { } } +// UpgradeDisableHooks will disable hooks for an upgrade operation. +func UpgradeDisableHooks(disable bool) UpdateOption { + return func(opts *options) { + opts.disableHooks = disable + } +} + // UpgradeDryRun will (if true) execute an upgrade as a dry run. func UpgradeDryRun(dry 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 d1cc19ae4..838e83647 100644 --- a/pkg/proto/hapi/services/tiller.pb.go +++ b/pkg/proto/hapi/services/tiller.pb.go @@ -226,6 +226,8 @@ type UpdateReleaseRequest struct { Values *hapi_chart.Config `protobuf:"bytes,3,opt,name=values" json:"values,omitempty"` // dry_run, if true, will run through the release logic, but neither create DryRun bool `protobuf:"varint,4,opt,name=dry_run,json=dryRun" json:"dry_run,omitempty"` + // DisableHooks causes the server to skip running any hooks for the upgrade. + DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"` } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } @@ -624,54 +626,54 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ } var fileDescriptor0 = []byte{ - // 769 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdb, 0x6e, 0xd3, 0x4a, - 0x14, 0xad, 0x93, 0xd4, 0x49, 0x76, 0x2f, 0x4a, 0xe7, 0xb4, 0x8d, 0x8f, 0x75, 0x0e, 0x42, 0x46, - 0x40, 0x29, 0xd4, 0x81, 0xf0, 0x8e, 0x94, 0xb6, 0x51, 0x5b, 0x35, 0xa4, 0xd2, 0x84, 0x82, 0xc4, - 0x03, 0x91, 0x9b, 0x4c, 0xa8, 0xc1, 0xb5, 0x83, 0x67, 0x52, 0xd1, 0x4f, 0xe0, 0x0f, 0xf8, 0x14, - 0x3e, 0x88, 0xbf, 0xe0, 0x85, 0xb9, 0xd8, 0x26, 0x17, 0x1b, 0x4c, 0x5f, 0x9c, 0x99, 0xd9, 0x6b, - 0xaf, 0x7d, 0xdf, 0x0a, 0x98, 0x97, 0xce, 0xd8, 0x6d, 0x50, 0x12, 0x5e, 0xbb, 0x03, 0x42, 0x1b, - 0xcc, 0xf5, 0x3c, 0x12, 0xda, 0xe3, 0x30, 0x60, 0x01, 0xda, 0x14, 0x32, 0x3b, 0x96, 0xd9, 0x4a, - 0x66, 0x6e, 0x4b, 0x8d, 0xc1, 0xa5, 0x13, 0x32, 0xf5, 0x55, 0x68, 0xb3, 0x3e, 0xfd, 0x1e, 0xf8, - 0x23, 0xf7, 0x7d, 0x24, 0x50, 0x26, 0x42, 0xe2, 0x11, 0x87, 0x92, 0xf8, 0x77, 0x46, 0x29, 0x96, - 0xb9, 0xfe, 0x28, 0x50, 0x02, 0xeb, 0xbb, 0x06, 0xff, 0x74, 0x5c, 0xca, 0xb0, 0x12, 0x51, 0x4c, - 0x3e, 0x4d, 0x08, 0x65, 0x68, 0x13, 0x96, 0x3d, 0xf7, 0xca, 0x65, 0x86, 0x76, 0x57, 0xdb, 0x29, - 0x62, 0x75, 0x41, 0xdb, 0xa0, 0x07, 0xa3, 0x11, 0x25, 0xcc, 0x28, 0xf0, 0xe7, 0x2a, 0x8e, 0x6e, - 0xe8, 0x05, 0x94, 0x69, 0x10, 0xb2, 0xfe, 0xc5, 0x8d, 0x51, 0xe4, 0x82, 0xf5, 0xe6, 0x7d, 0x3b, - 0x2d, 0x26, 0x5b, 0x58, 0xea, 0x71, 0xa0, 0x2d, 0x3e, 0xfb, 0x37, 0x58, 0xa7, 0xf2, 0x57, 0xf0, - 0x8e, 0x5c, 0x8f, 0x91, 0xd0, 0x28, 0x29, 0x5e, 0x75, 0x43, 0x47, 0x00, 0x92, 0x37, 0x08, 0x87, - 0x5c, 0xb6, 0x2c, 0xa9, 0x77, 0x72, 0x50, 0x9f, 0x09, 0x3c, 0xae, 0xd2, 0xf8, 0x68, 0xbd, 0x83, - 0x4a, 0x0c, 0xb0, 0x9a, 0xa0, 0x2b, 0xf3, 0x68, 0x05, 0xca, 0xe7, 0xdd, 0xd3, 0xee, 0xd9, 0x9b, - 0x6e, 0x6d, 0x09, 0x55, 0xa0, 0xd4, 0x6d, 0xbd, 0x6c, 0xd7, 0x34, 0xb4, 0x01, 0x6b, 0x9d, 0x56, - 0xef, 0x55, 0x1f, 0xb7, 0x3b, 0xed, 0x56, 0xaf, 0x7d, 0x58, 0x2b, 0x58, 0x77, 0xa0, 0x9a, 0xf0, - 0xa2, 0x32, 0x14, 0x5b, 0xbd, 0x03, 0xa5, 0x72, 0xd8, 0xe6, 0x27, 0xcd, 0xfa, 0xa2, 0xc1, 0xe6, - 0x6c, 0x1a, 0xe9, 0x38, 0xf0, 0x29, 0x11, 0x79, 0x1c, 0x04, 0x13, 0x3f, 0xc9, 0xa3, 0xbc, 0x20, - 0x04, 0x25, 0x9f, 0x7c, 0x8e, 0xb3, 0x28, 0xcf, 0x02, 0xc9, 0x02, 0xe6, 0x78, 0x32, 0x83, 0x1c, - 0x29, 0x2f, 0xe8, 0x19, 0x54, 0xa2, 0xaa, 0x51, 0x9e, 0x9b, 0xe2, 0xce, 0x4a, 0x73, 0x4b, 0xc5, - 0x1f, 0xd7, 0x37, 0xb2, 0x88, 0x13, 0x98, 0xb5, 0x07, 0xf5, 0x23, 0x12, 0x7b, 0xd2, 0x63, 0x0e, - 0x9b, 0x24, 0x55, 0x15, 0x76, 0x9d, 0x2b, 0x22, 0x9d, 0x11, 0x76, 0xf9, 0xd9, 0x7a, 0x0d, 0xc6, - 0x22, 0x3c, 0xf2, 0x3e, 0x05, 0x8f, 0x1e, 0x40, 0x49, 0xf4, 0x8f, 0xf4, 0x7d, 0xa5, 0x89, 0x66, - 0xbd, 0x39, 0xe1, 0x12, 0x2c, 0xe5, 0x96, 0x3d, 0xcd, 0x7b, 0x10, 0xf8, 0x8c, 0xf8, 0xec, 0x77, - 0x7e, 0x74, 0xe0, 0xdf, 0x14, 0x7c, 0xe4, 0x48, 0x03, 0xca, 0x91, 0x09, 0xa9, 0x93, 0x99, 0x85, - 0x18, 0x65, 0x7d, 0xe5, 0x05, 0x39, 0x1f, 0x0f, 0x1d, 0x46, 0x62, 0x51, 0xb6, 0x69, 0xf4, 0x90, - 0x17, 0x49, 0xcc, 0x53, 0x14, 0xd3, 0x86, 0xe2, 0x56, 0x43, 0x77, 0x20, 0xbe, 0x58, 0xc9, 0xd1, - 0x2e, 0xe8, 0xd7, 0x8e, 0xc7, 0x79, 0x64, 0x91, 0x92, 0xe8, 0x23, 0xa4, 0x1c, 0x46, 0x1c, 0x21, - 0x50, 0x1d, 0xca, 0xc3, 0xf0, 0xa6, 0x1f, 0x4e, 0x7c, 0xd9, 0xd4, 0x15, 0xac, 0xf3, 0x2b, 0x9e, - 0xf8, 0xd6, 0x31, 0x6c, 0xcd, 0x79, 0x76, 0xdb, 0x20, 0x7f, 0x68, 0xb0, 0x75, 0xe2, 0x53, 0xde, - 0x27, 0xde, 0x5c, 0x94, 0x49, 0x44, 0x5a, 0xee, 0x88, 0x0a, 0x7f, 0x13, 0x51, 0x71, 0x3a, 0xa2, - 0x24, 0xa7, 0xa5, 0xa9, 0x9c, 0xde, 0x83, 0xb5, 0xa1, 0x4b, 0x9d, 0x0b, 0x8f, 0xf4, 0x2f, 0x83, - 0xe0, 0x23, 0x95, 0xd3, 0x5b, 0xc1, 0xab, 0xd1, 0xe3, 0xb1, 0x78, 0x43, 0xff, 0x41, 0x55, 0x80, - 0xe9, 0xd8, 0x19, 0x10, 0x43, 0x97, 0xda, 0xbf, 0x1e, 0xd0, 0xff, 0x00, 0x21, 0x99, 0x50, 0xd2, - 0x97, 0xe4, 0x65, 0xa9, 0x5f, 0x95, 0x2f, 0x5d, 0xd1, 0x30, 0x27, 0xb0, 0x3d, 0x1f, 0xfc, 0x6d, - 0x13, 0x89, 0xa1, 0x7e, 0xee, 0xbb, 0xa9, 0x99, 0x4c, 0xeb, 0x97, 0x85, 0xd8, 0x0a, 0x8b, 0xb1, - 0x59, 0xa7, 0x60, 0x2c, 0x72, 0xde, 0xd2, 0xc1, 0xe6, 0xb7, 0x65, 0x58, 0x8f, 0x47, 0x54, 0x2d, - 0x3e, 0xe4, 0xc2, 0xea, 0xf4, 0xc6, 0x41, 0x8f, 0xb2, 0xf7, 0xe2, 0xdc, 0x72, 0x37, 0x77, 0xf3, - 0x40, 0x95, 0xab, 0xd6, 0xd2, 0x53, 0x0d, 0x51, 0xa8, 0xcd, 0xaf, 0x08, 0xb4, 0x97, 0xce, 0x91, - 0xb1, 0x79, 0x4c, 0x3b, 0x2f, 0x3c, 0x36, 0x8b, 0xae, 0x61, 0x63, 0x61, 0x1f, 0xa0, 0x3f, 0xd2, - 0xcc, 0x2e, 0x1a, 0xb3, 0x91, 0x1b, 0x9f, 0xd8, 0xfd, 0x00, 0x6b, 0x33, 0xe3, 0x89, 0x32, 0xb2, - 0x95, 0xb6, 0x5d, 0xcc, 0xc7, 0xb9, 0xb0, 0x89, 0xad, 0x2b, 0x58, 0x9f, 0x6d, 0x61, 0x94, 0x41, - 0x90, 0x3a, 0xe5, 0xe6, 0x93, 0x7c, 0xe0, 0xc4, 0x1c, 0xaf, 0xe3, 0x7c, 0x4b, 0x66, 0xd5, 0x31, - 0x63, 0x1c, 0xb2, 0xea, 0x98, 0xd5, 0xe9, 0xd6, 0xd2, 0x3e, 0xbc, 0xad, 0xc4, 0xe8, 0x0b, 0x5d, - 0xfe, 0xe9, 0x78, 0xfe, 0x33, 0x00, 0x00, 0xff, 0xff, 0xa6, 0x56, 0x6f, 0xa5, 0x0e, 0x09, 0x00, - 0x00, + // 774 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x56, 0xdb, 0x6e, 0xd3, 0x4c, + 0x10, 0xae, 0x93, 0x34, 0x87, 0xe9, 0x41, 0xe9, 0xfe, 0x6d, 0x93, 0xdf, 0xfa, 0x7f, 0x84, 0x8c, + 0x80, 0x52, 0xa8, 0x03, 0xe1, 0x1e, 0x29, 0x6d, 0xa3, 0xb6, 0x6a, 0x48, 0xa5, 0x0d, 0x05, 0x89, + 0x0b, 0x22, 0x37, 0xd9, 0x50, 0x83, 0x6b, 0x07, 0xef, 0xa6, 0xa2, 0x8f, 0xc0, 0x1b, 0x71, 0xc3, + 0xdb, 0xf0, 0x16, 0xdc, 0xb0, 0x07, 0xaf, 0xc9, 0xc1, 0x06, 0xd3, 0x1b, 0x67, 0x77, 0xe7, 0xdb, + 0x6f, 0x66, 0xbe, 0x99, 0x9d, 0x16, 0xcc, 0x4b, 0x67, 0xec, 0x36, 0x28, 0x09, 0xaf, 0xdd, 0x01, + 0xa1, 0x0d, 0xe6, 0x7a, 0x1e, 0x09, 0xed, 0x71, 0x18, 0xb0, 0x00, 0x6d, 0x0a, 0x9b, 0xad, 0x6d, + 0xb6, 0xb2, 0x99, 0xdb, 0xf2, 0xc6, 0xe0, 0xd2, 0x09, 0x99, 0xfa, 0x2a, 0xb4, 0x59, 0x9b, 0x3e, + 0x0f, 0xfc, 0x91, 0xfb, 0x3e, 0x32, 0x28, 0x17, 0x21, 0xf1, 0x88, 0x43, 0x89, 0xfe, 0x9d, 0xb9, + 0xa4, 0x6d, 0xae, 0x3f, 0x0a, 0x94, 0xc1, 0xfa, 0x6e, 0xc0, 0x3f, 0x1d, 0x97, 0x32, 0xac, 0x4c, + 0x14, 0x93, 0x4f, 0x13, 0x42, 0x19, 0xda, 0x84, 0x65, 0xcf, 0xbd, 0x72, 0x59, 0xdd, 0xb8, 0x6b, + 0xec, 0xe4, 0xb1, 0xda, 0xa0, 0x6d, 0x28, 0x06, 0xa3, 0x11, 0x25, 0xac, 0x9e, 0xe3, 0xc7, 0x15, + 0x1c, 0xed, 0xd0, 0x0b, 0x28, 0xd1, 0x20, 0x64, 0xfd, 0x8b, 0x9b, 0x7a, 0x9e, 0x1b, 0xd6, 0x9b, + 0xf7, 0xed, 0xa4, 0x9c, 0x6c, 0xe1, 0xa9, 0xc7, 0x81, 0xb6, 0xf8, 0xec, 0xdf, 0xe0, 0x22, 0x95, + 0xbf, 0x82, 0x77, 0xe4, 0x7a, 0x8c, 0x84, 0xf5, 0x82, 0xe2, 0x55, 0x3b, 0x74, 0x04, 0x20, 0x79, + 0x83, 0x70, 0xc8, 0x6d, 0xcb, 0x92, 0x7a, 0x27, 0x03, 0xf5, 0x99, 0xc0, 0xe3, 0x0a, 0xd5, 0x4b, + 0xeb, 0x1d, 0x94, 0x35, 0xc0, 0x6a, 0x42, 0x51, 0xb9, 0x47, 0x2b, 0x50, 0x3a, 0xef, 0x9e, 0x76, + 0xcf, 0xde, 0x74, 0xab, 0x4b, 0xa8, 0x0c, 0x85, 0x6e, 0xeb, 0x65, 0xbb, 0x6a, 0xa0, 0x0d, 0x58, + 0xeb, 0xb4, 0x7a, 0xaf, 0xfa, 0xb8, 0xdd, 0x69, 0xb7, 0x7a, 0xed, 0xc3, 0x6a, 0xce, 0xba, 0x03, + 0x95, 0x98, 0x17, 0x95, 0x20, 0xdf, 0xea, 0x1d, 0xa8, 0x2b, 0x87, 0x6d, 0xbe, 0x32, 0xac, 0x2f, + 0x06, 0x6c, 0xce, 0xca, 0x48, 0xc7, 0x81, 0x4f, 0x89, 0xd0, 0x71, 0x10, 0x4c, 0xfc, 0x58, 0x47, + 0xb9, 0x41, 0x08, 0x0a, 0x3e, 0xf9, 0xac, 0x55, 0x94, 0x6b, 0x81, 0x64, 0x01, 0x73, 0x3c, 0xa9, + 0x20, 0x47, 0xca, 0x0d, 0x7a, 0x06, 0xe5, 0xa8, 0x6a, 0x94, 0x6b, 0x93, 0xdf, 0x59, 0x69, 0x6e, + 0xa9, 0xfc, 0x75, 0x7d, 0x23, 0x8f, 0x38, 0x86, 0x59, 0x7b, 0x50, 0x3b, 0x22, 0x3a, 0x92, 0x1e, + 0x73, 0xd8, 0x24, 0xae, 0xaa, 0xf0, 0xeb, 0x5c, 0x11, 0x19, 0x8c, 0xf0, 0xcb, 0xd7, 0xd6, 0x6b, + 0xa8, 0x2f, 0xc2, 0xa3, 0xe8, 0x13, 0xf0, 0xe8, 0x01, 0x14, 0x44, 0xff, 0xc8, 0xd8, 0x57, 0x9a, + 0x68, 0x36, 0x9a, 0x13, 0x6e, 0xc1, 0xd2, 0x6e, 0xd9, 0xd3, 0xbc, 0x07, 0x81, 0xcf, 0x88, 0xcf, + 0x7e, 0x17, 0x47, 0x07, 0xfe, 0x4d, 0xc0, 0x47, 0x81, 0x34, 0xa0, 0x14, 0xb9, 0x90, 0x77, 0x52, + 0x55, 0xd0, 0x28, 0xeb, 0x1b, 0x2f, 0xc8, 0xf9, 0x78, 0xe8, 0x30, 0xa2, 0x4d, 0xe9, 0xae, 0xd1, + 0x43, 0x5e, 0x24, 0xf1, 0x9e, 0xa2, 0x9c, 0x36, 0x14, 0xb7, 0x7a, 0x74, 0x07, 0xe2, 0x8b, 0x95, + 0x1d, 0xed, 0x42, 0xf1, 0xda, 0xf1, 0x38, 0x8f, 0x2c, 0x52, 0x9c, 0x7d, 0x84, 0x94, 0x8f, 0x11, + 0x47, 0x08, 0x54, 0x83, 0xd2, 0x30, 0xbc, 0xe9, 0x87, 0x13, 0x5f, 0x36, 0x75, 0x19, 0x17, 0xf9, + 0x16, 0x4f, 0x7c, 0x74, 0x0f, 0xd6, 0x86, 0x2e, 0x75, 0x2e, 0x3c, 0xd2, 0xbf, 0x0c, 0x82, 0x8f, + 0x54, 0xf6, 0x75, 0x19, 0xaf, 0x46, 0x87, 0xc7, 0xe2, 0xcc, 0x3a, 0x86, 0xad, 0xb9, 0xf0, 0x6f, + 0xab, 0xc4, 0x0f, 0x03, 0xb6, 0x4e, 0x7c, 0xca, 0x9b, 0xc9, 0x9b, 0x93, 0x22, 0x4e, 0xdb, 0xc8, + 0x9c, 0x76, 0xee, 0x6f, 0xd2, 0xce, 0xcf, 0xa4, 0xad, 0x85, 0x2f, 0x4c, 0x09, 0x9f, 0x45, 0x0a, + 0xf4, 0x1f, 0x54, 0x04, 0x98, 0x8e, 0x9d, 0x01, 0xa9, 0x17, 0xe5, 0xed, 0x5f, 0x07, 0xe8, 0x7f, + 0x80, 0x90, 0x4c, 0x28, 0xe9, 0x4b, 0xf2, 0x92, 0xbc, 0x5f, 0x91, 0x27, 0x5d, 0xd1, 0x55, 0x27, + 0xb0, 0x3d, 0x9f, 0xfc, 0x6d, 0x85, 0xc4, 0x50, 0x3b, 0xf7, 0xdd, 0x44, 0x25, 0x93, 0x9a, 0x6a, + 0x21, 0xb7, 0x5c, 0x42, 0x99, 0x4f, 0xa1, 0xbe, 0xc8, 0x79, 0xcb, 0x00, 0x9b, 0x5f, 0x97, 0x61, + 0x5d, 0xbf, 0x63, 0x35, 0x1d, 0x91, 0x0b, 0xab, 0xd3, 0x63, 0x09, 0x3d, 0x4a, 0x1f, 0x9e, 0x73, + 0x7f, 0x01, 0xcc, 0xdd, 0x2c, 0x50, 0x15, 0xaa, 0xb5, 0xf4, 0xd4, 0x40, 0x14, 0xaa, 0xf3, 0x73, + 0x04, 0xed, 0x25, 0x73, 0xa4, 0x8c, 0x27, 0xd3, 0xce, 0x0a, 0xd7, 0x6e, 0xd1, 0x35, 0x6c, 0x2c, + 0x0c, 0x0d, 0xf4, 0x47, 0x9a, 0xd9, 0x69, 0x64, 0x36, 0x32, 0xe3, 0x63, 0xbf, 0x1f, 0x60, 0x6d, + 0xe6, 0x79, 0xa2, 0x14, 0xb5, 0x92, 0x46, 0x90, 0xf9, 0x38, 0x13, 0x36, 0xf6, 0x75, 0x05, 0xeb, + 0xb3, 0x2d, 0x8c, 0x52, 0x08, 0x12, 0x5f, 0xb9, 0xf9, 0x24, 0x1b, 0x38, 0x76, 0xc7, 0xeb, 0x38, + 0xdf, 0x92, 0x69, 0x75, 0x4c, 0x79, 0x0e, 0x69, 0x75, 0x4c, 0xeb, 0x74, 0x6b, 0x69, 0x1f, 0xde, + 0x96, 0x35, 0xfa, 0xa2, 0x28, 0xff, 0x33, 0x79, 0xfe, 0x33, 0x00, 0x00, 0xff, 0xff, 0x97, 0xd7, + 0x36, 0xd6, 0x33, 0x09, 0x00, 0x00, } From ebb7584ab289ce0cbd3dadaa6d80dce3b322c494 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 8 Aug 2016 14:35:21 -0700 Subject: [PATCH 20/22] fix(git): ignore _dist directory --- .gitignore | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3f1c658b5..4613d316a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ +.DS_Store .coverage/ +.vimrc +_dist/ +_proto/*.pb.go bin/ rootfs/tiller vendor/ -_proto/*.pb.go -.vimrc -.DS_Store From ad77fedf26ce63731c641e6a7859dee5f1ccc80a Mon Sep 17 00:00:00 2001 From: Matt Butcher Date: Mon, 8 Aug 2016 16:41:21 -0600 Subject: [PATCH 21/22] docs(developers): recommend minikube This reflects our internal decision to suggest minikube rather than commit resources to maintaining scripts/local-cluster.sh Closes #889 --- docs/architecture.md | 5 ++--- docs/developers.md | 28 ++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index de69b7553..272431eb7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -75,9 +75,8 @@ The Go files generated from the `proto` definitions are stored in Docker images are built by cross-compiling Linux binaries and then building a Docker image from the files in `rootfs`. -The `scripts/` directory contains a number of utility scripts, including -`local-cluster.sh`, which can start a full Kubernetes instance inside of -a Docker container. +The `scripts/` directory contains a number of utility scripts. Most of these +are used by the CI/CD pipeline. Go dependencies are managed with [Glide](https://github.com/Masterminds/glide) and stored in the diff --git a/docs/developers.md b/docs/developers.md index 79ece066a..5ebdc58fe 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -66,11 +66,31 @@ GCR registry. ## Running a Local Cluster -You can run tests locally using the `scripts/local-cluster.sh` script to -start Kubernetes inside of a Docker container. For OS X, you will need -to be running `docker-machine`. +For development, we highly recommend using the +[Kubernetes Minikube](https://github.com/kubernetes/minikube) +developer-oriented distribution. Once this is installed, you can use +`helm init` to install into the cluster. -Tiller should run on any >= 1.2 Kubernetes cluster with beta extensions. +For developing on Tiller, it is sometimes more expedient to run Tiller locally +instead of packaging it into an image and running it in-cluster. You can do +this by telling the Helm client to us a local instance. + +```console +$ make build +$ bin/tiller +``` + +And to configure the Helm client, use the `--host` flag or export the `HELM_HOST` +environment variable: + +```console +$ export HELM_HOST=localhost:44134 +$ helm install foo +``` + +(Note that you do not need to use `helm init` when you are running Tiller directly) + +Tiller should run on any >= 1.3 Kubernetes cluster. ## Contribution Guidelines From f99228778d9f297c5925af00922c6431eecb8fc5 Mon Sep 17 00:00:00 2001 From: Michelle Noorali Date: Mon, 8 Aug 2016 12:18:25 -0600 Subject: [PATCH 22/22] feat(*): integrate support for upgrade hooks --- cmd/helm/upgrade.go | 3 +- cmd/helm/upgrade_test.go | 8 +++-- cmd/tiller/release_server.go | 20 +++++------ cmd/tiller/release_server_test.go | 56 +++++++++++++++++++++++++++++++ pkg/helm/option.go | 1 - 5 files changed, 74 insertions(+), 14 deletions(-) diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 7328281aa..07eeae03b 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -95,7 +95,8 @@ func (u *upgradeCmd) run() error { return prettyError(err) } - fmt.Fprintf(u.out, "It's not you. It's me\nYour upgrade looks valid but this command is still under active development.\nHang tight.\n") + success := u.release + " has been upgraded. Happy Helming!\n" + fmt.Fprintf(u.out, success) return nil diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index 341670cd8..f51049e28 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -52,18 +52,22 @@ func TestUpgradeCmd(t *testing.T) { Description: "A Helm chart for Kubernetes", Version: "0.1.2", } + chartPath, err = chartutil.Create(cfile, tmpChart) if err != nil { t.Errorf("Error creating chart: %v", err) } - ch, _ = chartutil.Load(chartPath) + ch, err = chartutil.Load(chartPath) + if err != nil { + t.Errorf("Error loading updated chart: %v", err) + } tests := []releaseCase{ { name: "upgrade a release", args: []string{"funny-bunny", chartPath}, resp: releaseMock(&releaseOptions{name: "funny-bunny", version: 2, chart: ch}), - expected: "It's not you. It's me\nYour upgrade looks valid but this command is still under active development.\nHang tight.\n", + expected: "funny-bunny has been upgraded. Happy Helming!\n", }, } diff --git a/cmd/tiller/release_server.go b/cmd/tiller/release_server.go index afe13376e..1648f0b22 100644 --- a/cmd/tiller/release_server.go +++ b/cmd/tiller/release_server.go @@ -196,11 +196,11 @@ func (s *releaseServer) performUpdate(originalRelease, updatedRelease *release.R } // pre-ugrade hooks - //if !req.DisableHooks { - //if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, preUpgrade); err != nil { - //return res, err - //} - //} + if !req.DisableHooks { + if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, preUpgrade); err != nil { + return res, err + } + } kubeCli := s.env.KubeClient original := bytes.NewBufferString(originalRelease.Manifest) @@ -210,11 +210,11 @@ func (s *releaseServer) performUpdate(originalRelease, updatedRelease *release.R } // post-upgrade hooks - //if !req.DisableHooks { - //if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, postUpgrade); err != nil { - //return res, err - //} - //} + if !req.DisableHooks { + if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, postUpgrade); err != nil { + return res, err + } + } updatedRelease.Info.Status.Code = release.Status_DEPLOYED diff --git a/cmd/tiller/release_server_test.go b/cmd/tiller/release_server_test.go index a6e6c2fa8..23fc05fff 100644 --- a/cmd/tiller/release_server_test.go +++ b/cmd/tiller/release_server_test.go @@ -44,6 +44,16 @@ data: name: value ` +var manifestWithUpgradeHooks = `apiVersion: v1 +kind: ConfigMap +metadata: + name: test-cm + annotations: + "helm.sh/hook": post-upgrade,pre-upgrade +data: + name: value +` + func rsFixture() *releaseServer { return &releaseServer{ env: mockEnvironment(), @@ -302,6 +312,7 @@ func TestUpdateRelease(t *testing.T) { Metadata: &chart.Metadata{Name: "hello"}, Templates: []*chart.Template{ {Name: "hello", Data: []byte("hello: world")}, + {Name: "hooks", Data: []byte(manifestWithUpgradeHooks)}, }, }, } @@ -309,6 +320,7 @@ func TestUpdateRelease(t *testing.T) { if err != nil { t.Errorf("Failed updated: %s", err) } + if res.Release.Name == "" { t.Errorf("Expected release name.") } @@ -326,6 +338,21 @@ func TestUpdateRelease(t *testing.T) { t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) } + if len(updated.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) + } + if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { + t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) + } + + if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { + t.Errorf("Expected event 0 to be post upgrade") + } + + if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { + t.Errorf("Expected event 0 to be pre upgrade") + } + if len(res.Release.Manifest) == 0 { t.Errorf("No manifest returned: %v", res.Release) } @@ -343,6 +370,35 @@ func TestUpdateRelease(t *testing.T) { } } +func TestUpdateReleaseNoHooks(t *testing.T) { + c := context.Background() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "hello", Data: []byte("hello: world")}, + {Name: "hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + } + + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Errorf("Failed updated: %s", err) + } + + if hl := res.Release.Hooks[0].LastRun; hl != nil { + t.Errorf("Expected that no hooks were run. Got %d", hl) + } + +} + func TestUninstallRelease(t *testing.T) { c := context.Background() rs := rsFixture() diff --git a/pkg/helm/option.go b/pkg/helm/option.go index 80db158a5..2af4742da 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -244,7 +244,6 @@ func (o *options) rpcDeleteRelease(rlsName string, rlc rls.ReleaseServiceClient, // Executes tiller.UpdateRelease RPC. func (o *options) rpcUpdateRelease(rlsName string, chr *cpb.Chart, rlc rls.ReleaseServiceClient, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) { - //TODO: handle dryRun for _, opt := range opts { opt(o) }