Merge branch 'master' into feat/add-hpa-and-env

pull/5110/head
Naseem 6 years ago committed by GitHub
commit f9d6aadbb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,17 +0,0 @@
version: "{build}"
clone_folder: c:\go\src\k8s.io\helm
environment:
GOPATH: c:\go
PATH: c:\ProgramData\bin;$(PATH)
install:
- ps: iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/fishworks/gofish/master/scripts/install.ps1'))
- gofish init
- gofish install glide
- glide install --strip-vendor
cache:
- vendor -> glide.lock
build: "off"
deploy: "off"
test_script:
- go build .\cmd\...
- go test .\...

@ -4,13 +4,13 @@ jobs:
working_directory: /go/src/k8s.io/helm
parallelism: 3
docker:
- image: golang:1.11
- image: golang:1.12.5
environment:
PROJECT_NAME: "kubernetes-helm"
steps:
- checkout
- setup_remote_docker:
version: 17.09.0-ce
version: 18.06.0-ce
- restore_cache:
keys:
- glide-{{ checksum "glide.yaml" }}-{{ checksum "glide.lock" }}

5
.gitignore vendored

@ -12,3 +12,8 @@ vendor/
*.exe
.idea/
*.iml
*.swp
*~
.classpath
.project
.settings/**

@ -1,6 +1,6 @@
# Contributing Guidelines
The Kubernetes Helm project accepts contributions via GitHub pull requests. This document outlines the process to help get your contribution accepted.
The Helm project accepts contributions via GitHub pull requests. This document outlines the process to help get your contribution accepted.
## Reporting a Security Issue
@ -15,7 +15,7 @@ us a chance to try to fix the issue before it is exploited in the wild.
The sign-off is a simple line at the end of the explanation for a commit. All
commits needs to be signed. Your signature certifies that you wrote the patch or
otherwise have the right to contribute the material. The rules are pretty simple,
if you can certify the below (from [developercertificate.org](http://developercertificate.org/)):
if you can certify the below (from [developercertificate.org](https://developercertificate.org/)):
```
Developer Certificate of Origin
@ -172,10 +172,44 @@ contributing to Helm. All issue types follow the same general lifecycle. Differe
## How to Contribute a Patch
1. Fork the repo, develop and test your code changes.
1. Use sign-off when making each of your commits (see [above](#sign-your-work)).
1. **Fork** the repo [helm](https://github.com/helm/helm)
Go to https://github.com/helm/helm then hit the `Fork` button to fork your own copy of repository **helm** to your github account.
2. **Clone** the forked repo to your local working directory.
```sh
$ git clone https://github.com/$your_github_account/helm.git
```
3. Add an `upstream` remote to keep your fork in sync with the main repo.
```sh
$ cd helm
$ git remote add upstream https://github.com/helm/helm.git
$ git remote -v
origin https://github.com/$your_github_account/helm.git (fetch)
origin https://github.com/$your_github_account/helm.git (push)
upstream https://github.com/helm/helm.git (fetch)
upstream https://github.com/helm/helm.git (push)
```
4. Sync your local `master` branch.
```sh
$ git pull upstream master
```
5. Create a branch to add a new feature or fix issues.
```sh
$ git checkout -b new-feature
```
6. Make any change on the branch `new-feature` then build and test your codes.
7. Include in what will be committed.
```sh
$ git add <file>
```
8. Use sign-off when making each of your commits (see [above](#sign-your-work)).
If you forgot to sign some commits that are part of the contribution, you can ask [git to rewrite your commit history](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History).
1. Submit a pull request.
```sh
$ git commit --signoff
```
9. Submit a pull request.
Coding conventions and standards are explained in the official developer docs:
[Developers Guide](docs/developers.md)
@ -242,7 +276,7 @@ Documentation PRs will follow the same lifecycle as other PRs. They will also be
## The Triager
Each week, one of the core maintainers will serve as the designated "triager" starting after the
public standup meetings on Thursday. This person will be in charge triaging new PRs and issues
public stand-up meetings on Thursday. This person will be in charge triaging new PRs and issues
throughout the work week.
## Labels

@ -1,9 +1,10 @@
DOCKER_REGISTRY ?= gcr.io
IMAGE_PREFIX ?= kubernetes-helm
DEV_IMAGE ?= golang:1.11
DEV_IMAGE ?= golang:1.12.5
SHORT_NAME ?= tiller
SHORT_NAME_RUDDER ?= rudder
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
TARGET_OBJS ?= darwin-amd64.tar.gz darwin-amd64.tar.gz.sha256 linux-amd64.tar.gz linux-amd64.tar.gz.sha256 linux-386.tar.gz linux-386.tar.gz.sha256 linux-arm.tar.gz linux-arm.tar.gz.sha256 linux-arm64.tar.gz linux-arm64.tar.gz.sha256 linux-ppc64le.tar.gz linux-ppc64le.tar.gz.sha256 linux-s390x.tar.gz linux-s390x.tar.gz.sha256 windows-amd64.zip windows-amd64.zip.sha256
DIST_DIRS = find * -type d -exec
# go option
@ -44,10 +45,25 @@ dist:
$(DIST_DIRS) zip -r helm-${VERSION}-{}.zip {} \; \
)
.PHONY: fetch-dist
fetch-dist:
mkdir -p _dist
cd _dist && \
for obj in ${TARGET_OBJS} ; do \
curl -sSL -o helm-${VERSION}-$${obj} https://storage.googleapis.com/kubernetes-helm/helm-${VERSION}-$${obj} ; \
done
.PHONY: sign
sign:
for f in _dist/*.{gz,zip,sha256} ; do \
gpg --armor --detach-sign $${f} ; \
done
.PHONY: checksum
checksum:
for f in _dist/*.{gz,zip} ; do \
shasum -a 256 "$${f}" | awk '{print $$1}' > "$${f}.sha256" ; \
echo -n "Checksum: " && cat $${f}.sha256 ; \
done
.PHONY: check-docker

@ -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,6 +13,7 @@ reviewers:
- viglesiasce
emeritus:
- migmartri
- nebril
- seh
- vaikas-google
- rimusz

@ -3,8 +3,8 @@ Protobuf3 type declarations for the Helm API
Packages
- `hapi.chart` Complete serialization of Heml charts
- `hapi.chart` Complete serialization of Helm charts
- `hapi.release` Information about installed charts (Releases) such as metadata about when they were installed, their status, and how they were configured.
- `hapi.services.rudder` Definition for the ReleaseModuleService used by Tiller to manipulate releases on a given node
- `hapi.services.tiller` Definition of the ReleaseService provided by Tiller and used by Helm clients to manipulate releases cluster wide.
- `hapi.version` Version meta-data used by tiller to express it's version
- `hapi.version` Version metadata used by Tiller to express its version

@ -29,7 +29,7 @@ message Status {
UNKNOWN = 0;
// Status_DEPLOYED indicates that the release has been pushed to Kubernetes.
DEPLOYED = 1;
// Status_DELETED indicates that a release has been deleted from Kubermetes.
// Status_DELETED indicates that a release has been deleted from Kubernetes.
DELETED = 2;
// Status_SUPERSEDED indicates that this release object is outdated and a newer one exists.
SUPERSEDED = 3;

@ -92,6 +92,7 @@ message UpgradeReleaseRequest{
bool Wait = 4;
bool Recreate = 5;
bool Force = 6;
bool CleanupOnFail = 7;
}
message UpgradeReleaseResponse{
hapi.release.Release release = 1;
@ -105,6 +106,7 @@ message RollbackReleaseRequest{
bool Wait = 4;
bool Recreate = 5;
bool Force = 6;
bool CleanupOnFail = 7;
}
message RollbackReleaseResponse{
hapi.release.Release release = 1;

@ -214,6 +214,8 @@ message UpdateReleaseRequest {
string description = 12;
// Render subchart notes if enabled
bool subNotes = 13;
// Allow deletion of new resources created in this update when update failed
bool cleanup_on_fail = 14;
}
// UpdateReleaseResponse is the response to an update request.
@ -241,6 +243,8 @@ message RollbackReleaseRequest {
bool force = 8;
// Description, if set, will set the description for the rollback
string description = 9;
// Allow deletion of new resources created in this rollback when rollback failed
bool cleanup_on_fail = 10;
}
// RollbackReleaseResponse is the response to an update request.

@ -212,7 +212,8 @@ __helm_convert_bash_to_zsh() {
-e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \
-e "s/${LWORD}declare${RWORD}/__helm_declare/g" \
-e "s/\\\$(type${RWORD}/\$(__helm_type/g" \
-e 's/aliashash\["\(\w\+\)"\]/aliashash[\1]/g' \
-e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \
-e 's/FUNCNAME/funcstack/g' \
<<'BASH_COMPLETION_EOF'
`
out.Write([]byte(zshInitialization))

@ -73,7 +73,7 @@ the dependency charts stored locally. The path should start with a prefix of
repository: "file://../dependency_chart/nginx"
If the dependency chart is retrieved locally, it is not required to have the
repository added to helm by "helm add repo". Version matching is also supported
repository added to helm by "helm repo add". Version matching is also supported
for this case.
`

@ -29,11 +29,11 @@ const dependencyBuildDesc = `
Build out the charts/ directory from the requirements.lock file.
Build is used to reconstruct a chart's dependencies to the state specified in
the lock file. This will not re-negotiate dependencies, as 'helm dependency update'
does.
the lock file.
If no lock file is found, 'helm dependency build' will mirror the behavior
of 'helm dependency update'.
If no lock file is found, 'helm dependency build' will mirror the behavior of
the 'helm dependency update' command. This means it will update the on-disk
dependencies to mirror the requirements.yaml file and generate a lock file.
`
type dependencyBuildCmd struct {

@ -45,6 +45,7 @@ type getCmd struct {
out io.Writer
client helm.Interface
version int32
template string
}
func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -73,6 +74,7 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int32Var(&get.version, "revision", 0, "get the named release with revision")
f.StringVar(&get.template, "template", "", "go template for formatting the output, eg: {{.Release.Name}}")
cmd.AddCommand(newGetValuesCmd(nil, out))
cmd.AddCommand(newGetManifestCmd(nil, out))
@ -91,5 +93,9 @@ func (g *getCmd) run() error {
if err != nil {
return prettyError(err)
}
if g.template != "" {
return tpl(g.template, res, g.out)
}
return printRelease(g.out, res.Release)
}

@ -35,6 +35,13 @@ func TestGetCmd(t *testing.T) {
expected: "REVISION: 1\nRELEASED: (.*)\nCHART: foo-0.1.0-beta.1\nUSER-SUPPLIED VALUES:\nname: \"value\"\nCOMPUTED VALUES:\nname: value\n\nHOOKS:\n---\n# pre-install-hook\n" + helm.MockHookTemplate + "\nMANIFEST:",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "thomas-guide"})},
},
{
name: "get with a formatted release",
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "elevated-turkey"}),
args: []string{"elevated-turkey", "--template", "{{.Release.Chart.Metadata.Version}}"},
expected: "0.1.0-beta.1",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "elevated-turkey"})},
},
{
name: "get requires release name arg",
err: true,

@ -61,18 +61,19 @@ Common actions from this point include:
- helm list: list releases of charts
Environment:
$HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm
$HELM_HOST set an alternative Tiller host. The format is host:port
$HELM_NO_PLUGINS disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
$TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-system")
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
$HELM_TLS_CA_CERT path to TLS CA certificate used to verify the Helm client and Tiller server certificates (default "$HELM_HOME/ca.pem")
$HELM_TLS_CERT path to TLS client certificate file for authenticating to Tiller (default "$HELM_HOME/cert.pem")
$HELM_TLS_KEY path to TLS client key file for authenticating to Tiller (default "$HELM_HOME/key.pem")
$HELM_TLS_VERIFY enable TLS connection between Helm and Tiller and verify Tiller server certificate (default "false")
$HELM_TLS_ENABLE enable TLS connection between Helm and Tiller (default "false")
$HELM_KEY_PASSPHRASE set HELM_KEY_PASSPHRASE to the passphrase of your PGP private key. If set, you will not be prompted for
the passphrase while signing helm charts
- $HELM_HOME: set an alternative location for Helm files. By default, these are stored in ~/.helm
- $HELM_HOST: set an alternative Tiller host. The format is host:port
- $HELM_NO_PLUGINS: disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
- $TILLER_NAMESPACE: set an alternative Tiller namespace (default "kube-system")
- $KUBECONFIG: set an alternative Kubernetes configuration file (default "~/.kube/config")
- $HELM_TLS_CA_CERT: path to TLS CA certificate used to verify the Helm client and Tiller server certificates (default "$HELM_HOME/ca.pem")
- $HELM_TLS_CERT: path to TLS client certificate file for authenticating to Tiller (default "$HELM_HOME/cert.pem")
- $HELM_TLS_KEY: path to TLS client key file for authenticating to Tiller (default "$HELM_HOME/key.pem")
- $HELM_TLS_ENABLE: enable TLS connection between Helm and Tiller (default "false")
- $HELM_TLS_VERIFY: enable TLS connection between Helm and Tiller and verify Tiller server certificate (default "false")
- $HELM_TLS_HOSTNAME: the hostname or IP address used to verify the Tiller server certificate (default "127.0.0.1")
- $HELM_KEY_PASSPHRASE: set HELM_KEY_PASSPHRASE to the passphrase of your PGP private key. If set, you will not be prompted for the passphrase while signing helm charts
`

@ -30,6 +30,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/client-go/util/homedir"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/helmpath"
@ -137,7 +138,7 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error {
}
}
localRepoIndexFile := home.LocalRepository(localRepositoryIndexFile)
localRepoIndexFile := home.LocalRepository(installer.LocalRepositoryIndexFile)
if fi, err := os.Stat(localRepoIndexFile); err != nil {
i := repo.NewIndexFile()
if err := i.WriteFile(localRepoIndexFile, 0644); err != nil {

@ -31,11 +31,9 @@ import (
"k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/helm/portforwarder"
"k8s.io/helm/pkg/repo"
"k8s.io/helm/pkg/version"
)
@ -60,12 +58,6 @@ To dump a manifest containing the Tiller deployment YAML, combine the
'--dry-run' and '--debug' flags.
`
const (
stableRepository = "stable"
localRepository = "local"
localRepositoryIndexFile = "index.yaml"
)
var (
stableRepositoryURL = "https://kubernetes-charts.storage.googleapis.com"
// This is the IPv4 loopback, not localhost, because we have to force IPv4
@ -266,14 +258,8 @@ func (i *initCmd) run() error {
return nil
}
if err := ensureDirectories(i.home, i.out); err != nil {
return err
}
if err := ensureDefaultRepos(i.home, i.out, i.skipRefresh); err != nil {
return err
}
if err := ensureRepoFileFormat(i.home.RepositoryFile(), i.out); err != nil {
return err
if err := installer.Initialize(i.home, i.out, i.skipRefresh, settings, stableRepositoryURL, localRepositoryURL); err != nil {
return fmt.Errorf("error initializing: %s", err)
}
fmt.Fprintf(i.out, "$HELM_HOME has been configured at %s.\n", settings.Home)
@ -324,7 +310,6 @@ func (i *initCmd) run() error {
"$ helm init --tiller-image gcr.io/kubernetes-helm/tiller:v2.8.2\n\n")
}
fmt.Fprintln(i.out, "Happy Helming!")
return nil
}
@ -351,117 +336,6 @@ func (i *initCmd) ping(image string) error {
return nil
}
// ensureDirectories checks to see if $HELM_HOME exists.
//
// If $HELM_HOME does not exist, this function will create it.
func ensureDirectories(home helmpath.Home, out io.Writer) error {
configDirectories := []string{
home.String(),
home.Repository(),
home.Cache(),
home.LocalRepository(),
home.Plugins(),
home.Starters(),
home.Archive(),
}
for _, p := range configDirectories {
if fi, err := os.Stat(p); err != nil {
fmt.Fprintf(out, "Creating %s \n", p)
if err := os.MkdirAll(p, 0755); err != nil {
return fmt.Errorf("Could not create %s: %s", p, err)
}
} else if !fi.IsDir() {
return fmt.Errorf("%s must be a directory", p)
}
}
return nil
}
func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) error {
repoFile := home.RepositoryFile()
if fi, err := os.Stat(repoFile); err != nil {
fmt.Fprintf(out, "Creating %s \n", repoFile)
f := repo.NewRepoFile()
sr, err := initStableRepo(home.CacheIndex(stableRepository), out, skipRefresh, home)
if err != nil {
return err
}
lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local"), out, home)
if err != nil {
return err
}
f.Add(sr)
f.Add(lr)
if err := f.WriteFile(repoFile, 0644); err != nil {
return err
}
} else if fi.IsDir() {
return fmt.Errorf("%s must be a file, not a directory", repoFile)
}
return nil
}
func initStableRepo(cacheFile string, out io.Writer, skipRefresh bool, home helmpath.Home) (*repo.Entry, error) {
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, stableRepositoryURL)
c := repo.Entry{
Name: stableRepository,
URL: stableRepositoryURL,
Cache: cacheFile,
}
r, err := repo.NewChartRepository(&c, getter.All(settings))
if err != nil {
return nil, err
}
if skipRefresh {
return &c, nil
}
// In this case, the cacheFile is always absolute. So passing empty string
// is safe.
if err := r.DownloadIndexFile(""); err != nil {
return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", stableRepositoryURL, err.Error())
}
return &c, nil
}
func initLocalRepo(indexFile, cacheFile string, out io.Writer, home helmpath.Home) (*repo.Entry, error) {
if fi, err := os.Stat(indexFile); err != nil {
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", localRepository, localRepositoryURL)
i := repo.NewIndexFile()
if err := i.WriteFile(indexFile, 0644); err != nil {
return nil, err
}
//TODO: take this out and replace with helm update functionality
if err := createLink(indexFile, cacheFile, home); err != nil {
return nil, err
}
} else if fi.IsDir() {
return nil, fmt.Errorf("%s must be a file, not a directory", indexFile)
}
return &repo.Entry{
Name: localRepository,
URL: localRepositoryURL,
Cache: cacheFile,
}, nil
}
func ensureRepoFileFormat(file string, out io.Writer) error {
r, err := repo.LoadRepositoriesFile(file)
if err == repo.ErrRepoOutOfDate {
fmt.Fprintln(out, "Updating repository file format...")
if err := r.WriteFile(file, 0644); err != nil {
return err
}
}
return nil
}
// watchTillerUntilReady waits for the tiller pod to become available. This is useful in situations where we
// want to wait before we call New().
//

@ -28,7 +28,7 @@ import (
"github.com/ghodss/yaml"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -179,51 +179,6 @@ func TestInitCmd_dryRun(t *testing.T) {
}
}
func TestEnsureHome(t *testing.T) {
home, err := ioutil.TempDir("", "helm_home")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(home)
b := bytes.NewBuffer(nil)
hh := helmpath.Home(home)
settings.Home = hh
if err := ensureDirectories(hh, b); err != nil {
t.Error(err)
}
if err := ensureDefaultRepos(hh, b, false); err != nil {
t.Error(err)
}
if err := ensureDefaultRepos(hh, b, true); err != nil {
t.Error(err)
}
if err := ensureRepoFileFormat(hh.RepositoryFile(), b); err != nil {
t.Error(err)
}
expectedDirs := []string{hh.String(), hh.Repository(), hh.Cache(), hh.LocalRepository()}
for _, dir := range expectedDirs {
if fi, err := os.Stat(dir); err != nil {
t.Errorf("%s", err)
} else if !fi.IsDir() {
t.Errorf("%s is not a directory", fi)
}
}
if fi, err := os.Stat(hh.RepositoryFile()); err != nil {
t.Error(err)
} else if fi.IsDir() {
t.Errorf("%s should not be a directory", fi)
}
if fi, err := os.Stat(hh.LocalRepository(localRepositoryIndexFile)); err != nil {
t.Errorf("%s", err)
} else if fi.IsDir() {
t.Errorf("%s should not be a directory", fi)
}
}
func TestInitCmd_tlsOptions(t *testing.T) {
const testDir = "../../testdata"

@ -27,27 +27,26 @@ import (
"k8s.io/helm/pkg/chartutil"
)
const inspectDesc = `
const (
inspectDesc = `
This command inspects a chart and displays information. It takes a chart reference
('stable/drupal'), a full path to a directory or packaged chart, or a URL.
Inspect prints the contents of the Chart.yaml file and the values.yaml file.
`
const inspectValuesDesc = `
inspectValuesDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents
of the values.yaml file
`
const inspectChartDesc = `
inspectChartDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents
of the Charts.yaml file
`
const readmeChartDesc = `
readmeChartDesc = `
This command inspects a chart (directory, file, or URL) and displays the contents
of the README file
`
)
type inspectCmd struct {
chartpath string

@ -0,0 +1,163 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"fmt"
"io"
"os"
"k8s.io/helm/pkg/getter"
helm_env "k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/repo"
)
const (
stableRepository = "stable"
// LocalRepository is the standard name of the local repository
LocalRepository = "local"
// LocalRepositoryIndexFile is the standard name of the local repository index file
LocalRepositoryIndexFile = "index.yaml"
)
// Initialize initializes local config
//
// Returns an error if the command failed.
func Initialize(home helmpath.Home, out io.Writer, skipRefresh bool, settings helm_env.EnvSettings, stableRepositoryURL, localRepositoryURL string) error {
if err := ensureDirectories(home, out); err != nil {
return err
}
if err := ensureDefaultRepos(home, out, skipRefresh, settings, stableRepositoryURL, localRepositoryURL); err != nil {
return err
}
return ensureRepoFileFormat(home.RepositoryFile(), out)
}
// ensureDirectories checks to see if $HELM_HOME exists.
//
// If $HELM_HOME does not exist, this function will create it.
func ensureDirectories(home helmpath.Home, out io.Writer) error {
configDirectories := []string{
home.String(),
home.Repository(),
home.Cache(),
home.LocalRepository(),
home.Plugins(),
home.Starters(),
home.Archive(),
}
for _, p := range configDirectories {
if fi, err := os.Stat(p); err != nil {
fmt.Fprintf(out, "Creating %s \n", p)
if err := os.MkdirAll(p, 0755); err != nil {
return fmt.Errorf("Could not create %s: %s", p, err)
}
} else if !fi.IsDir() {
return fmt.Errorf("%s must be a directory", p)
}
}
return nil
}
func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool, settings helm_env.EnvSettings, stableRepositoryURL, localRepositoryURL string) error {
repoFile := home.RepositoryFile()
if fi, err := os.Stat(repoFile); err != nil {
fmt.Fprintf(out, "Creating %s \n", repoFile)
f := repo.NewRepoFile()
sr, err := initStableRepo(home.CacheIndex(stableRepository), home, out, skipRefresh, settings, stableRepositoryURL)
if err != nil {
return err
}
lr, err := initLocalRepo(home.LocalRepository(LocalRepositoryIndexFile), home.CacheIndex("local"), home, out, settings, localRepositoryURL)
if err != nil {
return err
}
f.Add(sr)
f.Add(lr)
if err := f.WriteFile(repoFile, 0644); err != nil {
return err
}
} else if fi.IsDir() {
return fmt.Errorf("%s must be a file, not a directory", repoFile)
}
return nil
}
func initStableRepo(cacheFile string, home helmpath.Home, out io.Writer, skipRefresh bool, settings helm_env.EnvSettings, stableRepositoryURL string) (*repo.Entry, error) {
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", stableRepository, stableRepositoryURL)
c := repo.Entry{
Name: stableRepository,
URL: stableRepositoryURL,
Cache: cacheFile,
}
r, err := repo.NewChartRepository(&c, getter.All(settings))
if err != nil {
return nil, err
}
if skipRefresh {
return &c, nil
}
// In this case, the cacheFile is always absolute. So passing empty string
// is safe.
if err := r.DownloadIndexFile(""); err != nil {
return nil, fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", stableRepositoryURL, err.Error())
}
return &c, nil
}
func initLocalRepo(indexFile, cacheFile string, home helmpath.Home, out io.Writer, settings helm_env.EnvSettings, localRepositoryURL string) (*repo.Entry, error) {
if fi, err := os.Stat(indexFile); err != nil {
fmt.Fprintf(out, "Adding %s repo with URL: %s \n", LocalRepository, localRepositoryURL)
i := repo.NewIndexFile()
if err := i.WriteFile(indexFile, 0644); err != nil {
return nil, err
}
//TODO: take this out and replace with helm update functionality
if err := createLink(indexFile, cacheFile, home); err != nil {
return nil, err
}
} else if fi.IsDir() {
return nil, fmt.Errorf("%s must be a file, not a directory", indexFile)
}
return &repo.Entry{
Name: LocalRepository,
URL: localRepositoryURL,
Cache: cacheFile,
}, nil
}
func ensureRepoFileFormat(file string, out io.Writer) error {
r, err := repo.LoadRepositoriesFile(file)
if err == repo.ErrRepoOutOfDate {
fmt.Fprintln(out, "Updating repository file format...")
if err := r.WriteFile(file, 0644); err != nil {
return err
}
}
return nil
}

@ -0,0 +1,120 @@
/*
Copyright The Helm Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"bytes"
"io/ioutil"
"os"
"testing"
helm_env "k8s.io/helm/pkg/helm/environment"
"k8s.io/helm/pkg/helm/helmpath"
)
func TestInitialize(t *testing.T) {
home, err := ioutil.TempDir("", "helm_home")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(home)
b := bytes.NewBuffer(nil)
hh := helmpath.Home(home)
settings := helm_env.EnvSettings{
Home: hh,
}
stableRepositoryURL := "https://kubernetes-charts.storage.googleapis.com"
localRepositoryURL := "http://127.0.0.1:8879/charts"
if err := Initialize(hh, b, false, settings, stableRepositoryURL, localRepositoryURL); err != nil {
t.Error(err)
}
expectedDirs := []string{hh.String(), hh.Repository(), hh.Cache(), hh.LocalRepository()}
for _, dir := range expectedDirs {
if fi, err := os.Stat(dir); err != nil {
t.Errorf("%s", err)
} else if !fi.IsDir() {
t.Errorf("%s is not a directory", fi)
}
}
if fi, err := os.Stat(hh.RepositoryFile()); err != nil {
t.Error(err)
} else if fi.IsDir() {
t.Errorf("%s should not be a directory", fi)
}
if fi, err := os.Stat(hh.LocalRepository(LocalRepositoryIndexFile)); err != nil {
t.Errorf("%s", err)
} else if fi.IsDir() {
t.Errorf("%s should not be a directory", fi)
}
}
func TestEnsureHome(t *testing.T) {
home, err := ioutil.TempDir("", "helm_home")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(home)
b := bytes.NewBuffer(nil)
hh := helmpath.Home(home)
settings := helm_env.EnvSettings{
Home: hh,
}
stableRepositoryURL := "https://kubernetes-charts.storage.googleapis.com"
localRepositoryURL := "http://127.0.0.1:8879/charts"
if err := ensureDirectories(hh, b); err != nil {
t.Error(err)
}
if err := ensureDefaultRepos(hh, b, false, settings, stableRepositoryURL, localRepositoryURL); err != nil {
t.Error(err)
}
if err := ensureDefaultRepos(hh, b, true, settings, stableRepositoryURL, localRepositoryURL); err != nil {
t.Error(err)
}
if err := ensureRepoFileFormat(hh.RepositoryFile(), b); err != nil {
t.Error(err)
}
expectedDirs := []string{hh.String(), hh.Repository(), hh.Cache(), hh.LocalRepository()}
for _, dir := range expectedDirs {
if fi, err := os.Stat(dir); err != nil {
t.Errorf("%s", err)
} else if !fi.IsDir() {
t.Errorf("%s is not a directory", fi)
}
}
if fi, err := os.Stat(hh.RepositoryFile()); err != nil {
t.Error(err)
} else if fi.IsDir() {
t.Errorf("%s should not be a directory", fi)
}
if fi, err := os.Stat(hh.LocalRepository(LocalRepositoryIndexFile)); err != nil {
t.Errorf("%s", err)
} else if fi.IsDir() {
t.Errorf("%s should not be a directory", fi)
}
}

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"os"

@ -16,7 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package main
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"os"

@ -36,6 +36,7 @@ import (
"k8s.io/helm/pkg/version"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/tiller/environment"
)
// Install uses Kubernetes client to install Tiller.
@ -226,8 +227,8 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
Image: opts.SelectImage(),
ImagePullPolicy: opts.pullPolicy(),
Ports: []v1.ContainerPort{
{ContainerPort: 44134, Name: "tiller"},
{ContainerPort: 44135, Name: "http"},
{ContainerPort: environment.DefaultTillerPort, Name: "tiller"},
{ContainerPort: environment.DefaultTillerProbePort, Name: "http"},
},
Env: []v1.EnvVar{
{Name: "TILLER_NAMESPACE", Value: opts.Namespace},
@ -237,7 +238,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/liveness",
Port: intstr.FromInt(44135),
Port: intstr.FromInt(environment.DefaultTillerProbePort),
},
},
InitialDelaySeconds: 1,
@ -247,7 +248,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
Handler: v1.Handler{
HTTPGet: &v1.HTTPGetAction{
Path: "/readiness",
Port: intstr.FromInt(44135),
Port: intstr.FromInt(environment.DefaultTillerProbePort),
},
},
InitialDelaySeconds: 1,
@ -341,7 +342,7 @@ func generateService(namespace string) *v1.Service {
Ports: []v1.ServicePort{
{
Name: "tiller",
Port: 44134,
Port: environment.DefaultTillerPort,
TargetPort: intstr.FromString("tiller"),
},
},

@ -24,7 +24,12 @@ import (
"k8s.io/helm/pkg/version"
)
const defaultImage = "gcr.io/kubernetes-helm/tiller"
const (
defaultImage = "gcr.io/kubernetes-helm/tiller"
fmtJSON OutputFormat = "json"
fmtYAML OutputFormat = "yaml"
)
// Options control how to install Tiller into a cluster, upgrade, and uninstall Tiller from a cluster.
type Options struct {
@ -154,11 +159,6 @@ func (f *OutputFormat) Type() string {
return "OutputFormat"
}
const (
fmtJSON OutputFormat = "json"
fmtYAML OutputFormat = "yaml"
)
// Set validates and sets the value of the OutputFormat
func (f *OutputFormat) Set(s string) error {
for _, of := range []OutputFormat{fmtJSON, fmtYAML} {

@ -31,13 +31,13 @@ const (
// Uninstall uses Kubernetes client to uninstall Tiller.
func Uninstall(client kubernetes.Interface, opts *Options) error {
if err := deleteService(client.Core(), opts.Namespace); err != nil {
if err := deleteService(client.CoreV1(), opts.Namespace); err != nil {
return err
}
if err := deleteDeployment(client, opts.Namespace); err != nil {
return err
}
return deleteSecret(client.Core(), opts.Namespace)
return deleteSecret(client.CoreV1(), opts.Namespace)
}
// deleteService deletes the Tiller Service resource

@ -166,7 +166,7 @@ func lintChart(path string, vals []byte, namespace string, strict bool) (support
chartPath = path
}
// Guard: Error out of this is not a chart.
// Guard: Error out if this is not a chart.
if _, err := os.Stat(filepath.Join(chartPath, "Chart.yaml")); err != nil {
return linter, errLintNoChart
}
@ -177,7 +177,7 @@ func lintChart(path string, vals []byte, namespace string, strict bool) (support
// vals merges values from files specified via -f/--values and
// directly via --set or --set-string or --set-file, marshaling them to YAML
//
// This func is implemented intentionally and separately from the `vals` func for the `install` and `upgrade` comammdsn.
// This func is implemented intentionally and separately from the `vals` func for the `install` and `upgrade` commands.
// Compared to the alternative func, this func lacks the parameters for tls opts - ca key, cert, and ca cert.
// That's because this command, `lint`, is explicitly forbidden from making server connections.
func (l *lintCmd) vals() ([]byte, error) {

@ -66,7 +66,7 @@ func printRelease(out io.Writer, rel *release.Release) error {
return tpl(printReleaseTemplate, data, out)
}
func tpl(t string, vals map[string]interface{}, out io.Writer) error {
func tpl(t string, vals interface{}, out io.Writer) error {
tt, err := template.New("_").Parse(t)
if err != nil {
return err

@ -24,6 +24,7 @@ import (
"github.com/spf13/cobra"
"k8s.io/helm/cmd/helm/installer"
"k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/repo"
@ -99,7 +100,7 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer, home helmpath.Ho
wg.Add(1)
go func(re *repo.ChartRepository) {
defer wg.Done()
if re.Config.Name == localRepository {
if re.Config.Name == installer.LocalRepository {
mu.Lock()
fmt.Fprintf(out, "...Skip %s chart repository\n", re.Config.Name)
mu.Unlock()
@ -124,6 +125,6 @@ func updateCharts(repos []*repo.ChartRepository, out io.Writer, home helmpath.Ho
return errors.New("Update Failed. Check log for details")
}
fmt.Fprintln(out, "Update Complete. ⎈ Happy Helming!⎈ ")
fmt.Fprintln(out, "Update Complete.")
return nil
}

@ -21,6 +21,7 @@ import (
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
@ -59,7 +60,11 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
Short: "uninstalls Tiller from a cluster",
Long: resetDesc,
PreRunE: func(cmd *cobra.Command, args []string) error {
if err := setupConnection(); !d.force && err != nil {
err := setupConnection()
if !d.force && err != nil {
return err
}
if d.force && err != nil && strings.EqualFold(err.Error(), "could not find tiller") {
return err
}
return nil

@ -47,6 +47,7 @@ type rollbackCmd struct {
timeout int64
wait bool
description string
cleanupOnFail bool
}
func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
@ -87,6 +88,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
f.Int64Var(&rollback.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&rollback.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.StringVar(&rollback.description, "description", "", "specify a description for the release")
f.BoolVar(&rollback.cleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this rollback when rollback failed")
// set defaults from environment
settings.InitTLS(f)
@ -104,12 +106,13 @@ func (r *rollbackCmd) run() error {
helm.RollbackVersion(r.revision),
helm.RollbackTimeout(r.timeout),
helm.RollbackWait(r.wait),
helm.RollbackDescription(r.description))
helm.RollbackDescription(r.description),
helm.RollbackCleanupOnFail(r.cleanupOnFail))
if err != nil {
return prettyError(err)
}
fmt.Fprintf(r.out, "Rollback was a success! Happy Helming!\n")
fmt.Fprintf(r.out, "Rollback was a success.\n")
return nil
}

@ -31,25 +31,25 @@ func TestRollbackCmd(t *testing.T) {
{
name: "rollback a release",
args: []string{"funny-honey", "1"},
expected: "Rollback was a success! Happy Helming!",
expected: "Rollback was a success.",
},
{
name: "rollback a release with timeout",
args: []string{"funny-honey", "1"},
flags: []string{"--timeout", "120"},
expected: "Rollback was a success! Happy Helming!",
expected: "Rollback was a success.",
},
{
name: "rollback a release with wait",
args: []string{"funny-honey", "1"},
flags: []string{"--wait"},
expected: "Rollback was a success! Happy Helming!",
expected: "Rollback was a success.",
},
{
name: "rollback a release with description",
args: []string{"funny-honey", "1"},
flags: []string{"--description", "foo"},
expected: "Rollback was a success! Happy Helming!",
expected: "Rollback was a success.",
},
{
name: "rollback a release without revision",

@ -33,6 +33,12 @@ import (
"k8s.io/helm/pkg/repo"
)
const (
sep = "\v"
// verSep is a separator for version fields in map keys.
verSep = "$$"
)
// Result is a search result.
//
// Score indicates how close it is to match. The higher the score, the longer
@ -49,16 +55,11 @@ type Index struct {
charts map[string]*repo.ChartVersion
}
const sep = "\v"
// NewIndex creats a new Index.
func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
}
// verSep is a separator for version fields in map keys.
const verSep = "$$"
// AddRepo adds a repository index to the search index.
func (i *Index) AddRepo(rname string, ind *repo.IndexFile, all bool) {
ind.SortEntries()

@ -1,3 +1,4 @@
appVersion: "3.3"
description: Deploy a basic Alpine Linux pod
home: https://k8s.io/helm
name: alpine

@ -10,6 +10,7 @@ metadata:
# The "release" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
app.kubernetes.io/instance: {{.Release.Name | quote }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
# This makes it easy to audit chart usage.
helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}"
values: {{.Values.test.Name}}

@ -4,3 +4,4 @@ name: novals
sources:
- https://github.com/helm/helm
version: 0.2.0
appVersion: 3.3

@ -10,6 +10,7 @@ metadata:
# The "release" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
app.kubernetes.io/instance: {{.Release.Name | quote }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
# This makes it easy to audit chart usage.
helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}"
annotations:

@ -112,6 +112,7 @@ type upgradeCmd struct {
devel bool
subNotes bool
description string
cleanupOnFail bool
certFile string
keyFile string
@ -179,6 +180,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f.BoolVar(&upgrade.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.BoolVar(&upgrade.subNotes, "render-subchart-notes", false, "render subchart notes along with parent")
f.StringVar(&upgrade.description, "description", "", "specify the description to use for the upgrade, rather than the default")
f.BoolVar(&upgrade.cleanupOnFail, "cleanup-on-fail", false, "allow deletion of new resources created in this upgrade when upgrade failed")
f.MarkDeprecated("disable-hooks", "use --no-hooks instead")
@ -248,7 +250,8 @@ func (u *upgradeCmd) run() error {
}
// Check chart requirements to make sure all dependencies are present in /charts
if ch, err := chartutil.Load(chartPath); err == nil {
ch, err := chartutil.Load(chartPath)
if err == nil {
if req, err := chartutil.LoadRequirements(ch); err == nil {
if err := renderutil.CheckDependencies(ch, req); err != nil {
return err
@ -260,9 +263,9 @@ func (u *upgradeCmd) run() error {
return prettyError(err)
}
resp, err := u.client.UpdateRelease(
resp, err := u.client.UpdateReleaseFromChart(
u.release,
chartPath,
ch,
helm.UpdateValueOverrides(rawVals),
helm.UpgradeDryRun(u.dryRun),
helm.UpgradeRecreate(u.recreate),
@ -273,10 +276,12 @@ func (u *upgradeCmd) run() error {
helm.ReuseValues(u.reuseValues),
helm.UpgradeSubNotes(u.subNotes),
helm.UpgradeWait(u.wait),
helm.UpgradeDescription(u.description))
helm.UpgradeDescription(u.description),
helm.UpgradeCleanupOnFail(u.cleanupOnFail))
if err != nil {
fmt.Fprintf(u.out, "UPGRADE FAILED\nROLLING BACK\nError: %v\n", prettyError(err))
fmt.Fprintf(u.out, "UPGRADE FAILED\nError: %v\n", prettyError(err))
if u.atomic {
fmt.Fprint(u.out, "ROLLING BACK")
rollback := &rollbackCmd{
out: u.out,
client: u.client,
@ -289,6 +294,7 @@ func (u *upgradeCmd) run() error {
description: "",
revision: releaseHistory.Releases[0].Version,
disableHooks: u.disableHooks,
cleanupOnFail: u.cleanupOnFail,
}
if err := rollback.run(); err != nil {
return err
@ -301,7 +307,7 @@ func (u *upgradeCmd) run() error {
printRelease(u.out, resp.Release)
}
fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release)
fmt.Fprintf(u.out, "Release %q has been upgraded.\n", u.release)
// Print the status like status command does
status, err := u.client.ReleaseStatus(u.release)

@ -96,7 +96,7 @@ func TestUpgradeCmd(t *testing.T) {
name: "upgrade a release",
args: []string{"funny-bunny", chartPath},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 2, Chart: ch}),
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"funny-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 2, Chart: ch})},
},
{
@ -104,7 +104,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"funny-bunny", chartPath},
flags: []string{"--timeout", "120"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 3, Chart: ch2}),
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"funny-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 3, Chart: ch2})},
},
{
@ -112,7 +112,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"funny-bunny", chartPath},
flags: []string{"--reset-values", "true"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 4, Chart: ch2}),
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"funny-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 4, Chart: ch2})},
},
{
@ -120,7 +120,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"funny-bunny", chartPath},
flags: []string{"--reuse-values", "true"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 5, Chart: ch2}),
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"funny-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 5, Chart: ch2})},
},
{
@ -128,7 +128,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"funny-bunny", chartPath},
flags: []string{"--atomic"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 6, Chart: ch}),
expected: "Release \"funny-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"funny-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "funny-bunny", Version: 6, Chart: ch})},
},
{
@ -136,7 +136,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"zany-bunny", chartPath},
flags: []string{"-i"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "zany-bunny", Version: 1, Chart: ch}),
expected: "Release \"zany-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"zany-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "zany-bunny", Version: 1, Chart: ch})},
},
{
@ -144,7 +144,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"crazy-bunny", chartPath},
flags: []string{"-i", "--timeout", "120"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 1, Chart: ch}),
expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"crazy-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 1, Chart: ch})},
},
{
@ -152,7 +152,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"crazy-bunny", chartPath},
flags: []string{"-i", "--description", "foo"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 1, Chart: ch, Description: "foo"}),
expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"crazy-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 1, Chart: ch, Description: "foo"})},
},
{
@ -160,7 +160,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"crazy-bunny", chartPath},
flags: []string{"--wait"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 2, Chart: ch2}),
expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"crazy-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 2, Chart: ch2})},
},
{
@ -168,7 +168,7 @@ func TestUpgradeCmd(t *testing.T) {
args: []string{"crazy-bunny", chartPath},
flags: []string{"--description", "foo"},
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 2, Chart: ch2}),
expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n",
expected: "Release \"crazy-bunny\" has been upgraded.\n",
rels: []*release.Release{helm.ReleaseMock(&helm.MockReleaseOptions{Name: "crazy-bunny", Version: 2, Chart: ch2, Description: "foo"})},
},
{

@ -27,7 +27,7 @@ import (
const verifyDesc = `
Verify that the given chart has a valid provenance file.
Provenance files provide crytographic verification that a chart has not been
Provenance files provide cryptographic verification that a chart has not been
tampered with, and was packaged by a trusted provider.
This command can be used to verify a local chart. Several other commands provide

@ -151,5 +151,5 @@ func formatVersion(v *pb.Version, short bool) string {
if short && v.GitCommit != "" {
return fmt.Sprintf("%s+g%s", v.SemVer, v.GitCommit[:7])
}
return fmt.Sprintf("%#v", v)
return fmt.Sprintf("&version.Version{SemVer:\"%s\", GitCommit:\"%s\", GitTreeState:\"%s\"}", v.SemVer, v.GitCommit, v.GitTreeState)
}

@ -131,7 +131,13 @@ func (r *ReleaseModuleServiceServer) RollbackRelease(ctx context.Context, in *ru
grpclog.Print("rollback")
c := bytes.NewBufferString(in.Current.Manifest)
t := bytes.NewBufferString(in.Target.Manifest)
err := kubeClient.Update(in.Target.Namespace, c, t, in.Force, in.Recreate, in.Timeout, in.Wait)
err := kubeClient.UpdateWithOptions(in.Target.Namespace, c, t, kube.UpdateOptions{
Force: in.Force,
Recreate: in.Recreate,
Timeout: in.Timeout,
ShouldWait: in.Wait,
CleanupOnFail: in.CleanupOnFail,
})
return &rudderAPI.RollbackReleaseResponse{}, err
}
@ -140,7 +146,13 @@ func (r *ReleaseModuleServiceServer) UpgradeRelease(ctx context.Context, in *rud
grpclog.Print("upgrade")
c := bytes.NewBufferString(in.Current.Manifest)
t := bytes.NewBufferString(in.Target.Manifest)
err := kubeClient.Update(in.Target.Namespace, c, t, in.Force, in.Recreate, in.Timeout, in.Wait)
err := kubeClient.UpdateWithOptions(in.Target.Namespace, c, t, kube.UpdateOptions{
Force: in.Force,
Recreate: in.Recreate,
Timeout: in.Timeout,
ShouldWait: in.Wait,
CleanupOnFail: in.CleanupOnFail,
})
// upgrade response object should be changed to include status
return &rudderAPI.UpgradeReleaseResponse{}, err
}

@ -36,6 +36,7 @@ import (
"google.golang.org/grpc/health"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/keepalive"
"k8s.io/klog"
// Import to initialize client auth plugins.
_ "k8s.io/client-go/plugin/pkg/client/auth"
@ -65,8 +66,8 @@ const (
storageMemory = "memory"
storageConfigMap = "configmap"
storageSecret = "secret"
storageSQL = "sql"
probeAddr = ":44135"
traceAddr = ":44136"
// defaultMaxHistory sets the maximum number of releases to 0: unlimited
@ -74,10 +75,16 @@ const (
)
var (
grpcAddr = flag.String("listen", ":44134", "address:port to listen on")
grpcAddr = flag.String("listen", fmt.Sprintf(":%v", environment.DefaultTillerPort), "address:port to listen on")
probeAddr = flag.String("probe-listen", fmt.Sprintf(":%v", environment.DefaultTillerProbePort), "address:port to listen on for probes")
enableTracing = flag.Bool("trace", false, "enable rpc tracing")
store = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap', 'memory', or 'secret'")
store = flag.String("storage", storageConfigMap, "storage driver to use. One of 'configmap', 'memory', 'sql' or 'secret'")
sqlDialect = flag.String("sql-dialect", "postgres", "SQL dialect to use (only postgres is supported for now")
sqlConnectionString = flag.String("sql-connection-string", "", "SQL connection string to use")
remoteReleaseModules = flag.Bool("experimental-release", false, "enable experimental release modules")
tlsEnable = flag.Bool("tls", tlsEnableEnvVarDefault(), "enable TLS")
tlsVerify = flag.Bool("tls-verify", tlsVerifyEnvVarDefault(), "enable TLS and verify remote certificate")
keyFile = flag.String("tls-key", tlsDefaultsFromEnv("tls-key"), "path to TLS private key file")
@ -100,6 +107,7 @@ var (
)
func main() {
klog.InitFlags(nil)
// TODO: use spf13/cobra for tiller instead of flags
flag.Parse()
@ -141,6 +149,18 @@ func start() {
env.Releases = storage.Init(secrets)
env.Releases.Log = newLogger("storage").Printf
case storageSQL:
sqlDriver, err := driver.NewSQL(
*sqlDialect,
*sqlConnectionString,
newLogger("storage/driver").Printf,
)
if err != nil {
logger.Fatalf("Cannot initialize SQL storage driver: %v", err)
}
env.Releases = storage.Init(sqlDriver)
env.Releases.Log = newLogger("storage").Printf
}
if *maxHistory > 0 {
@ -185,7 +205,7 @@ func start() {
logger.Printf("Starting Tiller %s (tls=%t)", version.GetVersion(), *tlsEnable || *tlsVerify)
logger.Printf("GRPC listening on %s", *grpcAddr)
logger.Printf("Probes listening on %s", probeAddr)
logger.Printf("Probes listening on %s", *probeAddr)
logger.Printf("Storage driver is %s", env.Releases.Name())
logger.Printf("Max history per release is %d", *maxHistory)
@ -211,7 +231,7 @@ func start() {
goprom.Register(rootServer)
addPrometheusHandler(mux)
if err := http.ListenAndServe(probeAddr, mux); err != nil {
if err := http.ListenAndServe(*probeAddr, mux); err != nil {
probeErrCh <- err
}
}()

@ -22,7 +22,7 @@ The directory that contains a chart MUST have the same name as the chart. Thus,
## Version Numbers
Wherever possible, Helm uses [SemVer 2](http://semver.org) to represent version numbers. (Note that Docker image tags do not necessarily follow SemVer, and are thus considered an unfortunate exception to the rule.)
Wherever possible, Helm uses [SemVer 2](https://semver.org) to represent version numbers. (Note that Docker image tags do not necessarily follow SemVer, and are thus considered an unfortunate exception to the rule.)
When SemVer versions are stored in Kubernetes labels, we conventionally alter the `+` character to an `_` character, as labels do not allow the `+` sign as a value.

@ -26,7 +26,7 @@ are recommended, and _should_ be placed onto a chart for global consistency. Tho
Name|Status|Description
-----|------|----------
`app.kubernetes.io/name` | REC | This should be the app name, reflecting the entire app. Usually `{{ template "name" . }}` is used for this. This is used by many Kubernetes manifests, and is not Helm-specific.
`helm.sh/chart` | REC | This should be the chart name and version: `{{ .Chart.Name }}-{{ .Chart.Version \| replace "+" "_" }}`.
`helm.sh/chart` | REC | This should be the chart name and version: `{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}`.
`app.kubernetes.io/managed-by` | REC | This should always be set to `{{ .Release.Service }}`. It is for finding all things managed by Tiller.
`app.kubernetes.io/instance` | REC | This should be the `{{ .Release.Name }}`. It aids in differentiating between different instances of the same application.
`app.kubernetes.io/version` | OPT | The version of the app and can be set to `{{ .Chart.AppVersion }}`.

@ -88,7 +88,7 @@ data is lost after one parse.
## Consider How Users Will Use Your Values
There are three potential sources of values:
There are four potential sources of values:
- A chart's `values.yaml` file
- A values file supplied by `helm install -f` or `helm upgrade -f`

@ -123,6 +123,35 @@ startup.
This part shows several ways to serve a chart repository.
### ChartMuseum
The Helm project provides an open-source Helm repository server called [ChartMuseum](https://chartmuseum.com) that you can host yourself.
ChartMuseum supports multiple cloud storage backends. Configure it to point to the directory or bucket containing your chart packages, and the index.yaml file will be generated dynamically.
It can be deployed easily as a [Helm chart](https://github.com/helm/charts/tree/master/stable/chartmuseum):
```
helm install stable/chartmuseum
```
and also as a [Docker image](https://hub.docker.com/r/chartmuseum/chartmuseum/tags):
```
docker run --rm -it \
-p 8080:8080 \
-v $(pwd)/charts:/charts \
-e DEBUG=true \
-e STORAGE=local \
-e STORAGE_LOCAL_ROOTDIR=/charts \
chartmuseum/chartmuseum
```
You can then add the repo to your local repository list:
```
helm repo add chartmuseum http://localhost:8080
```
ChartMuseum provides other features, such as an API for chart uploads. Please see the [README](https://github.com/helm/chartmuseum) for more info.
### Google Cloud Storage
The first step is to **create your GCS bucket**. We'll call ours
@ -153,6 +182,10 @@ Charts repository hosts its charts, so you may want to take a
You can also set up chart repositories using JFrog Artifactory.
Read more about chart repositories with JFrog Artifactory [here](https://www.jfrog.com/confluence/display/RTF/Helm+Chart+Repositories)
### ProGet
Helm chart repositories are supported by ProGet. For more information, visit the [Helm repository documentation](https://inedo.com/support/documentation/proget/feeds/helm) on the Inedo website.
### Github Pages example
In a similar way you can create charts repository using GitHub Pages.

@ -329,7 +329,7 @@ data:
- "Onions"
```
Now, in this example we've done something tricky. The `toppings: |-` line is declaring a multi-line string. So our list of toppings is actually not a YAML list. It's a big string. Why would we do this? Because the data in ConfigMaps `data` is composed of key/value pairs, where both the key and the value are simple strings. To understand why this is the case, take a look at the [Kubernetes ConfigMap docs](http://kubernetes.io/docs/user-guide/configmap/). For us, though, this detail doesn't matter much.
Now, in this example we've done something tricky. The `toppings: |-` line is declaring a multi-line string. So our list of toppings is actually not a YAML list. It's a big string. Why would we do this? Because the data in ConfigMaps `data` is composed of key/value pairs, where both the key and the value are simple strings. To understand why this is the case, take a look at the [Kubernetes ConfigMap docs](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/). For us, though, this detail doesn't matter much.
> The `|-` marker in YAML takes a multi-line string. This can be a useful technique for embedding big blocks of data inside of your manifests, as exemplified here.

@ -150,7 +150,7 @@ Template functions and pipelines are a powerful way to transform information and
## Operators are functions
Operators are implemented as functions that return a boolean value. To use `eq`, `ne`, `lt`, `gt`, `and`, `or`, `not` etcetera place the operator at the front of the statement followed by its parameters just as you would a function. To chain multiple operations together, separate individual functions by surrounding them with paranthesis.
Operators are implemented as functions that return a boolean value. To use `eq`, `ne`, `lt`, `gt`, `and`, `or`, `not` etcetera place the operator at the front of the statement followed by its parameters just as you would a function. To chain multiple operations together, separate individual functions by surrounding them with parentheses.
```yaml
{{/* include the body of this if statement when the variable .Values.fooString exists and is set to "foo" */}}

