Merge branch 'dev-v3' of github.com:helm/helm into feat-v3/oci-registry-mediatype

pull/5719/head
Josh Dolitsky 6 years ago
commit 04a3be1e69

@ -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

@ -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")

@ -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

@ -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

@ -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)
}

@ -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:
{}

@ -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:
{}

@ -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

@ -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

@ -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 }}

@ -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 -}}

@ -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 }}

@ -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 }}

@ -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 -}}

@ -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: {}

@ -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

@ -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

@ -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

@ -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

@ -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)

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 }}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -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 -}}

@ -0,0 +1,6 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: common-configmap
data:
myvalue: "Hello World"

@ -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

@ -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 }}

@ -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 -}}

@ -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 }}

@ -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 }}

@ -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 -}}

@ -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: {}

@ -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
}

@ -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")

@ -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

@ -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 werent 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 Tillers 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.
Tillers 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. Helms 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 Helms 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.
- Helms 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 its 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.
Dockers 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 charts 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
```
Were 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

@ -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)

@ -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`.

@ -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))
}

@ -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 {

@ -1,4 +1,3 @@
apiVersion: v1
name: albatross
description: A Helm chart for Kubernetes
version: 0.1.0

@ -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
}

@ -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,

@ -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)
}
}

@ -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")

@ -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)
}
}

@ -1,4 +1,3 @@
apiVersion: v1
description: A Helm chart for Kubernetes
version: 0.0.0
home: ""

@ -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"

Loading…
Cancel
Save