diff --git a/cmd/helm/create.go b/cmd/helm/create.go index e6a91b121..2e4ddebc5 100644 --- a/cmd/helm/create.go +++ b/cmd/helm/create.go @@ -78,6 +78,7 @@ func (o *createOptions) run(out io.Writer) error { cfile := &chart.Metadata{ Name: chartname, Description: "A Helm chart for Kubernetes", + Type: "application", Version: "0.1.0", AppVersion: "1.0", APIVersion: chart.APIVersionv1, diff --git a/cmd/helm/install.go b/cmd/helm/install.go index a0b23a829..18be2d299 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -35,6 +35,7 @@ import ( "k8s.io/helm/pkg/action" "k8s.io/helm/pkg/chart" "k8s.io/helm/pkg/chart/loader" + "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/downloader" "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/hapi/release" @@ -231,6 +232,11 @@ func (o *installOptions) run(out io.Writer) error { return err } + validInstallableChart, err := chartutil.IsChartInstallable(chartRequested) + if !validInstallableChart { + return err + } + if req := chartRequested.Metadata.Dependencies; req != nil { // If checkDependencies returns an error, we have unfulfilled dependencies. // As of Helm 2.4.0, this is treated as a stopping condition: diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index 9606a4d00..1a42c95f1 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -119,6 +119,25 @@ func TestInstall(t *testing.T) { cmd: "install badreq testdata/testcharts/chart-bad-requirements", wantError: true, }, + // Install, chart with library chart dependency + { + name: "install chart with library chart dependency", + cmd: "install withlibchartp testdata/testcharts/chart-with-lib-dep", + }, + // Install, library chart + { + name: "install library chart", + cmd: "install libchart testdata/testcharts/lib-chart", + wantError: true, + golden: "output/template-lib-chart.txt", + }, + // Install, chart with bad type + { + name: "install chart with bad type", + cmd: "install badtype testdata/testcharts/chart-bad-type", + wantError: true, + golden: "output/install-chart-bad-type.txt", + }, } runTestActionCmd(t, tests) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index c5ec6e36a..04395607d 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -134,6 +134,11 @@ func (o *packageOptions) run(out io.Writer) error { return err } + validChartType, err := chartutil.IsValidChartType(ch) + if !validChartType { + return err + } + overrideVals, err := o.mergedValues() if err != nil { return err diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index f6a35ac77..c7976ad80 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -147,6 +147,11 @@ func TestPackage(t *testing.T) { expect: fmt.Sprintf("does-not-exist: %s", statFileMsg), err: true, }, + { + name: "package testdata/testcharts/chart-bad-type", + args: []string{"testdata/testcharts/chart-bad-type"}, + err: true, + }, } origDir, err := os.Getwd() diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 20e556401..f89aaed2b 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -157,6 +157,11 @@ func (o *templateOptions) run(out io.Writer) error { return err } + validInstallableChart, err := chartutil.IsChartInstallable(c) + if !validInstallableChart { + return err + } + if req := c.Metadata.Dependencies; req != nil { if err := checkDependencies(c, req); err != nil { return err diff --git a/cmd/helm/template_test.go b/cmd/helm/template_test.go index 6dcc71fa3..d34409e44 100644 --- a/cmd/helm/template_test.go +++ b/cmd/helm/template_test.go @@ -76,6 +76,18 @@ func TestTemplateCmd(t *testing.T) { wantError: true, golden: "output/template-no-args.txt", }, + { + name: "check library chart", + cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/lib-chart"), + wantError: true, + golden: "output/template-lib-chart.txt", + }, + { + name: "check chart bad type", + cmd: fmt.Sprintf("template '%s'", "testdata/testcharts/chart-bad-type"), + wantError: true, + golden: "output/install-chart-bad-type.txt", + }, } runTestCmd(t, tests) } diff --git a/cmd/helm/testdata/output/install-chart-bad-type.txt b/cmd/helm/testdata/output/install-chart-bad-type.txt new file mode 100644 index 000000000..22308ac51 --- /dev/null +++ b/cmd/helm/testdata/output/install-chart-bad-type.txt @@ -0,0 +1 @@ +Error: Invalid chart types are not installable diff --git a/cmd/helm/testdata/output/template-lib-chart.txt b/cmd/helm/testdata/output/template-lib-chart.txt new file mode 100644 index 000000000..5f119cb51 --- /dev/null +++ b/cmd/helm/testdata/output/template-lib-chart.txt @@ -0,0 +1 @@ +Error: Library charts are not installable diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml new file mode 100644 index 000000000..95ddba77a --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-type/Chart.yaml @@ -0,0 +1,7 @@ +description: Deploy a basic Alpine Linux pod +home: https://k8s.io/helm +name: chart-bad-type +sources: +- https://github.com/helm/helm +version: 0.1.0 +type: foobar diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/README.md b/cmd/helm/testdata/testcharts/chart-bad-type/README.md new file mode 100644 index 000000000..3c32de5db --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-type/README.md @@ -0,0 +1,13 @@ +#Alpine: A simple Helm chart + +Run a single pod of Alpine Linux. + +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.yaml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/extra_values.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/extra_values.yaml new file mode 100644 index 000000000..468bbacbc --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-type/extra_values.yaml @@ -0,0 +1,2 @@ +test: + Name: extra-values diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/more_values.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/more_values.yaml new file mode 100644 index 000000000..3d21e1fed --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-type/more_values.yaml @@ -0,0 +1,2 @@ +test: + Name: more-values diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/templates/alpine-pod.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/templates/alpine-pod.yaml new file mode 100644 index 000000000..ee61f2056 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-type/templates/alpine-pod.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{.Release.Name}}-{{.Values.Name}}" + labels: + # The "heritage" label is used to track which tool deployed a given chart. + # It is useful for admins who want to see what releases a particular tool + # is responsible for. + heritage: {{.Release.Service | quote }} + # The "release" convention makes it easy to tie a release to all of the + # Kubernetes resources that were created as part of that release. + release: {{.Release.Name | quote }} + # This makes it easy to audit chart usage. + chart: "{{.Chart.Name}}-{{.Chart.Version}}" + values: {{.Values.test.Name}} +spec: + # This shows how to use a simple value. This will look for a passed-in value + # called restartPolicy. If it is not found, it will use the default value. + # {{default "Never" .restartPolicy}} is a slightly optimized version of the + # more conventional syntax: {{.restartPolicy | default "Never"}} + restartPolicy: {{default "Never" .Values.restartPolicy}} + containers: + - name: waiter + image: "alpine:3.3" + command: ["/bin/sleep","9000"] diff --git a/cmd/helm/testdata/testcharts/chart-bad-type/values.yaml b/cmd/helm/testdata/testcharts/chart-bad-type/values.yaml new file mode 100644 index 000000000..807e12aea --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-type/values.yaml @@ -0,0 +1 @@ +Name: my-alpine diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/.helmignore b/cmd/helm/testdata/testcharts/chart-with-lib-dep/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-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-lib-dep/Chart.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/Chart.yaml new file mode 100644 index 000000000..773cc9f32 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-lib-dep/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v1 +appVersion: "1.0" +description: A Helm chart for Kubernetes +name: chart-with-lib-dep +type: application +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/charts/common-0.0.5.tgz b/cmd/helm/testdata/testcharts/chart-with-lib-dep/charts/common-0.0.5.tgz new file mode 100644 index 000000000..ca0a64ae3 Binary files /dev/null and b/cmd/helm/testdata/testcharts/chart-with-lib-dep/charts/common-0.0.5.tgz differ diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/NOTES.txt b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/NOTES.txt new file mode 100644 index 000000000..a758b7971 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-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-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-lib-dep.fullname" . }}' + export SERVICE_IP=$(kubectl get svc {{ template "chart-with-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-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-lib-dep/templates/_helpers.tpl b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/_helpers.tpl new file mode 100644 index 000000000..b8be8cad6 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/_helpers.tpl @@ -0,0 +1,32 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "chart-with-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-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-lib-dep.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/deployment.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/deployment.yaml new file mode 100644 index 000000000..ff63aefc2 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/deployment.yaml @@ -0,0 +1,51 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: {{ template "chart-with-lib-dep.fullname" . }} + labels: + app: {{ template "chart-with-lib-dep.name" . }} + chart: {{ template "chart-with-lib-dep.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + app: {{ template "chart-with-lib-dep.name" . }} + release: {{ .Release.Name }} + template: + metadata: + labels: + app: {{ template "chart-with-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-lib-dep/templates/ingress.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/ingress.yaml new file mode 100644 index 000000000..0330f2fef --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/ingress.yaml @@ -0,0 +1,38 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "chart-with-lib-dep.fullname" . -}} +{{- $ingressPath := .Values.ingress.path -}} +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + app: {{ template "chart-with-lib-dep.name" . }} + chart: {{ template "chart-with-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-lib-dep/templates/service.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/service.yaml new file mode 100644 index 000000000..4c2b91a5a --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-lib-dep/templates/service.yaml @@ -0,0 +1,10 @@ +{{- 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 -}} diff --git a/cmd/helm/testdata/testcharts/chart-with-lib-dep/values.yaml b/cmd/helm/testdata/testcharts/chart-with-lib-dep/values.yaml new file mode 100644 index 000000000..a0cc07e9e --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-with-lib-dep/values.yaml @@ -0,0 +1,48 @@ +# Default values for chart-with-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/testdata/testcharts/lib-chart/.helmignore b/cmd/helm/testdata/testcharts/lib-chart/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/.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/lib-chart/Chart.yaml b/cmd/helm/testdata/testcharts/lib-chart/Chart.yaml new file mode 100755 index 000000000..4dcddc85e --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/Chart.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +description: Common chartbuilding components and helpers +name: lib-chart +version: 0.0.5 +appVersion: 0.0.5 +home: https://helm.sh +maintainers: +- name: technosophos + email: technosophos@gmail.com +- name: prydonius + email: adnan@bitnami.com +type: Library diff --git a/cmd/helm/testdata/testcharts/lib-chart/README.md b/cmd/helm/testdata/testcharts/lib-chart/README.md new file mode 100644 index 000000000..ca0459474 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_chartref.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_chartref.tpl new file mode 100644 index 000000000..e6c14866f --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_configmap.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_configmap.yaml new file mode 100644 index 000000000..03dbbf858 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_container.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_container.yaml new file mode 100644 index 000000000..540eb0e6a --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_deployment.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_deployment.yaml new file mode 100644 index 000000000..c49dae3eb --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_envvar.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_envvar.tpl new file mode 100644 index 000000000..709251f8f --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_fullname.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_fullname.tpl new file mode 100644 index 000000000..2da6cdf18 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_ingress.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_ingress.yaml new file mode 100644 index 000000000..522ab2425 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_metadata.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata.yaml new file mode 100644 index 000000000..f96ed09fe --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_metadata_annotations.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_annotations.tpl new file mode 100644 index 000000000..0c3b61c7c --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_metadata_labels.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_metadata_labels.tpl new file mode 100644 index 000000000..15fe00c5f --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_name.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_name.tpl new file mode 100644 index 000000000..1d42fb068 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_persistentvolumeclaim.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_persistentvolumeclaim.yaml new file mode 100644 index 000000000..6c1578c7e --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_secret.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_secret.yaml new file mode 100644 index 000000000..0615d35cb --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_service.yaml b/cmd/helm/testdata/testcharts/lib-chart/templates/_service.yaml new file mode 100644 index 000000000..67379525f --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_util.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_util.tpl new file mode 100644 index 000000000..a7d4cc751 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/templates/_volume.tpl b/cmd/helm/testdata/testcharts/lib-chart/templates/_volume.tpl new file mode 100644 index 000000000..521a1f48b --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/lib-chart/values.yaml b/cmd/helm/testdata/testcharts/lib-chart/values.yaml new file mode 100644 index 000000000..b7cf514d5 --- /dev/null +++ b/cmd/helm/testdata/testcharts/lib-chart/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/docs/charts.md b/docs/charts.md index 34d92b850..096b8ae6f 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -44,6 +44,7 @@ name: The name of the chart (required) version: A SemVer 2 version (required) kubeVersion: A SemVer range of compatible Kubernetes versions (optional) description: A single-sentence description of this project (optional) +type: It is the type of chart (optional) keywords: - A list of keywords about this project (optional) home: The URL of this project's home page (optional) @@ -117,6 +118,15 @@ project is: - Release the new chart version in the Chart Repository - Remove the chart from the source repository (e.g. git) +### Chart Types + +The `type` field defines the type of chart. There are 2 types: `application` +and `library`. Application is the default type and it is the standard chart +which can be operated on fully. The [library or helper chart](https://github.com/helm/charts/tree/master/incubator/common) +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. + ## Chart LICENSE, README and NOTES Charts can also contain files that describe the installation, configuration, usage and license of a diff --git a/pkg/chart/metadata.go b/pkg/chart/metadata.go index ef89b0096..95dfe88ef 100644 --- a/pkg/chart/metadata.go +++ b/pkg/chart/metadata.go @@ -62,4 +62,6 @@ type Metadata struct { KubeVersion string `json:"kubeVersion,omitempty"` // Dependencies are a list of dependencies for a chart. Dependencies []*Dependency `json:"dependencies,omitempty"` + // Specifies the chart type: application or library + Type string `json:"type,omitempty"` } diff --git a/pkg/chartutil/chartfile.go b/pkg/chartutil/chartfile.go index 38bcd54db..7e90966b4 100644 --- a/pkg/chartutil/chartfile.go +++ b/pkg/chartutil/chartfile.go @@ -20,6 +20,7 @@ import ( "io/ioutil" "os" "path/filepath" + "strings" "github.com/ghodss/yaml" "github.com/pkg/errors" @@ -82,3 +83,30 @@ func IsChartDir(dirName string) (bool, error) { return true, nil } + +// IsChartInstallable validates if a chart can be installed +// +// Application chart type is only installable +func IsChartInstallable(chart *chart.Chart) (bool, error) { + chartType := chart.Metadata.Type + if strings.EqualFold(chartType, "library") { + return false, errors.New("Library charts are not installable") + } + validChartType, _ := IsValidChartType(chart) + if !validChartType { + return false, errors.New("Invalid chart types are not installable") + } + return true, nil +} + +// IsValidChartType validates the chart type +// +// Valid types are: application or library +func IsValidChartType(chart *chart.Chart) (bool, error) { + chartType := chart.Metadata.Type + if chartType != "" && !strings.EqualFold(chartType, "library") && + !strings.EqualFold(chartType, "application") { + return false, errors.New("Invalid chart type. Valid types are: application or library") + } + return true, nil +}