diff --git a/OWNERS b/OWNERS index 32b26efa2..fcc3606c2 100644 --- a/OWNERS +++ b/OWNERS @@ -1,24 +1,11 @@ maintainers: - - adamreese - - bacongobbler - - jascott1 - - mattfarina - - michelleN - - nebril - - prydonius - - SlickNik - - technosophos - - thomastaylor312 - - viglesiasce -reviewers: - adamreese - bacongobbler - fibonacci1729 + - hickeyma - jascott1 - mattfarina - michelleN - - migmartri - - nebril - prydonius - SlickNik - technosophos @@ -26,5 +13,7 @@ reviewers: - viglesiasce emeritus: - migmartri + - nebril - seh - vaikas-google + - rimusz diff --git a/cmd/helm/install.go b/cmd/helm/install.go index 61ba90084..fefa18c05 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -18,6 +18,7 @@ package main import ( "io" + "time" "helm.sh/helm/pkg/release" @@ -121,7 +122,7 @@ func addInstallFlags(f *pflag.FlagSet, client *action.Install) { f.BoolVar(&client.DryRun, "dry-run", false, "simulate an install") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during install") f.BoolVar(&client.Replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production") - f.DurationVar(&client.Timeout, "timeout", 300, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") + f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") f.BoolVarP(&client.GenerateName, "generate-name", "g", false, "generate the name (and omit the NAME parameter)") f.StringVar(&client.NameTemplate, "name-template", "", "specify template used to name the release") diff --git a/cmd/helm/release_testing_run.go b/cmd/helm/release_testing_run.go index 4a639c75e..9608ba374 100644 --- a/cmd/helm/release_testing_run.go +++ b/cmd/helm/release_testing_run.go @@ -18,6 +18,7 @@ package main import ( "fmt" "io" + "time" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -68,7 +69,7 @@ func newReleaseTestRunCmd(cfg *action.Configuration, out io.Writer) *cobra.Comma } f := cmd.Flags() - f.DurationVar(&client.Timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)") + f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.Cleanup, "cleanup", false, "delete test pods upon completion") return cmd diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index a46ee8b9d..ff2236cfa 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "time" "github.com/spf13/cobra" @@ -60,7 +61,7 @@ func newRollbackCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.BoolVar(&client.Force, "force", false, "force resource update through delete/recreate if needed") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during rollback") - f.DurationVar(&client.Timeout, "timeout", 300, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") + f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") return cmd diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 65586c394..d4131acd3 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -64,6 +64,16 @@ func TestTemplateCmd(t *testing.T) { wantError: true, golden: "output/install-chart-bad-type.txt", }, + { + name: "check chart with dependency which is an app chart acting as a library chart", + cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-with-template-lib-dep"), + golden: "output/template-chart-with-template-lib-dep.txt", + }, + { + name: "check chart with dependency which is an app chart archive acting as a library chart", + cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-with-template-lib-archive-dep"), + golden: "output/template-chart-with-template-lib-archive-dep.txt", + }, } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/template-chart-with-template-lib-archive-dep.txt b/cmd/helm/testdata/output/template-chart-with-template-lib-archive-dep.txt new file mode 100644 index 000000000..23a0b984c --- /dev/null +++ b/cmd/helm/testdata/output/template-chart-with-template-lib-archive-dep.txt @@ -0,0 +1,61 @@ +--- +# Source: chart-with-template-lib-archive-dep/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app: chart-with-template-lib-archive-dep + chart: chart-with-template-lib-archive-dep-0.1.0 + heritage: Helm + release: RELEASE-NAME + name: release-name-chart-with-template-lib-archive-dep +spec: + ports: + - name: http + port: 80 + targetPort: http + selector: + app: chart-with-template-lib-archive-dep + release: RELEASE-NAME + type: ClusterIP +--- +# Source: chart-with-template-lib-archive-dep/templates/deployment.yaml +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: RELEASE-NAME-chart-with-template-lib-archive-dep + labels: + app: chart-with-template-lib-archive-dep + chart: chart-with-template-lib-archive-dep-0.1.0 + release: RELEASE-NAME + heritage: Helm +spec: + replicas: 1 + selector: + matchLabels: + app: chart-with-template-lib-archive-dep + release: RELEASE-NAME + template: + metadata: + labels: + app: chart-with-template-lib-archive-dep + release: RELEASE-NAME + spec: + containers: + - name: chart-with-template-lib-archive-dep + image: "nginx:stable" + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {} diff --git a/cmd/helm/testdata/output/template-chart-with-template-lib-dep.txt b/cmd/helm/testdata/output/template-chart-with-template-lib-dep.txt new file mode 100644 index 000000000..33147a73e --- /dev/null +++ b/cmd/helm/testdata/output/template-chart-with-template-lib-dep.txt @@ -0,0 +1,61 @@ +--- +# Source: chart-with-template-lib-dep/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app: chart-with-template-lib-dep + chart: chart-with-template-lib-dep-0.1.0 + heritage: Helm + release: RELEASE-NAME + name: release-name-chart-with-template-lib-dep +spec: + ports: + - name: http + port: 80 + targetPort: http + selector: + app: chart-with-template-lib-dep + release: RELEASE-NAME + type: ClusterIP +--- +# Source: chart-with-template-lib-dep/templates/deployment.yaml +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: RELEASE-NAME-chart-with-template-lib-dep + labels: + app: chart-with-template-lib-dep + chart: chart-with-template-lib-dep-0.1.0 + release: RELEASE-NAME + heritage: Helm +spec: + replicas: 1 + selector: + matchLabels: + app: chart-with-template-lib-dep + release: RELEASE-NAME + template: + metadata: + labels: + app: chart-with-template-lib-dep + release: RELEASE-NAME + spec: + containers: + - name: chart-with-template-lib-dep + image: "nginx:stable" + imagePullPolicy: IfNotPresent + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: + {} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/.helmignore b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/.helmignore @@ -0,0 +1,21 @@ +# 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 +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/Chart.yaml new file mode 100644 index 000000000..de53ce5e3 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: chart-with-template-lib-archive-dep +type: application +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/charts/common-0.0.5.tgz b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/charts/common-0.0.5.tgz new file mode 100644 index 000000000..465517824 Binary files /dev/null and b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/charts/common-0.0.5.tgz differ diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/NOTES.txt new file mode 100644 index 000000000..5c53ac03d --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "chart-with-template-lib-archive-dep.fullname" . }}) + export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "chart-with-template-lib-archive-dep.fullname" . }}' + export SERVICE_IP=$(kubectl get svc {{ template "chart-with-template-lib-archive-dep.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods -l "app={{ template "chart-with-template-lib-archive-dep.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:80 +{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/_helpers.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/_helpers.tpl new file mode 100644 index 000000000..76ca56b81 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "chart-with-template-lib-archive-dep.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "chart-with-template-lib-archive-dep.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "chart-with-template-lib-archive-dep.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/deployment.yaml new file mode 100644 index 000000000..6120e7def --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "chart-with-template-lib-archive-dep.fullname" . }} + labels: + app: {{ template "chart-with-template-lib-archive-dep.name" . }} + chart: {{ template "chart-with-template-lib-archive-dep.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "chart-with-template-lib-archive-dep.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "chart-with-template-lib-archive-dep.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/ingress.yaml new file mode 100644 index 000000000..d3325cf18 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/ingress.yaml @@ -0,0 +1,38 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "chart-with-template-lib-archive-dep.fullname" . -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "chart-with-template-lib-archive-dep.name" . }} + chart: {{ template "chart-with-template-lib-archive-dep.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/service.yaml new file mode 100644 index 000000000..bfcb080b4 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/templates/service.yaml @@ -0,0 +1,10 @@ +{{- template "common.service" (list . "chart-with-template-lib-archive-dep.service") -}} +{{- define "chart-with-template-lib-archive-dep.service" -}} +## Define overrides for your Service resource here, e.g. +# metadata: +# labels: +# custom: label +# spec: +# ports: +# - port: 8080 +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/values.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/values.yaml new file mode 100644 index 000000000..b5474cbbd --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-archive-dep/values.yaml @@ -0,0 +1,48 @@ +# Default values for chart-with-template-lib-archive-dep. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + tag: stable + pullPolicy: IfNotPresent + +nameOverride: "" +fullnameOverride: "" + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/.helmignore b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/.helmignore @@ -0,0 +1,21 @@ +# 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 +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/Chart.yaml new file mode 100644 index 000000000..cf6fc390b --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: chart-with-template-lib-dep +type: application +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/.helmignore b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/.helmignore new file mode 100755 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/.helmignore @@ -0,0 +1,21 @@ +# 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 +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/Chart.yaml new file mode 100755 index 000000000..ba14ca089 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +appVersion: 0.0.5 +description: Common chartbuilding components and helpers +home: https://helm.sh +maintainers: +- email: technosophos@gmail.com + name: technosophos +- email: adnan@bitnami.com + name: prydonius +name: common +version: 0.0.5 +type: library diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/README.md b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/README.md new file mode 100755 index 000000000..ca0459474 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/README.md @@ -0,0 +1,831 @@ +# Common: The Helm Helper Chart + +This chart is designed to make it easier for you to build and maintain Helm +charts. + +It provides utilities that reflect best practices of Kubernetes chart development, +making it faster for you to write charts. + +## Tips + +A few tips for working with Common: + +- Be careful when using functions that generate random data (like `common.fullname.unique`). + They may trigger unwanted upgrades or have other side effects. + +In this document, we use `RELEASE-NAME` as the name of the release. + +## Resource Kinds + +Kubernetes defines a variety of resource kinds, from `Secret` to `StatefulSet`. +We define some of the most common kinds in a way that lets you easily work with +them. + +The resource kind templates are designed to make it much faster for you to +define _basic_ versions of these resources. They allow you to extend and modify +just what you need, without having to copy around lots of boilerplate. + +To make use of these templates you must define a template that will extend the +base template (though it can be empty). The name of this template is then passed +to the base template, for example: + +```yaml +{{- template "common.service" (list . "mychart.service") -}} +{{- define "mychart.service" -}} +## Define overrides for your Service resource here, e.g. +# metadata: +# labels: +# custom: label +# spec: +# ports: +# - port: 8080 +{{- end -}} +``` + +Note that the `common.service` template defines two parameters: + + - The root context (usually `.`) + - A template name containing the service definition overrides + +A limitation of the Go template library is that a template can only take a +single argument. The `list` function is used to workaround this by constructing +a list or array of arguments that is passed to the template. + +The `common.service` template is responsible for rendering the templates with +the root context and merging any overrides. As you can see, this makes it very +easy to create a basic `Service` resource without having to copy around the +standard metadata and labels. + +Each implemented base resource is described in greater detail below. + +### `common.service` + +The `common.service` template creates a basic `Service` resource with the +following defaults: + +- Service type (ClusterIP, NodePort, LoadBalancer) made configurable by `.Values.service.type` +- Named port `http` configured on port 80 +- Selector set to `app: {{ template "common.name" }}, release: {{ .Release.Name | quote }}` to match the default used in the `Deployment` resource + +Example template: + +```yaml +{{- template "common.service" (list . "mychart.mail.service") -}} +{{- define "mychart.mail.service" -}} +metadata: + name: {{ template "common.fullname" . }}-mail # overrides the default name to add a suffix + labels: # appended to the labels section + protocol: mail +spec: + ports: # composes the `ports` section of the service definition. + - name: smtp + port: 25 + targetPort: 25 + - name: imaps + port: 993 + targetPort: 993 + selector: # this is appended to the default selector + protocol: mail +{{- end -}} +--- +{{ template "common.service" (list . "mychart.web.service") -}} +{{- define "mychart.web.service" -}} +metadata: + name: {{ template "common.fullname" . }}-www # overrides the default name to add a suffix + labels: # appended to the labels section + protocol: www +spec: + ports: # composes the `ports` section of the service definition. + - name: www + port: 80 + targetPort: 8080 +{{- end -}} +``` + +The above template defines _two_ services: a web service and a mail service. + +The most important part of a service definition is the `ports` object, which +defines the ports that this service will listen on. Most of the time, +`selector` is computed for you. But you can replace it or add to it. + +The output of the example above is: + +```yaml +apiVersion: v1 +kind: Service +metadata: + labels: + app: service + chart: service-0.1.0 + heritage: Tiller + protocol: mail + release: release-name + name: release-name-service-mail +spec: + ports: + - name: smtp + port: 25 + targetPort: 25 + - name: imaps + port: 993 + targetPort: 993 + selector: + app: service + release: release-name + protocol: mail + type: ClusterIP +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app: service + chart: service-0.1.0 + heritage: Tiller + protocol: www + release: release-name + name: release-name-service-www +spec: + ports: + - name: www + port: 80 + targetPort: 8080 + type: ClusterIP +``` + +## `common.deployment` + +The `common.deployment` template defines a basic `Deployment`. Underneath the +hood, it uses `common.container` (see next section). + +By default, the pod template within the deployment defines the labels `app: {{ template "common.name" . }}` +and `release: {{ .Release.Name | quote }` as this is also used as the selector. The +standard set of labels are not used as some of these can change during upgrades, +which causes the replica sets and pods to not correctly match. + +Example use: + +```yaml +{{- template "common.deployment" (list . "mychart.deployment") -}} +{{- define "mychart.deployment" -}} +## Define overrides for your Deployment resource here, e.g. +spec: + replicas: {{ .Values.replicaCount }} +{{- end -}} +``` + +## `common.container` + +The `common.container` template creates a basic `Container` spec to be used +within a `Deployment` or `ReplicaSet`. It holds the following defaults: + +- The name is set to the chart name +- Uses `.Values.image` to describe the image to run, with the following spec: + ```yaml + image: + repository: nginx + tag: stable + pullPolicy: IfNotPresent + ``` +- Exposes the named port `http` as port 80 +- Lays out the compute resources using `.Values.resources` + +Example use: + +```yaml +{{- template "common.deployment" (list . "mychart.deployment") -}} +{{- define "mychart.deployment" -}} +## Define overrides for your Deployment resource here, e.g. +spec: + template: + spec: + containers: + - {{ template "common.container" (list . "mychart.deployment.container") }} +{{- end -}} +{{- define "mychart.deployment.container" -}} +## Define overrides for your Container here, e.g. +livenessProbe: + httpGet: + path: / + port: 80 +readinessProbe: + httpGet: + path: / + port: 80 +{{- end -}} +``` + +The above example creates a `Deployment` resource which makes use of the +`common.container` template to populate the PodSpec's container list. The usage +of this template is similar to the other resources, you must define and +reference a template that contains overrides for the container object. + +The most important part of a container definition is the image you want to run. +As mentioned above, this is derived from `.Values.image` by default. It is a +best practice to define the image, tag and pull policy in your charts' values as +this makes it easy for an operator to change the image registry, or use a +specific tag or version. Another example of configuration that should be exposed +to chart operators is the container's required compute resources, as this is +also very specific to an operators environment. An example `values.yaml` for +your chart could look like: + +```yaml +image: + repository: nginx + tag: stable + pullPolicy: IfNotPresent +resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi +``` + +The output of running the above values through the earlier template is: + +```yaml +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + labels: + app: deployment + chart: deployment-0.1.0 + heritage: Tiller + release: release-name + name: release-name-deployment +spec: + template: + metadata: + labels: + app: deployment + spec: + containers: + - image: nginx:stable + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: / + port: 80 + name: deployment + ports: + - containerPort: 80 + name: http + readinessProbe: + httpGet: + path: / + port: 80 + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 100m + memory: 128Mi +``` + +## `common.configmap` + +The `common.configmap` template creates an empty `ConfigMap` resource that you +can override with your configuration. + +Example use: + +```yaml +{{- template "common.configmap" (list . "mychart.configmap") -}} +{{- define "mychart.configmap" -}} +data: + zeus: cat + athena: cat + julius: cat + one: |- + {{ .Files.Get "file1.txt" }} +{{- end -}} +``` + +Output: + +```yaml +apiVersion: v1 +data: + athena: cat + julius: cat + one: This is a file. + zeus: cat +kind: ConfigMap +metadata: + labels: + app: configmap + chart: configmap-0.1.0 + heritage: Tiller + release: release-name + name: release-name-configmap +``` + +## `common.secret` + +The `common.secret` template creates an empty `Secret` resource that you +can override with your secrets. + +Example use: + +```yaml +{{- template "common.secret" (list . "mychart.secret") -}} +{{- define "mychart.secret" -}} +data: + zeus: {{ print "cat" | b64enc }} + athena: {{ print "cat" | b64enc }} + julius: {{ print "cat" | b64enc }} + one: |- + {{ .Files.Get "file1.txt" | b64enc }} +{{- end -}} +``` + +Output: + +```yaml +apiVersion: v1 +data: + athena: Y2F0 + julius: Y2F0 + one: VGhpcyBpcyBhIGZpbGUuCg== + zeus: Y2F0 +kind: Secret +metadata: + labels: + app: secret + chart: secret-0.1.0 + heritage: Tiller + release: release-name + name: release-name-secret +type: Opaque +``` + +## `common.ingress` + +The `common.ingress` template is designed to give you a well-defined `Ingress` +resource, that can be configured using `.Values.ingress`. An example values file +that can be used to configure the `Ingress` resource is: + +```yaml +ingress: + hosts: + - chart-example.local + annotations: + kubernetes.io/ingress.class: nginx + kubernetes.io/tls-acme: "true" + tls: + - secretName: chart-example-tls + hosts: + - chart-example.local +``` + +Example use: + +```yaml +{{- template "common.ingress" (list . "mychart.ingress") -}} +{{- define "mychart.ingress" -}} +{{- end -}} +``` + +Output: + +```yaml +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + annotations: + kubernetes.io/ingress.class: nginx + kubernetes.io/tls-acme: "true" + labels: + app: ingress + chart: ingress-0.1.0 + heritage: Tiller + release: release-name + name: release-name-ingress +spec: + rules: + - host: chart-example.local + http: + paths: + - backend: + serviceName: release-name-ingress + servicePort: 80 + path: / + tls: + - hosts: + - chart-example.local + secretName: chart-example-tls +``` + +## `common.persistentvolumeclaim` + +`common.persistentvolumeclaim` can be used to easily add a +`PersistentVolumeClaim` resource to your chart that can be configured using +`.Values.persistence`: + +| Value | Description | +| ------------------------- | ------------------------------------------------------------------------------------------------------- | +| persistence.enabled | Whether or not to claim a persistent volume. If false, `common.volume.pvc` will use an emptyDir instead | +| persistence.storageClass | `StorageClass` name | +| persistence.accessMode | Access mode for persistent volume | +| persistence.size | Size of persistent volume | +| persistence.existingClaim | If defined, `PersistentVolumeClaim` is not created and `common.volume.pvc` helper uses this claim | + +An example values file that can be used to configure the +`PersistentVolumeClaim` resource is: + +```yaml +persistence: + enabled: true + storageClass: fast + accessMode: ReadWriteOnce + size: 8Gi +``` + +Example use: + +```yaml +{{- template "common.persistentvolumeclaim" (list . "mychart.persistentvolumeclaim") -}} +{{- define "mychart.persistentvolumeclaim" -}} +{{- end -}} +``` + +Output: + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + labels: + app: persistentvolumeclaim + chart: persistentvolumeclaim-0.1.0 + heritage: Tiller + release: release-name + name: release-name-persistentvolumeclaim +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 8Gi + storageClassName: "fast" +``` + +## Partial API Objects + +When writing Kubernetes resources, you may find the following helpers useful to +construct parts of the spec. + +### EnvVar + +Use the EnvVar helpers within a container spec to simplify specifying key-value +environment variables or referencing secrets as values. + +Example Use: + +```yaml +{{- template "common.deployment" (list . "mychart.deployment") -}} +{{- define "mychart.deployment" -}} +spec: + template: + spec: + containers: + - {{ template "common.container" (list . "mychart.deployment.container") }} +{{- end -}} +{{- define "mychart.deployment.container" -}} +{{- $fullname := include "common.fullname" . -}} +env: +- {{ template "common.envvar.value" (list "ZEUS" "cat") }} +- {{ template "common.envvar.secret" (list "ATHENA" "secret-name" "athena") }} +{{- end -}} +``` + +Output: + +```yaml +... + spec: + containers: + - env: + - name: ZEUS + value: cat + - name: ATHENA + valueFrom: + secretKeyRef: + key: athena + name: secret-name +... +``` + +### Volume + +Use the Volume helpers within a `Deployment` spec to help define ConfigMap and +PersistentVolumeClaim volumes. + +Example Use: + +```yaml +{{- template "common.deployment" (list . "mychart.deployment") -}} +{{- define "mychart.deployment" -}} +spec: + template: + spec: + volumes: + - {{ template "common.volume.configMap" (list "config" "configmap-name") }} + - {{ template "common.volume.pvc" (list "data" "pvc-name" .Values.persistence) }} +{{- end -}} +``` + +Output: + +```yaml +... + spec: + volumes: + - configMap: + name: configmap-name + name: config + - name: data + persistentVolumeClaim: + claimName: pvc-name +... +``` + +The `common.volume.pvc` helper uses the following configuration from the `.Values.persistence` object: + +| Value | Description | +| ------------------------- | ----------------------------------------------------- | +| persistence.enabled | If false, creates an `emptyDir` instead | +| persistence.existingClaim | If set, uses this instead of the passed in claim name | + +## Utilities + +### `common.fullname` + +The `common.fullname` template generates a name suitable for the `name:` field +in Kubernetes metadata. It is used like this: + +```yaml +name: {{ template "common.fullname" . }} +``` + +The following different values can influence it: + +```yaml +# By default, fullname uses '{{ .Release.Name }}-{{ .Chart.Name }}'. This +# overrides that and uses the given string instead. +fullnameOverride: "some-name" + +# This adds a prefix +fullnamePrefix: "pre-" +# This appends a suffix +fullnameSuffix: "-suf" + +# Global versions of the above +global: + fullnamePrefix: "pp-" + fullnameSuffix: "-ps" +``` + +Example output: + +```yaml +--- +# with the values above +name: pp-pre-some-name-suf-ps + +--- +# the default, for release "happy-panda" and chart "wordpress" +name: happy-panda-wordpress +``` + +Output of this function is truncated at 54 characters, which leaves 9 additional +characters for customized overriding. Thus you can easily extend this name +in your own charts: + +```yaml +{{- define "my.fullname" -}} + {{ template "common.fullname" . }}-my-stuff +{{- end -}} +``` + +### `common.fullname.unique` + +The `common.fullname.unique` variant of fullname appends a unique seven-character +sequence to the end of the common name field. + +This takes all of the same parameters as `common.fullname` + +Example template: + +```yaml +uniqueName: {{ template "common.fullname.unique" . }} +``` + +Example output: + +```yaml +uniqueName: release-name-fullname-jl0dbwx +``` + +It is also impacted by the prefix and suffix definitions, as well as by +`.Values.fullnameOverride` + +Note that the effective maximum length of this function is 63 characters, not 54. + +### `common.name` + +The `common.name` template generates a name suitable for the `app` label. It is used like this: + +```yaml +app: {{ template "common.name" . }} +``` + +The following different values can influence it: + +```yaml +# By default, name uses '{{ .Chart.Name }}'. This +# overrides that and uses the given string instead. +nameOverride: "some-name" + +# This adds a prefix +namePrefix: "pre-" +# This appends a suffix +nameSuffix: "-suf" + +# Global versions of the above +global: + namePrefix: "pp-" + nameSuffix: "-ps" +``` + +Example output: + +```yaml +--- +# with the values above +name: pp-pre-some-name-suf-ps + +--- +# the default, for chart "wordpress" +name: wordpress +``` + +Output of this function is truncated at 54 characters, which leaves 9 additional +characters for customized overriding. Thus you can easily extend this name +in your own charts: + +```yaml +{{- define "my.name" -}} + {{ template "common.name" . }}-my-stuff +{{- end -}} +``` + +### `common.metadata` + +The `common.metadata` helper generates the `metadata:` section of a Kubernetes +resource. + +This takes three objects: + - .top: top context + - .fullnameOverride: override the fullname with this name + - .metadata + - .labels: key/value list of labels + - .annotations: key/value list of annotations + - .hook: name(s) of hook(s) + +It generates standard labels, annotations, hooks, and a name field. + +Example template: + +```yaml +{{ template "common.metadata" (dict "top" . "metadata" .Values.bio) }} +--- +{{ template "common.metadata" (dict "top" . "metadata" .Values.pet "fullnameOverride" .Values.pet.fullnameOverride) }} +``` + +Example values: + +```yaml +bio: + name: example + labels: + first: matt + last: butcher + nick: technosophos + annotations: + format: bio + destination: archive + hook: pre-install + +pet: + fullnameOverride: Zeus + +``` + +Example output: + +```yaml +metadata: + name: release-name-metadata + labels: + app: metadata + heritage: "Tiller" + release: "RELEASE-NAME" + chart: metadata-0.1.0 + first: "matt" + last: "butcher" + nick: "technosophos" + annotations: + "destination": "archive" + "format": "bio" + "helm.sh/hook": "pre-install" +--- +metadata: + name: Zeus + labels: + app: metadata + heritage: "Tiller" + release: "RELEASE-NAME" + chart: metadata-0.1.0 + annotations: +``` + +Most of the common templates that define a resource type (e.g. `common.configmap` +or `common.job`) use this to generate the metadata, which means they inherit +the same `labels`, `annotations`, `nameOverride`, and `hook` fields. + +### `common.labelize` + +`common.labelize` turns a map into a set of labels. + +Example template: + +```yaml +{{- $map := dict "first" "1" "second" "2" "third" "3" -}} +{{- template "common.labelize" $map -}} +``` + +Example output: + +```yaml +first: "1" +second: "2" +third: "3" +``` + +### `common.labels.standard` + +`common.labels.standard` prints the standard set of labels. + +Example usage: + +``` +{{ template "common.labels.standard" . }} +``` + +Example output: + +```yaml +app: labelizer +heritage: "Tiller" +release: "RELEASE-NAME" +chart: labelizer-0.1.0 +``` + +### `common.hook` + +The `common.hook` template is a convenience for defining hooks. + +Example template: + +```yaml +{{ template "common.hook" "pre-install,post-install" }} +``` + +Example output: + +```yaml +"helm.sh/hook": "pre-install,post-install" +``` + +### `common.chartref` + +The `common.chartref` helper prints the chart name and version, escaped to be +legal in a Kubernetes label field. + +Example template: + +```yaml +chartref: {{ template "common.chartref" . }} +``` + +For the chart `foo` with version `1.2.3-beta.55+1234`, this will render: + +```yaml +chartref: foo-1.2.3-beta.55_1234 +``` + +(Note that `+` is an illegal character in label values) diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_chartref.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_chartref.tpl new file mode 100755 index 000000000..e6c14866f --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_chartref.tpl @@ -0,0 +1,14 @@ +{{- /* +common.chartref prints a chart name and version. + +It does minimal escaping for use in Kubernetes labels. + +Example output: + + zookeeper-1.2.3 + wordpress-3.2.1_20170219 + +*/ -}} +{{- define "common.chartref" -}} + {{- replace "+" "_" .Chart.Version | printf "%s-%s" .Chart.Name -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_configmap.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_configmap.yaml new file mode 100755 index 000000000..03dbbf858 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_configmap.yaml @@ -0,0 +1,9 @@ +{{- define "common.configmap.tpl" -}} +apiVersion: v1 +kind: ConfigMap +{{ template "common.metadata" . }} +data: {} +{{- end -}} +{{- define "common.configmap" -}} +{{- template "common.util.merge" (append . "common.configmap.tpl") -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_container.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_container.yaml new file mode 100755 index 000000000..540eb0e6a --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_container.yaml @@ -0,0 +1,15 @@ +{{- define "common.container.tpl" -}} +name: {{ .Chart.Name }} +image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" +imagePullPolicy: {{ .Values.image.pullPolicy }} +ports: +- name: http + containerPort: 80 +resources: +{{ toYaml .Values.resources | indent 2 }} +{{- end -}} +{{- define "common.container" -}} +{{- /* clear new line so indentation works correctly */ -}} +{{- println "" -}} +{{- include "common.util.merge" (append . "common.container.tpl") | indent 8 -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_deployment.yaml new file mode 100755 index 000000000..c49dae3eb --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_deployment.yaml @@ -0,0 +1,18 @@ +{{- define "common.deployment.tpl" -}} +apiVersion: extensions/v1beta1 +kind: Deployment +{{ template "common.metadata" . }} +spec: + template: + metadata: + labels: + app: {{ template "common.name" . }} + release: {{ .Release.Name | quote }} + spec: + containers: + - +{{ include "common.container.tpl" . | indent 8 }} +{{- end -}} +{{- define "common.deployment" -}} +{{- template "common.util.merge" (append . "common.deployment.tpl") -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_envvar.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_envvar.tpl new file mode 100755 index 000000000..709251f8f --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_envvar.tpl @@ -0,0 +1,31 @@ +{{- define "common.envvar.value" -}} + {{- $name := index . 0 -}} + {{- $value := index . 1 -}} + + name: {{ $name }} + value: {{ default "" $value | quote }} +{{- end -}} + +{{- define "common.envvar.configmap" -}} + {{- $name := index . 0 -}} + {{- $configMapName := index . 1 -}} + {{- $configMapKey := index . 2 -}} + + name: {{ $name }} + valueFrom: + configMapKeyRef: + name: {{ $configMapName }} + key: {{ $configMapKey }} +{{- end -}} + +{{- define "common.envvar.secret" -}} + {{- $name := index . 0 -}} + {{- $secretName := index . 1 -}} + {{- $secretKey := index . 2 -}} + + name: {{ $name }} + valueFrom: + secretKeyRef: + name: {{ $secretName }} + key: {{ $secretKey }} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_fullname.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_fullname.tpl new file mode 100755 index 000000000..2da6cdf18 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_fullname.tpl @@ -0,0 +1,39 @@ +{{- /* +fullname defines a suitably unique name for a resource by combining +the release name and the chart name. + +The prevailing wisdom is that names should only contain a-z, 0-9 plus dot (.) and dash (-), and should +not exceed 63 characters. + +Parameters: + +- .Values.fullnameOverride: Replaces the computed name with this given name +- .Values.fullnamePrefix: Prefix +- .Values.global.fullnamePrefix: Global prefix +- .Values.fullnameSuffix: Suffix +- .Values.global.fullnameSuffix: Global suffix + +The applied order is: "global prefix + prefix + name + suffix + global suffix" + +Usage: 'name: "{{- template "common.fullname" . -}}"' +*/ -}} +{{- define "common.fullname"}} + {{- $global := default (dict) .Values.global -}} + {{- $base := default (printf "%s-%s" .Release.Name .Chart.Name) .Values.fullnameOverride -}} + {{- $gpre := default "" $global.fullnamePrefix -}} + {{- $pre := default "" .Values.fullnamePrefix -}} + {{- $suf := default "" .Values.fullnameSuffix -}} + {{- $gsuf := default "" $global.fullnameSuffix -}} + {{- $name := print $gpre $pre $base $suf $gsuf -}} + {{- $name | lower | trunc 54 | trimSuffix "-" -}} +{{- end -}} + +{{- /* +common.fullname.unique adds a random suffix to the unique name. + +This takes the same parameters as common.fullname + +*/ -}} +{{- define "common.fullname.unique" -}} + {{ template "common.fullname" . }}-{{ randAlphaNum 7 | lower }} +{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_ingress.yaml new file mode 100755 index 000000000..522ab2425 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_ingress.yaml @@ -0,0 +1,27 @@ +{{- define "common.ingress.tpl" -}} +apiVersion: extensions/v1beta1 +kind: Ingress +{{ template "common.metadata" . }} + {{- if .Values.ingress.annotations }} + annotations: + {{ include "common.annote" .Values.ingress.annotations | indent 4 }} + {{- end }} +spec: + rules: + {{- range $host := .Values.ingress.hosts }} + - host: {{ $host }} + http: + paths: + - path: / + backend: + serviceName: {{ template "common.fullname" $ }} + servicePort: 80 + {{- end }} + {{- if .Values.ingress.tls }} + tls: +{{ toYaml .Values.ingress.tls | indent 4 }} + {{- end -}} +{{- end -}} +{{- define "common.ingress" -}} +{{- template "common.util.merge" (append . "common.ingress.tpl") -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata.yaml new file mode 100755 index 000000000..f96ed09fe --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata.yaml @@ -0,0 +1,10 @@ +{{- /* +common.metadata creates a standard metadata header. +It creates a 'metadata:' section with name and labels. +*/ -}} +{{ define "common.metadata" -}} +metadata: + name: {{ template "common.fullname" . }} + labels: +{{ include "common.labels.standard" . | indent 4 -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_annotations.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_annotations.tpl new file mode 100755 index 000000000..0c3b61c7c --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_annotations.tpl @@ -0,0 +1,18 @@ +{{- /* +common.hook defines a hook. + +This is to be used in a 'metadata.annotations' section. + +This should be called as 'template "common.metadata.hook" "post-install"' + +Any valid hook may be passed in. Separate multiple hooks with a ",". +*/ -}} +{{- define "common.hook" -}} +"helm.sh/hook": {{printf "%s" . | quote}} +{{- end -}} + +{{- define "common.annote" -}} +{{- range $k, $v := . }} +{{ $k | quote }}: {{ $v | quote }} +{{- end -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_labels.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_labels.tpl new file mode 100755 index 000000000..15fe00c5f --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_metadata_labels.tpl @@ -0,0 +1,28 @@ +{{- /* +common.labelize takes a dict or map and generates labels. + +Values will be quoted. Keys will not. + +Example output: + + first: "Matt" + last: "Butcher" + +*/ -}} +{{- define "common.labelize" -}} +{{- range $k, $v := . }} +{{ $k }}: {{ $v | quote }} +{{- end -}} +{{- end -}} + +{{- /* +common.labels.standard prints the standard Helm labels. + +The standard labels are frequently used in metadata. +*/ -}} +{{- define "common.labels.standard" -}} +app: {{ template "common.name" . }} +chart: {{ template "common.chartref" . }} +heritage: {{ .Release.Service | quote }} +release: {{ .Release.Name | quote }} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_name.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_name.tpl new file mode 100755 index 000000000..1d42fb068 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_name.tpl @@ -0,0 +1,29 @@ +{{- /* +name defines a template for the name of the chart. It should be used for the `app` label. +This is common practice in many Kubernetes manifests, and is not Helm-specific. + +The prevailing wisdom is that names should only contain a-z, 0-9 plus dot (.) and dash (-), and should +not exceed 63 characters. + +Parameters: + +- .Values.nameOverride: Replaces the computed name with this given name +- .Values.namePrefix: Prefix +- .Values.global.namePrefix: Global prefix +- .Values.nameSuffix: Suffix +- .Values.global.nameSuffix: Global suffix + +The applied order is: "global prefix + prefix + name + suffix + global suffix" + +Usage: 'name: "{{- template "common.name" . -}}"' +*/ -}} +{{- define "common.name"}} + {{- $global := default (dict) .Values.global -}} + {{- $base := default .Chart.Name .Values.nameOverride -}} + {{- $gpre := default "" $global.namePrefix -}} + {{- $pre := default "" .Values.namePrefix -}} + {{- $suf := default "" .Values.nameSuffix -}} + {{- $gsuf := default "" $global.nameSuffix -}} + {{- $name := print $gpre $pre $base $suf $gsuf -}} + {{- $name | lower | trunc 54 | trimSuffix "-" -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_persistentvolumeclaim.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_persistentvolumeclaim.yaml new file mode 100755 index 000000000..6c1578c7e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_persistentvolumeclaim.yaml @@ -0,0 +1,24 @@ +{{- define "common.persistentvolumeclaim.tpl" -}} +apiVersion: v1 +kind: PersistentVolumeClaim +{{ template "common.metadata" . }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- if .Values.persistence.storageClass }} +{{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" +{{- else }} + storageClassName: "{{ .Values.persistence.storageClass }}" +{{- end }} +{{- end }} +{{- end -}} +{{- define "common.persistentvolumeclaim" -}} +{{- $top := first . -}} +{{- if and $top.Values.persistence.enabled (not $top.Values.persistence.existingClaim) -}} +{{- template "common.util.merge" (append . "common.persistentvolumeclaim.tpl") -}} +{{- end -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_secret.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_secret.yaml new file mode 100755 index 000000000..0615d35cb --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_secret.yaml @@ -0,0 +1,10 @@ +{{- define "common.secret.tpl" -}} +apiVersion: v1 +kind: Secret +{{ template "common.metadata" . }} +type: Opaque +data: {} +{{- end -}} +{{- define "common.secret" -}} +{{- template "common.util.merge" (append . "common.secret.tpl") -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_service.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_service.yaml new file mode 100755 index 000000000..67379525f --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_service.yaml @@ -0,0 +1,17 @@ +{{- define "common.service.tpl" -}} +apiVersion: v1 +kind: Service +{{ template "common.metadata" . }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: 80 + targetPort: http + selector: + app: {{ template "common.name" . }} + release: {{ .Release.Name | quote }} +{{- end -}} +{{- define "common.service" -}} +{{- template "common.util.merge" (append . "common.service.tpl") -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_util.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_util.tpl new file mode 100755 index 000000000..a7d4cc751 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_util.tpl @@ -0,0 +1,15 @@ +{{- /* +common.util.merge will merge two YAML templates and output the result. + +This takes an array of three values: +- the top context +- the template name of the overrides (destination) +- the template name of the base (source) + +*/ -}} +{{- define "common.util.merge" -}} +{{- $top := first . -}} +{{- $overrides := fromYaml (include (index . 1) $top) | default (dict ) -}} +{{- $tpl := fromYaml (include (index . 2) $top) | default (dict ) -}} +{{- toYaml (merge $overrides $tpl) -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_volume.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_volume.tpl new file mode 100755 index 000000000..521a1f48b --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/_volume.tpl @@ -0,0 +1,22 @@ +{{- define "common.volume.configMap" -}} + {{- $name := index . 0 -}} + {{- $configMapName := index . 1 -}} + + name: {{ $name }} + configMap: + name: {{ $configMapName }} +{{- end -}} + +{{- define "common.volume.pvc" -}} + {{- $name := index . 0 -}} + {{- $claimName := index . 1 -}} + {{- $persistence := index . 2 -}} + + name: {{ $name }} + {{- if $persistence.enabled }} + persistentVolumeClaim: + claimName: {{ $persistence.existingClaim | default $claimName }} + {{- else }} + emptyDir: {} + {{- end -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/configmap.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/configmap.yaml new file mode 100644 index 000000000..b5bf1dfc3 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/templates/configmap.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: common-configmap +data: + myvalue: "Hello World" diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/values.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/values.yaml new file mode 100755 index 000000000..b7cf514d5 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/charts/common/values.yaml @@ -0,0 +1,4 @@ +# Default values for commons. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/NOTES.txt new file mode 100644 index 000000000..8f6bb9b1d --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/NOTES.txt @@ -0,0 +1,19 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ . }}{{ $.Values.ingress.path }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "chart-with-template-lib-dep.fullname" . }}) + export NODE_IP=$(kubectl get nodes -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get svc -w {{ template "chart-with-template-lib-dep.fullname" . }}' + export SERVICE_IP=$(kubectl get svc {{ template "chart-with-template-lib-dep.fullname" . }} -o jsonpath='{.status.loadBalancer.ingress[0].ip}') + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods -l "app={{ template "chart-with-template-lib-dep.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:80 +{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/_helpers.tpl b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/_helpers.tpl new file mode 100644 index 000000000..0ab79743d --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "chart-with-template-lib-dep.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "chart-with-template-lib-dep.fullname" -}} +{{- if .Values.fullnameOverride -}} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "chart-with-template-lib-dep.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/deployment.yaml new file mode 100644 index 000000000..c447579d0 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "chart-with-template-lib-dep.fullname" . }} + labels: + app: {{ template "chart-with-template-lib-dep.name" . }} + chart: {{ template "chart-with-template-lib-dep.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "chart-with-template-lib-dep.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "chart-with-template-lib-dep.name" . }} + release: {{ .Release.Name }} + spec: + containers: + - name: {{ .Chart.Name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: 80 + protocol: TCP + livenessProbe: + httpGet: + path: / + port: http + readinessProbe: + httpGet: + path: / + port: http + resources: +{{ toYaml .Values.resources | indent 12 }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: +{{ toYaml . | indent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 8 }} + {{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/ingress.yaml new file mode 100644 index 000000000..a978df4e7 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/ingress.yaml @@ -0,0 +1,38 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "chart-with-template-lib-dep.fullname" . -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "chart-with-template-lib-dep.name" . }} + chart: {{ template "chart-with-template-lib-dep.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ . }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: http + {{- end }} +{{- end }} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/service.yaml new file mode 100644 index 000000000..d532bb3d8 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/templates/service.yaml @@ -0,0 +1,10 @@ +{{- template "common.service" (list . "chart-with-template-lib-dep.service") -}} +{{- define "chart-with-template-lib-dep.service" -}} +## Define overrides for your Service resource here, e.g. +# metadata: +# labels: +# custom: label +# spec: +# ports: +# - port: 8080 +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/values.yaml b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/values.yaml new file mode 100644 index 000000000..d49955c26 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-template-lib-dep/values.yaml @@ -0,0 +1,48 @@ +# Default values for chart-with-template-lib-dep. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + tag: stable + pullPolicy: IfNotPresent + +nameOverride: "" +fullnameOverride: "" + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +nodeSelector: {} + +tolerations: [] + +affinity: {} diff --git a/cmd/helm/uninstall.go b/cmd/helm/uninstall.go index 814237a55..0710a3c45 100644 --- a/cmd/helm/uninstall.go +++ b/cmd/helm/uninstall.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "time" "github.com/spf13/cobra" @@ -67,7 +68,7 @@ func newUninstallCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&client.DryRun, "dry-run", false, "simulate a uninstall") f.BoolVar(&client.DisableHooks, "no-hooks", false, "prevent hooks from running during uninstallation") f.BoolVar(&client.KeepHistory, "keep-history", false, "remove all associated resources and mark the release as deleted, but retain the release history") - f.DurationVar(&client.Timeout, "timeout", 300, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") + f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") return cmd } diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index efc472d9e..4bbf46d4f 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -19,6 +19,7 @@ package main import ( "fmt" "io" + "time" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -142,7 +143,7 @@ func newUpgradeCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&client.Recreate, "recreate-pods", false, "performs pods restart for the resource if applicable") f.BoolVar(&client.Force, "force", false, "force resource update through delete/recreate if needed") f.BoolVar(&client.DisableHooks, "no-hooks", false, "disable pre/post upgrade hooks") - f.DurationVar(&client.Timeout, "timeout", 300, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") + f.DurationVar(&client.Timeout, "timeout", 300*time.Second, "time to wait for any individual Kubernetes operation (like Jobs for hooks)") f.BoolVar(&client.ResetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart") f.BoolVar(&client.ReuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.") f.BoolVar(&client.Wait, "wait", false, "if set, will wait until all Pods, PVCs, Services, and minimum number of Pods of a Deployment are in a ready state before marking the release as successful. It will wait for as long as --timeout") diff --git a/docs/charts.md b/docs/charts.md index 339c16bd7..9937dd25b 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -128,6 +128,10 @@ provides utilities or functions for the chart builder. A library chart differs from an application chart because it has no resource object and is therefore not installable. +**Note:** An application chart can be used as a library chart. This is enabled by setting the +type to `library`. The chart will then be rendered as a library chart where all utilities and +functions can be leveraged. All resource objects of the chart will not be rendered. + ## Chart LICENSE, README and NOTES Charts can also contain files that describe the installation, configuration, usage and license of a diff --git a/docs/faq.md b/docs/faq.md index bcb5afc7e..08f565e65 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -10,12 +10,155 @@ send us a pull request. Here's an exhaustive list of all the major changes introduced in Helm 3. +### Removal of Tiller + +During the Helm 2 development cycle, we introduced Tiller. Tiller played an important role for teams working on a shared +cluster - it made it possible for multiple different operators to interact with the same set of releases. + +With role-based access controls (RBAC) enabled by default in Kubernetes 1.6, locking down Tiller for use in a production +scenario became more difficult to manage. Due to the vast number of possible security policies, our stance was to +provide a permissive default configuration. This allowed first-time users to start experimenting with Helm and +Kubernetes without having to dive headfirst into the security controls. Unfortunately, this permissive configuration +could grant a user a broad range of permissions they weren’t intended to have. DevOps and SREs had to learn additional +operational steps when installing Tiller into a multi-tenant cluster. + +After hearing how community members were using Helm in certain scenarios, we found that Tiller’s release management +system did not need to rely upon an in-cluster operator to maintain state or act as a central hub for Helm release +information. Instead, we could simply fetch information from the Kubernetes API server, render the Charts client-side, +and store a record of the installation in Kubernetes. + +Tiller’s primary goal could be accomplished without Tiller, so one of the first decisions we made regarding Helm 3 was +to completely remove Tiller. + +With Tiller gone, the security model for Helm is radically simplified. Helm 3 now supports all the modern security, +identity, and authorization features of modern Kubernetes. Helm’s permissions are evaluated using your [kubeconfig file](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/). +Cluster administrators can restrict user permissions at whatever granularity they see fit. Releases are still recorded +in-cluster, and the rest of Helm’s functionality remains. + +### Release Names are now scoped to the Namespace + +With the removal of Tiller, the information about each release had to go somewhere. In Helm 2, this was stored in the +same namespace as Tiller. In practice, this meant that once a name was used by a release, no other release could use +that same name, even if it was deployed in a different namespace. + +In Helm 3, release information about a particular release is now stored in the same namespace as the release itself. +This means that users can now `helm install wordpress stable/wordpress` in two separate namespaces, and each can be +referred with `helm list` by changing the current namespace context. + ### Go import path changes In Helm 3, Helm switched the Go import path over from `k8s.io/helm` to `helm.sh/helm`. If you intend to upgrade to the Helm 3 Go client libraries, make sure to change your import paths. -### Helm delete +### Capabilities + +The `.Capabilities` built-in object available during the rendering stage has been simplified. + +[Built-in Objects](chart_template_guide/builtin_objects.md) + +### Validating Chart Values with JSONSchema + +A JSON Schema can now be imposed upon chart values. This ensures that values provided by the user follow the schema +laid out by the chart maintainer, providing better error reporting when the user provides an incorrect set of values for +a chart. + +Validation occurs when any of the following commands are invoked: + +* `helm install` +* `helm upgrade` +* `helm template` +* `helm lint` + +See the documentation on [Schema files](charts.md#schema-files) for more information. + +### Consolidation of requirements.yaml into Chart.yaml + +The Chart dependency management system moved from requirements.yaml and requirements.lock to Chart.yaml and Chart.lock, +meaning that charts that relied on the `helm dependency` subcommands will need some tweaking to work in Helm 3. + +In Helm 2, this is how a requirements.yaml looked: + +``` +dependencies: +- name: mariadb + version: 5.x.x + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: mariadb.enabled + tags: + - database +``` + +In Helm 3, the dependency is expressed the same way, but now from your Chart.yaml: + +``` +dependencies: +- name: mariadb + version: 5.x.x + repository: https://kubernetes-charts.storage.googleapis.com/ + condition: mariadb.enabled + tags: + - database +``` + +Charts are still downloaded and placed in the charts/ directory, so subcharts vendored into the charts/ directory will continue to work without modification. + +### Name (or --generate-name) is now required on install + +In Helm 2, if no name was provided, an auto-generated name would be given. In production, this proved to be more of a +nuisance than a helpful feature. In Helm 3, Helm will throw an error if no name is provided with `helm install`. + +For those who still wish to have a name auto-generated for you, you can use the `--generate-name` flag to create one for +you. + +### Pushing Charts to OCI Registries + +At a high level, a Chart Repository is a location where Charts can be stored and shared. The Helm client packs and ships +Helm Charts to a Chart Repository. Simply put, a Chart Repository is a basic HTTP server that houses an index.yaml file +and some packaged charts. + +While there are several benefits to the Chart Repository API meeting the most basic storage requirements, a few +drawbacks have started to show: + +- Chart Repositories have a very hard time abstracting most of the security implementations required in a production environment. Having a standard API for authentication and authorization is very important in production scenarios. +- Helm’s Chart provenance tools used for signing and verifying the integrity and origin of a chart are an optional piece of the Chart publishing process. +- In multi-tenant scenarios, the same Chart can be uploaded by another tenant, costing twice the storage cost to store the same content. Smarter chart repositories have been designed to handle this, but it’s not a part of the formal specification. +- Using a single index file for search, metadata information, and fetching Charts has made it difficult or clunky to design around in secure multi-tenant implementations. + +Docker’s Distribution project (also known as Docker Registry v2) is the successor to the Docker Registry project. Many +major cloud vendors have a product offering of the Distribution project, and with so many vendors offering the same +product, the Distribution project has benefited from many years of hardening, security best practices, and +battle-testing. + +Please have a look at `helm help chart` and `helm help registry` for more information on how to package a chart and +push it to a Docker registry. + +### Removal of helm serve + +`helm serve` ran a local Chart Repository on your machine for development purposes. However, it didn't receive much +uptake as a development tool and had numerous issues with its design. In the end, we decided to remove it and split it +out as a plugin. + +### Library chart support + +Helm 3 supports a class of chart called a “library chart”. This is a chart that is shared by other charts, but does not +create any release artifacts of its own. A library chart’s templates can only declare `define` elements. Globally scoped +non-`define` content is simply ignored. This allows users to re-use and share snippets of code that can be re-used across +many charts, avoiding redundancy and keeping charts [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). + +Library charts are declared in the dependencies directive in Chart.yaml, and are installed and managed like any other +chart. + +``` +dependencies: + - name: mylib + version: 1.x.x + repository: quay.io +``` + +We’re very excited to see the use cases this feature opens up for chart developers, as well as any best practices that +arise from consuming library charts. + +### CLI Command Renames In order to better align the verbiage from other package managers, `helm delete` was re-named to `helm uninstall`. `helm delete` is still retained as an alias to `helm uninstall`, so either form @@ -25,11 +168,12 @@ In Helm 2, in order to purge the release ledger, the `--purge` flag had to be pr functionality is now enabled by default. To retain the previous behaviour, use `helm uninstall --keep-history`. -### Capabilities +Additionally, several other commands were re-named to accommodate the same conventions: -Capabilities built-in has been simplified. +- `helm inspect` -> `helm show` +- `helm fetch` -> `helm pull` -[Built-in Objects](chart_template_guide/builtin_objects.md) +These commands have also retained their older verbs as aliases, so you can continue to use them in either form. ## Installing diff --git a/docs/install.md b/docs/install.md index cc684bf45..56d7171a2 100755 --- a/docs/install.md +++ b/docs/install.md @@ -65,12 +65,12 @@ the latest master branch. They are not official releases, and may not be stable. However, they offer the opportunity to test the cutting edge features. -Canary Helm binaries are stored in the [Kubernetes Helm GCS bucket](https://kubernetes-helm.storage.googleapis.com). +Canary Helm binaries are stored at [get.helm.sh](https://get.helm.sh). Here are links to the common builds: -- [Linux AMD64](https://kubernetes-helm.storage.googleapis.com/helm-canary-linux-amd64.tar.gz) -- [macOS AMD64](https://kubernetes-helm.storage.googleapis.com/helm-canary-darwin-amd64.tar.gz) -- [Experimental Windows AMD64](https://kubernetes-helm.storage.googleapis.com/helm-canary-windows-amd64.zip) +- [Linux AMD64](https://get.helm.sh/helm-canary-linux-amd64.tar.gz) +- [macOS AMD64](https://get.helm.sh/helm-canary-darwin-amd64.tar.gz) +- [Experimental Windows AMD64](https://get.helm.sh/helm-canary-windows-amd64.zip) ### From Source (Linux, macOS) diff --git a/docs/release_checklist.md b/docs/release_checklist.md index 8d3b78b23..56928a8b8 100644 --- a/docs/release_checklist.md +++ b/docs/release_checklist.md @@ -94,31 +94,6 @@ index 2109a0a..6f5a1a4 100644 BuildMetadata = "unreleased" ``` -The README stores links to the latest release for helm. We want to change the version to the first release candidate which we are releasing (more on that in step 5). - -```shell -$ git diff README.md -diff --git a/README.md b/README.md -index 022afd79..547839e2 100644 ---- a/README.md -+++ b/README.md -@@ -34,10 +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.7.0-darwin-amd64.tar.gz) --- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz) --- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-386.tar.gz) --- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-windows-amd64.tar.gz) -+- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.8.0-darwin-amd64.tar.gz) -+- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.8.0-linux-amd64.tar.gz) -+- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.8.0-linux-386.tar.gz) -+- [Windows](https://kubernetes-helm.storage.googleapis.com/helm-v2.8.0-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`. -``` - For patch releases, the old version number will be the latest patch release, so just bump the patch number, incrementing Z by one. ```shell @@ -149,24 +124,24 @@ git push upstream $RELEASE_CANDIDATE_NAME CircleCI will automatically create a tagged release image and client binary to test with. -For testers, the process to start testing after CircleCI finishes building the artifacts involves the following steps to grab the client from Google Cloud Storage: +After CircleCI finishes building the artifacts, use the following commands to fetch the client for testing: linux/amd64, using /bin/bash: ```shell -wget https://kubernetes-helm.storage.googleapis.com/helm-$RELEASE_CANDIDATE_NAME-linux-amd64.tar.gz +wget https://get.helm.sh/helm-$RELEASE_CANDIDATE_NAME-linux-amd64.tar.gz ``` darwin/amd64, using Terminal.app: ```shell -wget https://kubernetes-helm.storage.googleapis.com/helm-$RELEASE_CANDIDATE_NAME-darwin-amd64.tar.gz +wget https://get.helm.sh/helm-$RELEASE_CANDIDATE_NAME-darwin-amd64.tar.gz ``` windows/amd64, using PowerShell: ```shell -PS C:\> Invoke-WebRequest -Uri "https://kubernetes-helm.storage.googleapis.com/helm-$RELEASE_CANDIDATE_NAME-windows-amd64.tar.gz" -OutFile "helm-$ReleaseCandidateName-windows-amd64.tar.gz" +PS C:\> Invoke-WebRequest -Uri "https://get.helm.sh/helm-$RELEASE_CANDIDATE_NAME-windows-amd64.tar.gz" -OutFile "helm-$ReleaseCandidateName-windows-amd64.tar.gz" ``` Then, unpack and move the binary to somewhere on your $PATH, or move it somewhere and add it to your $PATH (e.g. /usr/local/bin/helm for linux/macOS, C:\Program Files\helm\helm.exe for Windows). @@ -230,9 +205,9 @@ The community keeps growing, and we'd love to see you there. Download Helm X.Y. The common platform binaries are here: -- [OSX](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-darwin-amd64.tar.gz) -- [Linux](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-amd64.tar.gz) -- [Windows](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-windows-amd64.tar.gz) +- [OSX](https://get.helm.sh/helm-vX.Y.Z-darwin-amd64.tar.gz) +- [Linux](https://get.helm.sh/helm-vX.Y.Z-linux-amd64.tar.gz) +- [Windows](https://get.helm.sh/helm-vX.Y.Z-windows-amd64.tar.gz) The [Quickstart Guide](https://docs.helm.sh/using_helm/#quickstart-guide) will get you going from there. For **upgrade instructions** or detailed installation notes, check the [install guide](https://docs.helm.sh/using_helm/#installing-helm). You can also use a [script to install](https://raw.githubusercontent.com/helm/helm/master/scripts/get) on any system with `bash`. diff --git a/pkg/action/printer.go b/pkg/action/printer.go index 30f46b0ee..abc0ce243 100644 --- a/pkg/action/printer.go +++ b/pkg/action/printer.go @@ -56,6 +56,10 @@ func PrintRelease(out io.Writer, rel *release.Release) { formatTestResults(lastRun.Results)) } + if strings.EqualFold(rel.Info.Description, "Dry run complete") { + fmt.Fprintf(out, "MANIFEST:\n%s\n", rel.Manifest) + } + if len(rel.Info.Notes) > 0 { fmt.Fprintf(out, "NOTES:\n%s\n", strings.TrimSpace(rel.Info.Notes)) } diff --git a/pkg/chart/loader/load.go b/pkg/chart/loader/load.go index cd886d8c7..2642d87b6 100644 --- a/pkg/chart/loader/load.go +++ b/pkg/chart/loader/load.go @@ -79,6 +79,12 @@ func LoadFiles(files []*BufferedFile) (*chart.Chart, error) { if err := yaml.Unmarshal(f.Data, c.Metadata); err != nil { return c, errors.Wrap(err, "cannot load Chart.yaml") } + // NOTE(bacongobbler): while the chart specification says that APIVersion must be set, + // Helm 2 accepted charts that did not provide an APIVersion in their chart metadata. + // Because of that, if APIVersion is unset, we should assume we're loading a v1 chart. + if c.Metadata.APIVersion == "" { + c.Metadata.APIVersion = chart.APIVersionV1 + } case f.Name == "Chart.lock": c.Lock = new(chart.Lock) if err := yaml.Unmarshal(f.Data, &c.Lock); err != nil { diff --git a/pkg/chart/loader/testdata/albatross/Chart.yaml b/pkg/chart/loader/testdata/albatross/Chart.yaml index b5188fde0..eeef737ff 100644 --- a/pkg/chart/loader/testdata/albatross/Chart.yaml +++ b/pkg/chart/loader/testdata/albatross/Chart.yaml @@ -1,4 +1,3 @@ -apiVersion: v1 name: albatross description: A Helm chart for Kubernetes version: 0.1.0 diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go index 17d6e2bb2..9caa3f87e 100644 --- a/pkg/chartutil/chartfile.go +++ b/pkg/chartutil/chartfile.go @@ -88,8 +88,7 @@ func IsChartDir(dirName string) (bool, error) { // // Application chart type is only installable func IsChartInstallable(chart *chart.Chart) (bool, error) { - chartType := chart.Metadata.Type - if strings.EqualFold(chartType, "library") { + if IsLibraryChart(chart) { return false, errors.New("Library charts are not installable") } validChartType, _ := IsValidChartType(chart) @@ -110,3 +109,16 @@ func IsValidChartType(chart *chart.Chart) (bool, error) { } return true, nil } + +// IsLibraryChart returns true if the chart is a library chart +func IsLibraryChart(c *chart.Chart) bool { + return strings.EqualFold(c.Metadata.Type, "library") +} + +// IsTemplateValid returns true if the template is valid for the chart type +func IsTemplateValid(templateName string, isLibChart bool) bool { + if isLibChart { + return strings.HasPrefix(filepath.Base(templateName), "_") + } + return true +} diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index b1bc93040..4d3e418bc 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -268,8 +268,12 @@ func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil. recAllTpls(child, templates, next) } + isLibChart := chartutil.IsLibraryChart(c) newParentID := c.ChartFullPath() for _, t := range c.Templates { + if !chartutil.IsTemplateValid(t.Name, isLibChart) { + continue + } templates[path.Join(newParentID, t.Name)] = renderable{ tpl: string(t.Data), vals: next, diff --git a/pkg/lint/lint_test.go b/pkg/lint/lint_test.go index e4fc67152..962a9ca41 100644 --- a/pkg/lint/lint_test.go +++ b/pkg/lint/lint_test.go @@ -35,12 +35,12 @@ const goodChartDir = "rules/testdata/goodone" func TestBadChart(t *testing.T) { m := All(badChartDir, values, namespace, strict).Messages - if len(m) != 5 { + if len(m) != 6 { t.Errorf("Number of errors %v", len(m)) t.Errorf("All didn't fail with expected errors, got %#v", m) } // There should be one INFO, 2 WARNINGs and one ERROR messages, check for them - var i, w, e, e2, e3 bool + var i, w, e, e2, e3, e4 bool for _, msg := range m { if msg.Severity == support.InfoSev { if strings.Contains(msg.Err.Error(), "icon is recommended") { @@ -62,9 +62,13 @@ func TestBadChart(t *testing.T) { if strings.Contains(msg.Err.Error(), "directory name (badchartfile) and chart name () must be the same") { e3 = true } + + if strings.Contains(msg.Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { + e4 = true + } } } - if !e || !e2 || !e3 || !w || !i { + if !e || !e2 || !e3 || !e4 || !w || !i { t.Errorf("Didn't find all the expected errors, got %#v", m) } } diff --git a/pkg/lint/rules/chartfile.go b/pkg/lint/rules/chartfile.go index f1abf55d4..da0675868 100644 --- a/pkg/lint/rules/chartfile.go +++ b/pkg/lint/rules/chartfile.go @@ -17,6 +17,7 @@ limitations under the License. package rules // import "helm.sh/helm/pkg/lint/rules" import ( + "fmt" "os" "path/filepath" @@ -48,6 +49,7 @@ func Chartfile(linter *support.Linter) { linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartNameDirMatch(linter.ChartDir, chartFile)) // Chart metadata + linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartAPIVersion(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartVersion(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartMaintainer(chartFile)) linter.RunLinterRule(support.ErrorSev, chartFileName, validateChartSources(chartFile)) @@ -85,6 +87,18 @@ func validateChartNameDirMatch(chartDir string, cf *chart.Metadata) error { return nil } +func validateChartAPIVersion(cf *chart.Metadata) error { + if cf.APIVersion == "" { + return errors.New("apiVersion is required. The value must be either \"v1\" or \"v2\"") + } + + if cf.APIVersion != "v1" && cf.APIVersion != "v2" { + return fmt.Errorf("apiVersion '%s' is not valid. The value must be either \"v1\" or \"v2\"", cf.APIVersion) + } + + return nil +} + func validateChartVersion(cf *chart.Metadata) error { if cf.Version == "" { return errors.New("version is required") diff --git a/pkg/lint/rules/chartfile_test.go b/pkg/lint/rules/chartfile_test.go index e9ef3a29d..4e71b860a 100644 --- a/pkg/lint/rules/chartfile_test.go +++ b/pkg/lint/rules/chartfile_test.go @@ -209,8 +209,8 @@ func TestChartfile(t *testing.T) { Chartfile(&linter) msgs := linter.Messages - if len(msgs) != 4 { - t.Errorf("Expected 3 errors, got %d", len(msgs)) + if len(msgs) != 5 { + t.Errorf("Expected 4 errors, got %d", len(msgs)) } if !strings.Contains(msgs[0].Err.Error(), "name is required") { @@ -221,12 +221,16 @@ func TestChartfile(t *testing.T) { t.Errorf("Unexpected message 1: %s", msgs[1].Err) } - if !strings.Contains(msgs[2].Err.Error(), "version 0.0.0 is less than or equal to 0") { + if !strings.Contains(msgs[2].Err.Error(), "apiVersion is required. The value must be either \"v1\" or \"v2\"") { t.Errorf("Unexpected message 2: %s", msgs[2].Err) } - if !strings.Contains(msgs[3].Err.Error(), "icon is recommended") { - t.Errorf("Unexpected message 3: %s", msgs[3].Err) + if !strings.Contains(msgs[3].Err.Error(), "version 0.0.0 is less than or equal to 0") { + t.Errorf("Unexpected message 3: %s", msgs[2].Err) + } + + if !strings.Contains(msgs[4].Err.Error(), "icon is recommended") { + t.Errorf("Unexpected message 4: %s", msgs[3].Err) } } diff --git a/pkg/lint/rules/testdata/badchartfile/Chart.yaml b/pkg/lint/rules/testdata/badchartfile/Chart.yaml index b64052eb9..dbb4a1501 100644 --- a/pkg/lint/rules/testdata/badchartfile/Chart.yaml +++ b/pkg/lint/rules/testdata/badchartfile/Chart.yaml @@ -1,4 +1,3 @@ -apiVersion: v1 description: A Helm chart for Kubernetes version: 0.0.0 home: "" diff --git a/scripts/get b/scripts/get index 4981d81d3..e8dd25d99 100755 --- a/scripts/get +++ b/scripts/get @@ -109,7 +109,7 @@ checkHelmInstalledVersion() { # for that binary. downloadFile() { HELM_DIST="helm-$TAG-$OS-$ARCH.tar.gz" - DOWNLOAD_URL="https://kubernetes-helm.storage.googleapis.com/$HELM_DIST" + DOWNLOAD_URL="https://get.helm.sh/$HELM_DIST" CHECKSUM_URL="$DOWNLOAD_URL.sha256" HELM_TMP_ROOT="$(mktemp -dt helm-installer-XXXXXX)" HELM_TMP_FILE="$HELM_TMP_ROOT/$HELM_DIST"