@ -51,8 +51,8 @@ already there.
- `NOTES.txt`: The "help text" for your chart. This will be displayed to your users
when they run `helm install`.
- `deployment.yaml`: A basic manifest for creating a Kubernetes [deployment](http://kubernetes.io/docs/user-guide/deployments/)
- `service.yaml`: A basic manifest for creating a [service endpoint](http://kubernetes.io/docs/user-guide/services/) for your deployment
- `deployment.yaml`: A basic manifest for creating a Kubernetes [deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)
- `service.yaml`: A basic manifest for creating a [service endpoint](https://kubernetes.io/docs/concepts/services-networking/service/) for your deployment
- `_helpers.tpl`: A place to put template helpers that you can re-use throughout the chart
And what we're going to do is... _remove them all!_ That way we can work through our tutorial from scratch. We'll actually create our own `NOTES.txt` and `_helpers.tpl` as we go.

@ -4,7 +4,7 @@ The `.helmignore` file is used to specify files you don't want to include in you
If this file exists, the `helm package` command will ignore all the files that match the pattern specified in the `.helmignore` file while packaging your application.
This can help in avoiding unncessary or sensitive files or directories from being added in your helm chart.
This can help in avoiding unnecessary or sensitive files or directories from being added in your helm chart.
The `.helmignore` file supports Unix shell glob matching, relative path matching, and negation (prefixed with !). Only one pattern per line is considered.

@ -1,6 +1,6 @@
# Creating a NOTES.txt File
In this section we are going to look at Helm's tool for providing instructions to your chart users. At the end of a `chart install` or `chart upgrade`, Helm can print out a block of helpful information for users. This information is highly customizable using templates.
In this section we are going to look at Helm's tool for providing instructions to your chart users. At the end of a `helm install` or `helm upgrade`, Helm can print out a block of helpful information for users. This information is highly customizable using templates.
To add installation notes to your chart, simply create a `templates/NOTES.txt` file. This file is plain text, but it is processed like as a template, and has all the normal template functions and objects available.

@ -114,6 +114,8 @@ metadata:
# I cannot reference .Chart.Name, but I can do $.Chart.Name
helm.sh/chart: "{{ $.Chart.Name }}-{{ $.Chart.Version }}"
app.kubernetes.io/instance: "{{ $.Release.Name }}"
# Value from appVersion in Chart.yaml
app.kubernetes.io/version: "{{ $.Chart.AppVersion }}"
app.kubernetes.io/managed-by: "{{ $.Release.Service }}"
type: kubernetes.io/tls
data:

@ -5,7 +5,7 @@ This guide is intended to give you, the chart developer, a strong understanding
But there are many things this guide has not covered when it comes to the practical day-to-day development of charts. Here are some useful pointers to other documentation that will help you as you create new charts:
- The [Helm Charts project](https://github.com/helm/charts) is an indispensable source of charts. That project is also sets the standard for best practices in chart development.
- The Kubernetes [User's Guide](http://kubernetes.io/docs/user-guide/) provides detailed examples of the various resource kinds that you can use, from ConfigMaps and Secrets to DaemonSets and Deployments.
- The Kubernetes [Documentation](https://kubernetes.io/docs/home/) provides detailed examples of the various resource kinds that you can use, from ConfigMaps and Secrets to DaemonSets and Deployments.
- The Helm [Charts Guide](../charts.md) explains the workflow of using charts.
- The Helm [Chart Hooks Guide](../charts_hooks.md) explains how to create lifecycle hooks.
- The Helm [Charts Tips and Tricks](../charts_tips_and_tricks.md) article provides some useful tips for writing charts.

@ -7,7 +7,7 @@ to read.
## Scalars and Collections
According to the [YAML spec](http://yaml.org/spec/1.2/spec.html), there are two
According to the [YAML spec](https://yaml.org/spec/1.2/spec.html), there are two
types of collections, and many scalar types.
The two types of collections are maps and sequences:

@ -71,7 +71,7 @@ Other fields will be silently ignored.
### Charts and Versioning
Every chart must have a version number. A version must follow the
[SemVer 2](http://semver.org/) standard. Unlike Helm Classic, Kubernetes
[SemVer 2](https://semver.org/) standard. Unlike Helm Classic, Kubernetes
Helm uses version numbers as release markers. Packages in repositories
are identified by name plus version.
@ -191,7 +191,7 @@ Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "stable" chart repository
...Successfully got an update from the "example" chart repository
...Successfully got an update from the "another" chart repository
Update Complete. Happy Helming!
Update Complete.
Saving 2 charts
Downloading apache from repo http://example.com/charts
Downloading mysql from repo http://another.example.com/charts
@ -492,7 +492,7 @@ create/update all of the above Kubernetes objects in the following order:
This is because when Helm installs/upgrades charts,
the Kubernetes objects from the charts and all its dependencies are
- aggregrated into a single set; then
- aggregated into a single set; then
- sorted by type followed by name; and then
- created/updated in that order.
@ -792,7 +792,7 @@ standard references that will help you out.
- [Go templates](https://godoc.org/text/template)
- [Extra template functions](https://godoc.org/github.com/Masterminds/sprig)
- [The YAML format](http://yaml.org/spec/)
- [The YAML format](https://yaml.org/spec/)
## Using Helm to Manage Charts

@ -129,6 +129,7 @@ metadata:
labels:
app.kubernetes.io/managed-by: {{.Release.Service | quote }}
app.kubernetes.io/instance: {{.Release.Name | quote }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
helm.sh/chart: "{{.Chart.Name}}-{{.Chart.Version}}"
annotations:
# This is what defines this resource as a hook. Without this line, the
@ -246,12 +247,10 @@ annotated.
### Automatically delete hook from previous release
When helm release being updated it is possible, that hook resource already exists in cluster. By default helm will try to create resource and fail with `"... already exists"` error.
When a helm release, that uses a hook, is being updated, it is possible that the hook resource might already exist in the cluster. In such circumstances, by default, helm will fail trying to install the hook resource with an `"... already exists"` error.
One might choose `"helm.sh/hook-delete-policy": "before-hook-creation"` over `"helm.sh/hook-delete-policy": "hook-succeeded,hook-failed"` because:
A common reason why the hook resource might already exist is that it was not deleted following use on a previous install/upgrade. There are, in fact, good reasons why one might want to keep the hook: for example, to aid manual debugging in case something went wrong. In this case, the recommended way of ensuring subsequent attempts to create the hook do not fail is to define a `"hook-delete-policy"` that can handle this: `"helm.sh/hook-delete-policy": "before-hook-creation"`. This hook annotation causes any existing hook to be removed, before the new hook is installed.
If it is preferred to actually delete the hook after each use (rather than have to handle it on a subsequent use, as shown above), then this can be achieved using a delete policy of `"helm.sh/hook-delete-policy": "hook-succeeded,hook-failed"`.
* It is convenient to keep failed hook job resource in kubernetes for example for manual debug.
* It may be necessary to keep succeeded hook resource in kubernetes for some reason.
* At the same time it is not desirable to do manual resource deletion before helm release upgrade.
`"helm.sh/hook-delete-policy": "before-hook-creation"` annotation on hook causes tiller to remove the hook from previous release if there is one before the new hook is launched and can be used with another policy.

@ -235,6 +235,9 @@ orphaned. Helm will no longer manage it in any way. This can lead to problems
if using `helm install --replace` on a release that has already been deleted, but
has kept resources.
To explicitly opt in to resource deletion, for example when overriding a chart's
default annotations, set the resource policy annotation value to `delete`.
## Using "Partials" and Template Includes
Sometimes you want to create some reusable parts in your chart, whether

@ -32,7 +32,7 @@ docker-test`.
To run Helm and Tiller locally, you can run `bin/helm` or `bin/tiller`.
- Helm and Tiller are known to run on macOS and most Linuxes, including
- Helm and Tiller are known to run on macOS and most Linux distributions, including
Alpine.
- Tiller must have access to a Kubernetes cluster. It learns about the
cluster by examining the Kube config files that `kubectl` uses.
@ -170,7 +170,7 @@ workflow for doing this is as follows:
5. When you are ready for us to review, sign your commit, push your branch to GitHub, and
then open a new pull request with us.
For Git commit messages, we follow the [Semantic Commit Messages](http://karma-runner.github.io/0.13/dev/git-commit-msg.html):
For Git commit messages, we follow the [Semantic Commit Messages](https://karma-runner.github.io/0.13/dev/git-commit-msg.html):
```
fix(helm): add --foo flag to 'helm install'
@ -201,7 +201,7 @@ Common scopes:
Read more:
- The [Deis Guidelines](https://github.com/deis/workflow/blob/master/src/contributing/submitting-a-pull-request.md)
were the inspiration for this section.
- Karma Runner [defines](http://karma-runner.github.io/0.13/dev/git-commit-msg.html) the semantic commit message idea.
- Karma Runner [defines](https://karma-runner.github.io/0.13/dev/git-commit-msg.html) the semantic commit message idea.
### Go Conventions

@ -10,6 +10,7 @@ metadata:
# The "app.kubernetes.io/instance" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
app.kubernetes.io/instance: {{ .Release.Name | quote }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
# This makes it easy to audit chart usage.
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version }}
app.kubernetes.io/name: {{ template "alpine.name" . }}

@ -37,7 +37,7 @@ are bundled with it.
## Chart Version
Charts are versioned according to the [SemVer 2
spec](http://semver.org). A version number is required on every chart.
spec](https://semver.org). A version number is required on every chart.
## Chart.yaml

@ -21,18 +21,19 @@ Common actions from this point include:
- helm list: list releases of charts
Environment:
$HELM_HOME set an alternative location for Helm files. By default, these are stored in ~/.helm
$HELM_HOST set an alternative Tiller host. The format is host:port
$HELM_NO_PLUGINS disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
$TILLER_NAMESPACE set an alternative Tiller namespace (default "kube-system")
$KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config")
$HELM_TLS_CA_CERT path to TLS CA certificate used to verify the Helm client and Tiller server certificates (default "$HELM_HOME/ca.pem")
$HELM_TLS_CERT path to TLS client certificate file for authenticating to Tiller (default "$HELM_HOME/cert.pem")
$HELM_TLS_KEY path to TLS client key file for authenticating to Tiller (default "$HELM_HOME/key.pem")
$HELM_TLS_VERIFY enable TLS connection between Helm and Tiller and verify Tiller server certificate (default "false")
$HELM_TLS_ENABLE enable TLS connection between Helm and Tiller (default "false")
$HELM_KEY_PASSPHRASE set HELM_KEY_PASSPHRASE to the passphrase of your PGP private key. If set, you will not be prompted for
the passphrase while signing helm charts
- $HELM_HOME: set an alternative location for Helm files. By default, these are stored in ~/.helm
- $HELM_HOST: set an alternative Tiller host. The format is host:port
- $HELM_NO_PLUGINS: disable plugins. Set HELM_NO_PLUGINS=1 to disable plugins.
- $TILLER_NAMESPACE: set an alternative Tiller namespace (default "kube-system")
- $KUBECONFIG: set an alternative Kubernetes configuration file (default "~/.kube/config")
- $HELM_TLS_CA_CERT: path to TLS CA certificate used to verify the Helm client and Tiller server certificates (default "$HELM_HOME/ca.pem")
- $HELM_TLS_CERT: path to TLS client certificate file for authenticating to Tiller (default "$HELM_HOME/cert.pem")
- $HELM_TLS_KEY: path to TLS client key file for authenticating to Tiller (default "$HELM_HOME/key.pem")
- $HELM_TLS_ENABLE: enable TLS connection between Helm and Tiller (default "false")
- $HELM_TLS_VERIFY: enable TLS connection between Helm and Tiller and verify Tiller server certificate (default "false")
- $HELM_TLS_HOSTNAME: the hostname or IP address used to verify the Tiller server certificate (default "127.0.0.1")
- $HELM_KEY_PASSPHRASE: set HELM_KEY_PASSPHRASE to the passphrase of your PGP private key. If set, you will not be prompted for the passphrase while signing helm charts
@ -78,4 +79,4 @@ Environment:
* [helm verify](helm_verify.md) - verify that a chart at the given path has been signed and is valid
* [helm version](helm_version.md) - print the client/server version information
###### Auto generated by spf13/cobra on 16-Oct-2018
###### Auto generated by spf13/cobra on 25-Apr-2019

@ -49,7 +49,7 @@ the dependency charts stored locally. The path should start with a prefix of
repository: "file://../dependency_chart/nginx"
If the dependency chart is retrieved locally, it is not required to have the
repository added to helm by "helm add repo". Version matching is also supported
repository added to helm by "helm repo add". Version matching is also supported
for this case.
@ -78,4 +78,4 @@ for this case.
* [helm dependency list](helm_dependency_list.md) - list the dependencies for the given chart
* [helm dependency update](helm_dependency_update.md) - update charts/ based on the contents of requirements.yaml
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 26-Mar-2019

@ -8,12 +8,11 @@ rebuild the charts/ directory based on the requirements.lock file
Build out the charts/ directory from the requirements.lock file.
Build is used to reconstruct a chart's dependencies to the state specified in
the lock file. This will not re-negotiate dependencies, as 'helm dependency update'
does.
If no lock file is found, 'helm dependency build' will mirror the behavior
of 'helm dependency update'.
the lock file.
If no lock file is found, 'helm dependency build' will mirror the behavior of
the 'helm dependency update' command. This means it will update the on-disk
dependencies to mirror the requirements.yaml file and generate a lock file.
```
helm dependency build [flags] CHART

@ -26,6 +26,7 @@ helm get [flags] RELEASE_NAME
```
-h, --help help for get
--revision int32 get the named release with revision
--template string go template for formatting the output, eg: {{.Release.Name}}
--tls enable TLS for request
--tls-ca-cert string path to TLS CA certificate file (default "$HELM_HOME/ca.pem")
--tls-cert string path to TLS certificate file (default "$HELM_HOME/cert.pem")
@ -54,4 +55,4 @@ helm get [flags] RELEASE_NAME
* [helm get notes](helm_get_notes.md) - displays the notes of the named release
* [helm get values](helm_get_values.md) - download the values file for a named release
###### Auto generated by spf13/cobra on 1-Sep-2018
###### Auto generated by spf13/cobra on 25-Mar-2019

@ -20,6 +20,7 @@ helm rollback [flags] [RELEASE] [REVISION]
### Options
```
--cleanup-on-fail allow deletion of new resources created in this rollback when rollback failed
--description string specify a description for the release
--dry-run simulate a rollback
--force force resource update through delete/recreate if needed
@ -52,4 +53,4 @@ helm rollback [flags] [RELEASE] [REVISION]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 29-Jan-2019
###### Auto generated by spf13/cobra on 5-Feb-2019

@ -68,6 +68,7 @@ helm upgrade [RELEASE] [CHART] [flags]
--atomic if set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
--cleanup-on-fail allow deletion of new resources created in this upgrade when upgrade failed
--description string specify the description to use for the upgrade, rather than the default
--devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
--dry-run simulate an upgrade
@ -117,4 +118,4 @@ helm upgrade [RELEASE] [CHART] [flags]
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 28-Jan-2019
###### Auto generated by spf13/cobra on 5-Feb-2019

@ -7,7 +7,7 @@ verify that a chart at the given path has been signed and is valid
Verify that the given chart has a valid provenance file.
Provenance files provide crytographic verification that a chart has not been
Provenance files provide cryptographic verification that a chart has not been
tampered with, and was packaged by a trusted provider.
This command can be used to verify a local chart. Several other commands provide
@ -42,4 +42,4 @@ helm verify [flags] PATH
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 25-Feb-2019

@ -12,7 +12,7 @@ Differences from Helm Classic:
- Helm's chart format has changed for the better:
- Dependencies are immutable and stored inside of a chart's `charts/`
directory.
- Charts are strongly versioned using [SemVer 2](http://semver.org/spec/v2.0.0.html)
- Charts are strongly versioned using [SemVer 2](https://semver.org/spec/v2.0.0.html)
- Charts can be loaded from directories or from chart archive files
- Helm supports Go templates without requiring you to run `generate`
or `template` commands.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 53 KiB

@ -60,21 +60,21 @@ The binary can also be installed via [`scoop`](https://scoop.sh) command-line in
scoop install helm
```
## From Script
### From Script
Helm now has an installer script that will automatically grab the latest version
of the Helm client and [install it locally](https://raw.githubusercontent.com/helm/helm/master/scripts/get).
of the Helm client and [install it locally](https://git.io/get_helm.sh).
You can fetch that script, and then execute it locally. It's well documented so
that you can read through it and understand what it is doing before you run it.
```
$ curl https://raw.githubusercontent.com/helm/helm/master/scripts/get > get_helm.sh
$ curl -LO https://git.io/get_helm.sh
$ chmod 700 get_helm.sh
$ ./get_helm.sh
```
Yes, you can `curl https://raw.githubusercontent.com/helm/helm/master/scripts/get | bash` that if you want to live on the edge.
Yes, you can `curl -L https://git.io/get_helm.sh | bash` that if you want to live on the edge.
### From Canary Builds
@ -353,7 +353,10 @@ in JSON format.
### Storage backends
By default, `tiller` stores release information in `ConfigMaps` in the namespace
where it is running. As of Helm 2.7.0, there is now a beta storage backend that
where it is running.
#### Secret storage backend
As of Helm 2.7.0, there is now a beta storage backend that
uses `Secrets` for storing release information. This was added for additional
security in protecting charts in conjunction with the release of `Secret`
encryption in Kubernetes.
@ -369,6 +372,31 @@ Currently, if you want to switch from the default backend to the secrets
backend, you'll have to do the migration for this on your own. When this backend
graduates from beta, there will be a more official path of migration
#### SQL storage backend
As of Helm 2.14.0 there is now a beta SQL storage backend that stores release
information in an SQL database (only postgres has been tested so far).
Using such a storage backend is particularly useful if your release information
weighs more than 1MB (in which case, it can't be stored in ConfigMaps/Secrets
because of internal limits in Kubernetes' underlying etcd key-value store).
To enable the SQL backend, you'll need to deploy a SQL database and init Tiller
with the following options:
```shell
helm init \
--override \
'spec.template.spec.containers[0].args'='{--storage=sql,--sql-dialect=postgres,--sql-connection-string=postgresql://tiller-postgres:5432/helm?user=helm&password=changeme}'
```
**PRODUCTION NOTES**: it's recommended to change the username and password of
the SQL database in production deployments. Enabling SSL is also a good idea.
Last, but not least, perform regular backups/snapshots of your SQL database.
Currently, if you want to switch from the default backend to the SQL backend,
you'll have to do the migration for this on your own. When this backend
graduates from beta, there will be a more official migration path.
## Conclusion
In most cases, installation is as simple as getting a pre-built `helm` binary

@ -19,13 +19,12 @@ Build out the charts/ directory from the requirements.lock file.
.PP
Build is used to reconstruct a chart's dependencies to the state specified in
the lock file. This will not re\-negotiate dependencies, as 'helm dependency update'
does.
the lock file.
.PP
If no lock file is found, 'helm dependency build' will mirror the behavior
of 'helm dependency update'.
If no lock file is found, 'helm dependency build' will mirror the behavior of
the 'helm dependency update' command. This means it will update the on-disk
dependencies to mirror the requirements.yaml file and generate a lock file.
.SH OPTIONS
.PP

@ -80,7 +80,7 @@ the '\-\-debug' and '\-\-dry\-run' flags can be combined. This will still requir
round\-trip to the Tiller server.
.PP
If \-\-verify is set, the chart MUST have a provenance file, and the provenenace
If \-\-verify is set, the chart MUST have a provenance file, and the provenance
fall MUST pass all verification steps.
.PP

@ -18,7 +18,7 @@ helm\-verify \- verify that a chart at the given path has been signed and is val
Verify that the given chart has a valid provenance file.
.PP
Provenance files provide crytographic verification that a chart has not been
Provenance files provide cryptographic verification that a chart has not been
tampered with, and was packaged by a trusted provider.
.PP

@ -180,7 +180,7 @@ The following pieces of provenance data are added:
* The signature (SHA256, just like Docker) of the chart package (the .tgz file)
is included, and may be used to verify the integrity of the chart package.
* The entire body is signed using the algorithm used by PGP (see
[http://keybase.io] for an emerging way of making crypto signing and
[https://keybase.io] for an emerging way of making crypto signing and
verification easy).
The combination of this gives users the following assurances:
@ -202,7 +202,7 @@ keywords:
- proxy
source:
- https://github.com/foo/bar
home: http://nginx.com
home: https://nginx.com
...
files:
@ -221,7 +221,7 @@ first is the Chart.yaml. The second is the checksums, a map of filenames to
SHA-256 digests (value shown is fake/truncated)
The signature block is a standard PGP signature, which provides [tamper
resistance](http://www.rossde.com/PGP/pgp_signatures.html).
resistance](https://www.rossde.com/PGP/pgp_signatures.html).
## Chart Repositories

@ -54,9 +54,11 @@ Once you have Helm ready, you can initialize the local CLI and also
install Tiller into your Kubernetes cluster in one step:
```console
$ helm init
$ helm init --history-max 200
```
**TIP:** Setting `--history-max` on helm init is recommended as configmaps and other objects in helm history can grow large in number if not purged by max limit. Without a max history set the history is kept indefinitely, leaving a large number of records for helm and tiller to maintain.
This will install Tiller into the Kubernetes cluster you saw with
`kubectl config current-context`.

@ -43,7 +43,7 @@ _Note: The cluster-admin role is created by default in a Kubernetes cluster, so
$ kubectl create -f rbac-config.yaml
serviceaccount "tiller" created
clusterrolebinding "tiller" created
$ helm init --service-account tiller
$ helm init --service-account tiller --history-max 200
```
### Example: Deploy Tiller in a namespace, restricted to deploying resources only in that namespace
@ -106,7 +106,6 @@ $ helm init --service-account tiller --tiller-namespace tiller-world
$HELM_HOME has been configured at /Users/awesome-user/.helm.
Tiller (the Helm server side component) has been installed into your Kubernetes Cluster.
Happy Helming!
$ helm install nginx --tiller-namespace tiller-world --namespace tiller-world
NAME: wayfaring-yak

@ -8,9 +8,9 @@ or [pull request](https://github.com/helm/helm/pulls).
## Article, Blogs, How-Tos, and Extra Documentation
- [Awesome Helm](https://github.com/cdwv/awesome-helm) - List of awesome Helm resources
- [CI/CD with Kubernetes, Helm & Wercker ](http://www.slideshare.net/Diacode/cicd-with-kubernetes-helm-wercker-madscalability)
- [CI/CD with Kubernetes, Helm & Wercker ](https://www.slideshare.net/Diacode/cicd-with-kubernetes-helm-wercker-madscalability)
- [Creating a Helm Plugin in 3 Steps](http://technosophos.com/2017/03/21/creating-a-helm-plugin.html)
- [Deploying Kubernetes Applications with Helm](http://cloudacademy.com/blog/deploying-kubernetes-applications-with-helm/)
- [Deploying Kubernetes Applications with Helm](https://cloudacademy.com/blog/deploying-kubernetes-applications-with-helm/)
- [GitLab, Consumer Driven Contracts, Helm and Kubernetes](https://medium.com/@enxebre/gitlab-consumer-driven-contracts-helm-and-kubernetes-b7235a60a1cb#.xwp1y4tgi)
- [Honestbee's Helm Chart Conventions](https://gist.github.com/so0k/f927a4b60003cedd101a0911757c605a)
- [Releasing backward-incompatible changes: Kubernetes, Jenkins, Prometheus Operator, Helm and Traefik](https://medium.com/@enxebre/releasing-backward-incompatible-changes-kubernetes-jenkins-plugin-prometheus-operator-helm-self-6263ca61a1b1#.e0c7elxhq)
@ -19,6 +19,7 @@ or [pull request](https://github.com/helm/helm/pulls).
- [Writing a Helm Chart](https://www.influxdata.com/packaged-kubernetes-deployments-writing-helm-chart/)
- [A basic walk through Kubernetes Helm](https://github.com/muffin87/helm-tutorial)
- [Tillerless Helm v2](https://rimusz.net/tillerless-helm/)
- [Generating Certificate Authorities and Certificates using Terraform](https://github.com/jbussdieker/tiller-ssl-terraform)
## Video, Audio, and Podcast
@ -68,6 +69,7 @@ Tools layered on top of Helm or Tiller.
- [Armada](https://github.com/att-comdev/armada) - Manage prefixed releases throughout various Kubernetes namespaces, and removes completed jobs for complex deployments. Used by the [Openstack-Helm](https://github.com/openstack/openstack-helm) team.
- [ChartMuseum](https://github.com/chartmuseum/chartmuseum) - Helm Chart Repository with support for Amazon S3 and Google Cloud Storage
- [Chartify](https://github.com/appscode/chartify) - Generate Helm charts from existing Kubernetes resources.
- [Cloudsmith](https://cloudsmith.io/l/helm-repository/) - Fully managed SaaS offering private Helm Chart Repositories
- [Codefresh](https://codefresh.io) - Kubernetes native CI/CD and management platform with UI dashboards for managing Helm charts and releases
- [Cog](https://github.com/ohaiwalt/cog-helm) - Helm chart to deploy Cog on Kubernetes
- [Drone.io Helm Plugin](http://plugins.drone.io/ipedrazas/drone-helm/) - Run Helm inside of the Drone CI/CD system
@ -90,7 +92,7 @@ Tools layered on top of Helm or Tiller.
Platforms, distributions, and services that include Helm support.
- [Fabric8](https://fabric8.io) - Integrated development platform for Kubernetes
- [Jenkins X](http://jenkins-x.io/) - open source automated CI/CD for Kubernetes which uses Helm for [promoting](http://jenkins-x.io/about/features/#promotion) applications through [environments via GitOps](http://jenkins-x.io/about/features/#environments)
- [Jenkins X](https://jenkins-x.io/) - open source automated CI/CD for Kubernetes which uses Helm for [promoting](https://jenkins-x.io/about/features/#promotion) applications through [environments via GitOps](https://jenkins-x.io/about/features/#environments)
- [Kubernetic](https://kubernetic.com/) - Kubernetes Desktop Client
- [Qstack](https://qstack.com)

@ -41,7 +41,7 @@ Just kidding! :trollface:
All releases will be of the form vX.Y.Z where X is the major version number, Y
is the minor version number and Z is the patch release number. This project
strictly follows [semantic versioning](http://semver.org/) so following this
strictly follows [semantic versioning](https://semver.org/) so following this
step is critical.
It is important to note that this document assumes that the git remote in your
@ -147,6 +147,24 @@ git add .
git commit -m "bump version to $RELEASE_CANDIDATE_NAME"
```
This will update it for the $RELEASE_BRANCH_NAME only. You will also need to pull
this change into the master branch for when the next release is being created.
```shell
# get the last commit id i.e. commit to bump the version
git log --format="%H" -n 1
# create new branch off master
git checkout master
git checkout -b bump-version-<release_version>
# cherry pick the commit using id from first command
git cherry-pick -x <commit-id>
# commit the change
git push origin bump-version-<release-version>
```
## 3. Commit and Push the Release Branch
In order for others to start testing, we can now push the release branch
@ -250,7 +268,25 @@ git tag --sign --annotate "${RELEASE_NAME}" --message "Helm release ${RELEASE_NA
git push upstream $RELEASE_NAME
```
## 7. Write the Release Notes
## 7. PGP Sign the downloads
While hashes provide a signature that the content of the downloads is what it
was generated, signed packages provide traceability of where the package came
from.
To do this, run the following `make` commands:
```shell
make clean
make fetch-dist
make sign
```
This will generate ascii armored signature files for each of the files pushed by CI.
All of the signature files need to be uploaded to the release on GitHub.
## 8. Write the Release Notes
We will auto-generate a changelog based on the commits that occurred during a
release cycle, but it is usually more beneficial to the end-user if the release
@ -286,14 +322,14 @@ The community keeps growing, and we'd love to see you there!
Download Helm X.Y. The common platform binaries are here:
- [MacOS amd64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-darwin-amd64.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-darwin-amd64.tar.gz.sha256))
- [Linux amd64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-amd64.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-amd64.tar.gz.sha256))
- [Linux arm](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm.tar.gz.sha256))
- [Linux arm64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm64.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm64.tar.gz.sha256))
- [Linux i386](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-386.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-386.tar.gz.sha256))
- [Linux ppc64le](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-ppc64le.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-ppc64le.tar.gz.sha256))
- [Linux s390x](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-s390x.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-s390x.tar.gz.sha256))
- [Windows amd64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-windows-amd64.zip) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-windows-amd64.zip.sha256))
- [MacOS amd64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-darwin-amd64.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-darwin-amd64.tar.gz.sha256) / CHECKSUM_VAL)
- [Linux amd64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-amd64.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-amd64.tar.gz.sha256) / CHECKSUM_VAL)
- [Linux arm](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm.tar.gz.sha256) / CHECKSUM_VAL)
- [Linux arm64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm64.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-arm64.tar.gz.sha256) / CHECKSUM_VAL)
- [Linux i386](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-386.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-386.tar.gz.sha256) / CHECKSUM_VAL)
- [Linux ppc64le](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-ppc64le.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-ppc64le.tar.gz.sha256) / CHECKSUM_VAL)
- [Linux s390x](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-s390x.tar.gz) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-linux-s390x.tar.gz.sha256) / CHECKSUM_VAL)
- [Windows amd64](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-windows-amd64.zip) ([checksum](https://storage.googleapis.com/kubernetes-helm/helm-vX.Y.Z-windows-amd64.zip.sha256) / CHECKSUM_VAL)
Once you have the client installed, upgrade Tiller with `helm init --upgrade`.
@ -332,10 +368,11 @@ git log --no-merges --pretty=format:'- %s %H (%aN)' $PREVIOUS_RELEASE..$RELEASE_
After generating the changelog, you will need to categorize the changes as shown
in the example above.
Once finished, go into GitHub and edit the release notes for the tagged release
with the notes written here.
Once finished, go into GitHub and edit the release notes for the tagged release with the notes written here.
Remember to attach the ascii armored signatures generated in the previous step to the release notes.
## 8. Evangelize
## 9. Evangelize
Congratulations! You're done. Go grab yourself a $DRINK_OF_CHOICE. You've earned
it.

@ -42,6 +42,8 @@ on getting ready within a small amount of time. For production configurations,
we urge readers to read [the official documentation](https://www.openssl.org) and
consult other resources.
There are other alternative ways to generating SSL CAs in addition to `openssl`, for example Terraform. They are not documented here but you can find links to these alternative means in [Related Projects and Documentation](https://helm.sh/docs/related/).
### Generate a Certificate Authority
The simplest way to generate a certificate authority is to run two commands:
@ -288,7 +290,7 @@ not available for public resolution.
By default, the Helm client connects to Tiller via tunnel (i.e. kube proxy) at 127.0.0.1. During the TLS handshake,
a target, usually provided as a hostname (e.g. example.com), is checked against the subject and subject alternative
names of the certificate (i.e. hostname verficiation). However, because of the tunnel, the target is an IP address.
names of the certificate (i.e. hostname verification). However, because of the tunnel, the target is an IP address.
Therefore, to validate the certificate, the IP address 127.0.0.1 must be listed as an IP subject alternative name
(IP SAN) in the Tiller certificate.

@ -17,9 +17,9 @@ cluster. Think of it like the Kubernetes equivalent of a Homebrew formula,
an Apt dpkg, or a Yum RPM file.
A *Repository* is the place where charts can be collected and shared.
It's like Perl's [CPAN archive](http://www.cpan.org) or the
[Fedora Package Database](https://admin.fedoraproject.org/pkgdb/), but for
Kubernetes packages.
It's like Perl's [CPAN archive](https://www.cpan.org) or the
[Fedora Package Database](https://apps.fedoraproject.org/packages/s/pkgdb), but
for Kubernetes packages.
A *Release* is an instance of a chart running in a Kubernetes cluster.
One chart can often be installed many times into the same cluster. And
@ -190,7 +190,7 @@ imageTag: 10.1.14-r3
## Specify a imagePullPolicy
## Default to 'Always' if imageTag is 'latest', else set to 'IfNotPresent'
## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images
## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images
##
# imagePullPolicy:
@ -359,7 +359,7 @@ update things that have changed since the last release.
```console
$ helm upgrade -f panda.yaml happy-panda stable/mariadb
Fetched stable/mariadb-0.3.0.tgz to /Users/mattbutcher/Code/Go/src/k8s.io/helm/mariadb-0.3.0.tgz
happy-panda has been upgraded. Happy Helming!
happy-panda has been upgraded.
Last Deployed: Wed Sep 28 12:47:54 2016
Namespace: default
Status: DEPLOYED

185
glide.lock generated

@ -1,13 +1,11 @@
hash: 2af9a5c4f891a0f44109a929a494b5aeaaffa3a87cd1f3881f25f79845703d5b
updated: 2018-12-14T21:39:31.112097Z
hash: 7571b58bbda7d85993d2b737b50d0c52f5fadce0c63e7fac064bc0a99faaefab
updated: 2019-05-07T10:43:27.329085-04:00
imports:
- name: cloud.google.com/go
version: 3b1ae45394a234c385be014e9a488f2bb6eef821
subpackages:
- compute/metadata
- internal
- name: github.com/aokoli/goutils
version: 9c37978a95bd5c709a15883b6242714ea6709e64
- name: github.com/asaskevich/govalidator
version: 7664702784775e51966f0885f5cd27435916517b
- name: github.com/Azure/go-ansiterm
@ -86,14 +84,18 @@ imports:
version: 449fdfce4d962303d702fec724ef0ad181c92528
subpackages:
- spdy
- name: github.com/emicklei/go-restful
version: ff4f55a206334ef123e4f79bbf348980da81ca46
subpackages:
- log
- name: github.com/evanphx/json-patch
version: 36442dbdb585210f8d5a1b45e67aa323c197d5c4
version: 5858425f75500d40c52783dce87d085a483ce135
- name: github.com/exponent-io/jsonpath
version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5
- name: github.com/fatih/camelcase
version: f6a740d52f961c60348ebb109adde9f4635d7540
- name: github.com/ghodss/yaml
version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
version: c7ce16629ff4cd059ed96ed06419dd3856fd3577
- name: github.com/go-openapi/jsonpointer
version: ef5f0afec364d3b9396b7b77b43dbe26bf1f8004
- name: github.com/go-openapi/jsonreference
@ -122,7 +124,7 @@ imports:
subpackages:
- lru
- name: github.com/golang/protobuf
version: 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
version: aa810b61a9c79d51363740d207bb46cf8e620ed5
subpackages:
- proto
- ptypes
@ -132,7 +134,7 @@ imports:
- name: github.com/google/btree
version: 7d79101e329e5a3adf994758c578dab82b90c017
- name: github.com/google/gofuzz
version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
version: 24818f796faf91cd76ec7bddd72458fbced7a6c1
- name: github.com/google/uuid
version: 064e2069ce9c359c118179501254f67d7d37ba24
- name: github.com/googleapis/gnostic
@ -142,7 +144,7 @@ imports:
- compiler
- extensions
- name: github.com/gophercloud/gophercloud
version: 781450b3c4fcb4f5182bcc5133adb4b2e4a09d1d
version: c818fa66e4c88b30db28038fe3f18f2f4a0db9a8
subpackages:
- openstack
- openstack/identity/v2/tenants
@ -162,17 +164,27 @@ imports:
- name: github.com/grpc-ecosystem/go-grpc-prometheus
version: 0c1b191dbfe51efdabe3c14b9f6f3b96429e0722
- name: github.com/hashicorp/golang-lru
version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
version: 20f1fb78b0740ba8c3cb143a61e86ba5c8669768
subpackages:
- simplelru
- name: github.com/huandu/xstrings
version: 3959339b333561bf62a38b424fd41517c2c90f40
version: f02667b379e2fb5916c3cda2cf31e0eb885d79f8
- name: github.com/imdario/mergo
version: 9316a62528ac99aaecb4e47eadd6dc8aa6533d58
- name: github.com/inconshreveable/mousetrap
version: 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
- name: github.com/jmoiron/sqlx
version: d161d7a76b5661016ad0b085869f77fd410f3e6a
subpackages:
- reflectx
- name: github.com/json-iterator/go
version: ab8a2e0c74be9d3be70b3184d9acc634935ded82
- name: github.com/liggitt/tabwriter
version: 89fcab3d43de07060e4fd4c1547430ed57e87f24
- name: github.com/lib/pq
version: 88edab0803230a3898347e77b474f8c1820a1f20
subpackages:
- oid
- name: github.com/mailru/easyjson
version: 2f5df55504ebc322e4d52d34df6a1f5b503bf26d
subpackages:
@ -181,10 +193,12 @@ imports:
- jwriter
- name: github.com/MakeNowJust/heredoc
version: bb23615498cded5e105af4ce27de75b089cbe851
- name: github.com/Masterminds/goutils
version: 41ac8693c5c10a92ea1ff5ac3a7f95646f6123b0
- name: github.com/Masterminds/semver
version: 517734cc7d6470c0d07130e40fd40bdeb9bcd3fd
version: c7af12943936e8c39859482e61f0574c2fd7fc75
- name: github.com/Masterminds/sprig
version: 15f9564e7e9cf0da02a48e0d25f12a7b83559aa6
version: 9f8fceff796fb9f4e992cd2bece016be0121ab74
- name: github.com/Masterminds/vcs
version: 3084677c2c188840777bff30054f2b553729d329
- name: github.com/mattn/go-runewidth
@ -206,16 +220,17 @@ imports:
- name: github.com/pkg/errors
version: 645ef00459ed84a119197bfb8d8205042c6df63d
- name: github.com/prometheus/client_golang
version: c5b7fccd204277076155f10851dad72b76a49317
version: 505eaef017263e299324067d40ca2c48f6a2cf50
subpackages:
- prometheus
- prometheus/internal
- prometheus/promhttp
- name: github.com/prometheus/client_model
version: fa8ad6fec33561be4280a8f0514318c79d7f6cb6
subpackages:
- go
- name: github.com/prometheus/common
version: 13ba4ddd0caa9c28ca7b7bffe1dfa9ed8d5ef207
version: cfeb6f9992ffa54aaa4f2170ade4067ee478b250
subpackages:
- expfmt
- internal/bitbucket.org/ww/goautoneg
@ -228,6 +243,10 @@ imports:
version: 8a290539e2e8629dbc4e6bad948158f790ec31f4
- name: github.com/PuerkitoBio/urlesc
version: 5bd2802263f21d8788851d5305584c82a5c75d7e
- name: github.com/rubenv/sql-migrate
version: 1007f53448d75fe14190968f5de4d95ed63ebb83
subpackages:
- sqlparse
- name: github.com/russross/blackfriday
version: 300106c228d52c8941d4b3de6054a6062a86dda3
- name: github.com/shurcooL/sanitized_anchor_name
@ -259,15 +278,15 @@ imports:
- scrypt
- ssh/terminal
- name: golang.org/x/net
version: 0ed95abb35c445290478a5348a7b38bb154135fd
version: 65e2d4e15006aab9813ff8769e768bbf4bb667a0
subpackages:
- context
- context/ctxhttp
- http/httpguts
- http2
- http2/hpack
- idna
- internal/timeseries
- lex/httplex
- trace
- name: golang.org/x/oauth2
version: a6bd8cefa1811bd24b86f8902872e4e8225f74c4
@ -281,7 +300,7 @@ imports:
subpackages:
- semaphore
- name: golang.org/x/sys
version: 95c6576299259db960f6c5b9b69ea52422860fce
version: b90733256f2e882e81d52f9126de08df5615afd9
subpackages:
- unix
- windows
@ -325,26 +344,42 @@ imports:
subpackages:
- googleapis/rpc/status
- name: google.golang.org/grpc
version: 5ffe3083946d5603a0578721101dc8165b1d5b5f
version: a02b0774206b209466313a0b525d2c738fe407eb
subpackages:
- balancer
- balancer/base
- balancer/roundrobin
- binarylog/grpc_binarylog_v1
- codes
- connectivity
- credentials
- grpclb/grpc_lb_v1/messages
- credentials/internal
- encoding
- encoding/proto
- grpclog
- health
- health/grpc_health_v1
- internal
- internal/backoff
- internal/binarylog
- internal/channelz
- internal/envconfig
- internal/grpcrand
- internal/grpcsync
- internal/syscall
- internal/transport
- keepalive
- metadata
- naming
- peer
- resolver
- resolver/dns
- resolver/passthrough
- stats
- status
- tap
- transport
- name: gopkg.in/gorp.v1
version: 6a667da9c028871f98598d85413e3fc4c6daa52e
- name: gopkg.in/inf.v0
version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
- name: gopkg.in/square/go-jose.v2
@ -354,12 +389,11 @@ imports:
- json
- jwt
- name: gopkg.in/yaml.v2
version: 670d4cfef0544295bc27a114dbac37980d83185a
version: 5420a8b6744d3b0345ab293f6fcba19c978f1183
- name: k8s.io/api
version: 05914d821849570fba9eacfb29466f2d8d3cd229
version: 6e4e0e4f393bf5e8bbff570acd13217aa5a770cd
subpackages:
- admission/v1beta1
- admissionregistration/v1alpha1
- admissionregistration/v1beta1
- apps/v1
- apps/v1beta1
@ -376,16 +410,21 @@ imports:
- batch/v1beta1
- batch/v2alpha1
- certificates/v1beta1
- coordination/v1
- coordination/v1beta1
- core/v1
- events/v1beta1
- extensions/v1beta1
- imagepolicy/v1alpha1
- networking/v1
- networking/v1beta1
- node/v1alpha1
- node/v1beta1
- policy/v1beta1
- rbac/v1
- rbac/v1alpha1
- rbac/v1beta1
- scheduling/v1
- scheduling/v1alpha1
- scheduling/v1beta1
- settings/v1alpha1
@ -393,11 +432,13 @@ imports:
- storage/v1alpha1
- storage/v1beta1
- name: k8s.io/apiextensions-apiserver
version: 0fe22c71c47604641d9aa352c785b7912c200562
version: 727a075fdec8319bf095330e344b3ccc668abc73
subpackages:
- pkg/apis/apiextensions
- pkg/apis/apiextensions/v1beta1
- pkg/features
- name: k8s.io/apimachinery
version: 2b1284ed4c93a43499e781493253e2ac5959c4fd
version: 6a84e37a896db9780c75367af8d2ed2bb944022e
subpackages:
- pkg/api/equality
- pkg/api/errors
@ -453,7 +494,7 @@ imports:
- third_party/forked/golang/netutil
- third_party/forked/golang/reflect
- name: k8s.io/apiserver
version: 3ccfe8365421eb08e334b195786a2973460741d8
version: 1ec86e4da56ce0573788fc12bb3a5530600c0e5d
subpackages:
- pkg/authentication/authenticator
- pkg/authentication/serviceaccount
@ -461,23 +502,31 @@ imports:
- pkg/features
- pkg/util/feature
- name: k8s.io/cli-runtime
version: 835b10687cb6556f6b113099ef925146a56d5981
version: d644b00f3b79346b7627329269bb25f2135f941c
subpackages:
- pkg/genericclioptions
- pkg/genericclioptions/printers
- pkg/genericclioptions/resource
- pkg/kustomize
- pkg/kustomize/k8sdeps
- pkg/kustomize/k8sdeps/configmapandsecret
- pkg/kustomize/k8sdeps/kunstruct
- pkg/kustomize/k8sdeps/kv
- pkg/kustomize/k8sdeps/transformer
- pkg/kustomize/k8sdeps/transformer/hash
- pkg/kustomize/k8sdeps/transformer/patch
- pkg/kustomize/k8sdeps/validator
- pkg/printers
- pkg/resource
- name: k8s.io/client-go
version: 8d9ed539ba3134352c586810e749e58df4e94e4f
version: 1a26190bd76a9017e289958b9fba936430aa3704
subpackages:
- discovery
- discovery/cached/disk
- discovery/fake
- dynamic
- dynamic/fake
- kubernetes
- kubernetes/fake
- kubernetes/scheme
- kubernetes/typed/admissionregistration/v1alpha1
- kubernetes/typed/admissionregistration/v1alpha1/fake
- kubernetes/typed/admissionregistration/v1beta1
- kubernetes/typed/admissionregistration/v1beta1/fake
- kubernetes/typed/apps/v1
@ -510,6 +559,8 @@ imports:
- kubernetes/typed/batch/v2alpha1/fake
- kubernetes/typed/certificates/v1beta1
- kubernetes/typed/certificates/v1beta1/fake
- kubernetes/typed/coordination/v1
- kubernetes/typed/coordination/v1/fake
- kubernetes/typed/coordination/v1beta1
- kubernetes/typed/coordination/v1beta1/fake
- kubernetes/typed/core/v1
@ -520,6 +571,12 @@ imports:
- kubernetes/typed/extensions/v1beta1/fake
- kubernetes/typed/networking/v1
- kubernetes/typed/networking/v1/fake
- kubernetes/typed/networking/v1beta1
- kubernetes/typed/networking/v1beta1/fake
- kubernetes/typed/node/v1alpha1
- kubernetes/typed/node/v1alpha1/fake
- kubernetes/typed/node/v1beta1
- kubernetes/typed/node/v1beta1/fake
- kubernetes/typed/policy/v1beta1
- kubernetes/typed/policy/v1beta1/fake
- kubernetes/typed/rbac/v1
@ -528,6 +585,8 @@ imports:
- kubernetes/typed/rbac/v1alpha1/fake
- kubernetes/typed/rbac/v1beta1
- kubernetes/typed/rbac/v1beta1/fake
- kubernetes/typed/scheduling/v1
- kubernetes/typed/scheduling/v1/fake
- kubernetes/typed/scheduling/v1alpha1
- kubernetes/typed/scheduling/v1alpha1/fake
- kubernetes/typed/scheduling/v1beta1
@ -574,30 +633,35 @@ imports:
- tools/pager
- tools/portforward
- tools/record
- tools/record/util
- tools/reference
- tools/remotecommand
- tools/watch
- transport
- transport/spdy
- util/buffer
- util/cert
- util/connrotation
- util/exec
- util/flowcontrol
- util/homedir
- util/integer
- util/jsonpath
- util/keyutil
- util/retry
- name: k8s.io/cloud-provider
version: 9c9d72d1bf90eb62005f5112f3eea019b272c44b
subpackages:
- features
- name: k8s.io/klog
version: 8139d8cb77af419532b33dfa7dd09fbc5f1d344f
version: 8e90cee79f823779174776412c13478955131846
- name: k8s.io/kube-openapi
version: c59034cc13d587f5ef4e85ca0ade0c1866ae8e1d
version: b3a7cee44a305be0a69e1b9ac03018307287e1b0
subpackages:
- pkg/common
- pkg/util/proto
- pkg/util/proto/testing
- pkg/util/proto/validation
- name: k8s.io/kubernetes
version: f2c8f1cadf1808ec28476682e49a3cce2b09efbf
version: b7394102d6ef778017f2ca4046abbaa23b88c290
subpackages:
- pkg/api/legacyscheme
- pkg/api/service
@ -630,6 +694,7 @@ imports:
- pkg/apis/certificates/v1beta1
- pkg/apis/coordination
- pkg/apis/coordination/install
- pkg/apis/coordination/v1
- pkg/apis/coordination/v1beta1
- pkg/apis/core
- pkg/apis/core/helper
@ -645,6 +710,7 @@ imports:
- pkg/apis/extensions/install
- pkg/apis/extensions/v1beta1
- pkg/apis/networking
- pkg/apis/node
- pkg/apis/policy
- pkg/apis/policy/install
- pkg/apis/policy/v1beta1
@ -655,6 +721,7 @@ imports:
- pkg/apis/rbac/v1beta1
- pkg/apis/scheduling
- pkg/apis/scheduling/install
- pkg/apis/scheduling/v1
- pkg/apis/scheduling/v1alpha1
- pkg/apis/scheduling/v1beta1
- pkg/apis/settings
@ -699,30 +766,54 @@ imports:
- pkg/kubectl/util/templates
- pkg/kubectl/util/term
- pkg/kubectl/validation
- pkg/kubelet/apis
- pkg/kubelet/types
- pkg/master/ports
- pkg/printers
- pkg/printers/internalversion
- pkg/scheduler/api
- pkg/security/apparmor
- pkg/serviceaccount
- pkg/util/file
- pkg/util/hash
- pkg/util/interrupt
- pkg/util/labels
- pkg/util/net/sets
- pkg/util/node
- pkg/util/parsers
- pkg/util/taints
- pkg/version
- name: k8s.io/utils
version: 66066c83e385e385ccc3c964b44fd7dcd413d0ed
version: c2654d5206da6b7b6ace12841e8f359bb89b443c
subpackages:
- clock
- buffer
- exec
- exec/testing
- integer
- net
- path
- pointer
- trace
- name: sigs.k8s.io/kustomize
version: a6f65144121d1955266b0cd836ce954c04122dc8
subpackages:
- pkg/commands/build
- pkg/constants
- pkg/expansion
- pkg/factory
- pkg/fs
- pkg/git
- pkg/gvk
- pkg/ifc
- pkg/ifc/transformer
- pkg/image
- pkg/internal/error
- pkg/loader
- pkg/patch
- pkg/patch/transformer
- pkg/resid
- pkg/resmap
- pkg/resource
- pkg/target
- pkg/transformers
- pkg/transformers/config
- pkg/transformers/config/defaultconfig
- pkg/types
- name: sigs.k8s.io/yaml
version: fd68e9863619f6ec2fdd8625fe1f02e7c877e480
- name: vbom.ml/util
@ -730,8 +821,10 @@ imports:
subpackages:
- sortorder
testImports:
- name: github.com/DATA-DOG/go-sqlmock
version: 472e287dbafe67e526a3797165b64cb14f34705a
- name: github.com/pmezard/go-difflib
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
version: 5d4384ee4fb2527b0a1256a821ebfc92f91efefc
subpackages:
- difflib
- name: github.com/stretchr/testify

@ -6,29 +6,33 @@ import:
- package: golang.org/x/sync
subpackages:
- semaphore
- package: golang.org/x/sys
version: b90733256f2e882e81d52f9126de08df5615afd9
subpackages:
- unix
- windows
- package: github.com/spf13/cobra
version: fe5e611709b0c57fa4a89136deaa8e1d4004d053
- package: github.com/spf13/pflag
version: ~1.0.1
- package: github.com/Masterminds/vcs
# Pin version of mergo that is compatible with both sprig and Kubernetes
- package: github.com/imdario/mergo
version: v0.3.5
- package: github.com/Masterminds/sprig
version: ^2.16.0
version: ^2.19.0
- package: github.com/ghodss/yaml
- package: github.com/Masterminds/semver
version: ~1.3.1
version: ~1.4.2
- package: github.com/technosophos/moniker
version: ~0.2
- package: github.com/golang/protobuf
version: 1643683e1b54a9e88ad26d98f81400c8c9d9f4f9
version: 1.2.0
subpackages:
- proto
- ptypes/any
- ptypes/timestamp
- package: google.golang.org/grpc
version: 1.7.2
version: 1.18.0
- package: github.com/gosuri/uitable
- package: github.com/asaskevich/govalidator
version: ^4.0.0
@ -42,27 +46,32 @@ import:
- package: github.com/BurntSushi/toml
version: ~0.3.0
- package: github.com/prometheus/client_golang
version: 0.8.0
version: 0.9.2
- package: github.com/grpc-ecosystem/go-grpc-prometheus
- package: k8s.io/kubernetes
version: release-1.13
version: v1.14.1
- package: k8s.io/client-go
version: kubernetes-1.13.1
version: kubernetes-1.14.1
- package: k8s.io/api
version: kubernetes-1.13.1
version: kubernetes-1.14.1
- package: k8s.io/apimachinery
version: kubernetes-1.13.1
version: kubernetes-1.14.1
- package: k8s.io/apiserver
version: kubernetes-1.13.1
version: kubernetes-1.14.1
- package: k8s.io/cli-runtime
version: kubernetes-1.13.1
version: kubernetes-1.14.1
- package: k8s.io/apiextensions-apiserver
version: kubernetes-1.13.1
version: kubernetes-1.14.1
- package: github.com/cyphar/filepath-securejoin
version: ^0.2.1
- package: github.com/jmoiron/sqlx
version: ^1.2.0
- package: github.com/rubenv/sql-migrate
testImports:
- package: github.com/stretchr/testify
version: ^1.1.4
subpackages:
- assert
- package: github.com/DATA-DOG/go-sqlmock
version: ^1.3.2

@ -40,7 +40,7 @@ var (
// Capabilities describes the capabilities of the Kubernetes cluster that Tiller is attached to.
type Capabilities struct {
// List of all supported API versions
// APIVersions list of all supported API versions
APIVersions VersionSet
// KubeVersion is the Kubernetes version
KubeVersion *version.Info

@ -72,6 +72,7 @@ image:
tag: stable
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
@ -176,10 +177,7 @@ kind: Ingress
metadata:
name: {{ $fullName }}
labels:
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
@ -215,10 +213,7 @@ kind: Deployment
metadata:
name: {{ include "<CHARTNAME>.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
@ -235,6 +230,10 @@ spec:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
volumes:
{{- toYaml .Values.volumes | nindent 8 }}
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
@ -306,10 +305,7 @@ kind: Service
metadata:
name: {{ include "<CHARTNAME>.fullname" . }}
labels:
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
@ -377,6 +373,19 @@ Create chart name and version as used by the chart label.
{{- define "<CHARTNAME>.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{/*
Common labels
*/}}
{{- define "<CHARTNAME>.labels" -}}
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
`
const defaultTestConnection = `apiVersion: v1
@ -384,10 +393,7 @@ kind: Pod
metadata:
name: "{{ include "<CHARTNAME>.fullname" . }}-test-connection"
labels:
app.kubernetes.io/name: {{ include "<CHARTNAME>.name" . }}
helm.sh/chart: {{ include "<CHARTNAME>.chart" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{ include "<CHARTNAME>.labels" . | indent 4 }}
annotations:
"helm.sh/hook": test-success
spec:

@ -85,7 +85,7 @@ type Requirements struct {
//
// It represents the state that the dependencies should be in.
type RequirementsLock struct {
// Genderated is the date the lock file was last generated.
// Generated is the date the lock file was last generated.
Generated time.Time `json:"generated"`
// Digest is a hash of the requirements file used to generate it.
Digest string `json:"digest"`

@ -63,6 +63,12 @@ func SaveDir(c *chart.Chart, dest string) error {
// Save templates
for _, f := range c.Templates {
n := filepath.Join(outdir, f.Name)
d := filepath.Dir(n)
if err := os.MkdirAll(d, 0755); err != nil {
return err
}
if err := ioutil.WriteFile(n, f.Data, 0644); err != nil {
return err
}

@ -48,6 +48,9 @@ func TestSave(t *testing.T) {
Files: []*any.Any{
{TypeUrl: "scheherazade/shahryar.txt", Value: []byte("1,001 Nights")},
},
Templates: []*chart.Template{
{Name: "templates/scheherazade/shahryar.txt.tmpl", Data: []byte("{{ \"1,001 Nights\" }}")},
},
}
where, err := Save(c, tmp)
@ -75,6 +78,9 @@ func TestSave(t *testing.T) {
if len(c2.Files) != 1 || c2.Files[0].TypeUrl != "scheherazade/shahryar.txt" {
t.Fatal("Files data did not match")
}
if len(c2.Templates) != 1 || c2.Templates[0].Name != "templates/scheherazade/shahryar.txt.tmpl" {
t.Fatal("Templates data did not match")
}
}
func TestSavePreservesTimestamps(t *testing.T) {
@ -100,6 +106,9 @@ func TestSavePreservesTimestamps(t *testing.T) {
Files: []*any.Any{
{TypeUrl: "scheherazade/shahryar.txt", Value: []byte("1,001 Nights")},
},
Templates: []*chart.Template{
{Name: "templates/scheherazade/shahryar.txt.tmpl", Data: []byte("{{ \"1,001 Nights\" }}")},
},
}
where, err := Save(c, tmp)
@ -171,6 +180,9 @@ func TestSaveDir(t *testing.T) {
Files: []*any.Any{
{TypeUrl: "scheherazade/shahryar.txt", Value: []byte("1,001 Nights")},
},
Templates: []*chart.Template{
{Name: "templates/scheherazade/shahryar.txt.tmpl", Data: []byte("{{ \"1,001 Nights\" }}")},
},
}
if err := SaveDir(c, tmp); err != nil {
@ -191,4 +203,7 @@ func TestSaveDir(t *testing.T) {
if len(c2.Files) != 1 || c2.Files[0].TypeUrl != "scheherazade/shahryar.txt" {
t.Fatal("Files data did not match")
}
if len(c2.Templates) != 1 || c2.Templates[0].Name != "templates/scheherazade/shahryar.txt.tmpl" {
t.Fatal("Templates data did not match")
}
}

@ -65,11 +65,11 @@ type ChartDownloader struct {
Keyring string
// HelmHome is the $HELM_HOME.
HelmHome helmpath.Home
// Getter collection for the operation
// Getters collection for the operation
Getters getter.Providers
// Chart repository username
// Username chart repository username
Username string
// Chart repository password
// Password chart repository password
Password string
}
@ -213,7 +213,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
cv, err := i.Get(chartName, version)
if err != nil {
return u, r.Client, fmt.Errorf("chart %q matching %s not found in %s index. (try 'helm repo update'). %s", chartName, version, r.Config.Name, err)
return u, r.Client, fmt.Errorf("chart %q matching version %q not found in %s index. (try 'helm repo update'). %s", chartName, version, r.Config.Name, err)
}
if len(cv.URLs) == 0 {
@ -243,14 +243,14 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge
return u, r.Client, nil
}
// If HttpGetter is used, this method sets the configured repository credentials on the HttpGetter.
// setCredentials if HttpGetter is used, this method sets the configured repository credentials on the HttpGetter.
func (c *ChartDownloader) setCredentials(r *repo.ChartRepository) {
if t, ok := r.Client.(*getter.HttpGetter); ok {
t.SetCredentials(c.getRepoCredentials(r))
}
}
// If this ChartDownloader is not configured to use credentials, and the chart repository sent as an argument is,
// getRepoCredentials if this ChartDownloader is not configured to use credentials, and the chart repository sent as an argument is,
// then the repository's configured credentials are returned.
// Else, this ChartDownloader's credentials are returned.
func (c *ChartDownloader) getRepoCredentials(r *repo.ChartRepository) (username, password string) {

@ -371,6 +371,9 @@ func (m *Manager) getRepoNames(deps []*chartutil.Dependency) (map[string]string,
// by Helm.
missing := []string{}
for _, dd := range deps {
if dd.Repository == "" {
return nil, fmt.Errorf("no 'repository' field specified for dependency: %q", dd.Name)
}
// if dep chart is from local path, verify the path is valid
if strings.HasPrefix(dd.Repository, "file://") {
if _, err := resolver.GetLocalPath(dd.Repository, m.ChartPath); err != nil {
@ -462,7 +465,7 @@ func (m *Manager) parallelRepoUpdate(repos []*repo.Entry) error {
}(r)
}
wg.Wait()
fmt.Fprintln(out, "Update Complete. ⎈Happy Helming!⎈")
fmt.Fprintln(out, "Update Complete.")
return nil
}
@ -601,7 +604,7 @@ func writeLock(chartpath string, lock *chartutil.RequirementsLock) error {
return ioutil.WriteFile(dest, data, 0644)
}
// archive a dep chart from local directory and save it into charts/
// tarFromLocalDir archive a dep chart from local directory and save it into charts/
func tarFromLocalDir(chartpath string, name string, repo string, version string) (string, error) {
destPath := filepath.Join(chartpath, "charts")

@ -18,6 +18,7 @@ package downloader
import (
"bytes"
"reflect"
"strings"
"testing"
"k8s.io/helm/pkg/chartutil"
@ -103,6 +104,7 @@ func TestGetRepoNames(t *testing.T) {
req []*chartutil.Dependency
expect map[string]string
err bool
expectedErr string
}{
{
name: "no repo definition failure",
@ -118,6 +120,14 @@ func TestGetRepoNames(t *testing.T) {
},
err: true,
},
{
name: "dependency entry missing 'repository' field -- e.g. spelled 'repo'",
req: []*chartutil.Dependency{
{Name: "dependency-missing-repository-field"},
},
err: true,
expectedErr: "no 'repository' field specified for dependency: \"dependency-missing-repository-field\"",
},
{
name: "no repo definition failure",
req: []*chartutil.Dependency{
@ -152,6 +162,9 @@ func TestGetRepoNames(t *testing.T) {
l, err := m.getRepoNames(tt.req)
if err != nil {
if tt.err {
if !strings.Contains(err.Error(), tt.expectedErr) {
t.Fatalf("%s: expected error: %s, got: %s", tt.name, tt.expectedErr, err.Error())
}
continue
}
t.Fatal(err)

@ -130,7 +130,7 @@ type renderable struct {
tpl string
// vals are the values to be supplied to the template.
vals chartutil.Values
// namespace prefix to the templates of the current chart
// basePath namespace prefix to the templates of the current chart
basePath string
}

@ -334,7 +334,7 @@ func (h *Client) connect(ctx context.Context) (conn *grpc.ClientConn, err error)
return conn, nil
}
// Executes tiller.ListReleases RPC.
// list executes tiller.ListReleases RPC.
func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.ListReleasesResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -365,7 +365,7 @@ func (h *Client) list(ctx context.Context, req *rls.ListReleasesRequest) (*rls.L
return resp, nil
}
// Executes tiller.InstallRelease RPC.
// install executes tiller.InstallRelease RPC.
func (h *Client) install(ctx context.Context, req *rls.InstallReleaseRequest) (*rls.InstallReleaseResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -377,7 +377,7 @@ func (h *Client) install(ctx context.Context, req *rls.InstallReleaseRequest) (*
return rlc.InstallRelease(ctx, req)
}
// Executes tiller.UninstallRelease RPC.
// delete executes tiller.UninstallRelease RPC.
func (h *Client) delete(ctx context.Context, req *rls.UninstallReleaseRequest) (*rls.UninstallReleaseResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -389,7 +389,7 @@ func (h *Client) delete(ctx context.Context, req *rls.UninstallReleaseRequest) (
return rlc.UninstallRelease(ctx, req)
}
// Executes tiller.UpdateRelease RPC.
// update executes tiller.UpdateRelease RPC.
func (h *Client) update(ctx context.Context, req *rls.UpdateReleaseRequest) (*rls.UpdateReleaseResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -401,7 +401,7 @@ func (h *Client) update(ctx context.Context, req *rls.UpdateReleaseRequest) (*rl
return rlc.UpdateRelease(ctx, req)
}
// Executes tiller.RollbackRelease RPC.
// rollback executes tiller.RollbackRelease RPC.
func (h *Client) rollback(ctx context.Context, req *rls.RollbackReleaseRequest) (*rls.RollbackReleaseResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -413,7 +413,7 @@ func (h *Client) rollback(ctx context.Context, req *rls.RollbackReleaseRequest)
return rlc.RollbackRelease(ctx, req)
}
// Executes tiller.GetReleaseStatus RPC.
// status executes tiller.GetReleaseStatus RPC.
func (h *Client) status(ctx context.Context, req *rls.GetReleaseStatusRequest) (*rls.GetReleaseStatusResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -425,7 +425,7 @@ func (h *Client) status(ctx context.Context, req *rls.GetReleaseStatusRequest) (
return rlc.GetReleaseStatus(ctx, req)
}
// Executes tiller.GetReleaseContent RPC.
// content executes tiller.GetReleaseContent RPC.
func (h *Client) content(ctx context.Context, req *rls.GetReleaseContentRequest) (*rls.GetReleaseContentResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -437,7 +437,7 @@ func (h *Client) content(ctx context.Context, req *rls.GetReleaseContentRequest)
return rlc.GetReleaseContent(ctx, req)
}
// Executes tiller.GetVersion RPC.
// version executes tiller.GetVersion RPC.
func (h *Client) version(ctx context.Context, req *rls.GetVersionRequest) (*rls.GetVersionResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -449,7 +449,7 @@ func (h *Client) version(ctx context.Context, req *rls.GetVersionRequest) (*rls.
return rlc.GetVersion(ctx, req)
}
// Executes tiller.GetHistory RPC.
// history executes tiller.GetHistory RPC.
func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.GetHistoryResponse, error) {
c, err := h.connect(ctx)
if err != nil {
@ -461,7 +461,7 @@ func (h *Client) history(ctx context.Context, req *rls.GetHistoryRequest) (*rls.
return rlc.GetHistory(ctx, req)
}
// Executes tiller.TestRelease RPC.
// test executes tiller.TestRelease RPC.
func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (<-chan *rls.TestReleaseResponse, <-chan error) {
errc := make(chan error, 1)
c, err := h.connect(ctx)
@ -499,7 +499,7 @@ func (h *Client) test(ctx context.Context, req *rls.TestReleaseRequest) (<-chan
return ch, errc
}
// Executes tiller.Ping RPC.
// ping executes tiller.Ping RPC.
func (h *Client) ping(ctx context.Context) error {
c, err := h.connect(ctx)
if err != nil {

@ -26,7 +26,8 @@ import (
rls "k8s.io/helm/pkg/proto/hapi/services"
)
const cmInputTemplate = `kind: ConfigMap
const (
cmInputTemplate = `kind: ConfigMap
apiVersion: v1
metadata:
name: example
@ -34,7 +35,7 @@ data:
Release:
{{.Release | toYaml | indent 4}}
`
const cmOutputTemplate = `
cmOutputTemplate = `
---
# Source: installChart/templates/cm.yaml
kind: ConfigMap
@ -53,6 +54,7 @@ data:
seconds: 242085845
`
)
var installChart *chart.Chart

@ -297,6 +297,20 @@ func DeleteDescription(description string) DeleteOption {
}
}
// UpgradeCleanupOnFail allows deletion of new resources created in this upgrade when upgrade failed
func UpgradeCleanupOnFail(cleanupOnFail bool) UpdateOption {
return func(opts *options) {
opts.updateReq.CleanupOnFail = cleanupOnFail
}
}
// RollbackCleanupOnFail allows deletion of new resources created in this rollback when rollback failed
func RollbackCleanupOnFail(cleanupOnFail bool) RollbackOption {
return func(opts *options) {
opts.rollbackReq.CleanupOnFail = cleanupOnFail
}
}
// DeleteDisableHooks will disable hooks for a deletion operation.
func DeleteDisableHooks(disable bool) DeleteOption {
return func(opts *options) {

@ -27,6 +27,7 @@ import (
"k8s.io/client-go/rest"
"k8s.io/helm/pkg/kube"
"k8s.io/helm/pkg/tiller/environment"
)
var (
@ -39,8 +40,7 @@ func New(namespace string, client kubernetes.Interface, config *rest.Config) (*k
if err != nil {
return nil, err
}
const tillerPort = 44134
t := kube.NewTunnel(client.CoreV1().RESTClient(), config, namespace, podName, tillerPort)
t := kube.NewTunnel(client.CoreV1().RESTClient(), config, namespace, podName, environment.DefaultTillerPort)
return t, t.ForwardPort()
}

@ -20,14 +20,14 @@ import (
"k8s.io/helm/pkg/proto/hapi/release"
)
const (
// HookAnno is the label name for a hook
const HookAnno = "helm.sh/hook"
HookAnno = "helm.sh/hook"
// HookWeightAnno is the label name for a hook weight
const HookWeightAnno = "helm.sh/hook-weight"
HookWeightAnno = "helm.sh/hook-weight"
// HookDeleteAnno is the label name for the delete policy for a hook
const HookDeleteAnno = "helm.sh/hook-delete-policy"
HookDeleteAnno = "helm.sh/hook-delete-policy"
)
// Types of hooks
const (

@ -23,18 +23,21 @@ import (
goerrors "errors"
"fmt"
"io"
"k8s.io/apimachinery/pkg/api/meta"
"log"
"sort"
"strings"
"time"
"k8s.io/apimachinery/pkg/api/meta"
"github.com/evanphx/json-patch"
appsv1 "k8s.io/api/apps/v1"
appsv1beta1 "k8s.io/api/apps/v1beta1"
appsv1beta2 "k8s.io/api/apps/v1beta2"
batch "k8s.io/api/batch/v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
extv1beta1 "k8s.io/api/extensions/v1beta1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@ -43,9 +46,10 @@ import (
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apimachinery/pkg/watch"
"k8s.io/cli-runtime/pkg/genericclioptions"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/kubernetes/scheme"
watchtools "k8s.io/client-go/tools/watch"
"k8s.io/kubernetes/pkg/api/legacyscheme"
@ -72,8 +76,14 @@ type Client struct {
// New creates a new Client.
func New(getter genericclioptions.RESTClientGetter) *Client {
if getter == nil {
getter = genericclioptions.NewConfigFlags()
getter = genericclioptions.NewConfigFlags(true)
}
err := apiextv1beta1.AddToScheme(scheme.Scheme)
if err != nil {
panic(err)
}
return &Client{
Factory: cmdutil.NewFactory(getter),
Log: nopLogger,
@ -140,8 +150,8 @@ func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result,
ContinueOnError().
NamespaceParam(namespace).
DefaultNamespace().
Stream(reader, "").
Schema(c.validator()).
Stream(reader, "").
Flatten().
Do().Infos()
return result, scrubValidationError(err)
@ -154,13 +164,41 @@ func (c *Client) Build(namespace string, reader io.Reader) (Result, error) {
return result, scrubValidationError(err)
}
// Return the resource info as internal
func resourceInfoToObject(info *resource.Info, c *Client) runtime.Object {
internalObj, err := asInternal(info)
if err != nil {
// If the problem is just that the resource is not registered, don't print any
// error. This is normal for custom resources.
if !runtime.IsNotRegisteredError(err) {
c.Log("Warning: conversion to internal type failed: %v", err)
}
// Add the unstructured object in this situation. It will still get listed, just
// with less information.
return info.Object
}
return internalObj
}
func sortByKey(objs map[string](map[string]runtime.Object)) []string {
var keys []string
// Create a simple slice, so we can sort it
for key := range objs {
keys = append(keys, key)
}
// Sort alphabetically by version/kind keys
sort.Strings(keys)
return keys
}
// Get gets Kubernetes resources as pretty-printed string.
//
// Namespace will set the namespace.
func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
// Since we don't know what order the objects come in, let's group them by the types, so
// Since we don't know what order the objects come in, let's group them by the types and then sort them, so
// that when we print them, they come out looking good (headers apply to subgroups, etc.).
objs := make(map[string][]runtime.Object)
objs := make(map[string](map[string]runtime.Object))
infos, err := c.BuildUnstructured(namespace, reader)
if err != nil {
return "", err
@ -181,19 +219,15 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
// versions per cluster, but this certainly won't hurt anything, so let's be safe.
gvk := info.ResourceMapping().GroupVersionKind
vk := gvk.Version + "/" + gvk.Kind
internalObj, err := asInternal(info)
if err != nil {
// If the problem is just that the resource is not registered, don't print any
// error. This is normal for custom resources.
if !runtime.IsNotRegisteredError(err) {
c.Log("Warning: conversion to internal type failed: %v", err)
}
// Add the unstructured object in this situation. It will still get listed, just
// with less information.
objs[vk] = append(objs[vk], info.Object)
} else {
objs[vk] = append(objs[vk], internalObj)
// Initialize map. The main map groups resources based on version/kind
// The second level is a simple 'Name' to 'Object', that will help sort
// the individual resource later
if objs[vk] == nil {
objs[vk] = make(map[string]runtime.Object)
}
// Map between the resource name to the underlying info object
objs[vk][info.Name] = resourceInfoToObject(info, c)
//Get the relation pods
objPods, err = c.getSelectRelationPod(info, objPods)
@ -211,8 +245,12 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
for key, podItems := range objPods {
for i := range podItems {
pod := &core.Pod{}
legacyscheme.Scheme.Convert(&podItems[i], pod, nil)
objs[key+"(related)"] = append(objs[key+"(related)"], pod)
if objs[key+"(related)"] == nil {
objs[key+"(related)"] = make(map[string]runtime.Object)
}
objs[key+"(related)"][pod.ObjectMeta.Name] = runtime.Object(pod)
}
}
@ -222,14 +260,28 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
// track of tab widths.
buf := new(bytes.Buffer)
printFlags := get.NewHumanPrintFlags()
for t, ot := range objs {
// Sort alphabetically by version/kind keys
vkKeys := sortByKey(objs)
// Iterate on sorted version/kind types
for _, t := range vkKeys {
if _, err = fmt.Fprintf(buf, "==> %s\n", t); err != nil {
return "", err
}
typePrinter, _ := printFlags.ToPrinter("")
for _, o := range ot {
if err := typePrinter.PrintObj(o, buf); err != nil {
c.Log("failed to print object type %s, object: %q :\n %v", t, o, err)
var sortedResources []string
for resource := range objs[t] {
sortedResources = append(sortedResources, resource)
}
sort.Strings(sortedResources)
// Now that each individual resource within the specific version/kind
// is sorted, we print each resource using the k8s printer
vk := objs[t]
for _, resourceName := range sortedResources {
if err := typePrinter.PrintObj(vk[resourceName], buf); err != nil {
c.Log("failed to print object type %s, object: %q :\n %v", t, resourceName, err)
return "", err
}
}
@ -246,13 +298,33 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) {
return buf.String(), nil
}
// Update reads in the current configuration and a target configuration from io.reader
// Deprecated; use UpdateWithOptions instead
func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
return c.UpdateWithOptions(namespace, originalReader, targetReader, UpdateOptions{
Force: force,
Recreate: recreate,
Timeout: timeout,
ShouldWait: shouldWait,
})
}
// UpdateOptions provides options to control update behavior
type UpdateOptions struct {
Force bool
Recreate bool
Timeout int64
ShouldWait bool
// Allow deletion of new resources created in this update when update failed
CleanupOnFail bool
}
// UpdateWithOptions reads in the current configuration and a target configuration from io.reader
// and creates resources that don't already exists, updates resources that have been modified
// in the target configuration and deletes resources from the current configuration that are
// not present in the target configuration.
//
// Namespace will set the namespaces.
func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error {
func (c *Client) UpdateWithOptions(namespace string, originalReader, targetReader io.Reader, opts UpdateOptions) error {
original, err := c.BuildUnstructured(namespace, originalReader)
if err != nil {
return fmt.Errorf("failed decoding reader into objects: %s", err)
@ -264,6 +336,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
return fmt.Errorf("failed decoding reader into objects: %s", err)
}
newlyCreatedResources := []*resource.Info{}
updateErrors := []string{}
c.Log("checking %d resources for changes", len(target))
@ -282,6 +355,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
if err := createResource(info); err != nil {
return fmt.Errorf("failed to create resource: %s", err)
}
newlyCreatedResources = append(newlyCreatedResources, info)
kind := info.Mapping.GroupVersionKind.Kind
c.Log("Created a new %s called %q\n", kind, info.Name)
@ -289,12 +363,21 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
}
originalInfo := original.Get(info)
// The resource already exists in the cluster, but it wasn't defined in the previous release.
// In this case, we consider it to be a resource that was previously un-managed by the release and error out,
// asking for the user to intervene.
//
// See https://github.com/helm/helm/issues/1193 for more info.
if originalInfo == nil {
kind := info.Mapping.GroupVersionKind.Kind
return fmt.Errorf("no %s with the name %q found", kind, info.Name)
return fmt.Errorf(
"kind %s with the name %q already exists in the cluster and wasn't defined in the previous release. Before upgrading, please either delete the resource from the cluster or remove it from the chart",
info.Mapping.GroupVersionKind.Kind,
info.Name,
)
}
if err := updateResource(c, info, originalInfo.Object, force, recreate); err != nil {
if err := updateResource(c, info, originalInfo.Object, opts.Force, opts.Recreate); err != nil {
c.Log("error updating the resource %q:\n\t %v", info.Name, err)
updateErrors = append(updateErrors, err.Error())
}
@ -302,11 +385,18 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
return nil
})
cleanupErrors := []string{}
if opts.CleanupOnFail && (err != nil || len(updateErrors) != 0) {
c.Log("Cleanup on fail enabled: cleaning up newly created resources due to update manifests failures")
cleanupErrors = c.cleanup(newlyCreatedResources)
}
switch {
case err != nil:
return err
return fmt.Errorf(strings.Join(append([]string{err.Error()}, cleanupErrors...), " && "))
case len(updateErrors) != 0:
return fmt.Errorf(strings.Join(updateErrors, " && "))
return fmt.Errorf(strings.Join(append(updateErrors, cleanupErrors...), " && "))
}
for _, info := range original.Difference(target) {
@ -319,8 +409,9 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
if err != nil {
c.Log("Unable to get annotations on %q, err: %s", info.Name, err)
}
if annotations != nil && annotations[ResourcePolicyAnno] == KeepPolicy {
c.Log("Skipping delete of %q due to annotation [%s=%s]", info.Name, ResourcePolicyAnno, KeepPolicy)
if ResourcePolicyIsKeep(annotations) {
policy := annotations[ResourcePolicyAnno]
c.Log("Skipping delete of %q due to annotation [%s=%s]", info.Name, ResourcePolicyAnno, policy)
continue
}
@ -328,12 +419,32 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader
c.Log("Failed to delete %q, err: %s", info.Name, err)
}
}
if shouldWait {
return c.waitForResources(time.Duration(timeout)*time.Second, target)
if opts.ShouldWait {
err := c.waitForResources(time.Duration(opts.Timeout)*time.Second, target)
if opts.CleanupOnFail && err != nil {
c.Log("Cleanup on fail enabled: cleaning up newly created resources due to wait failure during update")
cleanupErrors = c.cleanup(newlyCreatedResources)
return fmt.Errorf(strings.Join(append([]string{err.Error()}, cleanupErrors...), " && "))
}
return err
}
return nil
}
func (c *Client) cleanup(newlyCreatedResources []*resource.Info) (cleanupErrors []string) {
for _, info := range newlyCreatedResources {
kind := info.Mapping.GroupVersionKind.Kind
c.Log("Deleting newly created %s with the name %q in %s...", kind, info.Name, info.Namespace)
if err := deleteResource(info); err != nil {
c.Log("Error deleting newly created %s with the name %q in %s: %s", kind, info.Name, info.Namespace, err)
cleanupErrors = append(cleanupErrors, err.Error())
}
}
return
}
// Delete deletes Kubernetes resources from an io.reader.
//
// Namespace will set the namespace.
@ -385,6 +496,55 @@ func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int
return perform(infos, c.watchTimeout(time.Duration(timeout)*time.Second))
}
// WatchUntilCRDEstablished polls the given CRD until it reaches the established
// state. A CRD needs to reach the established state before CRs can be created.
//
// If a naming conflict condition is found, this function will return an error.
func (c *Client) WaitUntilCRDEstablished(reader io.Reader, timeout time.Duration) error {
infos, err := c.BuildUnstructured(metav1.NamespaceAll, reader)
if err != nil {
return err
}
return perform(infos, c.pollCRDEstablished(timeout))
}
func (c *Client) pollCRDEstablished(t time.Duration) ResourceActorFunc {
return func(info *resource.Info) error {
return c.pollCRDUntilEstablished(t, info)
}
}
func (c *Client) pollCRDUntilEstablished(timeout time.Duration, info *resource.Info) error {
return wait.PollImmediate(time.Second, timeout, func() (bool, error) {
err := info.Get()
if err != nil {
return false, fmt.Errorf("unable to get CRD: %v", err)
}
crd := &apiextv1beta1.CustomResourceDefinition{}
err = scheme.Scheme.Convert(info.Object, crd, nil)
if err != nil {
return false, fmt.Errorf("unable to convert to CRD type: %v", err)
}
for _, cond := range crd.Status.Conditions {
switch cond.Type {
case apiextv1beta1.Established:
if cond.Status == apiextv1beta1.ConditionTrue {
return true, nil
}
case apiextv1beta1.NamesAccepted:
if cond.Status == apiextv1beta1.ConditionFalse {
return false, fmt.Errorf("naming conflict detected for CRD %s", crd.GetName())
}
}
}
return false, nil
})
}
func perform(infos Result, fn ResourceActorFunc) error {
if len(infos) == 0 {
return ErrNoObjectsVisited

@ -21,26 +21,49 @@ import (
"io"
"io/ioutil"
"net/http"
"sort"
"strings"
"testing"
"time"
"k8s.io/api/core/v1"
apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/cli-runtime/pkg/genericclioptions/resource"
"k8s.io/cli-runtime/pkg/resource"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest/fake"
cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing"
kubectlscheme "k8s.io/kubernetes/pkg/kubectl/scheme"
)
func init() {
err := apiextv1beta1.AddToScheme(scheme.Scheme)
if err != nil {
panic(err)
}
// Tiller use the scheme from go-client, but the cmdtesting
// package used here is hardcoded to use the scheme from
// kubectl. So for testing, we need to add the CustomResourceDefinition
// type to both schemes.
err = apiextv1beta1.AddToScheme(kubectlscheme.Scheme)
if err != nil {
panic(err)
}
}
var (
codec = scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
unstructuredSerializer = resource.UnstructuredPlusDefaultContentConfig().NegotiatedSerializer
)
func getCodec() runtime.Codec {
return scheme.Codecs.LegacyCodec(scheme.Scheme.PrioritizedVersionsAllGroups()...)
}
func objBody(obj runtime.Object) io.ReadCloser {
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(getCodec(), obj))))
}
func newPod(name string) v1.Pod {
@ -77,6 +100,18 @@ func newPodList(names ...string) v1.PodList {
return list
}
func newService(name string) v1.Service {
ns := v1.NamespaceDefault
return v1.Service{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: ns,
SelfLink: "/api/v1/namespaces/default/services/" + name,
},
Spec: v1.ServiceSpec{},
}
}
func notFoundBody() *metav1.Status {
return &metav1.Status{
Code: http.StatusNotFound,
@ -90,7 +125,7 @@ func notFoundBody() *metav1.Status {
func newResponse(code int, obj runtime.Object) (*http.Response, error) {
header := http.Header{}
header.Set("Content-Type", runtime.ContentTypeJSON)
body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj))))
body := ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(getCodec(), obj))))
return &http.Response{StatusCode: code, Header: header, Body: body}, nil
}
@ -200,7 +235,7 @@ func TestUpdate(t *testing.T) {
// Test resource policy is respected
actions = nil
listA.Items[2].ObjectMeta.Annotations = map[string]string{ResourcePolicyAnno: KeepPolicy}
listA.Items[2].ObjectMeta.Annotations = map[string]string{ResourcePolicyAnno: "keep"}
if err := c.Update(v1.NamespaceDefault, objBody(&listA), objBody(&listB), false, false, 0, false); err != nil {
t.Fatal(err)
}
@ -211,6 +246,43 @@ func TestUpdate(t *testing.T) {
}
}
func TestUpdateNonManagedResourceError(t *testing.T) {
actual := newPodList("starfish")
current := newPodList()
target := newPodList("starfish")
tf := cmdtesting.NewTestFactory()
defer tf.Cleanup()
tf.UnstructuredClient = &fake.RESTClient{
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
t.Logf("got request %s %s", p, m)
switch {
case p == "/namespaces/default/pods/starfish" && m == "GET":
return newResponse(200, &actual.Items[0])
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
c := &Client{
Factory: tf,
Log: nopLogger,
}
if err := c.Update(v1.NamespaceDefault, objBody(&current), objBody(&target), false, false, 0, false); err != nil {
if err.Error() != "kind Pod with the name \"starfish\" already exists in the cluster and wasn't defined in the previous release. Before upgrading, please either delete the resource from the cluster or remove it from the chart" {
t.Fatal(err)
}
} else {
t.Fatalf("error expected")
}
}
func TestBuild(t *testing.T) {
tests := []struct {
name string
@ -295,6 +367,177 @@ func TestGet(t *testing.T) {
}
}
func TestResourceTypeSortOrder(t *testing.T) {
pod := newPod("my-pod")
service := newService("my-service")
c := newTestClient()
defer c.Cleanup()
c.TestFactory.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
t.Logf("got request %s %s", p, m)
switch {
case p == "/namespaces/default/pods/my-pod" && m == "GET":
return newResponse(200, &pod)
case p == "/namespaces/default/services/my-service" && m == "GET":
return newResponse(200, &service)
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
// Test sorting order
data := strings.NewReader(testResourceTypeSortOrder)
o, err := c.Get("default", data)
if err != nil {
t.Errorf("Expected missing results, got %q", err)
}
podIndex := strings.Index(o, "my-pod")
serviceIndex := strings.Index(o, "my-service")
if podIndex == -1 {
t.Errorf("Expected v1/Pod my-pod, got %s", o)
}
if serviceIndex == -1 {
t.Errorf("Expected v1/Service my-service, got %s", o)
}
if !sort.IntsAreSorted([]int{podIndex, serviceIndex}) {
t.Errorf("Expected order: [v1/Pod v1/Service], got %s", o)
}
}
func TestResourceSortOrder(t *testing.T) {
list := newPodList("albacore", "coral", "beluga")
c := newTestClient()
defer c.Cleanup()
c.TestFactory.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
p, m := req.URL.Path, req.Method
t.Logf("got request %s %s", p, m)
switch {
case p == "/namespaces/default/pods/albacore" && m == "GET":
return newResponse(200, &list.Items[0])
case p == "/namespaces/default/pods/coral" && m == "GET":
return newResponse(200, &list.Items[1])
case p == "/namespaces/default/pods/beluga" && m == "GET":
return newResponse(200, &list.Items[2])
default:
t.Fatalf("unexpected request: %s %s", req.Method, req.URL.Path)
return nil, nil
}
}),
}
// Test sorting order
data := strings.NewReader(testResourceSortOrder)
o, err := c.Get("default", data)
if err != nil {
t.Errorf("Expected missing results, got %q", err)
}
albacoreIndex := strings.Index(o, "albacore")
belugaIndex := strings.Index(o, "beluga")
coralIndex := strings.Index(o, "coral")
if albacoreIndex == -1 {
t.Errorf("Expected v1/Pod albacore, got %s", o)
}
if belugaIndex == -1 {
t.Errorf("Expected v1/Pod beluga, got %s", o)
}
if coralIndex == -1 {
t.Errorf("Expected v1/Pod coral, got %s", o)
}
if !sort.IntsAreSorted([]int{albacoreIndex, belugaIndex, coralIndex}) {
t.Errorf("Expected order: [albacore beluga coral], got %s", o)
}
}
func TestWaitUntilCRDEstablished(t *testing.T) {
testCases := map[string]struct {
conditions []apiextv1beta1.CustomResourceDefinitionCondition
returnConditionsAfter int
success bool
}{
"crd reaches established state after 2 requests": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
{
Type: apiextv1beta1.Established,
Status: apiextv1beta1.ConditionTrue,
},
},
returnConditionsAfter: 2,
success: true,
},
"crd does not reach established state before timeout": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{},
returnConditionsAfter: 100,
success: false,
},
"crd name is not accepted": {
conditions: []apiextv1beta1.CustomResourceDefinitionCondition{
{
Type: apiextv1beta1.NamesAccepted,
Status: apiextv1beta1.ConditionFalse,
},
},
returnConditionsAfter: 1,
success: false,
},
}
for tn, tc := range testCases {
func(name string) {
c := newTestClient()
defer c.Cleanup()
crdWithoutConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{})
crdWithConditions := newCrdWithStatus("name", apiextv1beta1.CustomResourceDefinitionStatus{
Conditions: tc.conditions,
})
requestCount := 0
c.TestFactory.UnstructuredClient = &fake.RESTClient{
GroupVersion: schema.GroupVersion{Version: "v1"},
NegotiatedSerializer: unstructuredSerializer,
Client: fake.CreateHTTPClient(func(req *http.Request) (*http.Response, error) {
var crd apiextv1beta1.CustomResourceDefinition
if requestCount < tc.returnConditionsAfter {
crd = crdWithoutConditions
} else {
crd = crdWithConditions
}
requestCount += 1
return newResponse(200, &crd)
}),
}
err := c.WaitUntilCRDEstablished(strings.NewReader(crdManifest), 5*time.Second)
if err != nil && tc.success {
t.Errorf("%s: expected no error, but got %v", name, err)
}
if err == nil && !tc.success {
t.Errorf("%s: expected error, but didn't get one", name)
}
}(tn)
}
}
func newCrdWithStatus(name string, status apiextv1beta1.CustomResourceDefinitionStatus) apiextv1beta1.CustomResourceDefinition {
crd := apiextv1beta1.CustomResourceDefinition{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: metav1.NamespaceDefault,
},
Spec: apiextv1beta1.CustomResourceDefinitionSpec{},
Status: status,
}
return crd
}
func TestPerform(t *testing.T) {
tests := []struct {
name string
@ -376,6 +619,35 @@ func TestReal(t *testing.T) {
}
}
const testResourceTypeSortOrder = `
kind: Service
apiVersion: v1
metadata:
name: my-service
---
kind: Pod
apiVersion: v1
metadata:
name: my-pod
`
const testResourceSortOrder = `
kind: Pod
apiVersion: v1
metadata:
name: albacore
---
kind: Pod
apiVersion: v1
metadata:
name: coral
---
kind: Pod
apiVersion: v1
metadata:
name: beluga
`
const testServiceManifest = `
kind: Service
apiVersion: v1
@ -533,3 +805,41 @@ spec:
ports:
- containerPort: 80
`
const crdManifest = `
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
creationTimestamp: null
labels:
controller-tools.k8s.io: "1.0"
name: applications.app.k8s.io
spec:
group: app.k8s.io
names:
kind: Application
plural: applications
scope: Namespaced
validation:
openAPIV3Schema:
properties:
apiVersion:
description: 'Description'
type: string
kind:
description: 'Kind'
type: string
metadata:
type: object
spec:
type: object
status:
type: object
version: v1beta1
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
`

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save