From edb5be3ac5f3359b331069c1989ddd71f6ca9f57 Mon Sep 17 00:00:00 2001 From: Ergin Babani Date: Tue, 15 Aug 2017 01:19:33 -0400 Subject: [PATCH 01/25] 'helm upgrade --install' uses the namespace from kube config. Use the current kube config namespace by default if --namespace is not specified. This makes 'helm upgrade --install' and 'helm install' handle namespaces the same way. --- cmd/helm/install.go | 2 +- cmd/helm/upgrade.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index b480b0b93..324f68c89 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -177,7 +177,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { f := cmd.Flags() f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)") f.StringVarP(&inst.name, "name", "n", "", "release name. If unspecified, it will autogenerate one for you") - f.StringVar(&inst.namespace, "namespace", "", "namespace to install the release into") + f.StringVar(&inst.namespace, "namespace", "", "namespace to install the release into. Defaults to the current kube config namespace.") 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.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index 989d73cdd..4b852198a 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -121,7 +121,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading") f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys") f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install") - f.StringVar(&upgrade.namespace, "namespace", "default", "namespace to install the release into (only used if --install is set)") + f.StringVar(&upgrade.namespace, "namespace", "", "namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace") 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") From e22dad066c7eeac3368a12d61a5fcc43c7d3909e Mon Sep 17 00:00:00 2001 From: Ergin Babani Date: Tue, 15 Aug 2017 01:40:56 -0400 Subject: [PATCH 02/25] Update docs with the upgrade/install namespace behaviour. --- docs/helm/helm_install.md | 4 ++-- docs/helm/helm_upgrade.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/helm/helm_install.md b/docs/helm/helm_install.md index 0a4cb48e8..8858f534d 100644 --- a/docs/helm/helm_install.md +++ b/docs/helm/helm_install.md @@ -76,7 +76,7 @@ helm install [CHART] --keyring string location of public keys used for verification (default "~/.gnupg/pubring.gpg") -n, --name string release name. If unspecified, it will autogenerate one for you --name-template string specify template used to name the release - --namespace string namespace to install the release into + --namespace string namespace to install the release into. Defaults to the current kube config namespace. --no-hooks prevent hooks from running during install --replace re-use the given name, even if that name is already used. This is unsafe in production --repo string chart repository url where to locate the requested chart @@ -106,4 +106,4 @@ helm install [CHART] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 23-Jun-2017 +###### Auto generated by spf13/cobra on 15-Aug-2017 diff --git a/docs/helm/helm_upgrade.md b/docs/helm/helm_upgrade.md index 934cadd13..fdf95854f 100644 --- a/docs/helm/helm_upgrade.md +++ b/docs/helm/helm_upgrade.md @@ -44,7 +44,7 @@ helm upgrade [RELEASE] [CHART] -i, --install if a release by this name doesn't already exist, run an install --key-file string identify HTTPS client using this SSL key file --keyring string path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg") - --namespace string namespace to install the release into (only used if --install is set) (default "default") + --namespace string namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace --no-hooks disable pre/post upgrade hooks --recreate-pods performs pods restart for the resource if applicable --repo string chart repository url where to locate the requested chart @@ -76,4 +76,4 @@ helm upgrade [RELEASE] [CHART] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 23-Jun-2017 +###### Auto generated by spf13/cobra on 15-Aug-2017 From 734b1245772ff4e8319e3648646e1c0f1d797d87 Mon Sep 17 00:00:00 2001 From: DockerZK Date: Sun, 16 Jul 2017 17:17:55 +0800 Subject: [PATCH 03/25] feat(*) add key helm.sh/hook-delete-policy to hook annotation When "helm.sh/hook-delete-policy: hook-succeeded" is provided in a hook's annotation, Tiller will automatically delete the hook after the hook is succeeded. When "helm.sh/hook-delete-policy: hook-failed" is provided in a hook's annotation, Tiller will automatically delete the hook after the hook is failed. Closes #1769 --- _proto/hapi/release/hook.proto | 6 ++ docs/charts_hooks.md | 18 ++++-- pkg/hooks/hooks.go | 9 +++ pkg/proto/hapi/release/hook.pb.go | 94 +++++++++++++++++++------------ pkg/tiller/hooks.go | 42 ++++++++++++-- pkg/tiller/release_server.go | 40 ++++++++++++- 6 files changed, 163 insertions(+), 46 deletions(-) diff --git a/_proto/hapi/release/hook.proto b/_proto/hapi/release/hook.proto index 2237883ca..22c1fedef 100644 --- a/_proto/hapi/release/hook.proto +++ b/_proto/hapi/release/hook.proto @@ -35,6 +35,10 @@ message Hook { RELEASE_TEST_SUCCESS = 9; RELEASE_TEST_FAILURE = 10; } + enum DeletePolicy { + SUCCEEDED = 0; + FAILED = 1; + } string name = 1; // Kind is the Kubernetes kind. string kind = 2; @@ -48,4 +52,6 @@ message Hook { google.protobuf.Timestamp last_run = 6; // Weight indicates the sort order for execution among similar Hook type int32 weight = 7; + // DeletePolicies are the policies that indicate when to delete the hook + repeated DeletePolicy delete_policies = 8; } diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index 6ac9bc2b0..dec3e552e 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -87,7 +87,7 @@ in the future.) It is considered good practice to add a hook weight, and set it to `0` if weight is not important. -### Hook resources are unmanaged +### Hook resources are not managed with correponding releases The resources that a hook creates are not tracked or managed as part of the release. Once Tiller verifies that the hook has reached its ready state, it @@ -95,8 +95,8 @@ will leave the hook resource alone. Practically speaking, this means that if you create resources in a hook, you cannot rely upon `helm delete` to remove the resources. To destroy such -resources, you need to write code to perform this operation in a `pre-delete` -or `post-delete` hook. +resources, you need to either write code to perform this operation in a `pre-delete` +or `post-delete` hook or add `"helm.sh/hook-delete-policy"` annotation to the hook template file. ## Writing a Hook @@ -122,6 +122,7 @@ metadata: # job is considered part of the release. "helm.sh/hook": post-install "helm.sh/hook-weight": "-5" + "helm/hook-delete-policy": hook-succeeded spec: template: metadata: @@ -160,7 +161,7 @@ and a config map as a pre-install hook. When subcharts declare hooks, those are also evaluated. There is no way for a top-level chart to disable the hooks declared by subcharts. -It is also possible to define a weight for a hook which will help build a +It is possible to define a weight for a hook which will help build a deterministic executing order. Weights are defined using the following annotation: ``` @@ -172,3 +173,12 @@ 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. +It is also possible to define policies that determine when to delete corresponding hook resources. Hook deletion policies are defined using the following annotation: + +``` + annotations: + "helm.sh/hook-delete-policy": hook-succeeded +``` + +When using `"helm.sh/hook-delete-policy"` annoation, you can choose its value from `"hook-succeeded"` and `"hook-failed"`. The value `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully excuted, while the value `"hook-failed"`specifies Tiller should delete the hook if the hook is failed during execuation. + diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go index e1fccb416..ed2d946a4 100644 --- a/pkg/hooks/hooks.go +++ b/pkg/hooks/hooks.go @@ -26,6 +26,9 @@ const HookAnno = "helm.sh/hook" // HookWeightAnno is the label name for a hook weight const HookWeightAnno = "helm.sh/hook-weight" +// HookDeleteAnno is the label name for the delete policy for a hook +const HookDeleteAnno = "helm.sh/hook-delete-policy" + // Types of hooks const ( PreInstall = "pre-install" @@ -40,6 +43,12 @@ const ( ReleaseTestFailure = "test-failure" ) +// Type of policy for deleting the hook +const ( + HookSucceeded = "hook-succeeded" + HookFailed = "hook-failed" +) + // FilterTestHooks filters the list of hooks are returns only testing hooks. func FilterTestHooks(hooks []*release.Hook) []*release.Hook { testHooks := []*release.Hook{} diff --git a/pkg/proto/hapi/release/hook.pb.go b/pkg/proto/hapi/release/hook.pb.go index f7d5419cc..bd9391c50 100644 --- a/pkg/proto/hapi/release/hook.pb.go +++ b/pkg/proto/hapi/release/hook.pb.go @@ -6,19 +6,9 @@ Package release is a generated protocol buffer package. It is generated from these files: hapi/release/hook.proto - hapi/release/info.proto - hapi/release/release.proto - hapi/release/status.proto - hapi/release/test_run.proto - hapi/release/test_suite.proto It has these top-level messages: Hook - Info - Release - Status - TestRun - TestSuite */ package release @@ -86,6 +76,27 @@ func (x Hook_Event) String() string { } func (Hook_Event) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } +type Hook_DeletePolicy int32 + +const ( + Hook_SUCCEEDED Hook_DeletePolicy = 0 + Hook_FAILED Hook_DeletePolicy = 1 +) + +var Hook_DeletePolicy_name = map[int32]string{ + 0: "SUCCEEDED", + 1: "FAILED", +} +var Hook_DeletePolicy_value = map[string]int32{ + "SUCCEEDED": 0, + "FAILED": 1, +} + +func (x Hook_DeletePolicy) String() string { + return proto.EnumName(Hook_DeletePolicy_name, int32(x)) +} +func (Hook_DeletePolicy) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 1} } + // Hook defines a hook object. type Hook struct { Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` @@ -101,6 +112,8 @@ type Hook struct { 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 type Weight int32 `protobuf:"varint,7,opt,name=weight" json:"weight,omitempty"` + // DeletePolicies are the policies that indicate when to delete the hook + DeletePolicies []Hook_DeletePolicy `protobuf:"varint,8,rep,packed,name=delete_policies,json=deletePolicies,enum=hapi.release.Hook_DeletePolicy" json:"delete_policies,omitempty"` } func (m *Hook) Reset() { *m = Hook{} } @@ -157,37 +170,48 @@ func (m *Hook) GetWeight() int32 { return 0 } +func (m *Hook) GetDeletePolicies() []Hook_DeletePolicy { + if m != nil { + return m.DeletePolicies + } + return nil +} + func init() { proto.RegisterType((*Hook)(nil), "hapi.release.Hook") proto.RegisterEnum("hapi.release.Hook_Event", Hook_Event_name, Hook_Event_value) + proto.RegisterEnum("hapi.release.Hook_DeletePolicy", Hook_DeletePolicy_name, Hook_DeletePolicy_value) } func init() { proto.RegisterFile("hapi/release/hook.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 371 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 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, + // 428 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x91, 0xd1, 0x6e, 0xda, 0x30, + 0x14, 0x86, 0x9b, 0x12, 0x12, 0x38, 0x50, 0xea, 0x59, 0xd3, 0x66, 0x71, 0x53, 0xc4, 0x15, 0xbb, + 0x09, 0x53, 0xa7, 0x3d, 0x40, 0x4a, 0xce, 0xd6, 0xaa, 0x11, 0x20, 0x27, 0x68, 0xd2, 0x6e, 0xa2, + 0x74, 0xb8, 0x10, 0x11, 0xe2, 0x88, 0x98, 0x4d, 0x7b, 0xa6, 0xbd, 0xce, 0x1e, 0x68, 0xb2, 0x09, + 0x59, 0xa5, 0xed, 0xee, 0x9c, 0xef, 0x7c, 0x76, 0xce, 0x1f, 0xc3, 0xdb, 0x6d, 0x5a, 0x66, 0xd3, + 0x83, 0xc8, 0x45, 0x5a, 0x89, 0xe9, 0x56, 0xca, 0x9d, 0x57, 0x1e, 0xa4, 0x92, 0xb4, 0xaf, 0x07, + 0x5e, 0x3d, 0x18, 0xde, 0x6c, 0xa4, 0xdc, 0xe4, 0x62, 0x6a, 0x66, 0x4f, 0xc7, 0xe7, 0xa9, 0xca, + 0xf6, 0xa2, 0x52, 0xe9, 0xbe, 0x3c, 0xe9, 0xe3, 0x5f, 0x36, 0xd8, 0xf7, 0x52, 0xee, 0x28, 0x05, + 0xbb, 0x48, 0xf7, 0x82, 0x59, 0x23, 0x6b, 0xd2, 0xe5, 0xa6, 0xd6, 0x6c, 0x97, 0x15, 0x6b, 0x76, + 0x79, 0x62, 0xba, 0xd6, 0xac, 0x4c, 0xd5, 0x96, 0xb5, 0x4e, 0x4c, 0xd7, 0x74, 0x08, 0x9d, 0x7d, + 0x5a, 0x64, 0xcf, 0xa2, 0x52, 0xcc, 0x36, 0xbc, 0xe9, 0xe9, 0x7b, 0x70, 0xc4, 0x77, 0x51, 0xa8, + 0x8a, 0xb5, 0x47, 0xad, 0xc9, 0xe0, 0x96, 0x79, 0x2f, 0x17, 0xf4, 0xf4, 0xb7, 0x3d, 0xd4, 0x02, + 0xaf, 0x3d, 0xfa, 0x11, 0x3a, 0x79, 0x5a, 0xa9, 0xe4, 0x70, 0x2c, 0x98, 0x33, 0xb2, 0x26, 0xbd, + 0xdb, 0xa1, 0x77, 0x8a, 0xe1, 0x9d, 0x63, 0x78, 0xf1, 0x39, 0x06, 0x77, 0xb5, 0xcb, 0x8f, 0x05, + 0x7d, 0x03, 0xce, 0x0f, 0x91, 0x6d, 0xb6, 0x8a, 0xb9, 0x23, 0x6b, 0xd2, 0xe6, 0x75, 0x47, 0xef, + 0xe1, 0x7a, 0x2d, 0x72, 0xa1, 0x44, 0x52, 0xca, 0x3c, 0xfb, 0x96, 0x89, 0x8a, 0x75, 0xcc, 0x26, + 0x37, 0xff, 0xd9, 0x24, 0x30, 0xe6, 0x52, 0x8b, 0x3f, 0xf9, 0x60, 0xfd, 0xb7, 0xcb, 0x44, 0x35, + 0xfe, 0x6d, 0x41, 0xdb, 0xac, 0x4a, 0x7b, 0xe0, 0xae, 0xe6, 0x8f, 0xf3, 0xc5, 0x97, 0x39, 0xb9, + 0xa0, 0xd7, 0xd0, 0x5b, 0x72, 0x4c, 0x1e, 0xe6, 0x51, 0xec, 0x87, 0x21, 0xb1, 0x28, 0x81, 0xfe, + 0x72, 0x11, 0xc5, 0x0d, 0xb9, 0xa4, 0x03, 0x00, 0xad, 0x04, 0x18, 0x62, 0x8c, 0xa4, 0x65, 0x8e, + 0x68, 0xa3, 0x06, 0xf6, 0xf9, 0x8e, 0xd5, 0xf2, 0x33, 0xf7, 0x03, 0x24, 0xed, 0xe6, 0x8e, 0x33, + 0x71, 0x0c, 0xe1, 0x98, 0xf0, 0x45, 0x18, 0xde, 0xf9, 0xb3, 0x47, 0xe2, 0xd2, 0x57, 0x70, 0x65, + 0x9c, 0x06, 0x75, 0x28, 0x83, 0xd7, 0x1c, 0x43, 0xf4, 0x23, 0x4c, 0x62, 0x8c, 0xe2, 0x24, 0x5a, + 0xcd, 0x66, 0x18, 0x45, 0xa4, 0xfb, 0xcf, 0xe4, 0x93, 0xff, 0x10, 0xae, 0x38, 0x12, 0x18, 0xbf, + 0x83, 0xfe, 0xcb, 0xd8, 0xf4, 0x0a, 0xba, 0xe6, 0x18, 0x06, 0x18, 0x90, 0x0b, 0x0a, 0xe0, 0x68, + 0x17, 0x03, 0x62, 0xdd, 0x75, 0xbf, 0xba, 0xf5, 0xef, 0x7a, 0x72, 0xcc, 0x5b, 0x7c, 0xf8, 0x13, + 0x00, 0x00, 0xff, 0xff, 0xb9, 0x8a, 0xe1, 0xaf, 0x89, 0x02, 0x00, 0x00, } diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 996253384..8d72f95cf 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -44,6 +44,12 @@ var events = map[string]release.Hook_Event{ hooks.ReleaseTestFailure: release.Hook_RELEASE_TEST_FAILURE, } +// deletePolices represents a mapping between the key in the annotation for label deleting policy and its real meaning +var deletePolices = map[string]release.Hook_DeletePolicy{ + hooks.HookSucceeded: release.Hook_SUCCEEDED, + hooks.HookFailed: release.Hook_FAILED, +} + // manifest represents a manifest file, which has a name and some content. type manifest struct { name string @@ -113,6 +119,13 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort // annotations: // helm.sh/hook: pre-install // +// To determine the policy to delete the hook, it looks for a YAML structure like this: +// +// kind: SomeKind +// apiVersion: v1 +// metadata: +// annotations: +// helm.sh/hook-delete-policy: hook-succeeded func (file *manifestFile) sort(result *result) error { for _, m := range file.entries { var entry util.SimpleHead @@ -149,12 +162,13 @@ func (file *manifestFile) sort(result *result) error { hw := calculateHookWeight(entry) h := &release.Hook{ - Name: entry.Metadata.Name, - Kind: entry.Kind, - Path: file.path, - Manifest: m, - Events: []release.Hook_Event{}, - Weight: hw, + Name: entry.Metadata.Name, + Kind: entry.Kind, + Path: file.path, + Manifest: m, + Events: []release.Hook_Event{}, + Weight: hw, + DeletePolicies: []release.Hook_DeletePolicy{}, } isKnownHook := false @@ -173,6 +187,22 @@ func (file *manifestFile) sort(result *result) error { } result.hooks = append(result.hooks, h) + + isKnownDeletePolices := false + dps, ok := entry.Metadata.Annotations[hooks.HookDeleteAnno] + if ok { + for _, dp := range strings.Split(dps, ",") { + dp = strings.ToLower(strings.TrimSpace(dp)) + p, exist := deletePolices[dp] + if exist { + isKnownDeletePolices = true + h.DeletePolicies = append(h.DeletePolicies, p) + } + } + if !isKnownDeletePolices { + log.Printf("info: skipping unknown hook delete policy: %q", dps) + } + } } return nil diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index db7c0b568..bab405e1f 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -30,6 +30,7 @@ import ( "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/hooks" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/services" @@ -347,12 +348,36 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin b.WriteString(h.Manifest) if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil { s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err) + // If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted + // under failed condition. If so, then clear the corresponding resource object in the hook + if hookShouldBeDeleted(h, hooks.HookFailed) { + b.Reset() + b.WriteString(h.Manifest) + s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, hooks.HookFailed) + if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil { + s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete) + return errHookDelete + } + } return err } - h.LastRun = timeconv.Now() } s.Log("hooks complete for %s %s", hook, name) + // If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted + // under succeeded condition. If so, then clear the corresponding resource object in each hook + for _, h := range executingHooks { + b := bytes.NewBufferString(h.Manifest) + if hookShouldBeDeleted(h, hooks.HookSucceeded) { + s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, hooks.HookSucceeded) + if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil { + s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete) + return errHookDelete + } + } + h.LastRun = timeconv.Now() + } + return nil } @@ -373,3 +398,16 @@ func validateReleaseName(releaseName string) error { return nil } + +// hookShouldBeDeleted determines whether the defined hook deletion policy matches the hook deletion polices +// supported by helm. If so, mark the hook as one should be deleted. +func hookShouldBeDeleted(hook *release.Hook, policy string) bool { + if dp, ok := deletePolices[policy]; ok { + for _, v := range hook.DeletePolicies { + if dp == v { + return true + } + } + } + return false +} From 4ffb9dfb4b29ddbce43e7f62063f1646df744805 Mon Sep 17 00:00:00 2001 From: xuhaigang Date: Thu, 24 Aug 2017 12:31:54 +0800 Subject: [PATCH 04/25] Fix(helm): Fix the bug of dependency update deleting subcharts In Helm 2.6.0, the new delete feature that deletes old versions of charts is deleting subcharts that aren't in requirements.yaml. In this patch, I judge the dependency whether it is included in the requirements.yaml before deleting it. Closes #2830 --- pkg/downloader/manager.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 388010fbc..c8f7299ac 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -217,9 +217,16 @@ func (m *Manager) downloadAll(deps []*chartutil.Dependency) error { } fmt.Fprintln(m.Out, "Deleting outdated charts") + c, _ := m.loadChartDir() + req, _ := chartutil.LoadRequirements(c) for _, dep := range deps { - if err := m.safeDeleteDep(dep.Name, destPath); err != nil { - return err + for _, r := range req.Dependencies { + if dep.Name == r.Name { + if err := m.safeDeleteDep(dep.Name, destPath); err != nil { + return err + } + break + } } } From 4e970172130d00cac87633e2fdb1eb2965893749 Mon Sep 17 00:00:00 2001 From: Maciej Kwiek Date: Tue, 22 Aug 2017 13:42:36 +0200 Subject: [PATCH 05/25] CircleCI 2 config --- .circleci/config.yml | 46 +++++++++++++++++++++++++++++++++++++++ circle.yml | 52 -------------------------------------------- 2 files changed, 46 insertions(+), 52 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 circle.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..c32c9f08d --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,46 @@ +version: 2 +jobs: + build: + working_directory: /go/src/k8s.io/helm + parallelism: 3 + docker: + - image: golang:1.8 + environment: + PROJECT_NAME: "kubernetes-helm" + steps: + - checkout + - run: + name: install dependencies + command: make bootstrap + - run: + name: go env + command: go env + - save_cache: + key: vendor-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }} + paths: + - vendor + - run: + name: test + command: ./scripts/ci.sh + deployment: + working_directory: /go/src/k8s.io/helm + docker: + - image: golang:1.8 + environment: + PROJECT_NAME: "kubernetes-helm" + filters: + tags: + only: /.*/ + branches: + only: master + steps: + - checkout + - restore_cache: + keys: + - vendor-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }} + - run: + name: go env + command: go env + - deploy: + name: deploy + command: ./scripts/ci/deploy.sh diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 5b30c8be4..000000000 --- a/circle.yml +++ /dev/null @@ -1,52 +0,0 @@ -machine: - pre: - - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0 - - environment: - GOVERSION: "1.8.3" - GOPATH: "${HOME}/.go_workspace" - WORKDIR: "${GOPATH}/src/k8s.io/helm" - PROJECT_NAME: "kubernetes-helm" - - services: - - docker - -dependencies: - cache_directories: - - "~/.glide" - - pre: - # remove old go files - - sudo rm -rf /usr/local/go - - rm -rf "$GOPATH" - - override: - # install go - - wget "https://storage.googleapis.com/golang/go${GOVERSION}.linux-amd64.tar.gz" -O "${HOME}/go${GOVERSION}.tar.gz" - - sudo tar -C /usr/local -xzf "${HOME}/go${GOVERSION}.tar.gz" - - # move repository to the canonical import path - - mkdir -p "$(dirname ${WORKDIR})" - - cp -R "${HOME}/helm" "${WORKDIR}" - - # install dependencies - - cd "${WORKDIR}" && make bootstrap - - post: - - go env - -test: - override: - - cd "${WORKDIR}" && ./scripts/ci.sh: - parallel: true - -deployment: - release: - tag: /.*/ - commands: - - cd "${WORKDIR}" && ./scripts/ci/deploy.sh - - canary: - branch: master - commands: - - cd "${WORKDIR}" && ./scripts/ci/deploy.sh From cdc0245d77708ad12079c5e3cc040dafefc53420 Mon Sep 17 00:00:00 2001 From: tamal Date: Sun, 27 Aug 2017 14:36:56 -0700 Subject: [PATCH 06/25] Update link to wheel --- docs/related.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/related.md b/docs/related.md index 22ac36380..d91d33f13 100644 --- a/docs/related.md +++ b/docs/related.md @@ -44,7 +44,7 @@ tag on their plugin repositories. Tools layered on top of Helm or Tiller. -- [Wheel](https://github.com/appscode/wheel) - Ajax friendly Helm Tiller Proxy using [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) +- [Swift](https://github.com/appscode/swift) - Ajax friendly Helm Tiller Proxy using [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) - [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 From a412a1209a51d97e771fe97e55ee8b7b294d3148 Mon Sep 17 00:00:00 2001 From: Tamal Saha Date: Mon, 28 Aug 2017 08:42:48 -0700 Subject: [PATCH 07/25] Update related.md --- docs/related.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/related.md b/docs/related.md index d91d33f13..c450e2607 100644 --- a/docs/related.md +++ b/docs/related.md @@ -44,7 +44,7 @@ tag on their plugin repositories. Tools layered on top of Helm or Tiller. -- [Swift](https://github.com/appscode/swift) - Ajax friendly Helm Tiller Proxy using [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) +- [AppsCode Swift](https://github.com/appscode/swift) - Ajax friendly Helm Tiller Proxy using [grpc-gateway](https://github.com/grpc-ecosystem/grpc-gateway) - [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 From 4a02a71f1ec6d5120dae51192136f1ff31a8ef6c Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Tue, 8 Aug 2017 11:38:38 -0700 Subject: [PATCH 08/25] WIP feat(helm): add `template` command This adds the functionality from the helm-template plugin to allow the rendering of templates without Tiller. Closes #2755 --- cmd/helm/helm.go | 1 + cmd/helm/template.go | 236 ++++++++++++++++++ cmd/helm/template_test.go | 160 ++++++++++++ docs/helm/helm.md | 3 +- docs/helm/helm_template.md | 50 ++++ .../subchart1/charts/subchartA/values.yaml | 2 +- .../charts/subchart1/templates/NOTES.txt | 1 + .../charts/subchart1/templates/service.yaml | 2 + pkg/tiller/hooks.go | 30 +-- pkg/tiller/hooks_test.go | 14 +- pkg/tiller/kind_sorter.go | 20 +- pkg/tiller/kind_sorter_test.go | 142 +++++------ pkg/tiller/release_modules.go | 2 +- pkg/tiller/release_server.go | 4 +- pkg/tiller/resource_policy.go | 14 +- 15 files changed, 570 insertions(+), 111 deletions(-) create mode 100644 cmd/helm/template.go create mode 100644 cmd/helm/template_test.go create mode 100644 docs/helm/helm_template.md create mode 100644 pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index bc8885b4b..61513770b 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -125,6 +125,7 @@ func newRootCmd(args []string) *cobra.Command { newHomeCmd(out), newInitCmd(out), newPluginCmd(out), + newTemplateCmd(out), // Hidden documentation generator command: 'helm docs' newDocsCmd(out), diff --git a/cmd/helm/template.go b/cmd/helm/template.go new file mode 100644 index 000000000..88ca92fc9 --- /dev/null +++ b/cmd/helm/template.go @@ -0,0 +1,236 @@ +/* +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 main + +import ( + "errors" + "fmt" + "io" + "os" + "path/filepath" + "regexp" + "strings" + "time" + + "github.com/spf13/cobra" + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/engine" + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + util "k8s.io/helm/pkg/releaseutil" + "k8s.io/helm/pkg/tiller" + "k8s.io/helm/pkg/timeconv" +) + +const templateDesc = ` +Render chart templates locally and display the output. + +This does not require Tiller. However, any values that would normally be +looked up or retrieved in-cluster will be faked locally. Additionally, none +of the server-side testing of chart validity (e.g. whether an API is supported) +is done. + +To render just one template in a chart, use '-x': + + $ helm template mychart -x templates/deployment.yaml +` + +type templateCmd struct { + namespace string + valueFiles valueFiles + chartPath string + out io.Writer + client helm.Interface + values []string + nameTemplate string + showNotes bool + releaseName string + renderFiles []string +} + +func newTemplateCmd(out io.Writer) *cobra.Command { + + t := &templateCmd{ + out: out, + } + + cmd := &cobra.Command{ + Use: "template [flags] CHART", + Short: fmt.Sprintf("locally render templates"), + Long: templateDesc, + RunE: t.run, + } + + f := cmd.Flags() + f.BoolVar(&t.showNotes, "notes", false, "show the computed NOTES.txt file as well") + f.StringVarP(&t.releaseName, "name", "n", "RELEASE-NAME", "release name") + f.StringArrayVarP(&t.renderFiles, "execute", "x", []string{}, "only execute the given templates") + f.VarP(&t.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)") + f.StringVar(&t.namespace, "namespace", "", "namespace to install the release into") + f.StringArrayVar(&t.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)") + f.StringVar(&t.nameTemplate, "name-template", "", "specify template used to name the release") + + return cmd +} + +func (t *templateCmd) run(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return errors.New("chart is required") + } + // verify chart path exists + if _, err := os.Stat(args[0]); err == nil { + if t.chartPath, err = filepath.Abs(args[0]); err != nil { + return err + } + } else { + return err + } + // verify specified templates exist relative to chart + rf := []string{} + var af string + var err error + if len(t.renderFiles) > 0 { + for _, f := range t.renderFiles { + if !filepath.IsAbs(f) { + af, err = filepath.Abs(t.chartPath + "/" + f) + if err != nil { + return fmt.Errorf("could not resolve template path: %s", err) + } + } else { + af = f + } + rf = append(rf, af) + + if _, err := os.Stat(af); err != nil { + return fmt.Errorf("could not resolve template path: %s", err) + } + } + } + + if t.namespace == "" { + t.namespace = defaultNamespace() + } + // get combined values and create config + rawVals, err := vals(t.valueFiles, t.values) + if err != nil { + return err + } + config := &chart.Config{Raw: string(rawVals), Values: map[string]*chart.Value{}} + + // If template is specified, try to run the template. + if t.nameTemplate != "" { + t.releaseName, err = generateName(t.nameTemplate) + if err != nil { + return err + } + } + + // Check chart requirements to make sure all dependencies are present in /charts + c, err := chartutil.Load(t.chartPath) + if err != nil { + return prettyError(err) + } + + if req, err := chartutil.LoadRequirements(c); err == nil { + if err := checkDependencies(c, req); err != nil { + return prettyError(err) + } + } else if err != chartutil.ErrRequirementsNotFound { + return fmt.Errorf("cannot load requirements: %v", err) + } + options := chartutil.ReleaseOptions{ + Name: t.releaseName, + Time: timeconv.Now(), + Namespace: t.namespace, + } + + err = chartutil.ProcessRequirementsEnabled(c, config) + if err != nil { + return err + } + err = chartutil.ProcessRequirementsImportValues(c) + if err != nil { + return err + } + + // Set up engine. + renderer := engine.New() + + vals, err := chartutil.ToRenderValues(c, config, options) + if err != nil { + return err + } + + out, err := renderer.Render(c, vals) + listManifests := []tiller.Manifest{} + if err != nil { + return err + } + // extract kind and name + re := regexp.MustCompile("kind:(.*)\n") + for k, v := range out { + match := re.FindStringSubmatch(v) + h := "Unknown" + if len(match) == 2 { + h = strings.TrimSpace(match[1]) + } + m := tiller.Manifest{Name: k, Content: v, Head: &util.SimpleHead{Kind: h}} + listManifests = append(listManifests, m) + } + in := func(needle string, haystack []string) bool { + // make needle path absolute + d := strings.Split(needle, "/") + dd := d[1:] + an := t.chartPath + "/" + strings.Join(dd, "/") + + for _, h := range haystack { + if h == an { + return true + } + } + return false + } + if settings.Debug { + rel := &release.Release{ + Name: t.releaseName, + Chart: c, + Config: config, + Version: 1, + Namespace: t.namespace, + Info: &release.Info{LastDeployed: timeconv.Timestamp(time.Now())}, + } + printRelease(os.Stdout, rel) + } + + for _, m := range tiller.SortByKind(listManifests) { + if len(t.renderFiles) > 0 && in(m.Name, rf) == false { + continue + } + data := m.Content + b := filepath.Base(m.Name) + if !t.showNotes && b == "NOTES.txt" { + continue + } + if strings.HasPrefix(b, "_") { + continue + } + fmt.Printf("---\n# Source: %s\n", m.Name) + fmt.Println(data) + } + return nil +} diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go new file mode 100644 index 000000000..b1e080493 --- /dev/null +++ b/cmd/helm/template_test.go @@ -0,0 +1,160 @@ +/* +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 main + +import ( + "bufio" + "bytes" + "fmt" + "io" + "os" + "path/filepath" + "strings" + "testing" +) + +var chartPath = "./../../pkg/chartutil/testdata/subpop/charts/subchart1" + +func TestTemplateCmd(t *testing.T) { + absChartPath, err := filepath.Abs(chartPath) + if err != nil { + t.Fatal(err) + } + tests := []struct { + name string + desc string + args []string + expectKey string + expectValue string + }{ + { + name: "check_name", + desc: "check for a known name in chart", + args: []string{chartPath}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "protocol: TCP\n name: nginx", + }, + { + name: "check_set_name", + desc: "verify --set values exist", + args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "protocol: TCP\n name: apache", + }, + { + name: "check_execute", + desc: "verify --execute single template", + args: []string{chartPath, "-x", "templates/service.yaml", "--set", "service.name=apache"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "protocol: TCP\n name: apache", + }, + { + name: "check_execute_absolute", + desc: "verify --execute single template", + args: []string{chartPath, "-x", absChartPath + "/" + "templates/service.yaml", "--set", "service.name=apache"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "protocol: TCP\n name: apache", + }, + { + name: "check_namespace", + desc: "verify --namespace", + args: []string{chartPath, "--namespace", "test"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "namespace: \"test\"", + }, + { + name: "check_release_name", + desc: "verify --release exists", + args: []string{chartPath, "--name", "test"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "release-name: \"test\"", + }, + { + name: "check_notes", + desc: "verify --notes shows notes", + args: []string{chartPath, "--notes", "true"}, + expectKey: "subchart1/templates/NOTES.txt", + expectValue: "Sample notes for subchart1", + }, + { + name: "check_values_files", + desc: "verify --values files values exist", + args: []string{chartPath, "--values", chartPath + "/charts/subchartA/values.yaml"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "name: apache", + }, + { + name: "check_name_template", + desc: "verify --name-template result exists", + args: []string{chartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"}, + expectKey: "subchart1/templates/service.yaml", + expectValue: "release-name: \"foobar-YWJj-baz\"", + }, + } + + var buf bytes.Buffer + for _, tt := range tests { + t.Run(tt.name, func(T *testing.T) { + // capture stdout + old := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + // execute template command + out := bytes.NewBuffer(nil) + cmd := newTemplateCmd(out) + cmd.SetArgs(tt.args) + err := cmd.Execute() + if err != nil { + t.Errorf("expected: %v, got %v", tt.expectValue, err) + } + // restore stdout + w.Close() + os.Stdout = old + var b bytes.Buffer + io.Copy(&b, r) + r.Close() + // scan yaml into map[]yaml + scanner := bufio.NewScanner(&b) + next := false + lastKey := "" + m := map[string]string{} + for scanner.Scan() { + if scanner.Text() == "---" { + next = true + } else if next { + // remove '# Source: ' + head := "# Source: " + lastKey = scanner.Text()[len(head):] + next = false + } else { + m[lastKey] = m[lastKey] + scanner.Text() + "\n" + } + } + if err := scanner.Err(); err != nil { + fmt.Fprintln(os.Stderr, "reading standard input:", err) + } + if v, ok := m[tt.expectKey]; ok { + if strings.Contains(v, tt.expectValue) == false { + t.Errorf("failed to match expected value %s in %s", tt.expectValue, v) + } + } else { + t.Errorf("could not find key %s", tt.expectKey) + } + buf.Reset() + }) + } +} diff --git a/docs/helm/helm.md b/docs/helm/helm.md index 2b15a3b12..d1749718a 100644 --- a/docs/helm/helm.md +++ b/docs/helm/helm.md @@ -61,9 +61,10 @@ Environment: * [helm search](helm_search.md) - search for a keyword in charts * [helm serve](helm_serve.md) - start a local http web server * [helm status](helm_status.md) - displays the status of the named release +* [helm template](helm_template.md) - locally render templates * [helm test](helm_test.md) - test a release * [helm upgrade](helm_upgrade.md) - upgrade a release * [helm verify](helm_verify.md) - verify that a chart at the given path has been signed and is valid * [helm version](helm_version.md) - print the client/server version information -###### Auto generated by spf13/cobra on 23-Jun-2017 +###### Auto generated by spf13/cobra on 8-Aug-2017 diff --git a/docs/helm/helm_template.md b/docs/helm/helm_template.md new file mode 100644 index 000000000..80ecdec39 --- /dev/null +++ b/docs/helm/helm_template.md @@ -0,0 +1,50 @@ +## helm template + +locally render templates + +### Synopsis + + + +Render chart templates locally and display the output. + +This does not require Tiller. However, any values that would normally be +looked up or retrieved in-cluster will be faked locally. Additionally, none +of the server-side testing of chart validity (e.g. whether an API is supported) +is done. + +To render just one template in a chart, use '-x': + + $ helm template mychart -x templates/deployment.yaml + + +``` +helm template [flags] CHART +``` + +### Options + +``` + -x, --execute stringArray only execute the given templates + -n, --name string release name (default "RELEASE-NAME") + --name-template string specify template used to name the release + --namespace string namespace to install the release into + --notes show the computed NOTES.txt file as well + --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) + -f, --values valueFiles specify values in a YAML file (can specify multiple) (default []) +``` + +### Options inherited from parent commands + +``` + --debug enable verbose output + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") + --host string address of Tiller. Overrides $HELM_HOST + --kube-context string name of the kubeconfig context to use + --tiller-namespace string namespace of Tiller (default "kube-system") +``` + +### SEE ALSO +* [helm](helm.md) - The Helm package manager for Kubernetes. + +###### Auto generated by spf13/cobra on 24-Aug-2017 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 712b3a2fa..f0381ae6a 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/charts/subchartA/values.yaml @@ -3,7 +3,7 @@ # Declare variables to be passed into your templates. # subchartA service: - name: nginx + name: apache type: ClusterIP externalPort: 80 internalPort: 80 diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt new file mode 100644 index 000000000..4bdf443f6 --- /dev/null +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/NOTES.txt @@ -0,0 +1 @@ +Sample notes for {{ .Chart.Name }} \ No newline at end of file diff --git a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml index fdf75aa91..bf7672e12 100644 --- a/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml +++ b/pkg/chartutil/testdata/subpop/charts/subchart1/templates/service.yaml @@ -4,6 +4,8 @@ metadata: name: {{ .Chart.Name }} labels: chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" + namespace: "{{ .Release.Namespace }}" + release-name: "{{ .Release.Name }}" spec: type: {{ .Values.service.type }} ports: diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 8d72f95cf..d87bb8caa 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -50,16 +50,16 @@ var deletePolices = map[string]release.Hook_DeletePolicy{ hooks.HookFailed: release.Hook_FAILED, } -// manifest represents a manifest file, which has a name and some content. -type manifest struct { - name string - content string - head *util.SimpleHead +// Manifest represents a manifest file, which has a name and some content. +type Manifest struct { + Name string + Content string + Head *util.SimpleHead } type result struct { hooks []*release.Hook - generic []manifest + generic []Manifest } type manifestFile struct { @@ -77,7 +77,7 @@ type manifestFile struct { // // Files that do not parse into the expected format are simply placed into a map and // returned. -func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []manifest, error) { +func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []Manifest, error) { result := &result{} for filePath, c := range files { @@ -141,20 +141,20 @@ func (file *manifestFile) sort(result *result) error { } if !hasAnyAnnotation(entry) { - result.generic = append(result.generic, manifest{ - name: file.path, - content: m, - head: &entry, + result.generic = append(result.generic, Manifest{ + Name: file.path, + Content: m, + Head: &entry, }) continue } hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno] if !ok { - result.generic = append(result.generic, manifest{ - name: file.path, - content: m, - head: &entry, + result.generic = append(result.generic, Manifest{ + Name: file.path, + Content: m, + Head: &entry, }) continue } diff --git a/pkg/tiller/hooks_test.go b/pkg/tiller/hooks_test.go index 7a7d2a2b5..658f859f4 100644 --- a/pkg/tiller/hooks_test.go +++ b/pkg/tiller/hooks_test.go @@ -194,7 +194,7 @@ metadata: } // Verify the sort order - sorted := []manifest{} + sorted := []Manifest{} for _, s := range data { manifests := util.SplitManifests(s.manifest) @@ -211,10 +211,10 @@ metadata: //only keep track of non-hook manifests if err == nil && s.hooks[name] == nil { - another := manifest{ - content: m, - name: name, - head: &sh, + another := Manifest{ + Content: m, + Name: name, + Head: &sh, } sorted = append(sorted, another) } @@ -223,8 +223,8 @@ metadata: sorted = sortByKind(sorted, InstallOrder) for i, m := range generic { - if m.content != sorted[i].content { - t.Errorf("Expected %q, got %q", m.content, sorted[i].content) + if m.Content != sorted[i].Content { + t.Errorf("Expected %q, got %q", m.Content, sorted[i].Content) } } } diff --git a/pkg/tiller/kind_sorter.go b/pkg/tiller/kind_sorter.go index e0549e2f4..f2447143b 100644 --- a/pkg/tiller/kind_sorter.go +++ b/pkg/tiller/kind_sorter.go @@ -84,7 +84,7 @@ var UninstallOrder SortOrder = []string{ // sortByKind does an in-place sort of manifests by Kind. // // Results are sorted by 'ordering' -func sortByKind(manifests []manifest, ordering SortOrder) []manifest { +func sortByKind(manifests []Manifest, ordering SortOrder) []Manifest { ks := newKindSorter(manifests, ordering) sort.Sort(ks) return ks.manifests @@ -92,10 +92,10 @@ func sortByKind(manifests []manifest, ordering SortOrder) []manifest { type kindSorter struct { ordering map[string]int - manifests []manifest + manifests []Manifest } -func newKindSorter(m []manifest, s SortOrder) *kindSorter { +func newKindSorter(m []Manifest, s SortOrder) *kindSorter { o := make(map[string]int, len(s)) for v, k := range s { o[k] = v @@ -114,11 +114,11 @@ func (k *kindSorter) Swap(i, j int) { k.manifests[i], k.manifests[j] = k.manifes func (k *kindSorter) Less(i, j int) bool { a := k.manifests[i] b := k.manifests[j] - first, aok := k.ordering[a.head.Kind] - second, bok := k.ordering[b.head.Kind] + first, aok := k.ordering[a.Head.Kind] + second, bok := k.ordering[b.Head.Kind] if first == second { // same kind (including unknown) so sub sort alphanumeric - return a.name < b.name + return a.Name < b.Name } // unknown kind is last if !aok { @@ -130,3 +130,11 @@ func (k *kindSorter) Less(i, j int) bool { // sort different kinds return first < second } + +// SortByKind sorts manifests in InstallOrder +func SortByKind(manifests []Manifest) []Manifest { + ordering := InstallOrder + ks := newKindSorter(manifests, ordering) + sort.Sort(ks) + return ks.manifests +} diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index a707176b3..f0ee66d9c 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -24,103 +24,103 @@ import ( ) func TestKindSorter(t *testing.T) { - manifests := []manifest{ + manifests := []Manifest{ { - name: "i", - head: &util.SimpleHead{Kind: "ClusterRole"}, + Name: "i", + Head: &util.SimpleHead{Kind: "ClusterRole"}, }, { - name: "j", - head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, + Name: "j", + Head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, }, { - name: "e", - head: &util.SimpleHead{Kind: "ConfigMap"}, + Name: "e", + Head: &util.SimpleHead{Kind: "ConfigMap"}, }, { - name: "u", - head: &util.SimpleHead{Kind: "CronJob"}, + Name: "u", + Head: &util.SimpleHead{Kind: "CronJob"}, }, { - name: "n", - head: &util.SimpleHead{Kind: "DaemonSet"}, + Name: "n", + Head: &util.SimpleHead{Kind: "DaemonSet"}, }, { - name: "r", - head: &util.SimpleHead{Kind: "Deployment"}, + Name: "r", + Head: &util.SimpleHead{Kind: "Deployment"}, }, { - name: "!", - head: &util.SimpleHead{Kind: "HonkyTonkSet"}, + Name: "!", + Head: &util.SimpleHead{Kind: "HonkyTonkSet"}, }, { - name: "v", - head: &util.SimpleHead{Kind: "Ingress"}, + Name: "v", + Head: &util.SimpleHead{Kind: "Ingress"}, }, { - name: "t", - head: &util.SimpleHead{Kind: "Job"}, + Name: "t", + Head: &util.SimpleHead{Kind: "Job"}, }, { - name: "c", - head: &util.SimpleHead{Kind: "LimitRange"}, + Name: "c", + Head: &util.SimpleHead{Kind: "LimitRange"}, }, { - name: "a", - head: &util.SimpleHead{Kind: "Namespace"}, + Name: "a", + Head: &util.SimpleHead{Kind: "Namespace"}, }, { - name: "f", - head: &util.SimpleHead{Kind: "PersistentVolume"}, + Name: "f", + Head: &util.SimpleHead{Kind: "PersistentVolume"}, }, { - name: "g", - head: &util.SimpleHead{Kind: "PersistentVolumeClaim"}, + Name: "g", + Head: &util.SimpleHead{Kind: "PersistentVolumeClaim"}, }, { - name: "o", - head: &util.SimpleHead{Kind: "Pod"}, + Name: "o", + Head: &util.SimpleHead{Kind: "Pod"}, }, { - name: "q", - head: &util.SimpleHead{Kind: "ReplicaSet"}, + Name: "q", + Head: &util.SimpleHead{Kind: "ReplicaSet"}, }, { - name: "p", - head: &util.SimpleHead{Kind: "ReplicationController"}, + Name: "p", + Head: &util.SimpleHead{Kind: "ReplicationController"}, }, { - name: "b", - head: &util.SimpleHead{Kind: "ResourceQuota"}, + Name: "b", + Head: &util.SimpleHead{Kind: "ResourceQuota"}, }, { - name: "k", - head: &util.SimpleHead{Kind: "Role"}, + Name: "k", + Head: &util.SimpleHead{Kind: "Role"}, }, { - name: "l", - head: &util.SimpleHead{Kind: "RoleBinding"}, + Name: "l", + Head: &util.SimpleHead{Kind: "RoleBinding"}, }, { - name: "d", - head: &util.SimpleHead{Kind: "Secret"}, + Name: "d", + Head: &util.SimpleHead{Kind: "Secret"}, }, { - name: "m", - head: &util.SimpleHead{Kind: "Service"}, + Name: "m", + Head: &util.SimpleHead{Kind: "Service"}, }, { - name: "h", - head: &util.SimpleHead{Kind: "ServiceAccount"}, + Name: "h", + Head: &util.SimpleHead{Kind: "ServiceAccount"}, }, { - name: "s", - head: &util.SimpleHead{Kind: "StatefulSet"}, + Name: "s", + Head: &util.SimpleHead{Kind: "StatefulSet"}, }, { - name: "w", - content: "", - head: &util.SimpleHead{Kind: "APIService"}, + Name: "w", + Content: "", + Head: &util.SimpleHead{Kind: "APIService"}, }, } @@ -139,7 +139,7 @@ func TestKindSorter(t *testing.T) { } defer buf.Reset() for _, r := range sortByKind(manifests, test.order) { - buf.WriteString(r.name) + buf.WriteString(r.Name) } if got := buf.String(); got != test.expected { t.Errorf("Expected %q, got %q", test.expected, got) @@ -150,42 +150,42 @@ func TestKindSorter(t *testing.T) { // TestKindSorterSubSort verifies manifests of same kind are also sorted alphanumeric func TestKindSorterSubSort(t *testing.T) { - manifests := []manifest{ + manifests := []Manifest{ { - name: "a", - head: &util.SimpleHead{Kind: "ClusterRole"}, + Name: "a", + Head: &util.SimpleHead{Kind: "ClusterRole"}, }, { - name: "A", - head: &util.SimpleHead{Kind: "ClusterRole"}, + Name: "A", + Head: &util.SimpleHead{Kind: "ClusterRole"}, }, { - name: "0", - head: &util.SimpleHead{Kind: "ConfigMap"}, + Name: "0", + Head: &util.SimpleHead{Kind: "ConfigMap"}, }, { - name: "1", - head: &util.SimpleHead{Kind: "ConfigMap"}, + Name: "1", + Head: &util.SimpleHead{Kind: "ConfigMap"}, }, { - name: "z", - head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, + Name: "z", + Head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, }, { - name: "!", - head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, + Name: "!", + Head: &util.SimpleHead{Kind: "ClusterRoleBinding"}, }, { - name: "u3", - head: &util.SimpleHead{Kind: "Unknown"}, + Name: "u3", + Head: &util.SimpleHead{Kind: "Unknown"}, }, { - name: "u1", - head: &util.SimpleHead{Kind: "Unknown"}, + Name: "u1", + Head: &util.SimpleHead{Kind: "Unknown"}, }, { - name: "u2", - head: &util.SimpleHead{Kind: "Unknown"}, + Name: "u2", + Head: &util.SimpleHead{Kind: "Unknown"}, }, } for _, test := range []struct { @@ -200,7 +200,7 @@ func TestKindSorterSubSort(t *testing.T) { t.Run(test.description, func(t *testing.T) { defer buf.Reset() for _, r := range sortByKind(manifests, test.order) { - buf.WriteString(r.name) + buf.WriteString(r.Name) } if got := buf.String(); got != test.expected { t.Errorf("Expected %q, got %q", test.expected, got) diff --git a/pkg/tiller/release_modules.go b/pkg/tiller/release_modules.go index 73b95e60c..b5fbbeb44 100644 --- a/pkg/tiller/release_modules.go +++ b/pkg/tiller/release_modules.go @@ -166,7 +166,7 @@ func DeleteRelease(rel *release.Release, vs chartutil.VersionSet, kubeClient env errs = []error{} for _, file := range filesToDelete { - b := bytes.NewBufferString(strings.TrimSpace(file.content)) + b := bytes.NewBufferString(strings.TrimSpace(file.Content)) if b.Len() == 0 { continue } diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index bab405e1f..44d5d847a 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -300,8 +300,8 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values // Aggregate all valid manifests into one big doc. b := bytes.NewBuffer(nil) for _, m := range manifests { - b.WriteString("\n---\n# Source: " + m.name + "\n") - b.WriteString(m.content) + b.WriteString("\n---\n# Source: " + m.Name + "\n") + b.WriteString(m.Content) } return hooks, b, notes, nil diff --git a/pkg/tiller/resource_policy.go b/pkg/tiller/resource_policy.go index 89f303197..2102ab66b 100644 --- a/pkg/tiller/resource_policy.go +++ b/pkg/tiller/resource_policy.go @@ -29,18 +29,18 @@ const resourcePolicyAnno = "helm.sh/resource-policy" // during an uninstallRelease action. const keepPolicy = "keep" -func filterManifestsToKeep(manifests []manifest) ([]manifest, []manifest) { - remaining := []manifest{} - keep := []manifest{} +func filterManifestsToKeep(manifests []Manifest) ([]Manifest, []Manifest) { + remaining := []Manifest{} + keep := []Manifest{} for _, m := range manifests { - if m.head.Metadata == nil || m.head.Metadata.Annotations == nil || len(m.head.Metadata.Annotations) == 0 { + if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 { remaining = append(remaining, m) continue } - resourcePolicyType, ok := m.head.Metadata.Annotations[resourcePolicyAnno] + resourcePolicyType, ok := m.Head.Metadata.Annotations[resourcePolicyAnno] if !ok { remaining = append(remaining, m) continue @@ -55,10 +55,10 @@ func filterManifestsToKeep(manifests []manifest) ([]manifest, []manifest) { return keep, remaining } -func summarizeKeptManifests(manifests []manifest) string { +func summarizeKeptManifests(manifests []Manifest) string { message := "These resources were kept due to the resource policy:\n" for _, m := range manifests { - details := "[" + m.head.Kind + "] " + m.head.Metadata.Name + "\n" + details := "[" + m.Head.Kind + "] " + m.Head.Metadata.Name + "\n" message = message + details } return message From 46ce97c1b0a6fc0562a374e960a0d9570e13ca9f Mon Sep 17 00:00:00 2001 From: Justin Scott Date: Fri, 25 Aug 2017 12:43:44 -0700 Subject: [PATCH 09/25] feat(helm): use openssl sha1 command Currently the bash script that installs Helm is hard-coded to use openssl sha command but some distros like Debian 9 only have sha1. Since the popular current distros have sha1 we can switch to that. Closes(#2859) --- scripts/get | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/get b/scripts/get index 8b4a83a2e..4ebc892af 100755 --- a/scripts/get +++ b/scripts/get @@ -130,7 +130,7 @@ downloadFile() { # installs it. installFile() { HELM_TMP="/tmp/$PROJECT_NAME" - local sum=$(openssl sha -sha256 ${HELM_TMP_FILE} | awk '{print $2}') + local sum=$(openssl sha1 -sha256 ${HELM_TMP_FILE} | awk '{print $2}') local expected_sum=$(cat ${HELM_SUM_FILE}) if [ "$sum" != "$expected_sum" ]; then echo "SHA sum of $HELM_TMP does not match. Aborting." From 608fd7318a1637de7ee0e161906861e8e2184b30 Mon Sep 17 00:00:00 2001 From: DoctorZK Date: Wed, 30 Aug 2017 15:01:54 +0800 Subject: [PATCH 10/25] fix(tiller): fix a warning bug in hook annotation --- pkg/tiller/hooks.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index 8d72f95cf..741913f22 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -171,17 +171,18 @@ func (file *manifestFile) sort(result *result) error { DeletePolicies: []release.Hook_DeletePolicy{}, } - isKnownHook := false + isUnknownHook := false for _, hookType := range strings.Split(hookTypes, ",") { hookType = strings.ToLower(strings.TrimSpace(hookType)) e, ok := events[hookType] - if ok { - isKnownHook = true - h.Events = append(h.Events, e) + if !ok { + isUnknownHook = true + break } + h.Events = append(h.Events, e) } - if !isKnownHook { + if isUnknownHook { log.Printf("info: skipping unknown hook: %q", hookTypes) continue } From a1f536cb2b9d09d3011127c7eadb3a586e9b6ae3 Mon Sep 17 00:00:00 2001 From: kfirstri Date: Wed, 30 Aug 2017 17:15:31 +0300 Subject: [PATCH 11/25] right syntax is helm.sh/hook-delete-policy according to commit 734b1245772ff4e8319e3648646e1c0f1d797d87 --- docs/charts_hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index dec3e552e..92c50e20c 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -122,7 +122,7 @@ metadata: # job is considered part of the release. "helm.sh/hook": post-install "helm.sh/hook-weight": "-5" - "helm/hook-delete-policy": hook-succeeded + "helm.sh/hook-delete-policy": hook-succeeded spec: template: metadata: From 58e8e3b756aea0916a73ba9300704230de23fc7a Mon Sep 17 00:00:00 2001 From: Scott Rigby Date: Fri, 1 Sep 2017 18:02:52 -0400 Subject: [PATCH 12/25] Fixes #2888. Clarifies that use of default command should stay DRY --- docs/chart_template_guide/functions_and_pipelines.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/chart_template_guide/functions_and_pipelines.md b/docs/chart_template_guide/functions_and_pipelines.md index 7eb1ca26c..54eb8e24f 100644 --- a/docs/chart_template_guide/functions_and_pipelines.md +++ b/docs/chart_template_guide/functions_and_pipelines.md @@ -138,7 +138,13 @@ data: food: "PIZZA" ``` -It is considered good (almost mandatory) practice to set defaults with `default` for any object that originates from `.Values`. (In some places, an `if` conditional guard may be better suited. We'll see those in the next section.) +In an actual chart, all static default values should live in the values.yaml, and should not be repeated using the `default` command (otherwise they would be redundant). However, the `default` command is perfect for computed values, which can not be declared inside values.yaml. For example: + +```yaml +drink: {{ .Values.favorite.drink | default (printf "%s-tea" (include "fullname" .)) }} +``` + +In some places, an `if` conditional guard may be better suited than `default`. We'll see those in the next section. Template functions and pipelines are a powerful way to transform information and then insert it into your YAML. But sometimes it's necessary to add some template logic that is a little more sophisticated than just inserting a string. In the next section we will look at the control structures provided by the template language. From 56af57ea94c1d74f55a64c310a81b759f010569a Mon Sep 17 00:00:00 2001 From: ReSearchITEng Date: Sat, 2 Sep 2017 17:09:00 +0300 Subject: [PATCH 13/25] easy copy/paste for those living on the edge easy copy/paste for those living on the edge :) --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index ed0bd4ead..3691d56b2 100755 --- a/docs/install.md +++ b/docs/install.md @@ -48,7 +48,7 @@ $ chmod 700 get_helm.sh $ ./get_helm.sh ``` -Yes, you can `curl ...| bash` that if you want to live on the edge. +Yes, you can `curl https://raw.githubusercontent.com/kubernetes/helm/master/scripts/get | bash` that if you want to live on the edge. ### From Canary Builds From 6258730d65c9c55aeda8b5b57e822eb67015248c Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Tue, 5 Sep 2017 10:32:56 -0700 Subject: [PATCH 14/25] add section on semantic versioning This documentation was ripped from the Helm v2.2.0 release notes. I've referenced it enough times that it should be placed somewhere in the documentation. --- CONTRIBUTING.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7005d4f4b..c98d49a05 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -36,6 +36,7 @@ Whether you are a user or contributor, official support channels include: Before opening a new issue or submitting a new pull request, it's helpful to search the project - it's likely that another user has already reported the issue you're facing, or it's a known issue that we're already aware of. ## Milestones + We use milestones to track progress of releases. There are also 2 special milestones used for helping us keep work organized: `Upcoming - Minor` and `Upcoming - Major` @@ -52,10 +53,27 @@ An issue that we are not sure we will be doing will not be added to any mileston A milestone (and hence release) is considered done when all outstanding issues/PRs have been closed or moved to another milestone. +## Semver + +Helm maintains a strong commitment to backward compatibility. All of our changes to protocols and formats are backward compatible from Helm 2.0 until Helm 3.0. No features, flags, or commands are removed or substantially modified (other than bug fixes). + +We also try very hard to not change publicly accessible Go library definitions inside of the `pkg/` directory of our source code. + +For a quick summary of our backward compatibility guidelines for releases between 2.0 and 3.0: + +- Protobuf and gRPC changes MUST be backward compatible. +- Command line commands, flags, and arguments MUST be backward compatible +- File formats (such as Chart.yaml, repositories.yaml, and requirements.yaml) MUST be backward compatible +- Any chart that worked on a previous version of Helm MUST work on a new version of Helm (barring the cases where (a) Kubernetes itself changed, and (b) the chart worked because it exploited a bug) +- Chart repository functionality MUST be backward compatible +- Go libraries inside of `pkg/` SHOULD remain backward compatible (though code inside of `cmd/` may be changed from release to release without notice). + ## Issues + Issues are used as the primary method for tracking anything to do with the Helm project. ### Issue Types + There are 4 types of issues (each with their own corresponding [label](#labels)): - Question: These are support or functionality inquiries that we want to have a record of for future reference. Generally these are questions that are too complex or large to store in the @@ -72,6 +90,7 @@ from a "Proposal" or can be submitted individually depending on the size. - Bugs: These track bugs with the code or problems with the documentation (i.e. missing or incomplete) ### Issue Lifecycle + The issue lifecycle is mainly driven by the core maintainers, but is good information for those contributing to Helm. All issue types follow the same general lifecycle. Differences are noted below. 1. Issue creation @@ -107,9 +126,11 @@ https://github.com/kubernetes/helm/blob/master/docs/developers.md The next section contains more information on the workflow followed for PRs ## Pull Requests + Like any good open source project, we use Pull Requests to track code changes ### PR Lifecycle + 1. PR creation - We more than welcome PRs that are currently in progress. They are a great way to keep track of important work that is in-flight, but useful for others to see. If a PR is a work in progress, @@ -149,19 +170,23 @@ Like any good open source project, we use Pull Requests to track code changes merge the PR once it is approved. #### Documentation PRs + Documentation PRs will follow the same lifecycle as other PRs. They will also be labeled with the `docs` label. For documentation, special attention will be paid to spelling, grammar, and clarity (whereas those things don't matter *as* much for comments in code). ## The Triager + Each week, one of the core maintainers will serve as the designated "triager" starting after the public standup meetings on Thursday. This person will be in charge triaging new PRs and issues throughout the work week. ## Labels + The following tables define all label types used for Helm. It is split up by category. ### Common + | Label | Description | | ----- | ----------- | | `bug` | Marks an issue as a bug or a PR as a bugfix | @@ -173,6 +198,7 @@ The following tables define all label types used for Helm. It is split up by cat | `refactor` | Indicates that the issue is a code refactor and is not fixing a bug or adding additional functionality | ### Issue Specific + | Label | Description | | ----- | ----------- | | `help wanted` | This issue is one the core maintainers cannot get to right now and would appreciate help with | @@ -182,6 +208,7 @@ The following tables define all label types used for Helm. It is split up by cat | `wont fix` | The issue has been discussed and will not be implemented (or accepted in the case of a proposal) | ### PR Specific + | Label | Description | | ----- | ----------- | | `awaiting review` | The PR has been triaged and is ready for someone to review | @@ -194,6 +221,7 @@ The following tables define all label types used for Helm. It is split up by cat | `picked` | This PR has been picked into a feature branch | #### Size labels + Size labels are used to indicate how "dangerous" a PR is. The guidelines below are used to assign the labels, but ultimately this can be changed by the maintainers. For example, even if a PR only makes 30 lines of changes in 1 file, but it changes key functionality, it will likely be labeled as `size/large` From 8b512808df5f5f25a433d9d4e689309ef3ad6ec9 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Wed, 6 Sep 2017 10:01:38 -0700 Subject: [PATCH 15/25] bump k8s version in helm lint to 1.7 --- pkg/lint/rules/template.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 73206b80c..3ccd9acdb 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -58,7 +58,7 @@ func Templates(linter *support.Linter) { APIVersions: chartutil.DefaultVersionSet, KubeVersion: &version.Info{ Major: "1", - Minor: "6", + Minor: "7", GoVersion: runtime.Version(), Compiler: runtime.Compiler, Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), From f588315b7628a137d329be2ed10708844c839628 Mon Sep 17 00:00:00 2001 From: "Steven E. Harris" Date: Wed, 6 Sep 2017 14:36:25 -0400 Subject: [PATCH 16/25] Remove spurious field in kind sorter test (#2667) The test never reads the manifest type's "content" field, so there's no reason to populate it explicitly with its zero value. --- pkg/tiller/kind_sorter_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/tiller/kind_sorter_test.go b/pkg/tiller/kind_sorter_test.go index f0ee66d9c..6996731ca 100644 --- a/pkg/tiller/kind_sorter_test.go +++ b/pkg/tiller/kind_sorter_test.go @@ -118,9 +118,8 @@ func TestKindSorter(t *testing.T) { Head: &util.SimpleHead{Kind: "StatefulSet"}, }, { - Name: "w", - Content: "", - Head: &util.SimpleHead{Kind: "APIService"}, + Name: "w", + Head: &util.SimpleHead{Kind: "APIService"}, }, } From 8fd8a7c324b4c34235bcaa1b55594119a9527df8 Mon Sep 17 00:00:00 2001 From: Adnan Abdulhussein Date: Thu, 7 Sep 2017 10:43:53 +0100 Subject: [PATCH 17/25] fix(create): port-forward should use internal port cc @james-w --- pkg/chartutil/create.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 317737b2e..fe06e14e0 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -221,7 +221,7 @@ const defaultNotes = `1. Get the application URL by running these commands: {{- else if contains "ClusterIP" .Values.service.type }} export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app={{ template "name" . }},release={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") echo "Visit http://127.0.0.1:8080 to use your application" - kubectl port-forward $POD_NAME 8080:{{ .Values.service.externalPort }} + kubectl port-forward $POD_NAME 8080:{{ .Values.service.internalPort }} {{- end }} ` From 415e52bf555f28d0262f9d75ce8a38982c23f714 Mon Sep 17 00:00:00 2001 From: Amanda Cameron Date: Sun, 19 Feb 2017 16:19:38 -0500 Subject: [PATCH 18/25] Add support for sub-directory scanning as in issue #1401 --- pkg/chartutil/save.go | 8 ++++ pkg/repo/chartrepo_test.go | 13 +++++- pkg/repo/index.go | 21 +++++++++- pkg/repo/index_test.go | 38 +++++++++++------- .../repository/universe/zarthal-1.0.0.tgz | Bin 0 -> 1121 bytes 5 files changed, 61 insertions(+), 19 deletions(-) create mode 100644 pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz diff --git a/pkg/chartutil/save.go b/pkg/chartutil/save.go index e3b5c1afd..b89917d96 100644 --- a/pkg/chartutil/save.go +++ b/pkg/chartutil/save.go @@ -115,6 +115,14 @@ func Save(c *chart.Chart, outDir string) (string, error) { filename := fmt.Sprintf("%s-%s.tgz", cfile.Name, cfile.Version) filename = filepath.Join(outDir, filename) + if stat, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) { + if err := os.MkdirAll(filepath.Dir(filename), 0755); !os.IsExist(err) { + return "", err + } + } else if !stat.IsDir() { + return "", fmt.Errorf("is not a directory: %s", filepath.Dir(filename)) + } + f, err := os.Create(filename) if err != nil { return "", err diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index f6d6df74e..9f1bc995a 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -54,6 +54,7 @@ func TestLoadChartRepository(t *testing.T) { filepath.Join(testRepository, "frobnitz-1.2.3.tgz"), filepath.Join(testRepository, "sprocket-1.1.0.tgz"), filepath.Join(testRepository, "sprocket-1.2.0.tgz"), + filepath.Join(testRepository, "universe/zarthal-1.0.0.tgz"), } if r.Config.Name != testRepository { @@ -118,8 +119,8 @@ func verifyIndex(t *testing.T, actual *IndexFile) { } entries := actual.Entries - if numEntries := len(entries); numEntries != 2 { - t.Errorf("Expected 2 charts to be listed in index file but got %v", numEntries) + if numEntries := len(entries); numEntries != 3 { + t.Errorf("Expected 3 charts to be listed in index file but got %v", numEntries) } expects := map[string]ChartVersions{ @@ -145,6 +146,14 @@ func verifyIndex(t *testing.T, actual *IndexFile) { }, }, }, + "zarthal": { + { + Metadata: &chart.Metadata{ + Name: "zarthal", + Version: "1.0.0", + }, + }, + }, } for name, versions := range expects { diff --git a/pkg/repo/index.go b/pkg/repo/index.go index 4e59b8d58..174ceea01 100644 --- a/pkg/repo/index.go +++ b/pkg/repo/index.go @@ -231,9 +231,26 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) { if err != nil { return nil, err } + moreArchives, err := filepath.Glob(filepath.Join(dir, "**/*.tgz")) + if err != nil { + return nil, err + } + archives = append(archives, moreArchives...) + index := NewIndexFile() for _, arch := range archives { - fname := filepath.Base(arch) + fname, err := filepath.Rel(dir, arch) + if err != nil { + return index, err + } + + var parentDir string + parentDir, fname = filepath.Split(fname) + parentURL, err := urlutil.URLJoin(baseURL, parentDir) + if err != nil { + parentURL = filepath.Join(baseURL, parentDir) + } + c, err := chartutil.Load(arch) if err != nil { // Assume this is not a chart. @@ -243,7 +260,7 @@ func IndexDirectory(dir, baseURL string) (*IndexFile, error) { if err != nil { return index, err } - index.Add(c.Metadata, fname, baseURL, hash) + index.Add(c.Metadata, fname, parentURL, hash) } return index, nil } diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index 17a1cc209..ba426b174 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -278,27 +278,35 @@ func TestIndexDirectory(t *testing.T) { t.Fatal(err) } - if l := len(index.Entries); l != 2 { - t.Fatalf("Expected 2 entries, got %d", l) + if l := len(index.Entries); l != 3 { + t.Fatalf("Expected 3 entries, got %d", l) } // Other things test the entry generation more thoroughly. We just test a // few fields. - cname := "frobnitz" - frobs, ok := index.Entries[cname] - if !ok { - t.Fatalf("Could not read chart %s", cname) - } - frob := frobs[0] - if len(frob.Digest) == 0 { - t.Errorf("Missing digest of file %s.", frob.Name) + corpus := []struct{ chartName, downloadLink string }{ + {"frobnitz", "http://localhost:8080/frobnitz-1.2.3.tgz"}, + {"zarthal", "http://localhost:8080/universe/zarthal-1.0.0.tgz"}, } - if frob.URLs[0] != "http://localhost:8080/frobnitz-1.2.3.tgz" { - t.Errorf("Unexpected URLs: %v", frob.URLs) - } - if frob.Name != "frobnitz" { - t.Errorf("Expected frobnitz, got %q", frob.Name) + + for _, test := range corpus { + cname := test.chartName + frobs, ok := index.Entries[cname] + if !ok { + t.Fatalf("Could not read chart %s", cname) + } + + frob := frobs[0] + if len(frob.Digest) == 0 { + t.Errorf("Missing digest of file %s.", frob.Name) + } + if frob.URLs[0] != test.downloadLink { + t.Errorf("Unexpected URLs: %v", frob.URLs) + } + if frob.Name != cname { + t.Errorf("Expected %q, got %q", cname, frob.Name) + } } } diff --git a/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz b/pkg/repo/testdata/repository/universe/zarthal-1.0.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..90cb34bd5f36439ee2e986c21c766b55a4442ec8 GIT binary patch literal 1121 zcmV-n1fKgJiwFR8Z?Rbb1MQs8ZsSB8$7fgK5{X>|Jiw41SM|TK<0R5btPr9cKu8q0 z>}6t48mo3DvYr0fW#I)la^c9GJ7?a27vK#zz#H&$5+{w*B)c?qx9#_%{xr77o*CEq z{h6`#Gt95#*@2RwN;AXmIBj9RkZC@UG20my(Q@5MNmNVA78bLG?YU0N7oNu~+Yz46 z_}T7;u%2<2%an#vM_+Ft!s1CDCF-f|S#I01+>S6hZQpkt+jSaFhs%q#o@E=t>-etS zc8khjwbGkb-t>Ke*JGya=5_W_=(u0#HL1GqP4(Vh*n^2JSk`|bT$^$4ItNu!>c33n zFqFZD^TdeL`(Fo+Rk84* z(;%M^RE`-|i+qCRbObE>oubtL9jf~Z+w8yas`~Fatrp|8=;S@O|Dno)G#cfR9(MWR zAj&xXNk02oGzL0RT|UV3QP(upnM_8pG6J16j@9|8PQ$F**ysIJ_l8maf;P+R_Iap7 z88#9b4RaFu-xX&n+K;I{6(=8GY>o4wEIK+ID1Jbd{7}hcasE*Eba;O06<^Z;^-%Fo z<4iTChsrUWP^XjIvJK0yV7D%1t^Yrf@mOU$u?SoJzrP#{7RJHG{I3>7#s4qQ|EiI| za_voz9bBDK|97bFD{S-sw(u+Z@3|iQe=Y3sXMFnXc|VF35A?)eXT?T8IvS_42n~#x zYi$ZIz_gjvI`ClCzs5sXAxh2$ra z6O!j7pOHKv*&})9@wA=49^K>}0RR9100000xPL6CFI$()jO|YjX3Iy-zSd?>rYzIr zG*Df}*QX<+7FN@dEiS0$JDZ=rl<$mPOqRxG8PupOKXGS^OlxeOevMtoa^@J;x_oYB n8%;Ltc-|%e00000000000000000000fV;terNp1g0C)fZNQq8~ literal 0 HcmV?d00001 From 8e13a5bd1f9302f717343ce09924aabfa300e50c Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 8 Sep 2017 12:23:32 -0700 Subject: [PATCH 19/25] fix(ci): move deploy instructions to build job https://circleci.com/docs/2.0/configuration-reference/#deploy --- .circleci/config.yml | 29 ++++------------------------- {scripts/ci => .circleci}/deploy.sh | 23 ++++++++++++++++++----- scripts/ci.sh => .circleci/test.sh | 0 3 files changed, 22 insertions(+), 30 deletions(-) rename {scripts/ci => .circleci}/deploy.sh (67%) rename scripts/ci.sh => .circleci/test.sh (100%) diff --git a/.circleci/config.yml b/.circleci/config.yml index c32c9f08d..273643b7c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,38 +9,17 @@ jobs: PROJECT_NAME: "kubernetes-helm" steps: - checkout + - setup_remote_docker - run: name: install dependencies command: make bootstrap - - run: - name: go env - command: go env - save_cache: key: vendor-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }} paths: - vendor - run: name: test - command: ./scripts/ci.sh - deployment: - working_directory: /go/src/k8s.io/helm - docker: - - image: golang:1.8 - environment: - PROJECT_NAME: "kubernetes-helm" - filters: - tags: - only: /.*/ - branches: - only: master - steps: - - checkout - - restore_cache: - keys: - - vendor-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }} - - run: - name: go env - command: go env + command: .circleci/test.sh - deploy: - name: deploy - command: ./scripts/ci/deploy.sh + name: deploy + command: .circleci/deploy.sh diff --git a/scripts/ci/deploy.sh b/.circleci/deploy.sh similarity index 67% rename from scripts/ci/deploy.sh rename to .circleci/deploy.sh index 6f8199a18..84bcfa33b 100755 --- a/scripts/ci/deploy.sh +++ b/.circleci/deploy.sh @@ -15,6 +15,11 @@ # limitations under the License. set -euo pipefail +# Skip on pull request builds +if [[ -n "${CIRCLE_PR_NUMBER:-}" ]]; then + exit +fi + : ${GCLOUD_SERVICE_KEY:?"GCLOUD_SERVICE_KEY environment variable is not set"} : ${PROJECT_NAME:?"PROJECT_NAME environment variable is not set"} @@ -27,13 +32,21 @@ else exit 1 fi -echo "Updating gcloud components" -sudo /opt/google-cloud-sdk/bin/gcloud --quiet components update +echo "Install docker client" +VER="17.03.0-ce" +curl -L -o /tmp/docker-$VER.tgz https://get.docker.com/builds/Linux/x86_64/docker-$VER.tgz +tar -xz -C /tmp -f /tmp/docker-$VER.tgz +mv /tmp/docker/* /usr/bin + +echo "Install gcloud components" +export CLOUDSDK_CORE_DISABLE_PROMPTS=1 +curl https://sdk.cloud.google.com | bash +${HOME}/google-cloud-sdk/bin/gcloud --quiet components update echo "Configuring gcloud authentication" echo "${GCLOUD_SERVICE_KEY}" | base64 --decode > "${HOME}/gcloud-service-key.json" -sudo /opt/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file "${HOME}/gcloud-service-key.json" -sudo /opt/google-cloud-sdk/bin/gcloud config set project "${PROJECT_NAME}" +${HOME}/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file "${HOME}/gcloud-service-key.json" +${HOME}/google-cloud-sdk/bin/gcloud config set project "${PROJECT_NAME}" docker login -e 1234@5678.com -u _json_key -p "$(cat ${HOME}/gcloud-service-key.json)" https://gcr.io echo "Building the tiller image" @@ -47,4 +60,4 @@ make build-cross make dist checksum VERSION="${VERSION}" echo "Pushing binaries to gs bucket" -sudo /opt/google-cloud-sdk/bin/gsutil cp ./_dist/* "gs://${PROJECT_NAME}" +${HOME}/google-cloud-sdk/bin/gsutil cp ./_dist/* "gs://${PROJECT_NAME}" diff --git a/scripts/ci.sh b/.circleci/test.sh similarity index 100% rename from scripts/ci.sh rename to .circleci/test.sh From 46598952ea8b2c624ea102c5cb467033eb63c39f Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 8 Sep 2017 13:57:56 -0700 Subject: [PATCH 20/25] fix(ci): lower number of parallel builds circleci chokes with too many processes --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2070fd4d0..737be298c 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ build: .PHONY: build-cross build-cross: LDFLAGS += -extldflags "-static" build-cross: - CGO_ENABLED=0 gox -output="_dist/{{.OS}}-{{.Arch}}/{{.Dir}}" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/$(APP) + CGO_ENABLED=0 gox -parallel=3 -output="_dist/{{.OS}}-{{.Arch}}/{{.Dir}}" -osarch='$(TARGETS)' $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/$(APP) .PHONY: dist dist: From d55782de944d06dddda867be1b5c85a972b35030 Mon Sep 17 00:00:00 2001 From: flyer103 Date: Sat, 9 Sep 2017 05:15:56 +0800 Subject: [PATCH 21/25] cmd/helm,docs/helm: amend default namespace of tiller --- cmd/helm/helm.go | 2 +- docs/helm/helm.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 61513770b..6fe08b07e 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -69,7 +69,7 @@ Environment: $HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm $HELM_HOST set an alternative Tiller host. The format is host:port $HELM_NO_PLUGINS disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. - $TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-namespace") + $TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-system") $KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config") ` diff --git a/docs/helm/helm.md b/docs/helm/helm.md index d1749718a..36e41835e 100644 --- a/docs/helm/helm.md +++ b/docs/helm/helm.md @@ -25,7 +25,7 @@ Environment: $HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm $HELM_HOST set an alternative Tiller host. The format is host:port $HELM_NO_PLUGINS disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins. - $TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-namespace") + $TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-system") $KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config") From 111482519de5421015aaaefecdb399bcff5b08c2 Mon Sep 17 00:00:00 2001 From: Matthew Fisher Date: Mon, 11 Sep 2017 12:22:29 -0700 Subject: [PATCH 22/25] add Windows release to README Also bumps the release version to v2.6.1. --- README.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3d08a41f9..21a009e2c 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,10 @@ Think of it like apt/yum/homebrew for Kubernetes. Binary downloads of the Helm client can be found at the following links: -- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.0-darwin-amd64.tar.gz) -- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.0-linux-amd64.tar.gz) -- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.0-linux-386.tar.gz) +- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-darwin-amd64.tar.gz) +- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-linux-amd64.tar.gz) +- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-linux-386.tar.gz) +- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.6.1-windows-amd64.tar.gz) Unpack the `helm` binary and add it to your PATH and you are good to go! macOS/[homebrew](https://brew.sh/) users can also use `brew install kubernetes-helm`. From 895e8d086ff859ba9fb50f8b72a97e3d934d9472 Mon Sep 17 00:00:00 2001 From: Hui Chen Date: Wed, 6 Sep 2017 16:40:50 +0800 Subject: [PATCH 23/25] fix unbound variable issue when get version --- scripts/get | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/scripts/get b/scripts/get index 4ebc892af..88a6fa1a7 100755 --- a/scripts/get +++ b/scripts/get @@ -191,9 +191,14 @@ export INPUT_ARGUMENTS="${@}" set -u while [[ $# -gt 0 ]]; do case $1 in - '--version'|-v) - export DESIRED_VERSION="${2}" + '--version'|-v) shift + if [[ $# -ne 0 ]]; then + export DESIRED_VERSION="${1}" + else + echo -e "Please provide the desired version. e.g. --version v2.4.0 or -v latest" + exit 0 + fi ;; '--help'|-h) help From 550d350a0405157d061bd2e9e80ec71e350b5fbe Mon Sep 17 00:00:00 2001 From: Sharno Date: Tue, 12 Sep 2017 13:14:23 -0400 Subject: [PATCH 24/25] Adding output for the created repos when `helm init` --- cmd/helm/init.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/cmd/helm/init.go b/cmd/helm/init.go index cc70c45ba..163653692 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -295,11 +295,11 @@ func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) err if fi, err := os.Stat(repoFile); err != nil { fmt.Fprintf(out, "Creating %s \n", repoFile) f := repo.NewRepoFile() - sr, err := initStableRepo(home.CacheIndex(stableRepository), skipRefresh) + sr, err := initStableRepo(home.CacheIndex(stableRepository), out, skipRefresh) if err != nil { return err } - lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local")) + lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local"), out) if err != nil { return err } @@ -314,7 +314,8 @@ func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) err return nil } -func initStableRepo(cacheFile string, skipRefresh bool) (*repo.Entry, error) { +func initStableRepo(cacheFile string, out io.Writer, skipRefresh bool) (*repo.Entry, error) { + fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, stableRepositoryURL) c := repo.Entry{ Name: stableRepository, URL: stableRepositoryURL, @@ -338,8 +339,9 @@ func initStableRepo(cacheFile string, skipRefresh bool) (*repo.Entry, error) { return &c, nil } -func initLocalRepo(indexFile, cacheFile string) (*repo.Entry, error) { +func initLocalRepo(indexFile, cacheFile string, out io.Writer) (*repo.Entry, error) { if fi, err := os.Stat(indexFile); err != nil { + fmt.Fprintf(out, "Adding %s repo with URL: %s \n", localRepository, localRepositoryURL) i := repo.NewIndexFile() if err := i.WriteFile(indexFile, 0644); err != nil { return nil, err From c903a4417fe3b85ad5667f987bd445abd5ac53d6 Mon Sep 17 00:00:00 2001 From: Frederi Mandin <15192504+frederimandin@users.noreply.github.com> Date: Wed, 13 Sep 2017 17:27:41 +0200 Subject: [PATCH 25/25] Corrected a typo in Easy in-cluster installation --- docs/install.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/install.md b/docs/install.md index 3691d56b2..af5d57a7b 100755 --- a/docs/install.md +++ b/docs/install.md @@ -102,7 +102,7 @@ whatever cluster `kubectl` connects to by default (`kubectl config view`). Once it connects, it will install `tiller` into the `kube-system` namespace. -After `helm init`, you should be able to run `kubectl get po --namespace +After `helm init`, you should be able to run `kubectl get pods --namespace kube-system` and see Tiller running. You can explicitly tell `helm init` to...