Merge branch 'master' of https://github.com/kubernetes/helm into cg/kube_create_goroutine

pull/5611/head
Charlie Getzen 6 years ago
commit 6b2f408f8e

@ -4,7 +4,7 @@ jobs:
working_directory: /go/src/k8s.io/helm
parallelism: 3
docker:
- image: golang:1.12.2
- image: golang:1.13.1
environment:
PROJECT_NAME: "kubernetes-helm"
steps:

@ -22,6 +22,8 @@ fi
: ${GCLOUD_SERVICE_KEY:?"GCLOUD_SERVICE_KEY environment variable is not set"}
: ${PROJECT_NAME:?"PROJECT_NAME environment variable is not set"}
: ${AZURE_STORAGE_CONNECTION_STRING:?"AZURE_STORAGE_CONNECTION_STRING environment variable is not set"}
: ${AZURE_STORAGE_CONTAINER_NAME:?"AZURE_STORAGE_CONTAINER_NAME environment variable is not set"}
VERSION=
if [[ -n "${CIRCLE_TAG:-}" ]]; then
@ -50,6 +52,14 @@ ${HOME}/google-cloud-sdk/bin/gcloud auth activate-service-account --key-file "${
${HOME}/google-cloud-sdk/bin/gcloud config set project "${PROJECT_NAME}"
docker login -u _json_key -p "$(cat ${HOME}/gcloud-service-key.json)" https://gcr.io
echo "Installing Azure CLI"
apt update
apt install -y apt-transport-https
echo "deb [arch=amd64] https://packages.microsoft.com/repos/azure-cli/ stretch main" | tee /etc/apt/sources.list.d/azure-cli.list
curl -L https://packages.microsoft.com/keys/microsoft.asc | apt-key add
apt update
apt install -y azure-cli
echo "Building the tiller image"
make docker-build VERSION="${VERSION}"
@ -62,3 +72,9 @@ make dist checksum VERSION="${VERSION}"
echo "Pushing binaries to gs bucket"
${HOME}/google-cloud-sdk/bin/gsutil cp ./_dist/* "gs://${PROJECT_NAME}"
echo "Pushing binaries to Azure"
az storage blob upload-batch -s _dist/ -d "$AZURE_STORAGE_CONTAINER_NAME" --pattern 'helm-*' --connection-string "$AZURE_STORAGE_CONNECTION_STRING"
echo "Pushing KEYS file to Azure"
az storage blob upload -f "KEYS" -n "KEYS" -c "$AZURE_STORAGE_CONTAINER_NAME" --connection-string "$AZURE_STORAGE_CONNECTION_STRING"

@ -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)
@ -229,6 +263,8 @@ Like any good open source project, we use Pull Requests (PRs) to track code chan
- PRs should stay open until merged or if they have not been active for more than 30 days.
This will help keep the PR queue to a manageable size and reduce noise. Should the PR need
to stay open (like in the case of a WIP), the `keep open` label can be added.
- Before merging a PR, refer to the topic on [Size Labels](#size-labels) below to determine if
the PR requires more than one LGTM to merge.
- If the owner of the PR is listed in `OWNERS`, that user **must** merge their own PRs or explicitly
request another OWNER do that for them.
- If the owner of a PR is _not_ listed in `OWNERS`, any maintainer may merge the PR once it is approved.
@ -268,7 +304,7 @@ The following tables define all label types used for Helm. It is split up by cat
| `help wanted` | This issue is one the core maintainers cannot get to right now and would appreciate help with |
| `proposal` | This issue is a proposal |
| `question/support` | This issue is a support request or question |
| `starter` | This issue is a good for someone new to contributing to Helm |
| `good first issue` | This issue is a good for someone new to contributing to Helm |
| `wont fix` | The issue has been discussed and will not be implemented (or accepted in the case of a proposal) |
### PR Specific
@ -286,13 +322,20 @@ The following tables define all label types used for Helm. It is split up by cat
Size labels are used to indicate how "dangerous" a PR is. The guidelines below are used to assign the
labels, but ultimately this can be changed by the maintainers. For example, even if a PR only makes
30 lines of changes in 1 file, but it changes key functionality, it will likely be labeled as `size/large`
30 lines of changes in 1 file, but it changes key functionality, it will likely be labeled as `size/L`
because it requires sign off from multiple people. Conversely, a PR that adds a small feature, but requires
another 150 lines of tests to cover all cases, could be labeled as `size/small` even though the number
another 150 lines of tests to cover all cases, could be labeled as `size/S` even though the number
lines is greater than defined below.
PRs submitted by a core maintainer, regardless of size, only requires approval from one additional
maintainer. This ensures there are at least two maintainers who are aware of any significant PRs
introduced to the codebase.
| Label | Description |
| ----- | ----------- |
| `size/small` | Anything less than or equal to 4 files and 150 lines. Only small amounts of manual testing may be required |
| `size/medium` | Anything greater than `size/small` and less than or equal to 8 files and 300 lines. Manual validation should be required. |
| `size/large` | Anything greater than `size/medium`. This should be thoroughly tested before merging and always requires 2 approvals. This also should be applied to anything that is a significant logic change. |
| `size/XS` | Anything less than or equal to 9 lines ignoring generated files. Only small amounts of manual testing may be required. |
| `size/S` | Anything greater than `size/XS` less than or equal to 29 lines ignoring the generated files. Only small amounts of manual testing may be required. |
| `size/M` | Anything greater than `size/S` less than or equal to 99 lines ignoring the generated files. Manual validation should be required. |
| `size/L` | Anything greater than `size/M` less than or equal to 499 lines ignoring the generated files. This should be thoroughly tested before merging and always requires 2 approvals. This also should be applied to anything that is a significant logic change. |
| `size/XL` | Anything greater than `size/L` less than or equal to 999 lines ignoring the generated files. This should be thoroughly tested before merging and always requires 2 approvals. This also should be applied to anything that is a significant logic change. |
| `size/XXL` | Anything greater than `size/XL`. This should be thoroughly tested before merging and always requires 2 approvals. This also should be applied to anything that is a significant logic change. |

271
KEYS

@ -0,0 +1,271 @@
This file contains the PGP keys of developers who have signed releases of Helm.
For your convenience, commands are provided for those who use pgp and gpg.
For users to import keys:
pgp < KEYS
or
gpg --import KEYS
Developers to add their keys:
pgp -kxa <your name> and append it to this file.
or
(pgpk -ll <your name> && pgpk -xa <your name>) >> KEYS
or
(gpg --list-sigs <your name>
&& gpg --armor --export <your name>) >> KEYS
pub rsa4096/0x461449C25E36B98E 2017-11-10 [SC]
672C657BE06B4B30969C4A57461449C25E36B98E
uid [ultimate] Matthew Farina <matt@mattfarina.com>
sig 3 0x461449C25E36B98E 2017-11-10 Matthew Farina <matt@mattfarina.com>
sig 0x2CDBBFBB37AE822A 2018-12-12 Adnan Abdulhussein <prydonius@gmail.com>
sig 0x1EF612347F8A9958 2018-12-12 Adam Reese <adam@reese.io>
sig 0x62F49E747D911B60 2018-12-12 Matt Butcher <matt.butcher@microsoft.com>
sub rsa4096/0xCCCE67689DF05738 2017-11-10 [E]
sig 0x461449C25E36B98E 2017-11-10 Matthew Farina <matt@mattfarina.com>
sub rsa4096/0x9436E80BFBA46909 2017-11-10 [S] [expires: 2022-11-09]
sig 0x461449C25E36B98E 2017-11-10 Matthew Farina <matt@mattfarina.com>
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFoFERgBEADdhgM8EPo9fxnu2iW75r4uha2TrhWaO3EJIo53sa6U9nePIeWc
oWqjDZqYvIMJcylfocrVi4m6HdNcPrWo5pSWeKd8J9X8d4BUhoKFmJdHqWzgokwW
Rk06Doro2FHFyHoPPrI3a1HGVWA0xFhBYqSbim4j/Q0FouS566MofeRGnnacJ88z
Z7yErN5Gy4jk7pOgwvMewoGpEd8FMcyYSJfSjeoqdIZYp89EKTLbgQZuOJ9yVZnY
c0mtpH57UbkrkGv8hRuViWSO99q/mpMQyWQGYVoTV4QM/0q4jUbkRazaeY3N4hGC
I6Xf4ilWyNmmVODI6JcvWY+vXPtxIKjEjYiomVCF6jCYWWCA7cf3+kqJ+T4sc0NF
fseR/TAOkDV/XsZ1ufbSHBEiZTIjLvoAGJ+u+3go+UysVVCw4L1NSGFeDrZ97KSe
w0MeuV2SYfdZ4so7k4YDNbBLTVx0V/wl+laFtdjo167D18AYw54HIv3snHkjABfY
7Q06Ye7FuuKzdrj9KpmzUYnN3hRGqe84GIcM3D5+vElj0vyg8th32Dig5Xi38s0M
sz7hPg+oFk7csslMVAnLtWYvsv2FMSKB9FUHYv9AJ6yjYfyLlQgjjda0z6Sq5zpu
qVZqTNSxEIZFDKfTgQV6rocIK5VKP063KS6qwpHzPxKADaLTUPOWeum9/wARAQAB
tCRNYXR0aGV3IEZhcmluYSA8bWF0dEBtYXR0ZmFyaW5hLmNvbT6JAk4EEwEIADgW
IQRnLGV74GtLMJacSldGFEnCXja5jgUCWgURGAIbAwULCQgHAwUVCgkICwUWAwIB
AAIeAQIXgAAKCRBGFEnCXja5jjtQEADJvSx67Qz8gTxvUH3HaMsXaeb6BG3zLJXj
34pqAGNkKB4/ZgpFVYE1R0QuvYn9CbFpD1UcSank3L3xBroeOEUN3kvOg3D6Bv8f
mtwtW1TDjaWDTa0mZ8icanjXVNfK3K8pAwni2FPrW/tesEt/8GI48ZxPMzHk1qrL
8mETLRn1EBL3vq5qPDIK87XhhW9WAgwsadn6BQKSTSVVUACBAlV7EbqE4DHqhwYz
D1HrEIAtXkkb9JJejUnAbiOqPmm9s6iWC13K1P27FB8EEYiKxL8kb7xv5xW7+Pmg
kb03OqZtZYu9Fl1MF1zVQe4mXVflcbj7mYU1kb8vepD6bOUA89z8FggU2Q38cxkD
TYQsxpGwWz3nvEu29KbHmjQja1+G5D8kQ8bv1mNdiXQbOz51v2+7vowKKUoPQfp9
n8Ez4dxWVrFtf218Mtt8wbYmmVYijLIBDArYKDeVqNNua8YC9641DcvRdCCvaYEx
Q9vWKjpAWmXKy2bb7TQ2TjGRh+Ly47z+PTluqUeYuBREAN4Hd4xwiClRbhb3I9To
YTJkPOkaOR967zBho5orA8xww4hcsufhjqsoU0/MGbG6jvJihHFR9Jq+0gVzakca
K8tGRSA8l5xdjow5dVOPzeXuKDPuvHEwa63TWsH5H8s6iembNT1H9bate8wQT1TN
9PH/6sthz4kCMwQQAQgAHRYhBFER2nPfEtjoEspGLyzbv7s3roIqBQJcET6LAAoJ
ECzbv7s3roIqozgQAIG5IqJ7hYjndCLW2MBLEa9oA04QSgF9qcqfiG00tjhBVwEK
YE6r7BUgC7r7dP1xVa/+5lVRATfiJ+Raq7udm/RQsamyp9Q8xBOuavPcJDZMX5m7
OqPZMs+TDFPYM914GIWPAQf9ehaHHnmCNZXExxYlnZBPFsOcLYSNGH/xQeiA+q3F
tCOdRhjcpbt4rcx+Jq/l6X3cxstFwcYeljhvebblpwcVNJVArVrWZmosFl3rz3bs
PKfZKAvjV65knRkra73ZjN+YEYMMr6MzvVh/cnigk9XHgu5Y7imLv9qf1leyFCaa
oJoQDAcHIfs/eQmaEbYUyw/jX53/PyGqXlmkW7D3wqAGH5yx+ske7otCiaHHoTK0
vHsEvO9b4dLtr0uMMNRO7St+3EtMa070s537XymG1HSeW8QbVEg/+w2YW5DyTe5p
WaNJS6WUc7UuIgEWvgitVxhUheZRumh5/EW673yI8iUchGslAuL1W5R1rXQfMPVA
BsI8D8pWs9EKjP4Lpu1Wgoxm0O4kaAxRbbHjrIYLtoRRrakr+kfqjZ/rJM89JQpl
NWNBZ61IDKROj7U2kLAxCJSB3RfAuqinyFGjxod7ENW7u6z0SCdupybbmylAfD+T
t3Z2DBB9tjxNnsgb2pbcm8cDGrJOZhIDdcVChvMXnHNxEmXbHvTKocci0t4viQIz
BBABCgAdFiEESdCchsPcjaPwoHYiHvYSNH+KmVgFAlwRP38ACgkQHvYSNH+KmVgP
rxAAkhggTXggRwpWzgU7PRsj347DqtH3f/2EfTOhAi6PGOiw2EFocTrx47WHAjs6
XFT+c0yHCv58fGHKrrfeOT1VCjk2xf0NSdf00CTHO+DqepNiXzFYCJ0fUTL3w2JC
ugrfhwEdVH3TYJffFlmi0VZVCrGT3ZU1H+N/mVcd4FniOPWaGYoSG15iift4cAO/
CynMFUbl5NYCuE/z9lR8o/3KSu7vuffLsvXdkxCX6fjxkSWcBKgH7ts7OWyPv9H1
r/I295CoG9ZmeKVtScY7lamb+vOw9ryHbTACo0aprPQ1kCjr+3JIJdodNkRQvzZX
Ayxmc/zWSmPlJ7zjVkmoLaU7YmN7dPaVpQiELQGKhm/TyH++ZxoA4Rw4dwtqqk86
+F5ncsqJ107IW7ce6lnZVEvUBD4DHkMRQQZOA9hWBxVeDznjXzfpNNTB07mtzArG
nrbbnNu3epUPthZlhQ8C+dZeBOfGzyr3Aj6CQqKMziiL2Tf4Coa7PhHRBs6rf1PD
xNhnnybCvaMJEMSyX6b/lqb967yVI6g3TXQvi0cGGvYmwEBOiKkXSRHtQBjC1Ocq
qUjzg1dvyfJu84S0kSt2oEHL5n1TAvIrwqNNOwS6CL0x2pSLOVhZmpummSqybvsF
YJjctDJvBA7URB9asMOK3CS6UsJaVzUFkybxaYIdUPylh1mJAjMEEAEKAB0WIQSr
olKVmPZibEINM1ti9J50fZEbYAUCXBE1mgAKCRBi9J50fZEbYEcVEACOTG1qO0m/
+8T2S8rskKDrgoXMi22x3n4SqdKIA5TwWdWp18nVyXIxUWvI1cS73WupHNtEKTLc
+yObvNo1N3syj/5c14RcRLUcWTFKs596TcUP5/xNH33j0nFplKplBP4MegnduXsB
HibxiEycpkTFVxc3xbW9KeWSzqEHxxOXE1okL0SDWTj/oNRToaDc4zdm26veZd25
ycxqRkksZZCPuczqb2SB/mDqHx1jl4z2B6CzN3OUzMk40a77xwZXKNGTO4+fMEOJ
Flch8YQXh+gPbS1F/Q7qCrQOkhoV3nI/0CxNgWNcPrUd52xtGHzgxbdrgT7L0XMO
/KmIu1O8E+znjOxcSAklwh1xLsT01193vbVyW2pcmmtqo1ku0taLlw4T7VHQNb88
uOKucXlA10L2lFFnqBWLOuZDcVpgywMjIrKTPoEpDcVPaBUDQCFBZE9ogA/Edhlo
mxGxhtzG/O6wwFcLoleMH1Lf6zMxhwOAIvkWVjsuQ312uVy1RNY7b3UFrxOw8/qq
UBy6AFE/dp9PF8BIQ37NHKeAlvCexEedwJi4RwH0hUQkBhxBeNrTOEE7cCaZ9Shz
IWhPKxSRKKblYY4fpDzl2uMBwdetk9jfZF2ofoSOKXTVh+YJ8PzncD6xJVesbMIW
0aPkERdmz8JeGBclBR0miED+zidofWCgD7kCDQRaBREYARAAqiqhYIA3ci/sJ7y3
mJaQ/lsL2nsy+RgW52ETpLp3tIO2r3rxNn7CB/kJhPimDIo4OJSV2bl3Sr2llgwX
PrBQ+Z5bCUV70uc1U0vvJEW/r9tkyOu3YV7VXWXtaQWkCgxIqWgNJvU5A/9/6vz9
u1RdMZwxpjy/4HuWvHYRXlJmeeca/BEoaYWMRlECuJjIBcAzuVJTlKBT7x7U4Ptc
qqZGbzr0+zU39y1kMXu/ayldlsF3k6DKYZYNaa8cKNqorV0FqBVm1JZSjiAAWqGp
tmYxUmv/riY6cP28tP3G6noH1XqzEvZ3fdYIsGM29YQ1Y1vrVrrBVju/aMzss498
czxMtp8e0sudHt+ommUDkA2WBEPuqJPIcOj+7bvFiv6smyxcU8VmsyEapknq+Dq8
wG0w3fGsRdy8puc5COz/3xuiFlHQ97wtnnmyWbmdQmx7EfZcGWFfnK6HwEXAbcjO
aaFwSISK8ROgqoKfTss6/8Go+vbmtKJQH2w1fQArnPHGu9qFM/sBNhZ+ieiZ6x1H
CdU3qvuycFZMSsMhk4ER2vJdeJ8tu2jUhMOIuA/VUgUblCJkAaBE9wXaiibCZ/XT
XBXVb81v+EpLsoc5G/wrg35D5U/Gqqc+KAABK2zHa4L7rIs6jb2daeRrUBytsWm2
Exq5sE1Uf5mioHtZpbr6rKIGzT0AEQEAAYkCNgQYAQgAIBYhBGcsZXvga0swlpxK
V0YUScJeNrmOBQJaBREYAhsMAAoJEEYUScJeNrmOb2oQALYcLV3wFFR5v9zpEPdS
haOIpYyuFBkN0FoID+w7Hb7R3pyl7c6nLI9tyFEkJBM1faGke8vKj6HZSfcyX1Lo
2rBL+yW7Gu8z3uEbkTnPFew9LnutGFuFTnbpVdLcpsbm2lG5yhdmjvJBKI4CfX4Z
UFlhyGtwqsl+1lpUgvOuMI2HjyHcFbzkhiSRDQvtXCgJu6orjzEvqiKNM4MM7PMJ
AwU0Lf3NV/p1H2mFllfotmXVZ/TjXuGcOYH56gcf4XpkuD5Vb2Qhu7IbR6TneC5j
yPdC0yQYcXqrpYhNBmlbXIoEL1m0xXhrFVPxS3QeMfkhQOqjvhaxBGCt29YJaTfQ
ugN7I1YfEJIxTap8xzEdJ+80YL3iNCIzaWSsd/xUKpobHSsu4RU1cv//S+5qD3WZ
NfcUoBgmfPC7NXCoKrEVXk5QKh3efKnAkMQrxdWRiwSuenf4Yk4fWXcTyCXsMPVB
qjcZRuOpow7tU9AuBoMyJ1XrznHoubdnc29iGN51Hrhvp/uNxjsCgPgQtpL/8znk
dgfzXU5CYJDYHa6fubUTHVZfLKbzBEI2XY1nqVu+QEO86tkY9Ef4PFMknThTAJDC
ph3xIx/sBb5s3c/XH9JgWEiyO3rMEzZecgF34OJgwnc5gl63a4k1cF0cxzkCZYi3
k6XI/RkkRzdN1CSdCapbDJDvuQINBFoFEeUBEAChZUqlI7FLQIY6GEo0bhJ4oMp2
jQi22zb9ZmqqcmRbWfNKfCfm/cXNDabccqzPRTWezq6hVYYPz6cSnzXpxPBIQufZ
IoMVLKDbTS0RTFVwQsYu9qGdZ52J2bq6qMWK0I2n6lECNkbOB0bZ3aPxe3yw4McP
6u+SU+b0ArMvIGqq1cmKSpkAQB0kBK/gGzEj26d30jMSN393BZ/ESEs7PZyaie3O
CdT71Cmh6xNxv0IwmgbUo54diXL9hEYTrI3hPyCKFeAoiTjlpz9ah7DPoOHgd9lD
Rd4a6VdMrdz7m5aFWo/NVuoty9spGYLG0p9N7zSaUAdO/96mn+W18hbL7EkU7/Db
Ubt5ZP34YOI46aI8YRZKiTq6NI4WglZDxu9PFGoCx4lyvhgKOwcQHySverAyb0Y1
qeNCL9uk6oBHB2bXlAhBBOORtL5rGD+ICCuCV4g1ZEoN7sJBMxNMXORzRZ1crdlr
10lld/Mg0udl2Hgatfx+i+Y0ae/W0Ibr417H5q7iHr85ivTQ6mRU3hMuzQSoWZK8
vixjvOK401Gre22q5jq1IPinACcu6VUto9Wbo8C1msSsWgHrqLRFeqp18BoIVY5s
QCvcsGlyD7MdJQohpmJ7al/kNVOidhGf7TtcSolWF7gLZacMRYbGWhbDhpOIhIpl
jiWTg8oWRl9KPbwzBQARAQABiQRyBBgBCAAmFiEEZyxle+BrSzCWnEpXRhRJwl42
uY4FAloFEeUCGwIFCQlmAYACQAkQRhRJwl42uY7BdCAEGQEIAB0WIQRxHyjVEOHg
vL1fa/6UNugL+6RpCQUCWgUR5QAKCRCUNugL+6RpCSgsD/40XzObgPRpbIRQaJL1
FgynrXUh3dJHdqB5Yi/pYshFuI+nnjpAGTyYyk75WlfvUmzY4HgNmh9yCjWketc0
SdulPkWQ093Y38bQ9WGVQ7NLnZ47AUTuImqEdKcR4wu9F3nGD+cyNWE5fao62tYd
hlzrP1rLz8kALtswc9PVYLEKnqNCBtlGoWdeW7K1lYVG4666/uYvHzOzsUQ0MqVT
HDjpvxEcVRA0EW47m2TVj6IYAsM+0J93aFRr4OKXf4bu1ejxRz4Pdx73QsjeZwlN
5F4FpnmegdUbNR3azeGcF0qiOjPCNu3xi5lDFPKCRZLnCAqMsvv92Z/GWryNAuDj
H9tsmbDUwYXc1QUbdsu+p2jVm79yPgJUIvcy/kwOd0/GYUDOme2NvhF252aOO6Mt
OnTCrQoX0mIY/IisIjwi+2LEpQVyNDu7AGu581LYFGhBDUqiy5CyQ2neHS+k9iq2
06dVdqETpiybizUZm2aQ8FlRV0j6PVKrqAzi0cMYJC+Gh/fNvx61goJ1tEDdh+LK
Mw0Js7OCtH7Wu1D0U/qDl3137PIBSv10BZ3SkbZDqivV5YhyGhvEewiXsbamE6VZ
AHGZ5pfd/0tkqAW9UQqw1AdqYBsAtE4yeU63xPcz7B4VyyIdRNxnjQiEg+SEpDyy
Gl2kGtt+cIbEYZovTrrW2cM0FzGhD/4rRIDfd+IvhZ86BbYoIv4oreiZVjIhFAYI
7e0DfVliBXNOHFErghu3FisUrfTM5g7RHA0Snk8OGO/Yu2mSXYKVvygIlfi3i+7B
0eZxhZEOsHXgO3v4WtY5/67Q1XXF9J7MY9Ke9gqp0E8HRFsECfEoSCRdaaic5PIT
veUEkHs6q6W+J5ULNTqdWsmSdgNWQh3Zbhh0Ih9m9nioAlZHaKnEZXGt8GsUimr7
ffRuYgxF+kuWT8UwQu0Tc47QrYgZIpxH4WI6Rc6qKAo/4DLK2Q3Y15kJFqi8He0t
U7fWXMtrdQxxkz94WTFokISVVRZxSfZ8VkGjVHAgk6NVBgp+2zjiwfwS16qbOUOY
ikR3WTCbyStdePLaXgAFxA7g/pl5/f0IF3/IoGdTGjWoRqnBZG7NfP7bYF1CKe4f
a87Z47LriyL70BFosJqBNMJUEorS9w8sBbnmMUdpGMyk7PH386W95ib7AEOtRttL
uzYetY4LljxgMsloRgYX+Kg5i6fkntG6rod8LNYg7jWObWaIqlPoTo1RNoujYAnE
qdCDQHoUOgtZ4v6+QaxI3WV1KPBsPb7SAjuphubIQVK/6qHse9OoWVwWAABXHFqX
2qV4dyq6mq87ohTcRrZqt64ekD8H3Qe4xkYSzsWZTc0qovhs+G+dSTJ709xuV2EP
+YMbPW0/IQ==
=g11H
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2019-05-15 [SC]
F1261BDE929012C8FF2E501D6EA5D7598529A53E
uid [ultimate] Martin Hickey <martin.hickey@ie.ibm.com>
sig 3 6EA5D7598529A53E 2019-05-15 Martin Hickey <martin.hickey@ie.ibm.com>
sub rsa4096 2019-05-15 [E]
sig 6EA5D7598529A53E 2019-05-15 Martin Hickey <martin.hickey@ie.ibm.com>
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
1qneNQ==
=oCPv
-----END PGP PUBLIC KEY BLOCK-----
pub rsa4096 2018-03-14 [SC]
967F8AC5E2216F9F4FD270AD92AA783CBAAE8E3B
uid [ultimate] Matthew Fisher <matt.fisher@microsoft.com>
sig 3 92AA783CBAAE8E3B 2018-03-14 Matthew Fisher <matt.fisher@microsoft.com>
sub rsa4096 2018-03-14 [E]
sig 92AA783CBAAE8E3B 2018-03-14 Matthew Fisher <matt.fisher@microsoft.com>
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBFqpgxYBEAC1+yf/KFw2AQlineurz7Oz8NyYMlx1JnxZvMOFrL6jbZGyyzyy
jBX5Ii++79Wq1T3BL+F/UFhgruQbbzL8SiAc8Q55Ec7z/BVxM7iQPLCnFRqztllx
Ia1D1dZ9aFIw4P92kQgOQGPOgIxFRwEPA0ZX5nbZfL/teNhphW7vHaauk9xEJddm
Pyy3l9xCRIKQVMwuCaLeH0ZZpBllddwuRV4ptlQ30MpOnaalQda9/j3VhNFEX8Nj
nu8GHn+f4Lzy6XmhHb++JB3AIo5ZfwaUS2xMrnObtvmGHR3+uP/kblh9MzZlmL4T
ldclyGaV7z9Z/xGwnX/+r7xna/fr3mey3GXm29BOP2sUBBQCba05X5nYUd2TjWsZ
OZtE6sLuzUzeOTLEDu28IJoiaYnLKDNzDmuVM26xAYVWXUdCGgn+1rAp0t5OGgHm
qTexvPmckgp3yw+tcPUkR6nh0ft7pmeoK53AQHMt6fk7plZCTuu5UvxZE/oDzt4X
w9+vSTD5GzsNGrTYLTYUSL0muK+iM/uuJtFNJUREOucXfmWxulUsxwOB0st7hnLs
4JmFSr3av1en1WqqdiXswOrdK2msTm4J2+fsOU1jnyF//RJmj+1KPpRDCBTzpAFS
SzE/rRaLZBVE8k2vT0L6yBXvGJ2ONK9TkGT5fnyXu8zDu1d2Koj0c+6m9wARAQAB
tCpNYXR0aGV3IEZpc2hlciA8bWF0dC5maXNoZXJAbWljcm9zb2Z0LmNvbT6JAk4E
EwEIADgWIQSWf4rF4iFvn0/ScK2Sqng8uq6OOwUCWqmDFgIbAwULCQgHAgYVCgkI
CwIEFgIDAQIeAQIXgAAKCRCSqng8uq6OOyTsD/979LDS7ONHIHNoRf7Uud40To0S
/domtZM0rXUCBdbe5R4/xah0HvM1u8aN4OC6U7i0LCXSmEOZxQLKxKBWfX4/d6k7
lBwuQBSlcM6cM6nDfPInT0C3o8caP8lOGeNAdOkMxrqiEO4gHNP5BvWCV+jQSU5X
uvGhKNTMcpaf+DqZAFbR6zpdL7t5JCK0B0RRhFfaGWb19t3REukI5OF5M5SN7EtQ
XWK/1fyzsltrjTSXgMWuxtJjBchltjme/S3XpHeeoSCm1WWh3a140tCC662ydU1u
EZIlUrn8dfMpH0BY6bb0/4dhHvCJ3bw+zZoCzFJM/LksjP5i+Q4mUOD8PvFWh5aS
46F827YiMdqD/eDMr1QRe66fPw5EtWTHgnf3PX+NmN8lgn2o280AkRXqkrCgl580
B+lFwZ6hfan2F8RIHXNbF+9Zvc7Nh8bG8s4I8s6uiufmsmOuFdp47J4//q1W0HcU
0fqajDnEhExtGkgwIsum1Ndwq2sWZT/ko7PYyC3J6mbr/MXTvd2TxtnMgG6kpyPv
p3HlDaBw1aO5vO5mji4RTsoZi12MITIyvPsFWh0WtXkJLNaJ30bFSEx5fiJILxu0
bBoBK0LUhB1Q+8G3Kea3+q3MuOQFnFfjPlMH6q84jpU5Lv5BaW17IeZ2kIfVYrcG
vBvtZ5VHDzY4EhGmlbkCDQRaqYMWARAA3wYv6jbE1PjXwIUWSSO9zxQLBKg7Cn7d
g+wwKx+N5DHjSdQBous6DGwN/wEZfXJOn14S9Yg4p4owmiyJDn0oqJ0BLdsMELoO
imCIZ+zn3AjCWdk2b0oCOhyTwhaVhVgi8yMQruMSUG9/3lkVoFae/GMC32nmE2A0
BOnj9fVIhIrDKt9OSeTXXRNVaRvNFo9ry8S1hDxgfQ2unD6J0mMPhLH2O7CRZDFW
FyH09E/rhrIDvI3Z7mZw2ufGKR0YEu7fJ0BBBSbIqUOMsUnQNWomb2j/QZyYmhTS
Hg9YRB807H3b+5GuZim+DSUk5DQV2IENEg9LDYvhDftE5COYB3tZUnvEpOvNybBl
URxD8Kgqlb3j93l2FcD1QrIGW5VCmkkuD612ZG+NjMq0ZXlQjv6gxAYir8GTKkWt
tS1OatDm6qe6xEFypT6nlvxOYFxLeFkVVGt4H4QW6+MXvnwMofL0G6fOhRvdlq3R
US9n3WqzTpCwfvJs2lhYi+c3/2nwCx5G42OT9Ix0UFkYwxhGk6PRleKOMsw28PFr
a8DVjyKGOVn+9auVhPXYQcN0sZqFl8LBDkUtaniiRD4WKH91aKYgmX1qo8sJZMhx
t/ZoHOfoHDEEa+kLqfsWu3htyTP1gleCAA8kDcRiy1v/G8v3+p2ioI6q1qegigbr
AqTHcWNOltcAEQEAAYkCNgQYAQgAIBYhBJZ/isXiIW+fT9JwrZKqeDy6ro47BQJa
qYMWAhsMAAoJEJKqeDy6ro47T7gP/j/3R9hPg+kJCErlEKPqxsEOxxlaHx+f4UGg
Zm+P6QK2SrqbrqcPhoKUXeHlbCMm2euxKTonIawgCIr44kCZvp3B8pCGUCR+M0mf
aXGO1O6EJ3MmtlbXJ+OyBAhxpklUWdM6favuzi62fAmvwEKQf1reG/9r+toJb5N4
KwrrdZNUaLJWhb6D0fwB+1fWJbdRnDO1rozcA+YJGhhunpxF2b2nZ5OtqNuGmbqV
ofxL6/0lM4HqLNcUBlUyQihjk1+hzfWji95SlzIxP2EhH6gJh/e+/EDCaVVV00CM
0n/0dEB25nAuSMGgUx2utNmfCUP84IErGzSUlXdzN20aW5xiBFU3/uSWyz80IGuy
WeyRzksmphGdLwef+sWLKGrOJh+DkOxxpFMRaIqGEG2YViQCg3gyzjiJuI/XAdlK
AhqwVKfRke24vgifd1tN+zeFs+m28Hpw7989vky1hDvqdpK5/fiJfqIBsF0jir/H
AgtqmbiqemX9rUa3uDkBsvyu+Ou41l+wL6ahj9Pnu0+9hQnpeZERIyhq4LWn7gGb
xk5y63wrvGbeS5lev//012oSzWQfSdFWqQVzMTVtOojGFWgvwRCwZiWEPQkRIV5r
VNXtXPUdKiOEkWin01ZrwDPEyBjr3pcnu2mbgLeJETODnCRi79KA5kCtd65JbNF7
Qknjx8fW
=jz9T
-----END PGP PUBLIC KEY BLOCK-----

@ -1,6 +1,6 @@
DOCKER_REGISTRY ?= gcr.io
IMAGE_PREFIX ?= kubernetes-helm
DEV_IMAGE ?= golang:1.12.2
DEV_IMAGE ?= golang:1.13.1
SHORT_NAME ?= tiller
SHORT_NAME_RUDDER ?= rudder
TARGETS ?= darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le linux/s390x windows/amd64
@ -50,7 +50,7 @@ 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} ; \
curl -sSL -o helm-${VERSION}-$${obj} https://get.helm.sh/helm-${VERSION}-$${obj} ; \
done
.PHONY: sign
@ -141,6 +141,10 @@ docker-test-style: check-docker
protoc:
$(MAKE) -C _proto/ all
.PHONY: generate
generate:
$(GO) generate ./...
.PHONY: docs
docs: build
@scripts/update-docs.sh

@ -1,30 +1,18 @@
maintainers:
- adamreese
- bacongobbler
- hickeyma
- jascott1
- mattfarina
- michelleN
- prydonius
- SlickNik
- technosophos
- thomastaylor312
- viglesiasce
reviewers:
- adamreese
- bacongobbler
- fibonacci1729
- jascott1
- hickeyma
- jdolitsky
- mattfarina
- michelleN
- migmartri
- nebril
- prydonius
- SlickNik
- technosophos
- thomastaylor312
- viglesiasce
emeritus:
- jascott1
- migmartri
- nebril
- seh

@ -43,6 +43,8 @@ If you want to use a package manager:
- [Chocolatey](https://chocolatey.org/) users can use `choco install kubernetes-helm`.
- [Scoop](https://scoop.sh/) users can use `scoop install helm`.
- [GoFish](https://gofi.sh/) users can use `gofish install helm`.
- [Snap](https://snapcraft.io/) users can use `sudo snap install helm
--classic`.
To rapidly get Helm up and running, start with the [Quick Start Guide](https://docs.helm.sh/using_helm/#quickstart-guide).

@ -7,4 +7,4 @@ Packages
- `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

@ -56,4 +56,6 @@ message Hook {
int32 weight = 7;
// DeletePolicies are the policies that indicate when to delete the hook
repeated DeletePolicy delete_policies = 8;
// DeleteTimeout indicates how long to wait for a resource to be deleted before timing out
int64 delete_timeout = 9;
}

@ -41,7 +41,7 @@ message Status {
PENDING_INSTALL = 6;
// Status_PENDING_UPGRADE indicates that an upgrade operation is underway.
PENDING_UPGRADE = 7;
// Status_PENDING_ROLLBACK indicates that an rollback operation is underway.
// Status_PENDING_ROLLBACK indicates that a rollback operation is underway.
PENDING_ROLLBACK = 8;
}

@ -126,13 +126,6 @@ __helm_compgen() {
__helm_compopt() {
true # don't do anything. Not supported by bashcompinit in zsh
}
__helm_declare() {
if [ "$1" == "-F" ]; then
whence -w "$@"
else
builtin declare "$@"
fi
}
__helm_ltrim_colon_completions()
{
if [[ "$1" == *:* && "$COMP_WORDBREAKS" == *:* ]]; then
@ -194,7 +187,7 @@ autoload -U +X bashcompinit && bashcompinit
# use word boundary patterns for BSD or GNU sed
LWORD='[[:<:]]'
RWORD='[[:>:]]'
if sed --help 2>&1 | grep -q GNU; then
if sed --help 2>&1 | grep -q 'GNU\|BusyBox'; then
LWORD='\<'
RWORD='\>'
fi
@ -210,7 +203,7 @@ __helm_convert_bash_to_zsh() {
-e "s/${LWORD}__ltrim_colon_completions${RWORD}/__helm_ltrim_colon_completions/g" \
-e "s/${LWORD}compgen${RWORD}/__helm_compgen/g" \
-e "s/${LWORD}compopt${RWORD}/__helm_compopt/g" \
-e "s/${LWORD}declare${RWORD}/__helm_declare/g" \
-e "s/${LWORD}declare${RWORD}/builtin declare/g" \
-e "s/\\\$(type${RWORD}/\$(__helm_type/g" \
-e 's/aliashash\["\(.\{1,\}\)"\]/aliashash[\1]/g' \
-e 's/FUNCNAME/funcstack/g' \

@ -31,7 +31,8 @@ import (
const createDesc = `
This command creates a chart directory along with the common files and
directories used in a chart.
directories used in a chart. It provides a basic example and is not
meant to cover all Kubernetes resources.
For example, 'helm create foo' will create a directory structure that looks
something like this:
@ -54,6 +55,10 @@ something like this:
do not exist, Helm will attempt to create them as it goes. If the given
destination exists and there are files in that directory, conflicting files
will be overwritten, but other files will be left alone.
The chart that is created by invoking this command contains a Deployment, Ingress
and a Service. To use other Kubernetes resources with your chart, refer to
[The Chart Template Developer's Guide](https://helm.sh/docs/chart_template_guide).
`
type createCmd struct {
@ -68,7 +73,7 @@ func newCreateCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "create NAME",
Short: "create a new chart with the given name",
Short: "Create a new chart with the given name",
Long: createDesc,
RunE: func(cmd *cobra.Command, args []string) error {
cc.home = settings.Home
@ -83,7 +88,7 @@ func newCreateCmd(out io.Writer) *cobra.Command {
},
}
cmd.Flags().StringVarP(&cc.starter, "starter", "p", "", "the named Helm starter scaffold")
cmd.Flags().StringVarP(&cc.starter, "starter", "p", "", "The name or absolute path to Helm starter scaffold")
return cmd
}
@ -101,6 +106,10 @@ func (c *createCmd) run() error {
if c.starter != "" {
// Create from the starter
lstarter := filepath.Join(c.home.Starters(), c.starter)
// If path is absolute, we don't want to prefix it with helm starters folder
if filepath.IsAbs(c.starter) {
lstarter = c.starter
}
return chartutil.CreateFrom(cfile, filepath.Dir(c.name), lstarter)
}

@ -143,7 +143,99 @@ func TestCreateStarterCmd(t *testing.T) {
t.Errorf("Wrong API version: %q", c.Metadata.ApiVersion)
}
expectedTemplateCount := 7
expectedTemplateCount := 8
if l := len(c.Templates); l != expectedTemplateCount {
t.Errorf("Expected %d templates, got %d", expectedTemplateCount, l)
}
found := false
for _, tpl := range c.Templates {
if tpl.Name == "templates/foo.tpl" {
found = true
data := tpl.Data
if string(data) != "test" {
t.Errorf("Expected template 'test', got %q", string(data))
}
}
}
if !found {
t.Error("Did not find foo.tpl")
}
}
func TestCreateStarterAbsoluteCmd(t *testing.T) {
cname := "testchart"
// Make a temp dir
tdir, err := ioutil.TempDir("", "helm-create-")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tdir)
thome, err := tempHelmHome(t)
if err != nil {
t.Fatal(err)
}
cleanup := resetEnv()
defer func() {
os.RemoveAll(thome.String())
cleanup()
}()
settings.Home = thome
// Create a starter.
starterchart := filepath.Join(tdir, "starters")
os.Mkdir(starterchart, 0755)
if dest, err := chartutil.Create(&chart.Metadata{Name: "starterchart"}, starterchart); err != nil {
t.Fatalf("Could not create chart: %s", err)
} else {
t.Logf("Created %s", dest)
}
tplpath := filepath.Join(starterchart, "starterchart", "templates", "foo.tpl")
if err := ioutil.WriteFile(tplpath, []byte("test"), 0755); err != nil {
t.Fatalf("Could not write template: %s", err)
}
// CD into it
pwd, err := os.Getwd()
if err != nil {
t.Fatal(err)
}
if err := os.Chdir(tdir); err != nil {
t.Fatal(err)
}
defer os.Chdir(pwd)
// Run a create
cmd := newCreateCmd(ioutil.Discard)
cmd.ParseFlags([]string{"--starter", filepath.Join(starterchart, "starterchart")})
if err := cmd.RunE(cmd, []string{cname}); err != nil {
t.Errorf("Failed to run create: %s", err)
return
}
// Test that the chart is there
if fi, err := os.Stat(cname); err != nil {
t.Fatalf("no chart directory: %s", err)
} else if !fi.IsDir() {
t.Fatalf("chart is not directory")
}
c, err := chartutil.LoadDir(cname)
if err != nil {
t.Fatal(err)
}
if c.Metadata.Name != cname {
t.Errorf("Expected %q name, got %q", cname, c.Metadata.Name)
}
if c.Metadata.ApiVersion != chartutil.ApiVersionV1 {
t.Errorf("Wrong API version: %q", c.Metadata.ApiVersion)
}
expectedTemplateCount := 8
if l := len(c.Templates); l != expectedTemplateCount {
t.Errorf("Expected %d templates, got %d", expectedTemplateCount, l)
}

@ -56,7 +56,7 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
Use: "delete [flags] RELEASE_NAME [...]",
Aliases: []string{"del"},
SuggestFor: []string{"remove", "rm"},
Short: "given a release name, delete the release from Kubernetes",
Short: "Given a release name, delete the release from Kubernetes",
Long: deleteDesc,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -79,11 +79,11 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.BoolVar(&del.dryRun, "dry-run", false, "simulate a delete")
f.BoolVar(&del.disableHooks, "no-hooks", false, "prevent hooks from running during deletion")
f.BoolVar(&del.purge, "purge", false, "remove the release from the store and make its name free for later use")
f.Int64Var(&del.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.StringVar(&del.description, "description", "", "specify a description for the release")
f.BoolVar(&del.dryRun, "dry-run", false, "Simulate a delete")
f.BoolVar(&del.disableHooks, "no-hooks", false, "Prevent hooks from running during deletion")
f.BoolVar(&del.purge, "purge", false, "Remove the release from the store and make its name free for later use")
f.Int64Var(&del.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.StringVar(&del.description, "description", "", "Specify a description for the release")
// set defaults from environment
settings.InitTLS(f)

@ -91,7 +91,7 @@ func newDependencyCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "dependency update|build|list",
Aliases: []string{"dep", "dependencies"},
Short: "manage a chart's dependencies",
Short: "Manage a chart's dependencies",
Long: dependencyDesc,
}
@ -113,7 +113,7 @@ func newDependencyListCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "list [flags] CHART",
Aliases: []string{"ls"},
Short: "list the dependencies for the given chart",
Short: "List the dependencies for the given chart",
Long: dependencyListDesc,
RunE: func(cmd *cobra.Command, args []string) error {
cp := "."

@ -49,7 +49,7 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "build [flags] CHART",
Short: "rebuild the charts/ directory based on the requirements.lock file",
Short: "Rebuild the charts/ directory based on the requirements.lock file",
Long: dependencyBuildDesc,
RunE: func(cmd *cobra.Command, args []string) error {
dbc.helmhome = settings.Home
@ -63,8 +63,8 @@ func newDependencyBuildCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.BoolVar(&dbc.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&dbc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&dbc.verify, "verify", false, "Verify the packages against signatures")
f.StringVar(&dbc.keyring, "keyring", defaultKeyring(), "Keyring containing public keys")
return cmd
}

@ -57,7 +57,7 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "update [flags] CHART",
Aliases: []string{"up"},
Short: "update charts/ based on the contents of requirements.yaml",
Short: "Update charts/ based on the contents of requirements.yaml",
Long: dependencyUpDesc,
RunE: func(cmd *cobra.Command, args []string) error {
cp := "."
@ -78,9 +78,9 @@ func newDependencyUpdateCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.BoolVar(&duc.verify, "verify", false, "verify the packages against signatures")
f.StringVar(&duc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.BoolVar(&duc.skipRefresh, "skip-refresh", false, "do not refresh the local repository cache")
f.BoolVar(&duc.verify, "verify", false, "Verify the packages against signatures")
f.StringVar(&duc.keyring, "keyring", defaultKeyring(), "Keyring containing public keys")
f.BoolVar(&duc.skipRefresh, "skip-refresh", false, "Do not refresh the local repository cache")
return cmd
}

@ -59,8 +59,8 @@ func newDocsCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&dc.dest, "dir", "./", "directory to which documentation is written")
f.StringVar(&dc.docTypeString, "type", "markdown", "the type of documentation to generate (markdown, man, bash)")
f.StringVar(&dc.dest, "dir", "./", "Directory to which documentation is written")
f.StringVar(&dc.docTypeString, "type", "markdown", "The type of documentation to generate (markdown, man, bash)")
return cmd
}

@ -73,7 +73,7 @@ func newFetchCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "fetch [flags] [chart URL | repo/chartname] [...]",
Short: "download a chart from a repository and (optionally) unpack it in local directory",
Short: "Download a chart from a repository and (optionally) unpack it in local directory",
Long: fetchDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
@ -96,20 +96,20 @@ func newFetchCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.BoolVar(&fch.untar, "untar", false, "if set to true, will untar the chart after downloading it")
f.StringVar(&fch.untardir, "untardir", ".", "if untar is specified, this flag specifies the name of the directory into which the chart is expanded")
f.BoolVar(&fch.verify, "verify", false, "verify the package against its signature")
f.BoolVar(&fch.verifyLater, "prov", false, "fetch the provenance file, but don't perform verification")
f.StringVar(&fch.version, "version", "", "specific version of a chart. Without this, the latest version is fetched")
f.StringVar(&fch.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.StringVarP(&fch.destdir, "destination", "d", ".", "location to write the chart. If this and tardir are specified, tardir is appended to this")
f.StringVar(&fch.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&fch.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&fch.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&fch.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&fch.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.StringVar(&fch.username, "username", "", "chart repository username")
f.StringVar(&fch.password, "password", "", "chart repository password")
f.BoolVar(&fch.untar, "untar", false, "If set to true, will untar the chart after downloading it")
f.StringVar(&fch.untardir, "untardir", ".", "If untar is specified, this flag specifies the name of the directory into which the chart is expanded")
f.BoolVar(&fch.verify, "verify", false, "Verify the package against its signature")
f.BoolVar(&fch.verifyLater, "prov", false, "Fetch the provenance file, but don't perform verification")
f.StringVar(&fch.version, "version", "", "Specific version of a chart. Without this, the latest version is fetched")
f.StringVar(&fch.keyring, "keyring", defaultKeyring(), "Keyring containing public keys")
f.StringVarP(&fch.destdir, "destination", "d", ".", "Location to write the chart. If this and tardir are specified, tardir is appended to this")
f.StringVar(&fch.repoURL, "repo", "", "Chart repository url where to locate the requested chart")
f.StringVar(&fch.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file")
f.StringVar(&fch.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file")
f.StringVar(&fch.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&fch.devel, "devel", false, "Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.StringVar(&fch.username, "username", "", "Chart repository username")
f.StringVar(&fch.password, "password", "", "Chart repository password")
return cmd
}

@ -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 {
@ -55,7 +56,7 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "get [flags] RELEASE_NAME",
Short: "download a named release",
Short: "Download a named release",
Long: getHelp,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -72,7 +73,8 @@ 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.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)
}

@ -45,7 +45,7 @@ func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
}
cmd := &cobra.Command{
Use: "hooks [flags] RELEASE_NAME",
Short: "download all hooks for a named release",
Short: "Download all hooks for a named release",
Long: getHooksHelp,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -59,7 +59,7 @@ func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command {
}
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int32Var(&ghc.version, "revision", 0, "get the named release with revision")
f.Int32Var(&ghc.version, "revision", 0, "Get the named release with revision")
// set defaults from environment
settings.InitTLS(f)

@ -47,7 +47,7 @@ func newGetManifestCmd(client helm.Interface, out io.Writer) *cobra.Command {
}
cmd := &cobra.Command{
Use: "manifest [flags] RELEASE_NAME",
Short: "download the manifest for a named release",
Short: "Download the manifest for a named release",
Long: getManifestHelp,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -62,7 +62,7 @@ func newGetManifestCmd(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.Int32Var(&get.version, "revision", 0, "Get the named release with revision")
// set defaults from environment
settings.InitTLS(f)

@ -44,7 +44,7 @@ func newGetNotesCmd(client helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "notes [flags] RELEASE_NAME",
Short: "displays the notes of the named release",
Short: "Displays the notes of the named release",
Long: getNotesHelp,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -61,7 +61,7 @@ func newGetNotesCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int32Var(&get.version, "revision", 0, "get the notes of the named release with revision")
f.Int32Var(&get.version, "revision", 0, "Get the notes of the named release with revision")
// set defaults from environment
settings.InitTLS(f)

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

@ -47,7 +47,7 @@ func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command {
}
cmd := &cobra.Command{
Use: "values [flags] RELEASE_NAME",
Short: "download the values file for a named release",
Short: "Download the values file for a named release",
Long: getValuesHelp,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -62,9 +62,9 @@ func newGetValuesCmd(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.BoolVarP(&get.allValues, "all", "a", false, "dump all (computed) values")
f.StringVar(&get.output, "output", "yaml", "output the specified format (json or yaml)")
f.Int32Var(&get.version, "revision", 0, "Get the named release with revision")
f.BoolVarP(&get.allValues, "all", "a", false, "Dump all (computed) values")
f.StringVar(&get.output, "output", "yaml", "Output the specified format (json or yaml)")
// set defaults from environment
settings.InitTLS(f)

@ -30,7 +30,10 @@ import (
func TestGetValuesCmd(t *testing.T) {
releaseWithValues := helm.ReleaseMock(&helm.MockReleaseOptions{
Name: "thomas-guide",
Chart: &chart.Chart{Values: &chart.Config{Raw: `foo2: "bar2"`}},
Chart: &chart.Chart{
Metadata: &chart.Metadata{Name: "thomas-guide-chart-name"},
Values: &chart.Config{Raw: `foo2: "bar2"`},
},
Config: &chart.Config{Raw: `foo: "bar"`},
})

@ -39,6 +39,104 @@ import (
"k8s.io/helm/pkg/tlsutil"
)
const (
bashCompletionFunc = `
__helm_override_flag_list=(--kubeconfig --kube-context --host --tiller-namespace --home)
__helm_override_flags()
{
local ${__helm_override_flag_list[*]##*-} two_word_of of var
for w in "${words[@]}"; do
if [ -n "${two_word_of}" ]; then
eval "${two_word_of##*-}=\"${two_word_of}=\${w}\""
two_word_of=
continue
fi
for of in "${__helm_override_flag_list[@]}"; do
case "${w}" in
${of}=*)
eval "${of##*-}=\"${w}\""
;;
${of})
two_word_of="${of}"
;;
esac
done
done
for var in "${__helm_override_flag_list[@]##*-}"; do
if eval "test -n \"\$${var}\""; then
eval "echo \${${var}}"
fi
done
}
__helm_binary_name()
{
local helm_binary
helm_binary="${words[0]}"
__helm_debug "${FUNCNAME[0]}: helm_binary is ${helm_binary}"
echo ${helm_binary}
}
__helm_list_releases()
{
__helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out filter
# Use ^ to map from the start of the release name
filter="^${words[c]}"
# Use eval in case helm_binary_name or __helm_override_flags contains a variable (e.g., $HOME/bin/h2)
if out=$(eval $(__helm_binary_name) list $(__helm_override_flags) -a -q -m 1000 ${filter} 2>/dev/null); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__helm_list_repos()
{
__helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out oflags
oflags=$(__helm_override_flags)
__helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}"
# Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2)
if out=$(eval $(__helm_binary_name) repo list ${oflags} 2>/dev/null | tail +2 | cut -f1); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__helm_list_plugins()
{
__helm_debug "${FUNCNAME[0]}: c is $c words[c] is ${words[c]}"
local out oflags
oflags=$(__helm_override_flags)
__helm_debug "${FUNCNAME[0]}: __helm_override_flags are ${oflags}"
# Use eval in case helm_binary_name contains a variable (e.g., $HOME/bin/h2)
if out=$(eval $(__helm_binary_name) plugin list ${oflags} 2>/dev/null | tail +2 | cut -f1); then
COMPREPLY=( $( compgen -W "${out[*]}" -- "$cur" ) )
fi
}
__helm_custom_func()
{
__helm_debug "${FUNCNAME[0]}: c is $c words[@] is ${words[@]}"
case ${last_command} in
helm_delete | helm_history | helm_status | helm_test |\
helm_upgrade | helm_rollback | helm_get_*)
__helm_list_releases
return
;;
helm_repo_remove | helm_repo_update)
__helm_list_repos
return
;;
helm_plugin_remove | helm_plugin_update)
__helm_list_plugins
return
;;
*)
;;
esac
}
`
)
var (
tillerTunnel *kube.Tunnel
settings helm_env.EnvSettings
@ -55,25 +153,25 @@ It will also set up any necessary local configuration.
Common actions from this point include:
- helm search: search for charts
- helm fetch: download a chart to your local directory to view
- helm install: upload the chart to Kubernetes
- helm list: list releases of charts
- helm search: Search for charts
- helm fetch: Download a chart to your local directory to view
- helm install: Upload the chart to Kubernetes
- 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_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
- $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
`
@ -103,6 +201,7 @@ func newRootCmd(args []string) *cobra.Command {
PersistentPostRun: func(*cobra.Command, []string) {
teardown()
},
BashCompletionFunction: bashCompletionFunc,
}
flags := cmd.PersistentFlags()
@ -147,7 +246,7 @@ func newRootCmd(args []string) *cobra.Command {
newDocsCmd(out),
// Deprecated
markDeprecated(newRepoUpdateCmd(out), "use 'helm repo update'\n"),
markDeprecated(newRepoUpdateCmd(out), "Use 'helm repo update'\n"),
)
flags.Parse(args)

@ -36,6 +36,7 @@ type releaseInfo struct {
Updated string `json:"updated"`
Status string `json:"status"`
Chart string `json:"chart"`
AppVersion string `json:"appVersion"`
Description string `json:"description"`
}
@ -50,11 +51,11 @@ configures the maximum length of the revision list returned.
The historical release set is printed as a formatted table, e.g:
$ helm history angry-bird --max=4
REVISION UPDATED STATUS CHART DESCRIPTION
1 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Initial install
2 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Upgraded successfully
3 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 Rolled back to 2
4 Mon Oct 3 10:15:13 2016 DEPLOYED alpine-0.1.0 Upgraded successfully
REVISION UPDATED STATUS CHART APP VERSION DESCRIPTION
1 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 1.1 Initial install
2 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 1.2 Upgraded successfully
3 Mon Oct 3 10:15:13 2016 SUPERSEDED alpine-0.1.0 1.1 Rolled back to 2
4 Mon Oct 3 10:15:13 2016 DEPLOYED alpine-0.1.0 1.3 Upgraded successfully
`
type historyCmd struct {
@ -72,7 +73,7 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "history [flags] RELEASE_NAME",
Long: historyHelp,
Short: "fetch release history",
Short: "Fetch release history",
Aliases: []string{"hist"},
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -89,9 +90,9 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int32Var(&his.max, "max", 256, "maximum number of revision to include in history")
f.UintVar(&his.colWidth, "col-width", 60, "specifies the max column width of output")
f.StringVarP(&his.outputFormat, "output", "o", "table", "prints the output in the specified format (json|table|yaml)")
f.Int32Var(&his.max, "max", 256, "Maximum number of revisions to include in history")
f.UintVar(&his.colWidth, "col-width", 60, "Specifies the max column width of output")
f.StringVarP(&his.outputFormat, "output", "o", "table", "Prints the output in the specified format (json|table|yaml)")
// set defaults from environment
settings.InitTLS(f)
@ -136,16 +137,17 @@ func getReleaseHistory(rls []*release.Release) (history releaseHistory) {
for i := len(rls) - 1; i >= 0; i-- {
r := rls[i]
c := formatChartname(r.Chart)
a := appVersionFromChart(r.Chart)
t := timeconv.String(r.Info.LastDeployed)
s := r.Info.Status.Code.String()
v := r.Version
d := r.Info.Description
rInfo := releaseInfo{
Revision: v,
Updated: t,
Status: s,
Chart: c,
AppVersion: a,
Description: d,
}
history = append(history, rInfo)
@ -158,10 +160,10 @@ func formatAsTable(releases releaseHistory, colWidth uint) []byte {
tbl := uitable.New()
tbl.MaxColWidth = colWidth
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "DESCRIPTION")
tbl.AddRow("REVISION", "UPDATED", "STATUS", "CHART", "APP VERSION", "DESCRIPTION")
for i := 0; i <= len(releases)-1; i++ {
r := releases[i]
tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.Description)
tbl.AddRow(r.Revision, r.Updated, r.Status, r.Chart, r.AppVersion, r.Description)
}
return tbl.Bytes()
}
@ -174,3 +176,10 @@ func formatChartname(c *chart.Chart) string {
}
return fmt.Sprintf("%s-%s", c.Metadata.Name, c.Metadata.Version)
}
func appVersionFromChart(c *chart.Chart) string {
if c == nil || c.Metadata == nil {
return "MISSING"
}
return c.Metadata.AppVersion
}

@ -23,59 +23,62 @@ import (
"github.com/spf13/cobra"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/proto/hapi/chart"
rpb "k8s.io/helm/pkg/proto/hapi/release"
)
func TestHistoryCmd(t *testing.T) {
mk := func(name string, vers int32, code rpb.Status_Code) *rpb.Release {
mk := func(name string, vers int32, code rpb.Status_Code, appVersion string) *rpb.Release {
ch := &chart.Chart{
Metadata: &chart.Metadata{
Name: "foo",
Version: "0.1.0-beta.1",
AppVersion: appVersion,
},
}
return helm.ReleaseMock(&helm.MockReleaseOptions{
Name: name,
Chart: ch,
Version: vers,
StatusCode: code,
})
}
releases := []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED, "1.4"),
mk("angry-bird", 3, rpb.Status_SUPERSEDED, "1.3"),
mk("angry-bird", 2, rpb.Status_SUPERSEDED, "1.2"),
mk("angry-bird", 1, rpb.Status_SUPERSEDED, "1.1"),
}
tests := []releaseCase{
{
name: "get history for release",
args: []string{"angry-bird"},
rels: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
mk("angry-bird", 2, rpb.Status_SUPERSEDED),
mk("angry-bird", 1, rpb.Status_SUPERSEDED),
},
expected: "REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \n1 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n2 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\n",
rels: releases,
expected: "REVISION\tUPDATED \tSTATUS \tCHART \tAPP VERSION\tDESCRIPTION \n1 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\t1.1 \tRelease mock\n2 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\t1.2 \tRelease mock\n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\t1.3 \tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\t1.4 \tRelease mock\n",
},
{
name: "get history with max limit set",
args: []string{"angry-bird"},
flags: []string{"--max", "2"},
rels: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
},
expected: "REVISION\tUPDATED \tSTATUS \tCHART \tDESCRIPTION \n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\tRelease mock\n",
rels: releases,
expected: "REVISION\tUPDATED \tSTATUS \tCHART \tAPP VERSION\tDESCRIPTION \n3 \t(.*)\tSUPERSEDED\tfoo-0.1.0-beta.1\t1.3 \tRelease mock\n4 \t(.*)\tDEPLOYED \tfoo-0.1.0-beta.1\t1.4 \tRelease mock\n",
},
{
name: "get history with yaml output format",
args: []string{"angry-bird"},
flags: []string{"--output", "yaml"},
rels: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
},
expected: "- chart: foo-0.1.0-beta.1\n description: Release mock\n revision: 3\n status: SUPERSEDED\n updated: (.*)\n- chart: foo-0.1.0-beta.1\n description: Release mock\n revision: 4\n status: DEPLOYED\n updated: (.*)\n\n",
rels: releases[:2],
expected: "- appVersion: \"1.3\"\n chart: foo-0.1.0-beta.1\n description: Release mock\n revision: 3\n status: SUPERSEDED\n updated: (.*)\n- appVersion: \"1.4\"\n chart: foo-0.1.0-beta.1\n description: Release mock\n revision: 4\n status: DEPLOYED\n updated: (.*)\n\n",
},
{
name: "get history with json output format",
args: []string{"angry-bird"},
flags: []string{"--output", "json"},
rels: []*rpb.Release{
mk("angry-bird", 4, rpb.Status_DEPLOYED),
mk("angry-bird", 3, rpb.Status_SUPERSEDED),
},
expected: `[{"revision":3,"updated":".*","status":"SUPERSEDED","chart":"foo\-0.1.0-beta.1","description":"Release mock"},{"revision":4,"updated":".*","status":"DEPLOYED","chart":"foo\-0.1.0-beta.1","description":"Release mock"}]\n`,
rels: releases[:2],
expected: `[{"revision":3,"updated":".*","status":"SUPERSEDED","chart":"foo\-0.1.0-beta.1","appVersion":"1.3","description":"Release mock"},{"revision":4,"updated":".*","status":"DEPLOYED","chart":"foo\-0.1.0-beta.1","appVersion":"1.4","description":"Release mock"}]\n`,
},
}

@ -31,7 +31,7 @@ any helm configuration files live.
func newHomeCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "home",
Short: "displays the location of HELM_HOME",
Short: "Displays the location of HELM_HOME",
Long: longHomeHelp,
Run: func(cmd *cobra.Command, args []string) {
h := settings.Home

@ -96,7 +96,7 @@ func newInitCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "init",
Short: "initialize Helm on both client and server",
Short: "Initialize Helm on both client and server",
Long: initDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 0 {
@ -111,38 +111,38 @@ func newInitCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVarP(&i.image, "tiller-image", "i", "", "override Tiller image")
f.BoolVar(&i.canary, "canary-image", false, "use the canary Tiller image")
f.BoolVar(&i.upgrade, "upgrade", false, "upgrade if Tiller is already installed")
f.BoolVar(&i.forceUpgrade, "force-upgrade", false, "force upgrade of Tiller to the current helm version")
f.BoolVarP(&i.clientOnly, "client-only", "c", false, "if set does not install Tiller")
f.BoolVar(&i.dryRun, "dry-run", false, "do not install local or remote")
f.BoolVar(&i.skipRefresh, "skip-refresh", false, "do not refresh (download) the local repository cache")
f.BoolVar(&i.wait, "wait", false, "block until Tiller is running and ready to receive requests")
f.StringVarP(&i.image, "tiller-image", "i", "", "Override Tiller image")
f.BoolVar(&i.canary, "canary-image", false, "Use the canary Tiller image")
f.BoolVar(&i.upgrade, "upgrade", false, "Upgrade if Tiller is already installed")
f.BoolVar(&i.forceUpgrade, "force-upgrade", false, "Force upgrade of Tiller to the current helm version")
f.BoolVarP(&i.clientOnly, "client-only", "c", false, "If set does not install Tiller")
f.BoolVar(&i.dryRun, "dry-run", false, "Do not install local or remote")
f.BoolVar(&i.skipRefresh, "skip-refresh", false, "Do not refresh (download) the local repository cache")
f.BoolVar(&i.wait, "wait", false, "Block until Tiller is running and ready to receive requests")
// TODO: replace TLS flags with pkg/helm/environment.AddFlagsTLS() in Helm 3
//
// NOTE (bacongobbler): we can't do this in Helm 2 because the flag names differ, and `helm init --tls-ca-cert`
// doesn't conform with the rest of the TLS flag names (should be --tiller-tls-ca-cert in Helm 3)
f.BoolVar(&tlsEnable, "tiller-tls", false, "install Tiller with TLS enabled")
f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "install Tiller with TLS enabled and to verify remote certificates")
f.StringVar(&tlsKeyFile, "tiller-tls-key", "", "path to TLS key file to install with Tiller")
f.StringVar(&tlsCertFile, "tiller-tls-cert", "", "path to TLS certificate file to install with Tiller")
f.StringVar(&tlsCaCertFile, "tls-ca-cert", "", "path to CA root certificate")
f.StringVar(&tlsServerName, "tiller-tls-hostname", settings.TillerHost, "the server name used to verify the hostname on the returned certificates from Tiller")
f.BoolVar(&tlsEnable, "tiller-tls", false, "Install Tiller with TLS enabled")
f.BoolVar(&tlsVerify, "tiller-tls-verify", false, "Install Tiller with TLS enabled and to verify remote certificates")
f.StringVar(&tlsKeyFile, "tiller-tls-key", "", "Path to TLS key file to install with Tiller")
f.StringVar(&tlsCertFile, "tiller-tls-cert", "", "Path to TLS certificate file to install with Tiller")
f.StringVar(&tlsCaCertFile, "tls-ca-cert", "", "Path to CA root certificate")
f.StringVar(&tlsServerName, "tiller-tls-hostname", settings.TillerHost, "The server name used to verify the hostname on the returned certificates from Tiller")
f.StringVar(&stableRepositoryURL, "stable-repo-url", stableRepositoryURL, "URL for stable repository")
f.StringVar(&localRepositoryURL, "local-repo-url", localRepositoryURL, "URL for local repository")
f.BoolVar(&i.opts.EnableHostNetwork, "net-host", false, "install Tiller with net=host")
f.StringVar(&i.serviceAccount, "service-account", "", "name of service account")
f.IntVar(&i.maxHistory, "history-max", 0, "limit the maximum number of revisions saved per release. Use 0 for no limit.")
f.IntVar(&i.replicas, "replicas", 1, "amount of tiller instances to run on the cluster")
f.BoolVar(&i.opts.EnableHostNetwork, "net-host", false, "Install Tiller with net=host")
f.StringVar(&i.serviceAccount, "service-account", "", "Name of service account")
f.IntVar(&i.maxHistory, "history-max", 0, "Limit the maximum number of revisions saved per release. Use 0 for no limit.")
f.IntVar(&i.replicas, "replicas", 1, "Amount of tiller instances to run on the cluster")
f.StringVar(&i.opts.NodeSelectors, "node-selectors", "", "labels to specify the node on which Tiller is installed (app=tiller,helm=rocks)")
f.VarP(&i.opts.Output, "output", "o", "skip installation and output Tiller's manifest in specified format (json or yaml)")
f.StringArrayVar(&i.opts.Values, "override", []string{}, "override values for the Tiller Deployment manifest (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.BoolVar(&i.opts.AutoMountServiceAccountToken, "automount-service-account-token", true, "auto-mount the given service account to tiller")
f.StringVar(&i.opts.NodeSelectors, "node-selectors", "", "Labels to specify the node on which Tiller is installed (app=tiller,helm=rocks)")
f.VarP(&i.opts.Output, "output", "o", "Skip installation and output Tiller's manifest in specified format (json or yaml)")
f.StringArrayVar(&i.opts.Values, "override", []string{}, "Override values for the Tiller Deployment manifest (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.BoolVar(&i.opts.AutoMountServiceAccountToken, "automount-service-account-token", true, "Auto-mount the given service account to tiller")
return cmd
}
@ -282,8 +282,9 @@ func (i *initCmd) run() error {
if err := i.ping(i.opts.SelectImage()); err != nil {
return err
}
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been upgraded to the current version.")
fmt.Fprintln(i.out, "\nTiller (the Helm server-side component) has been updated to", i.opts.SelectImage(), ".")
} else {
debug("The error received while trying to init: %s", err)
fmt.Fprintln(i.out, "Warning: Tiller is already installed in the cluster.\n"+
"(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)")
}
@ -310,7 +311,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
}

@ -28,8 +28,8 @@ import (
"github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/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"
"k8s.io/apimachinery/pkg/runtime"
@ -83,7 +83,7 @@ func TestInitCmd_exists(t *testing.T) {
defer os.RemoveAll(home)
var buf bytes.Buffer
fc := fake.NewSimpleClientset(&v1beta1.Deployment{
fc := fake.NewSimpleClientset(&appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: v1.NamespaceDefault,
Name: "tiller-deploy",

@ -82,7 +82,7 @@ func newInspectCmd(out io.Writer) *cobra.Command {
inspectCommand := &cobra.Command{
Use: "inspect [CHART]",
Short: "inspect a chart",
Short: "Inspect a chart",
Long: inspectDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "chart name"); err != nil {
@ -145,62 +145,62 @@ func newInspectCmd(out io.Writer) *cobra.Command {
cmds := []*cobra.Command{inspectCommand, readmeSubCmd, valuesSubCmd, chartSubCmd}
vflag := "verify"
vdesc := "verify the provenance data for this chart"
vdesc := "Verify the provenance data for this chart"
for _, subCmd := range cmds {
subCmd.Flags().BoolVar(&insp.verify, vflag, false, vdesc)
}
kflag := "keyring"
kdesc := "path to the keyring containing public verification keys"
kdesc := "Path to the keyring containing public verification keys"
kdefault := defaultKeyring()
for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.keyring, kflag, kdefault, kdesc)
}
verflag := "version"
verdesc := "version of the chart. By default, the newest chart is shown"
verdesc := "Version of the chart. By default, the newest chart is shown"
for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.version, verflag, "", verdesc)
}
repoURL := "repo"
repoURLdesc := "chart repository url where to locate the requested chart"
repoURLdesc := "Chart repository url where to locate the requested chart"
for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc)
}
username := "username"
usernamedesc := "chart repository username where to locate the requested chart"
usernamedesc := "Chart repository username where to locate the requested chart"
inspectCommand.Flags().StringVar(&insp.username, username, "", usernamedesc)
valuesSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc)
chartSubCmd.Flags().StringVar(&insp.username, username, "", usernamedesc)
password := "password"
passworddesc := "chart repository password where to locate the requested chart"
passworddesc := "Chart repository password where to locate the requested chart"
inspectCommand.Flags().StringVar(&insp.password, password, "", passworddesc)
valuesSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc)
chartSubCmd.Flags().StringVar(&insp.password, password, "", passworddesc)
develFlag := "devel"
develDesc := "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored."
develDesc := "Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored."
for _, subCmd := range cmds {
subCmd.Flags().BoolVar(&insp.devel, develFlag, false, develDesc)
}
certFile := "cert-file"
certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle"
certFiledesc := "Verify certificates of HTTPS-enabled servers using this CA bundle"
for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.certFile, certFile, "", certFiledesc)
}
keyFile := "key-file"
keyFiledesc := "identify HTTPS client using this SSL key file"
keyFiledesc := "Identify HTTPS client using this SSL key file"
for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.keyFile, keyFile, "", keyFiledesc)
}
caFile := "ca-file"
caFiledesc := "chart repository url where to locate the requested chart"
caFiledesc := "Chart repository url where to locate the requested chart"
for _, subCmd := range cmds {
subCmd.Flags().StringVar(&insp.caFile, caFile, "", caFiledesc)
}

@ -143,6 +143,7 @@ type installCmd struct {
certFile string
keyFile string
caFile string
output string
}
type valueFiles []string
@ -170,7 +171,7 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "install [CHART]",
Short: "install a chart archive",
Short: "Install a chart archive",
Long: installDesc,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -199,33 +200,34 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.VarP(&inst.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.StringVarP(&inst.name, "name", "n", "", "release name. If unspecified, it will autogenerate one for you")
f.StringVar(&inst.namespace, "namespace", "", "namespace to install the release into. Defaults to the current kube config namespace.")
f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install")
f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install")
f.BoolVar(&inst.disableCRDHook, "no-crd-hook", false, "prevent CRD hooks from running, but run other hooks")
f.BoolVar(&inst.replace, "replace", false, "re-use the given name, even if that name is already used. This is unsafe in production")
f.StringArrayVar(&inst.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&inst.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&inst.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.StringVar(&inst.nameTemplate, "name-template", "", "specify template used to name the release")
f.BoolVar(&inst.verify, "verify", false, "verify the package before installing it")
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "location of public keys used for verification")
f.StringVar(&inst.version, "version", "", "specify the exact chart version to install. If this is not specified, the latest version is installed")
f.Int64Var(&inst.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&inst.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.BoolVar(&inst.atomic, "atomic", false, "if set, installation process purges chart on fail, also sets --wait flag")
f.StringVar(&inst.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&inst.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&inst.password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&inst.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&inst.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&inst.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&inst.devel, "devel", false, "use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.BoolVar(&inst.depUp, "dep-up", false, "run helm dependency update before installing the chart")
f.BoolVar(&inst.subNotes, "render-subchart-notes", false, "render subchart notes along with the parent")
f.StringVar(&inst.description, "description", "", "specify a description for the release")
f.VarP(&inst.valueFiles, "values", "f", "Specify values in a YAML file or a URL(can specify multiple)")
f.StringVarP(&inst.name, "name", "n", "", "The release name. If unspecified, it will autogenerate one for you")
f.StringVar(&inst.namespace, "namespace", "", "Namespace to install the release into. Defaults to the current kube config namespace.")
f.BoolVar(&inst.dryRun, "dry-run", false, "Simulate an install")
f.BoolVar(&inst.disableHooks, "no-hooks", false, "Prevent hooks from running during install")
f.BoolVar(&inst.disableCRDHook, "no-crd-hook", false, "Prevent CRD hooks from running, but run other hooks")
f.BoolVar(&inst.replace, "replace", false, "Re-use the given name, even if that name is already used. This is unsafe in production")
f.StringArrayVar(&inst.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&inst.stringValues, "set-string", []string{}, "Set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&inst.fileValues, "set-file", []string{}, "Set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.StringVar(&inst.nameTemplate, "name-template", "", "Specify template used to name the release")
f.BoolVar(&inst.verify, "verify", false, "Verify the package before installing it")
f.StringVar(&inst.keyring, "keyring", defaultKeyring(), "Location of public keys used for verification")
f.StringVar(&inst.version, "version", "", "Specify the exact chart version to install. If this is not specified, the latest version is installed")
f.Int64Var(&inst.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&inst.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.BoolVar(&inst.atomic, "atomic", false, "If set, installation process purges chart on fail, also sets --wait flag")
f.StringVar(&inst.repoURL, "repo", "", "Chart repository url where to locate the requested chart")
f.StringVar(&inst.username, "username", "", "Chart repository username where to locate the requested chart")
f.StringVar(&inst.password, "password", "", "Chart repository password where to locate the requested chart")
f.StringVar(&inst.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file")
f.StringVar(&inst.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file")
f.StringVar(&inst.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle")
f.BoolVar(&inst.devel, "devel", false, "Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.")
f.BoolVar(&inst.depUp, "dep-up", false, "Run helm dependency update before installing the chart")
f.BoolVar(&inst.subNotes, "render-subchart-notes", false, "Render subchart notes along with the parent")
f.StringVar(&inst.description, "description", "", "Specify a description for the release")
bindOutputFlag(cmd, &inst.output)
// set defaults from environment
settings.InitTLS(f)
@ -335,7 +337,10 @@ func (i *installCmd) run() error {
if rel == nil {
return nil
}
if outputFormat(i.output) == outputTable {
i.printRelease(rel)
}
// If this is a dry run, we can't display status.
if i.dryRun {
@ -351,8 +356,8 @@ func (i *installCmd) run() error {
if err != nil {
return prettyError(err)
}
PrintStatus(i.out, status)
return nil
return write(i.out, &statusWriter{status}, outputFormat(i.output))
}
// Merges source and destination map, preferring values from the source map

@ -191,6 +191,22 @@ func TestInstall(t *testing.T) {
flags: []string{"--name-template", "{{UPPER \"foobar\"}}"},
err: true,
},
// Install, using --output json
{
name: "install using output json",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name virgil --output json", " "),
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
expected: regexp.QuoteMeta(`{"name":"virgil","info":{"status":{"code":1},"first_deployed":{"seconds":242085845},"last_deployed":{"seconds":242085845},"Description":"Release mock"},"namespace":"default"}`),
},
// Install, using --output yaml
{
name: "install using output yaml",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name virgil --output yaml", " "),
resp: helm.ReleaseMock(&helm.MockReleaseOptions{Name: "virgil"}),
expected: "info:\n Description: Release mock\n first_deployed:\n seconds: 242085845\n last_deployed:\n seconds: 242085845\n status:\n code: 1\nname: virgil\nnamespace: default\n",
},
}
runReleaseCases(t, tests, func(c *helm.FakeClient, out io.Writer) *cobra.Command {

@ -17,32 +17,32 @@ limitations under the License.
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"errors"
"fmt"
"io/ioutil"
"strings"
"github.com/Masterminds/semver"
"github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/client-go/kubernetes"
appsv1client "k8s.io/client-go/kubernetes/typed/apps/v1"
corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
extensionsclient "k8s.io/client-go/kubernetes/typed/extensions/v1beta1"
"k8s.io/helm/pkg/version"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/tiller/environment"
)
// Install uses Kubernetes client to install Tiller.
//
// Returns an error if the command failed.
func Install(client kubernetes.Interface, opts *Options) error {
if err := createDeployment(client.ExtensionsV1beta1(), opts); err != nil {
if err := createDeployment(client.AppsV1(), opts); err != nil {
return err
}
if err := createService(client.CoreV1(), opts.Namespace); err != nil {
@ -60,51 +60,108 @@ func Install(client kubernetes.Interface, opts *Options) error {
//
// Returns an error if the command failed.
func Upgrade(client kubernetes.Interface, opts *Options) error {
obj, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err != nil {
appsobj, err := client.AppsV1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err == nil {
// Can happen in two cases:
// 1. helm init inserted an apps/v1 Deployment up front in Kubernetes
// 2. helm init inserted an extensions/v1beta1 Deployment against a K8s cluster already
// supporting apps/v1 Deployment. In such a case K8s is returning the apps/v1 object anyway.`
// (for the same reason "kubectl convert" is being deprecated)
return upgradeAppsTillerDeployment(client, opts, appsobj)
}
extensionsobj, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Get(deploymentName, metav1.GetOptions{})
if err == nil {
// User performed helm init against older version of kubernetes (Previous to 1.9)
return upgradeExtensionsTillerDeployment(client, opts, extensionsobj)
}
return err
}
func upgradeAppsTillerDeployment(client kubernetes.Interface, opts *Options, obj *appsv1.Deployment) error {
// Update the PodTemplateSpec section of the deployment
if err := updatePodTemplate(&obj.Spec.Template.Spec, opts); err != nil {
return err
}
tillerImage := obj.Spec.Template.Spec.Containers[0].Image
if semverCompare(tillerImage) == -1 && !opts.ForceUpgrade {
return errors.New("current Tiller version is newer, use --force-upgrade to downgrade")
if _, err := client.AppsV1().Deployments(opts.Namespace).Update(obj); err != nil {
return err
}
obj.Spec.Template.Spec.Containers[0].Image = opts.SelectImage()
obj.Spec.Template.Spec.Containers[0].ImagePullPolicy = opts.pullPolicy()
obj.Spec.Template.Spec.ServiceAccountName = opts.ServiceAccount
// If the service does not exist that would mean we are upgrading from a Tiller version
// that didn't deploy the service, so install it.
_, err := client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return createService(client.CoreV1(), opts.Namespace)
}
return err
}
func upgradeExtensionsTillerDeployment(client kubernetes.Interface, opts *Options, obj *extensionsv1beta1.Deployment) error {
// Update the PodTemplateSpec section of the deployment
if err := updatePodTemplate(&obj.Spec.Template.Spec, opts); err != nil {
return err
}
if _, err := client.ExtensionsV1beta1().Deployments(opts.Namespace).Update(obj); err != nil {
return err
}
// If the service does not exist that would mean we are upgrading from a Tiller version
// that didn't deploy the service, so install it.
_, err = client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
_, err := client.CoreV1().Services(opts.Namespace).Get(serviceName, metav1.GetOptions{})
if apierrors.IsNotFound(err) {
return createService(client.CoreV1(), opts.Namespace)
}
return err
}
// semverCompare returns whether the client's version is older, equal or newer than the given image's version.
func semverCompare(image string) int {
split := strings.Split(image, ":")
if len(split) < 2 {
// If we don't know the version, we consider the client version newer.
return 1
func updatePodTemplate(podSpec *v1.PodSpec, opts *Options) error {
tillerImage := podSpec.Containers[0].Image
clientImage := opts.SelectImage()
if semverCompare(tillerImage, clientImage) == -1 && !opts.ForceUpgrade {
return fmt.Errorf("current Tiller version %s is newer than client version %s, use --force-upgrade to downgrade", tillerImage, clientImage)
}
podSpec.Containers[0].Image = clientImage
podSpec.Containers[0].ImagePullPolicy = opts.pullPolicy()
podSpec.ServiceAccountName = opts.ServiceAccount
return nil
}
tillerVersion, err := semver.NewVersion(split[1])
// semverCompare returns whether the client's version is older, equal or newer than the given image's version.
func semverCompare(tillerImage, clientImage string) int {
tillerVersion, err := string2semver(tillerImage)
if err != nil {
// same thing with unparsable tiller versions (e.g. canary releases).
return 1
}
clientVersion, err := semver.NewVersion(version.Version)
// clientVersion, err := semver.NewVersion(currentVersion)
clientVersion, err := string2semver(clientImage)
if err != nil {
// aaaaaand same thing with unparsable helm versions (e.g. canary releases).
return 1
}
return clientVersion.Compare(tillerVersion)
}
func string2semver(image string) (*semver.Version, error) {
split := strings.Split(image, ":")
if len(split) < 2 {
// If we don't know the version, we consider the client version newer.
return nil, fmt.Errorf("no repository in image %s", image)
}
return semver.NewVersion(split[1])
}
// createDeployment creates the Tiller Deployment resource.
func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options) error {
func createDeployment(client appsv1client.DeploymentsGetter, opts *Options) error {
obj, err := generateDeployment(opts)
if err != nil {
return err
@ -117,14 +174,14 @@ func createDeployment(client extensionsclient.DeploymentsGetter, opts *Options)
// Deployment gets a deployment object that can be used to generate a manifest
// as a string. This object should not be submitted directly to the Kubernetes
// api
func Deployment(opts *Options) (*v1beta1.Deployment, error) {
func Deployment(opts *Options) (*appsv1.Deployment, error) {
dep, err := generateDeployment(opts)
if err != nil {
return nil, err
}
dep.TypeMeta = metav1.TypeMeta{
Kind: "Deployment",
APIVersion: "extensions/v1beta1",
APIVersion: "apps/v1",
}
return dep, nil
}
@ -196,7 +253,7 @@ func parseNodeSelectorsInto(labels string, m map[string]string) error {
}
return nil
}
func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
func generateDeployment(opts *Options) (*appsv1.Deployment, error) {
labels := generateLabels(map[string]string{"name": "tiller"})
nodeSelectors := map[string]string{}
if len(opts.NodeSelectors) > 0 {
@ -205,14 +262,17 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
return nil, err
}
}
d := &v1beta1.Deployment{
d := &appsv1.Deployment{
ObjectMeta: metav1.ObjectMeta{
Namespace: opts.Namespace,
Name: deploymentName,
Labels: labels,
},
Spec: v1beta1.DeploymentSpec{
Spec: appsv1.DeploymentSpec{
Replicas: opts.getReplicas(),
Selector: &metav1.LabelSelector{
MatchLabels: labels,
},
Template: v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
@ -226,8 +286,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 +297,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 +307,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,
@ -296,7 +356,7 @@ func generateDeployment(opts *Options) (*v1beta1.Deployment, error) {
// merge them and convert back to Deployment
if len(opts.Values) > 0 {
// base deployment struct
var dd v1beta1.Deployment
var dd appsv1.Deployment
// get YAML from original deployment
dy, err := yaml.Marshal(d)
if err != nil {
@ -341,7 +401,7 @@ func generateService(namespace string) *v1.Service {
Ports: []v1.ServicePort{
{
Name: "tiller",
Port: 44134,
Port: environment.DefaultTillerPort,
TargetPort: intstr.FromString("tiller"),
},
},

@ -17,14 +17,15 @@ limitations under the License.
package installer // import "k8s.io/helm/cmd/helm/installer"
import (
"encoding/json"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/ghodss/yaml"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
"k8s.io/api/extensions/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes/fake"
@ -191,7 +192,7 @@ func TestInstall(t *testing.T) {
fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
l := obj.GetLabels()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l)
@ -238,7 +239,7 @@ func TestInstallHA(t *testing.T) {
fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
replicas := obj.Spec.Replicas
if int(*replicas) != 2 {
t.Errorf("expected replicas = 2, got '%d'", replicas)
@ -262,7 +263,7 @@ func TestInstall_WithTLS(t *testing.T) {
fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
l := obj.GetLabels()
if reflect.DeepEqual(l, map[string]string{"app": "helm"}) {
t.Errorf("expected labels = '', got '%s'", l)
@ -330,7 +331,7 @@ func TestInstall_WithTLS(t *testing.T) {
func TestInstall_canary(t *testing.T) {
fc := &fake.Clientset{}
fc.AddReactor("create", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.CreateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.CreateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != "gcr.io/kubernetes-helm/tiller:canary" {
t.Errorf("expected canary image, got '%s'", i)
@ -368,7 +369,7 @@ func TestUpgrade(t *testing.T) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -407,7 +408,7 @@ func TestUpgrade_serviceNotFound(t *testing.T) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -452,7 +453,7 @@ func TestUgrade_newerVersion(t *testing.T) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -512,7 +513,7 @@ func TestUpgrade_identical(t *testing.T) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -553,7 +554,7 @@ func TestUpgrade_canaryClient(t *testing.T) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -594,7 +595,7 @@ func TestUpgrade_canaryServer(t *testing.T) {
return true, existingDeployment, nil
})
fc.AddReactor("update", "deployments", func(action testcore.Action) (bool, runtime.Object, error) {
obj := action.(testcore.UpdateAction).GetObject().(*v1beta1.Deployment)
obj := action.(testcore.UpdateAction).GetObject().(*appsv1.Deployment)
i := obj.Spec.Template.Spec.Containers[0].Image
if i != image {
t.Errorf("expected image = '%s', got '%s'", image, i)
@ -716,9 +717,32 @@ func TestDeployment_WithSetValues(t *testing.T) {
// convert our expected value to match the result type for comparison
ev := tt.expect
intType := reflect.TypeOf(int64(0))
floatType := reflect.TypeOf(float64(0))
switch pvt := pv.(type) {
case json.Number:
evv := reflect.ValueOf(ev)
evv = reflect.Indirect(evv)
switch ev.(type) {
case float32, float64:
evv = evv.Convert(floatType)
if fpv, err := pv.(json.Number).Float64(); err != nil {
t.Errorf("Failed to convert json number to float: %s", err)
} else if fpv != evv.Float() {
t.Errorf("%s: expected float value %q, got %f", tt.name, tt.expect, fpv)
}
case byte, int, int32, int64:
evv = evv.Convert(intType)
if ipv, err := pv.(json.Number).Int64(); err != nil {
t.Errorf("Failed to convert json number to int: %s", err)
} else if ipv != evv.Int() {
t.Errorf("%s: expected int value %q, got %d", tt.name, tt.expect, ipv)
}
default:
t.Errorf("Unknown primitive type: %s", reflect.TypeOf(ev))
}
case float64:
floatType := reflect.TypeOf(float64(0))
v := reflect.ValueOf(ev)
v = reflect.Indirect(v)
if !v.Type().ConvertibleTo(floatType) {

@ -61,7 +61,7 @@ func newLintCmd(out io.Writer) *cobra.Command {
}
cmd := &cobra.Command{
Use: "lint [flags] PATH",
Short: "examines a chart for possible issues",
Short: "Examines a chart for possible issues",
Long: longLintHelp,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) > 0 {
@ -71,12 +71,12 @@ func newLintCmd(out io.Writer) *cobra.Command {
},
}
cmd.Flags().VarP(&l.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
cmd.Flags().StringArrayVar(&l.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
cmd.Flags().StringArrayVar(&l.sValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
cmd.Flags().StringArrayVar(&l.fValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
cmd.Flags().StringVar(&l.namespace, "namespace", "default", "namespace to put the release into")
cmd.Flags().BoolVar(&l.strict, "strict", false, "fail on lint warnings")
cmd.Flags().VarP(&l.valueFiles, "values", "f", "Specify values in a YAML file (can specify multiple)")
cmd.Flags().StringArrayVar(&l.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
cmd.Flags().StringArrayVar(&l.sValues, "set-string", []string{}, "Set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
cmd.Flags().StringArrayVar(&l.fValues, "set-file", []string{}, "Set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
cmd.Flags().StringVar(&l.namespace, "namespace", "default", "Namespace to put the release into")
cmd.Flags().BoolVar(&l.strict, "strict", false, "Fail on lint warnings")
return cmd
}
@ -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) {

@ -104,7 +104,7 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "list [flags] [FILTER]",
Short: "list releases",
Short: "List releases",
Long: listHelp,
Aliases: []string{"ls"},
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
@ -121,21 +121,21 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.BoolVarP(&list.short, "short", "q", false, "output short (quiet) listing format")
f.BoolVarP(&list.byDate, "date", "d", false, "sort by release date")
f.BoolVarP(&list.sortDesc, "reverse", "r", false, "reverse the sort order")
f.IntVarP(&list.limit, "max", "m", 256, "maximum number of releases to fetch")
f.StringVarP(&list.offset, "offset", "o", "", "next release name in the list, used to offset from start value")
f.BoolVarP(&list.all, "all", "a", false, "show all releases, not just the ones marked DEPLOYED")
f.BoolVar(&list.deleted, "deleted", false, "show deleted releases")
f.BoolVar(&list.deleting, "deleting", false, "show releases that are currently being deleted")
f.BoolVar(&list.deployed, "deployed", false, "show deployed releases. If no other is specified, this will be automatically enabled")
f.BoolVar(&list.failed, "failed", false, "show failed releases")
f.BoolVar(&list.pending, "pending", false, "show pending releases")
f.StringVar(&list.namespace, "namespace", "", "show releases within a specific namespace")
f.UintVar(&list.colWidth, "col-width", 60, "specifies the max column width of output")
f.StringVar(&list.output, "output", "", "output the specified format (json or yaml)")
f.BoolVarP(&list.byChartName, "chart-name", "c", false, "sort by chart name")
f.BoolVarP(&list.short, "short", "q", false, "Output short (quiet) listing format")
f.BoolVarP(&list.byDate, "date", "d", false, "Sort by release date")
f.BoolVarP(&list.sortDesc, "reverse", "r", false, "Reverse the sort order")
f.IntVarP(&list.limit, "max", "m", 256, "Maximum number of releases to fetch")
f.StringVarP(&list.offset, "offset", "o", "", "Next release name in the list, used to offset from start value")
f.BoolVarP(&list.all, "all", "a", false, "Show all releases, not just the ones marked DEPLOYED")
f.BoolVar(&list.deleted, "deleted", false, "Show deleted releases")
f.BoolVar(&list.deleting, "deleting", false, "Show releases that are currently being deleted")
f.BoolVar(&list.deployed, "deployed", false, "Show deployed releases. If no other is specified, this will be automatically enabled")
f.BoolVar(&list.failed, "failed", false, "Show failed releases")
f.BoolVar(&list.pending, "pending", false, "Show pending releases")
f.StringVar(&list.namespace, "namespace", "", "Show releases within a specific namespace")
f.UintVar(&list.colWidth, "col-width", 60, "Specifies the max column width of output")
f.StringVar(&list.output, "output", "", "Output the specified format (json or yaml)")
f.BoolVarP(&list.byChartName, "chart-name", "c", false, "Sort by chart name")
// TODO: Do we want this as a feature of 'helm list'?
//f.BoolVar(&list.superseded, "history", true, "show historical releases")

@ -70,7 +70,7 @@ func newPackageCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "package [flags] [CHART_PATH] [...]",
Short: "package a chart directory into a chart archive",
Short: "Package a chart directory into a chart archive",
Long: packageDesc,
RunE: func(cmd *cobra.Command, args []string) error {
pkg.home = settings.Home
@ -96,14 +96,14 @@ func newPackageCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.BoolVar(&pkg.save, "save", true, "save packaged chart to local chart repository")
f.BoolVar(&pkg.sign, "sign", false, "use a PGP private key to sign this package")
f.StringVar(&pkg.key, "key", "", "name of the key to use when signing. Used if --sign is true")
f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "location of a public keyring")
f.StringVar(&pkg.version, "version", "", "set the version on the chart to this semver version")
f.StringVar(&pkg.appVersion, "app-version", "", "set the appVersion on the chart to this version")
f.StringVarP(&pkg.destination, "destination", "d", ".", "location to write the chart.")
f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `update dependencies from "requirements.yaml" to dir "charts/" before packaging`)
f.BoolVar(&pkg.save, "save", true, "Save packaged chart to local chart repository")
f.BoolVar(&pkg.sign, "sign", false, "Use a PGP private key to sign this package")
f.StringVar(&pkg.key, "key", "", "Name of the key to use when signing. Used if --sign is true")
f.StringVar(&pkg.keyring, "keyring", defaultKeyring(), "Location of a public keyring")
f.StringVar(&pkg.version, "version", "", "Set the version on the chart to this semver version")
f.StringVar(&pkg.appVersion, "app-version", "", "Set the appVersion on the chart to this version")
f.StringVarP(&pkg.destination, "destination", "d", ".", "Location to write the chart.")
f.BoolVarP(&pkg.dependencyUpdate, "dependency-update", "u", false, `Update dependencies from "requirements.yaml" to dir "charts/" before packaging`)
return cmd
}

@ -33,7 +33,7 @@ Manage client-side Helm plugins.
func newPluginCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "plugin",
Short: "add, list, or remove Helm plugins",
Short: "Add, list, or remove Helm plugins",
Long: pluginHelp,
}
cmd.AddCommand(

@ -44,7 +44,7 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command {
pcmd := &pluginInstallCmd{out: out}
cmd := &cobra.Command{
Use: "install [options] <path|url>...",
Short: "install one or more Helm plugins",
Short: "Install one or more Helm plugins",
Long: pluginInstallDesc,
PreRunE: func(cmd *cobra.Command, args []string) error {
return pcmd.complete(args)
@ -53,7 +53,7 @@ func newPluginInstallCmd(out io.Writer) *cobra.Command {
return pcmd.run()
},
}
cmd.Flags().StringVar(&pcmd.version, "version", "", "specify a version constraint. If this is not specified, the latest version is installed")
cmd.Flags().StringVar(&pcmd.version, "version", "", "Specify a version constraint. If this is not specified, the latest version is installed")
return cmd
}

@ -34,7 +34,7 @@ func newPluginListCmd(out io.Writer) *cobra.Command {
pcmd := &pluginListCmd{out: out}
cmd := &cobra.Command{
Use: "list",
Short: "list installed Helm plugins",
Short: "List installed Helm plugins",
RunE: func(cmd *cobra.Command, args []string) error {
pcmd.home = settings.Home
return pcmd.run()

@ -38,7 +38,7 @@ func newPluginRemoveCmd(out io.Writer) *cobra.Command {
pcmd := &pluginRemoveCmd{out: out}
cmd := &cobra.Command{
Use: "remove <plugin>...",
Short: "remove one or more Helm plugins",
Short: "Remove one or more Helm plugins",
PreRunE: func(cmd *cobra.Command, args []string) error {
return pcmd.complete(args)
},

@ -39,7 +39,7 @@ func newPluginUpdateCmd(out io.Writer) *cobra.Command {
pcmd := &pluginUpdateCmd{out: out}
cmd := &cobra.Command{
Use: "update <plugin>...",
Short: "update one or more Helm plugins",
Short: "Update one or more Helm plugins",
PreRunE: func(cmd *cobra.Command, args []string) error {
return pcmd.complete(args)
},

@ -17,16 +17,31 @@ limitations under the License.
package main
import (
"encoding/json"
"fmt"
"io"
"text/template"
"time"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/timeconv"
)
type outputFormat string
const (
outputFlag = "output"
outputTable outputFormat = "table"
outputJSON outputFormat = "json"
outputYAML outputFormat = "yaml"
)
var printReleaseTemplate = `REVISION: {{.Release.Version}}
RELEASED: {{.ReleaseDate}}
CHART: {{.Release.Chart.Metadata.Name}}-{{.Release.Chart.Metadata.Version}}
@ -66,7 +81,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
@ -80,3 +95,66 @@ func debug(format string, args ...interface{}) {
fmt.Printf(format, args...)
}
}
// bindOutputFlag will add the output flag to the given command and bind the
// value to the given string pointer
func bindOutputFlag(cmd *cobra.Command, varRef *string) {
cmd.Flags().StringVarP(varRef, outputFlag, "o", string(outputTable), fmt.Sprintf("Prints the output in the specified format. Allowed values: %s, %s, %s", outputTable, outputJSON, outputYAML))
}
type outputWriter interface {
WriteTable(out io.Writer) error
WriteJSON(out io.Writer) error
WriteYAML(out io.Writer) error
}
func write(out io.Writer, ow outputWriter, format outputFormat) error {
switch format {
case outputTable:
return ow.WriteTable(out)
case outputJSON:
return ow.WriteJSON(out)
case outputYAML:
return ow.WriteYAML(out)
}
return fmt.Errorf("unsupported format %s", format)
}
// encodeJSON is a helper function to decorate any error message with a bit more
// context and avoid writing the same code over and over for printers
func encodeJSON(out io.Writer, obj interface{}) error {
enc := json.NewEncoder(out)
err := enc.Encode(obj)
if err != nil {
return fmt.Errorf("unable to write JSON output: %s", err)
}
return nil
}
// encodeYAML is a helper function to decorate any error message with a bit more
// context and avoid writing the same code over and over for printers
func encodeYAML(out io.Writer, obj interface{}) error {
raw, err := yaml.Marshal(obj)
if err != nil {
return fmt.Errorf("unable to write YAML output: %s", err)
}
// Append a newline, as with a JSON encoder
raw = append(raw, []byte("\n")...)
_, err = out.Write(raw)
if err != nil {
return fmt.Errorf("unable to write YAML output: %s", err)
}
return nil
}
// encodeTable is a helper function to decorate any error message with a bit
// more context and avoid writing the same code over and over for printers
func encodeTable(out io.Writer, table *uitable.Table) error {
raw := table.Bytes()
raw = append(raw, []byte("\n")...)
_, err := out.Write(raw)
if err != nil {
return fmt.Errorf("unable to write table output: %s", err)
}
return nil
}

@ -50,7 +50,7 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "test [RELEASE]",
Short: "test a release",
Short: "Test a release",
Long: releaseTestDesc,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -66,9 +66,9 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int64Var(&rlsTest.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&rlsTest.cleanup, "cleanup", false, "delete test pods upon completion")
f.BoolVar(&rlsTest.parallel, "parallel", false, "run test pods in parallel")
f.Int64Var(&rlsTest.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&rlsTest.cleanup, "cleanup", false, "Delete test pods upon completion")
f.BoolVar(&rlsTest.parallel, "parallel", false, "Run test pods in parallel")
// set defaults from environment
settings.InitTLS(f)

@ -33,7 +33,7 @@ Example usage:
func newRepoCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "repo [FLAGS] add|remove|list|index|update [ARGS]",
Short: "add, list, remove, update, and index chart repositories",
Short: "Add, list, remove, update, and index chart repositories",
Long: repoHelm,
}

@ -17,16 +17,20 @@ limitations under the License.
package main
import (
"context"
"fmt"
"io"
"syscall"
"time"
"golang.org/x/crypto/ssh/terminal"
"github.com/gofrs/flock"
"github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal"
"k8s.io/helm/pkg/getter"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/repo"
"syscall"
)
type repoAddCmd struct {
@ -49,7 +53,7 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "add [flags] [NAME] [URL]",
Short: "add a chart repository",
Short: "Add a chart repository",
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "name for the chart repository", "the url of the chart repository"); err != nil {
return err
@ -64,12 +68,12 @@ func newRepoAddCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&add.username, "username", "", "chart repository username")
f.StringVar(&add.password, "password", "", "chart repository password")
f.BoolVar(&add.noupdate, "no-update", false, "raise error if repo is already registered")
f.StringVar(&add.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&add.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&add.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
f.StringVar(&add.username, "username", "", "Chart repository username")
f.StringVar(&add.password, "password", "", "Chart repository password")
f.BoolVar(&add.noupdate, "no-update", false, "Raise error if repo is already registered")
f.StringVar(&add.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file")
f.StringVar(&add.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file")
f.StringVar(&add.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle")
return cmd
}
@ -131,6 +135,25 @@ func addRepository(name, url, username, password string, home helmpath.Home, cer
return fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", url, err.Error())
}
// Lock the repository file for concurrent goroutines or processes synchronization
fileLock := flock.New(home.RepositoryFile())
lockCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
locked, err := fileLock.TryLockContext(lockCtx, time.Second)
if err == nil && locked {
defer fileLock.Unlock()
}
if err != nil {
return err
}
// Re-read the repositories file before updating it as its content may have been changed
// by a concurrent execution after the first read and before being locked
f, err = repo.LoadRepositoriesFile(home.RepositoryFile())
if err != nil {
return err
}
f.Update(&c)
return f.WriteFile(home.RepositoryFile(), 0644)

@ -17,13 +17,18 @@ limitations under the License.
package main
import (
"fmt"
"io"
"os"
"os/exec"
"strings"
"sync"
"testing"
"github.com/spf13/cobra"
"k8s.io/helm/pkg/helm"
"k8s.io/helm/pkg/helm/helmpath"
"k8s.io/helm/pkg/repo"
"k8s.io/helm/pkg/repo/repotest"
)
@ -101,3 +106,111 @@ func TestRepoAdd(t *testing.T) {
t.Errorf("Duplicate repository name was added")
}
}
func TestRepoAddConcurrentGoRoutines(t *testing.T) {
ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
cleanup := resetEnv()
defer func() {
ts.Stop()
os.RemoveAll(thome.String())
cleanup()
}()
settings.Home = thome
if err := ensureTestHome(settings.Home, t); err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(3)
for i := 0; i < 3; i++ {
go func(name string) {
defer wg.Done()
if err := addRepository(name, ts.URL(), "", "", settings.Home, "", "", "", true); err != nil {
t.Error(err)
}
}(fmt.Sprintf("%s-%d", testName, i))
}
wg.Wait()
f, err := repo.LoadRepositoriesFile(settings.Home.RepositoryFile())
if err != nil {
t.Error(err)
}
var name string
for i := 0; i < 3; i++ {
name = fmt.Sprintf("%s-%d", testName, i)
if !f.Has(name) {
t.Errorf("%s was not successfully inserted into %s", name, settings.Home.RepositoryFile())
}
}
}
// Same as TestRepoAddConcurrentGoRoutines but with repository additions in sub-processes
func TestRepoAddConcurrentSubProcesses(t *testing.T) {
goWantHelperProcess := os.Getenv("GO_WANT_HELPER_PROCESS")
if goWantHelperProcess == "" {
// parent
ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
settings.Home = thome
cleanup := resetEnv()
defer func() {
ts.Stop()
os.RemoveAll(thome.String())
cleanup()
}()
if err := ensureTestHome(settings.Home, t); err != nil {
t.Fatal(err)
}
var wg sync.WaitGroup
wg.Add(2)
for i := 0; i < 2; i++ {
go func(name string) {
defer wg.Done()
cmd := exec.Command(os.Args[0], "-test.run=^TestRepoAddConcurrentSubProcesses$")
cmd.Env = append(os.Environ(), fmt.Sprintf("GO_WANT_HELPER_PROCESS=%s,%s", name, ts.URL()), fmt.Sprintf("HELM_HOME=%s", settings.Home))
out, err := cmd.CombinedOutput()
if len(out) > 0 || err != nil {
t.Fatalf("child process: %q, %v", out, err)
}
}(fmt.Sprintf("%s-%d", testName, i))
}
wg.Wait()
f, err := repo.LoadRepositoriesFile(settings.Home.RepositoryFile())
if err != nil {
t.Error(err)
}
var name string
for i := 0; i < 2; i++ {
name = fmt.Sprintf("%s-%d", testName, i)
if !f.Has(name) {
t.Errorf("%s was not successfully inserted into %s", name, settings.Home.RepositoryFile())
}
}
} else {
// child
s := strings.Split(goWantHelperProcess, ",")
settings.Home = helmpath.Home(os.Getenv("HELM_HOME"))
repoName := s[0]
tsURL := s[1]
if err := addRepository(repoName, tsURL, "", "", settings.Home, "", "", "", true); err != nil {
t.Fatal(err)
}
os.Exit(0)
}
}

@ -50,7 +50,7 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "index [flags] [DIR]",
Short: "generate an index file given a directory containing packaged charts",
Short: "Generate an index file given a directory containing packaged charts",
Long: repoIndexDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if err := checkArgsLength(len(args), "path to a directory"); err != nil {
@ -64,8 +64,8 @@ func newRepoIndexCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&index.url, "url", "", "url of chart repository")
f.StringVar(&index.merge, "merge", "", "merge the generated index into the given index")
f.StringVar(&index.url, "url", "", "URL of the chart repository")
f.StringVar(&index.merge, "merge", "", "Merge the generated index into the given index")
return cmd
}

@ -17,8 +17,6 @@ limitations under the License.
package main
import (
"errors"
"fmt"
"io"
"github.com/gosuri/uitable"
@ -31,6 +29,12 @@ import (
type repoListCmd struct {
out io.Writer
home helmpath.Home
output string
}
type repositoryElement struct {
Name string
URL string
}
func newRepoListCmd(out io.Writer) *cobra.Command {
@ -38,29 +42,63 @@ func newRepoListCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "list [flags]",
Short: "list chart repositories",
Short: "List chart repositories",
RunE: func(cmd *cobra.Command, args []string) error {
list.home = settings.Home
return list.run()
},
}
bindOutputFlag(cmd, &list.output)
return cmd
}
func (a *repoListCmd) run() error {
f, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
repoFile, err := repo.LoadRepositoriesFile(a.home.RepositoryFile())
if err != nil {
return err
}
if len(f.Repositories) == 0 {
return errors.New("no repositories to show")
return write(a.out, &repoListWriter{repoFile.Repositories}, outputFormat(a.output))
}
//////////// Printer implementation below here
type repoListWriter struct {
repos []*repo.Entry
}
func (r *repoListWriter) WriteTable(out io.Writer) error {
table := uitable.New()
table.AddRow("NAME", "URL")
for _, re := range f.Repositories {
for _, re := range r.repos {
table.AddRow(re.Name, re.URL)
}
fmt.Fprintln(a.out, table)
return encodeTable(out, table)
}
func (r *repoListWriter) WriteJSON(out io.Writer) error {
return r.encodeByFormat(out, outputJSON)
}
func (r *repoListWriter) WriteYAML(out io.Writer) error {
return r.encodeByFormat(out, outputYAML)
}
func (r *repoListWriter) encodeByFormat(out io.Writer, format outputFormat) error {
var repolist []repositoryElement
for _, re := range r.repos {
repolist = append(repolist, repositoryElement{Name: re.Name, URL: re.URL})
}
switch format {
case outputJSON:
return encodeJSON(out, repolist)
case outputYAML:
return encodeYAML(out, repolist)
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}

@ -39,7 +39,7 @@ func newRepoRemoveCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "remove [flags] [NAME]",
Aliases: []string{"rm"},
Short: "remove a chart repository",
Short: "Remove a chart repository",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
return fmt.Errorf("need at least one argument, name of chart repository")

@ -36,15 +36,24 @@ Information is cached locally, where it is used by commands like 'helm search'.
'helm update' is the deprecated form of 'helm repo update'. It will be removed in
future releases.
You can specify the name of a repository you want to update.
$ helm repo update <repo_name>
To update all the repositories, use 'helm repo update'.
`
var errNoRepositories = errors.New("no repositories found. You must add one before updating")
var errNoRepositoriesMatchingRepoName = errors.New("no repositories found matching the provided name. Verify if the repo exists")
type repoUpdateCmd struct {
update func([]*repo.ChartRepository, io.Writer, helmpath.Home, bool) error
home helmpath.Home
out io.Writer
strict bool
name string
}
func newRepoUpdateCmd(out io.Writer) *cobra.Command {
@ -53,18 +62,21 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command {
update: updateCharts,
}
cmd := &cobra.Command{
Use: "update",
Use: "update [REPO_NAME]",
Aliases: []string{"up"},
Short: "update information of available charts locally from chart repositories",
Short: "Update information of available charts locally from chart repositories",
Long: updateDesc,
RunE: func(cmd *cobra.Command, args []string) error {
u.home = settings.Home
if len(args) != 0 {
u.name = args[0]
}
return u.run()
},
}
f := cmd.Flags()
f.BoolVar(&u.strict, "strict", false, "fail on update warnings")
f.BoolVar(&u.strict, "strict", false, "Fail on update warnings")
return cmd
}
@ -84,8 +96,22 @@ func (u *repoUpdateCmd) run() error {
if err != nil {
return err
}
if len(u.name) != 0 {
if cfg.Name == u.name {
repos = append(repos, r)
break
} else {
continue
}
} else {
repos = append(repos, r)
}
}
if len(repos) == 0 {
return errNoRepositoriesMatchingRepoName
}
return u.update(repos, u.out, u.home, u.strict)
}
@ -125,6 +151,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
}

@ -132,3 +132,78 @@ func TestUpdateCmdStrictFlag(t *testing.T) {
t.Errorf("Expected 'Unable to get an update', got %q", got)
}
}
func TestUpdateCmdWithSingleRepoNameWhichDoesntExist(t *testing.T) {
thome, err := tempHelmHome(t)
if err != nil {
t.Fatal(err)
}
cleanup := resetEnv()
defer func() {
os.RemoveAll(thome.String())
cleanup()
}()
settings.Home = thome
out := bytes.NewBuffer(nil)
cmd := newRepoUpdateCmd(out)
if err = cmd.RunE(cmd, []string{"randomRepo"}); err == nil {
t.Fatal("expected error due to wrong repo name")
}
if got := fmt.Sprintf("%v", err); !strings.Contains(got, "no repositories found matching the provided name. Verify if the repo exists") {
t.Errorf("Expected 'no repositories found matching the provided name. Verify if the repo exists', got %q", got)
}
}
func TestUpdateRepo(t *testing.T) {
ts, thome, err := repotest.NewTempServer("testdata/testserver/*.*")
if err != nil {
t.Fatal(err)
}
hh := helmpath.Home(thome)
cleanup := resetEnv()
defer func() {
ts.Stop()
os.RemoveAll(thome.String())
cleanup()
}()
if err := ensureTestHome(hh, t); err != nil {
t.Fatal(err)
}
settings.Home = thome
if err := addRepository("repo1", ts.URL(), "", "", hh, "", "", "", true); err != nil {
t.Error(err)
}
if err := addRepository("repo2", ts.URL(), "", "", hh, "", "", "", true); err != nil {
t.Error(err)
}
out := bytes.NewBuffer(nil)
cmd := newRepoUpdateCmd(out)
if err = cmd.RunE(cmd, []string{"repo1"}); err != nil {
t.Fatal("expected to update repo1 correctly")
}
got := out.String()
if !strings.Contains(got, "Successfully got an update from the \"repo1\"") {
t.Errorf("Expected to successfully update \"repo1\" repository, got %q", got)
}
if strings.Contains(got, "Successfully got an update from the \"repo2\"") {
t.Errorf("Shouldn't have updated \"repo2\" repository, got %q", got)
}
if !strings.Contains(got, "Update Complete.") {
t.Error("Update was not successful")
}
}

@ -21,6 +21,7 @@ import (
"fmt"
"io"
"os"
"strings"
"github.com/spf13/cobra"
@ -56,10 +57,14 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "reset",
Short: "uninstalls Tiller from a cluster",
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
@ -79,8 +84,8 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.BoolVarP(&d.force, "force", "f", false, "forces Tiller uninstall even if there are releases installed, or if Tiller is not in ready state. Releases are not deleted.)")
f.BoolVar(&d.removeHelmHome, "remove-helm-home", false, "if set deletes $HELM_HOME")
f.BoolVarP(&d.force, "force", "f", false, "Forces Tiller uninstall even if there are releases installed, or if Tiller is not in ready state. Releases are not deleted.)")
f.BoolVar(&d.removeHelmHome, "remove-helm-home", false, "If set, deletes $HELM_HOME")
// set defaults from environment
settings.InitTLS(f)
@ -110,7 +115,7 @@ func (d *resetCmd) run() error {
}
if err := installer.Uninstall(d.kubeClient, &installer.Options{Namespace: d.namespace}); err != nil {
return fmt.Errorf("error unstalling Tiller: %s", err)
return fmt.Errorf("error uninstalling Tiller: %s", err)
}
if d.removeHelmHome {

@ -58,7 +58,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "rollback [flags] [RELEASE] [REVISION]",
Short: "roll back a release to a previous revision",
Short: "Rollback a release to a previous revision",
Long: rollbackDesc,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -81,14 +81,14 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.BoolVar(&rollback.dryRun, "dry-run", false, "simulate a rollback")
f.BoolVar(&rollback.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&rollback.force, "force", false, "force resource update through delete/recreate if needed")
f.BoolVar(&rollback.disableHooks, "no-hooks", false, "prevent hooks from running during rollback")
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")
f.BoolVar(&rollback.dryRun, "dry-run", false, "Simulate a rollback")
f.BoolVar(&rollback.recreate, "recreate-pods", false, "Performs pods restart for the resource if applicable")
f.BoolVar(&rollback.force, "force", false, "Force resource update through delete/recreate if needed")
f.BoolVar(&rollback.disableHooks, "no-hooks", false, "Prevent hooks from running during rollback")
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)
@ -112,7 +112,7 @@ func (r *rollbackCmd) run() error {
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",

@ -35,6 +35,21 @@ Search reads through all of the repositories configured on the system, and
looks for matches.
Repositories are managed with 'helm repo' commands.
To look for charts with a particular name (such as stable/mysql), try
searching using vertical tabs (\v). Vertical tabs are used as the delimiter
between search fields. For example:
helm search --regexp '\vstable/mysql\v'
To search for charts using common keywords (such as "database" or
"key-value store"), use
helm search database
or
helm search key-value store
`
// searchMaxScore suggests that any score higher than this is not considered a match.
@ -48,6 +63,14 @@ type searchCmd struct {
regexp bool
version string
colWidth uint
output string
}
type chartElement struct {
Name string
Version string
AppVersion string
Description string
}
func newSearchCmd(out io.Writer) *cobra.Command {
@ -55,7 +78,7 @@ func newSearchCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "search [keyword]",
Short: "search for a keyword in charts",
Short: "Search for a keyword in charts",
Long: searchDesc,
RunE: func(cmd *cobra.Command, args []string) error {
sc.helmhome = settings.Home
@ -64,10 +87,11 @@ func newSearchCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.BoolVarP(&sc.regexp, "regexp", "r", false, "use regular expressions for searching")
f.BoolVarP(&sc.versions, "versions", "l", false, "show the long listing, with each version of each chart on its own line")
f.StringVarP(&sc.version, "version", "v", "", "search using semantic versioning constraints")
f.UintVar(&sc.colWidth, "col-width", 60, "specifies the max column width of output")
f.BoolVarP(&sc.regexp, "regexp", "r", false, "Use regular expressions for searching")
f.BoolVarP(&sc.versions, "versions", "l", false, "Show the long listing, with each version of each chart on its own line")
f.StringVarP(&sc.version, "version", "v", "", "Search using semantic versioning constraints")
f.UintVar(&sc.colWidth, "col-width", 60, "Specifies the max column width of output")
bindOutputFlag(cmd, &sc.output)
return cmd
}
@ -95,9 +119,7 @@ func (s *searchCmd) run(args []string) error {
return err
}
fmt.Fprintln(s.out, s.formatSearchResults(data, s.colWidth))
return nil
return write(s.out, &searchWriter{data, s.colWidth}, outputFormat(s.output))
}
func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, error) {
@ -128,19 +150,6 @@ func (s *searchCmd) applyConstraint(res []*search.Result) ([]*search.Result, err
return data, nil
}
func (s *searchCmd) formatSearchResults(res []*search.Result, colWidth uint) string {
if len(res) == 0 {
return "No results found"
}
table := uitable.New()
table.MaxColWidth = colWidth
table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION")
for _, r := range res {
table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description)
}
return table.String()
}
func (s *searchCmd) buildIndex() (*search.Index, error) {
// Load the repositories.yaml
rf, err := repo.LoadRepositoriesFile(s.helmhome.RepositoryFile())
@ -162,3 +171,53 @@ func (s *searchCmd) buildIndex() (*search.Index, error) {
}
return i, nil
}
//////////// Printer implementation below here
type searchWriter struct {
results []*search.Result
columnWidth uint
}
func (r *searchWriter) WriteTable(out io.Writer) error {
if len(r.results) == 0 {
_, err := out.Write([]byte("No results found\n"))
if err != nil {
return fmt.Errorf("unable to write results: %s", err)
}
return nil
}
table := uitable.New()
table.MaxColWidth = r.columnWidth
table.AddRow("NAME", "CHART VERSION", "APP VERSION", "DESCRIPTION")
for _, r := range r.results {
table.AddRow(r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description)
}
return encodeTable(out, table)
}
func (r *searchWriter) WriteJSON(out io.Writer) error {
return r.encodeByFormat(out, outputJSON)
}
func (r *searchWriter) WriteYAML(out io.Writer) error {
return r.encodeByFormat(out, outputYAML)
}
func (r *searchWriter) encodeByFormat(out io.Writer, format outputFormat) error {
var chartList []chartElement
for _, r := range r.results {
chartList = append(chartList, chartElement{r.Name, r.Chart.Version, r.Chart.AppVersion, r.Chart.Description})
}
switch format {
case outputJSON:
return encodeJSON(out, chartList)
case outputYAML:
return encodeYAML(out, chartList)
}
// Because this is a non-exported function and only called internally by
// WriteJSON and WriteYAML, we shouldn't get invalid types
return nil
}

@ -55,7 +55,7 @@ type Index struct {
charts map[string]*repo.ChartVersion
}
// NewIndex creats a new Index.
// NewIndex creates a new Index.
func NewIndex() *Index {
return &Index{lines: map[string]string{}, charts: map[string]*repo.ChartVersion{}}
}

@ -18,6 +18,7 @@ package main
import (
"io"
"strings"
"testing"
"github.com/spf13/cobra"
@ -84,6 +85,30 @@ func TestSearchCmd(t *testing.T) {
flags: []string{"--regexp"},
err: true,
},
{
name: "search for 'maria', expect one match output json",
args: []string{"maria"},
flags: strings.Split("--output json", " "),
expected: `[{"Name":"testing/mariadb","Version":"0.3.0","Appversion":"","Description":"Chart for MariaDB"}]`,
},
{
name: "search for 'alpine', expect two matches output json",
args: []string{"alpine"},
flags: strings.Split("--output json", " "),
expected: `[{"Name":"testing/alpine","Version":"0.2.0","Appversion":"2.3.4","Description":"Deploy a basic Alpine Linux pod"}]`,
},
{
name: "search for 'maria', expect one match output yaml",
args: []string{"maria"},
flags: strings.Split("--output yaml", " "),
expected: "- AppVersion: \"\"\n Description: Chart for MariaDB\n Name: testing/mariadb\n Version: 0.3.0\n\n",
},
{
name: "search for 'alpine', expect two matches output yaml",
args: []string{"alpine"},
flags: strings.Split("--output yaml", " "),
expected: "- AppVersion: 2.3.4\n Description: Deploy a basic Alpine Linux pod\n Name: testing/alpine\n Version: 0.2.0\n\n",
},
}
cleanup := resetEnv()

@ -53,7 +53,7 @@ func newServeCmd(out io.Writer) *cobra.Command {
srv := &serveCmd{out: out}
cmd := &cobra.Command{
Use: "serve",
Short: "start a local http web server",
Short: "Start a local http web server",
Long: serveDesc,
PreRunE: func(cmd *cobra.Command, args []string) error {
return srv.complete()
@ -64,9 +64,9 @@ func newServeCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&srv.repoPath, "repo-path", "", "local directory path from which to serve charts")
f.StringVar(&srv.address, "address", "127.0.0.1:8879", "address to listen on")
f.StringVar(&srv.url, "url", "", "external URL of chart repository")
f.StringVar(&srv.repoPath, "repo-path", "", "Local directory path from which to serve charts")
f.StringVar(&srv.address, "address", "127.0.0.1:8879", "Address to listen on")
f.StringVar(&srv.url, "url", "", "External URL of chart repository")
return cmd
}

@ -17,13 +17,11 @@ limitations under the License.
package main
import (
"encoding/json"
"fmt"
"io"
"regexp"
"text/tabwriter"
"github.com/ghodss/yaml"
"github.com/gosuri/uitable"
"github.com/gosuri/uitable/util/strutil"
"github.com/spf13/cobra"
@ -61,7 +59,7 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "status [flags] RELEASE_NAME",
Short: "displays the status of the named release",
Short: "Displays the status of the named release",
Long: statusHelp,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -78,8 +76,8 @@ func newStatusCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.Int32Var(&status.version, "revision", 0, "if set, display the status of the named release with revision")
f.StringVarP(&status.outfmt, "output", "o", "", "output the status in the specified format (json or yaml)")
f.Int32Var(&status.version, "revision", 0, "If set, display the status of the named release with revision")
bindOutputFlag(cmd, &status.outfmt)
// set defaults from environment
settings.InitTLS(f)
@ -93,27 +91,26 @@ func (s *statusCmd) run() error {
return prettyError(err)
}
switch s.outfmt {
case "":
PrintStatus(s.out, res)
return nil
case "json":
data, err := json.Marshal(res)
if err != nil {
return fmt.Errorf("Failed to Marshal JSON output: %s", err)
return write(s.out, &statusWriter{res}, outputFormat(s.outfmt))
}
s.out.Write(data)
return nil
case "yaml":
data, err := yaml.Marshal(res)
if err != nil {
return fmt.Errorf("Failed to Marshal YAML output: %s", err)
type statusWriter struct {
status *services.GetReleaseStatusResponse
}
s.out.Write(data)
func (s *statusWriter) WriteTable(out io.Writer) error {
PrintStatus(out, s.status)
// There is no error handling here due to backwards compatibility with
// PrintStatus
return nil
}
return fmt.Errorf("Unknown output format %q", s.outfmt)
func (s *statusWriter) WriteJSON(out io.Writer) error {
return encodeJSON(out, s.status)
}
func (s *statusWriter) WriteYAML(out io.Writer) error {
return encodeYAML(out, s.status)
}
// PrintStatus prints out the status of a release. Shared because also used by

@ -86,24 +86,25 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "template [flags] CHART",
Short: fmt.Sprintf("locally render templates"),
Short: "Locally render templates",
Long: templateDesc,
RunE: t.run,
}
cmd.SetOutput(out)
f := cmd.Flags()
f.BoolVar(&t.showNotes, "notes", false, "show the computed NOTES.txt file as well")
f.StringVarP(&t.releaseName, "name", "n", "release-name", "release name")
f.BoolVar(&t.releaseIsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall")
f.StringArrayVarP(&t.renderFiles, "execute", "x", []string{}, "only execute the given templates")
f.VarP(&t.valueFiles, "values", "f", "specify values in a YAML file (can specify multiple)")
f.StringVar(&t.namespace, "namespace", "", "namespace to install the release into")
f.StringArrayVar(&t.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&t.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&t.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.StringVar(&t.nameTemplate, "name-template", "", "specify template used to name the release")
f.StringVar(&t.kubeVersion, "kube-version", defaultKubeVersion, "kubernetes version used as Capabilities.KubeVersion.Major/Minor")
f.StringVar(&t.outputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
f.BoolVar(&t.showNotes, "notes", false, "Show the computed NOTES.txt file as well")
f.StringVarP(&t.releaseName, "name", "n", "release-name", "Release name")
f.BoolVar(&t.releaseIsUpgrade, "is-upgrade", false, "Set .Release.IsUpgrade instead of .Release.IsInstall")
f.StringArrayVarP(&t.renderFiles, "execute", "x", []string{}, "Only execute the given templates")
f.VarP(&t.valueFiles, "values", "f", "Specify values in a YAML file (can specify multiple)")
f.StringVar(&t.namespace, "namespace", "", "Namespace to install the release into")
f.StringArrayVar(&t.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&t.stringValues, "set-string", []string{}, "Set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&t.fileValues, "set-file", []string{}, "Set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.StringVar(&t.nameTemplate, "name-template", "", "Specify template used to name the release")
f.StringVar(&t.kubeVersion, "kube-version", defaultKubeVersion, "Kubernetes version used as Capabilities.KubeVersion.Major/Minor")
f.StringVar(&t.outputDir, "output-dir", "", "Writes the executed templates to files in output-dir instead of stdout")
return cmd
}
@ -241,20 +242,20 @@ func (t *templateCmd) run(cmd *cobra.Command, args []string) error {
if whitespaceRegex.MatchString(data) {
continue
}
err = writeToFile(t.outputDir, m.Name, data)
err = writeToFile(t.outputDir, m.Name, data, t.out)
if err != nil {
return err
}
continue
}
fmt.Printf("---\n# Source: %s\n", m.Name)
fmt.Println(data)
fmt.Fprintf(t.out, "---\n# Source: %s\n", m.Name)
fmt.Fprintln(t.out, data)
}
return nil
}
// write the <data> to <output-dir>/<name>
func writeToFile(outputDir string, name string, data string) error {
func writeToFile(outputDir string, name string, data string, out io.Writer) error {
outfileName := strings.Join([]string{outputDir, name}, string(filepath.Separator))
err := ensureDirectoryForFile(outfileName)
@ -275,7 +276,7 @@ func writeToFile(outputDir string, name string, data string) error {
return err
}
fmt.Printf("wrote %s\n", outfileName)
fmt.Fprintf(out, "wrote %s\n", outfileName)
return nil
}

@ -20,7 +20,6 @@ import (
"bufio"
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"strings"
@ -158,7 +157,7 @@ func TestTemplateCmd(t *testing.T) {
},
{
name: "check_invalid_name_template",
desc: "verify the relase name generate by template is invalid",
desc: "verify the release name generate by template is invalid",
args: []string{subchart1ChartPath, "--name-template", "foobar-{{ b64enc \"abc\" }}-baz"},
expectError: "is invalid",
},
@ -178,14 +177,9 @@ func TestTemplateCmd(t *testing.T) {
},
}
var buf bytes.Buffer
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
// capture stdout
old := os.Stdout
r, w, _ := os.Pipe()
os.Stdout = w
// execute template command
out := bytes.NewBuffer(nil)
cmd := newTemplateCmd(out)
@ -206,14 +200,8 @@ func TestTemplateCmd(t *testing.T) {
} else if err != nil {
t.Errorf("expected no error, got %v", err)
}
// restore stdout
w.Close()
os.Stdout = old
var b bytes.Buffer
io.Copy(&b, r)
r.Close()
// scan yaml into map[<path>]yaml
scanner := bufio.NewScanner(&b)
scanner := bufio.NewScanner(out)
next := false
lastKey := ""
m := map[string]string{}
@ -239,7 +227,6 @@ func TestTemplateCmd(t *testing.T) {
} else {
t.Errorf("could not find key %s", tt.expectKey)
}
buf.Reset()
})
}
}

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

@ -117,6 +117,7 @@ type upgradeCmd struct {
certFile string
keyFile string
caFile string
output string
}
func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
@ -128,7 +129,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "upgrade [RELEASE] [CHART]",
Short: "upgrade a release",
Short: "Upgrade a release",
Long: upgradeDesc,
PreRunE: func(_ *cobra.Command, _ []string) error { return setupConnection() },
RunE: func(cmd *cobra.Command, args []string) error {
@ -152,37 +153,38 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command {
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file or a URL(can specify multiple)")
f.BoolVar(&upgrade.dryRun, "dry-run", false, "simulate an upgrade")
f.BoolVar(&upgrade.recreate, "recreate-pods", false, "performs pods restart for the resource if applicable")
f.BoolVar(&upgrade.force, "force", false, "force resource update through delete/recreate if needed")
f.StringArrayVar(&upgrade.values, "set", []string{}, "set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&upgrade.stringValues, "set-string", []string{}, "set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&upgrade.fileValues, "set-file", []string{}, "set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "disable pre/post upgrade hooks. DEPRECATED. Use no-hooks")
f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "disable pre/post upgrade hooks")
f.BoolVar(&upgrade.verify, "verify", false, "verify the provenance of the chart before upgrading")
f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "path to the keyring that contains public signing keys")
f.BoolVarP(&upgrade.install, "install", "i", false, "if a release by this name doesn't already exist, run an install")
f.StringVar(&upgrade.namespace, "namespace", "", "namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace")
f.StringVar(&upgrade.version, "version", "", "specify the exact chart version to use. If this is not specified, the latest version is used")
f.Int64Var(&upgrade.timeout, "timeout", 300, "time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&upgrade.resetValues, "reset-values", false, "when upgrading, reset the values to the ones built into the chart")
f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "when upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.")
f.BoolVar(&upgrade.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.BoolVar(&upgrade.atomic, "atomic", false, "if set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag")
f.StringVar(&upgrade.repoURL, "repo", "", "chart repository url where to locate the requested chart")
f.StringVar(&upgrade.username, "username", "", "chart repository username where to locate the requested chart")
f.StringVar(&upgrade.password, "password", "", "chart repository password where to locate the requested chart")
f.StringVar(&upgrade.certFile, "cert-file", "", "identify HTTPS client using this SSL certificate file")
f.StringVar(&upgrade.keyFile, "key-file", "", "identify HTTPS client using this SSL key file")
f.StringVar(&upgrade.caFile, "ca-file", "", "verify certificates of HTTPS-enabled servers using this CA bundle")
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")
f.VarP(&upgrade.valueFiles, "values", "f", "Specify values in a YAML file or a URL(can specify multiple)")
f.BoolVar(&upgrade.dryRun, "dry-run", false, "Simulate an upgrade")
f.BoolVar(&upgrade.recreate, "recreate-pods", false, "Performs pods restart for the resource if applicable")
f.BoolVar(&upgrade.force, "force", false, "Force resource update through delete/recreate if needed")
f.StringArrayVar(&upgrade.values, "set", []string{}, "Set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&upgrade.stringValues, "set-string", []string{}, "Set STRING values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2)")
f.StringArrayVar(&upgrade.fileValues, "set-file", []string{}, "Set values from respective files specified via the command line (can specify multiple or separate values with commas: key1=path1,key2=path2)")
f.BoolVar(&upgrade.disableHooks, "disable-hooks", false, "Disable pre/post upgrade hooks. DEPRECATED. Use no-hooks")
f.BoolVar(&upgrade.disableHooks, "no-hooks", false, "Disable pre/post upgrade hooks")
f.BoolVar(&upgrade.verify, "verify", false, "Verify the provenance of the chart before upgrading")
f.StringVar(&upgrade.keyring, "keyring", defaultKeyring(), "Path to the keyring that contains public signing keys")
f.BoolVarP(&upgrade.install, "install", "i", false, "If a release by this name doesn't already exist, run an install")
f.StringVar(&upgrade.namespace, "namespace", "", "Namespace to install the release into (only used if --install is set). Defaults to the current kube config namespace")
f.StringVar(&upgrade.version, "version", "", "Specify the exact chart version to use. If this is not specified, the latest version is used")
f.Int64Var(&upgrade.timeout, "timeout", 300, "Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks)")
f.BoolVar(&upgrade.resetValues, "reset-values", false, "When upgrading, reset the values to the ones built into the chart")
f.BoolVar(&upgrade.reuseValues, "reuse-values", false, "When upgrading, reuse the last release's values and merge in any overrides from the command line via --set and -f. If '--reset-values' is specified, this is ignored.")
f.BoolVar(&upgrade.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.BoolVar(&upgrade.atomic, "atomic", false, "If set, upgrade process rolls back changes made in case of failed upgrade, also sets --wait flag")
f.StringVar(&upgrade.repoURL, "repo", "", "Chart repository url where to locate the requested chart")
f.StringVar(&upgrade.username, "username", "", "Chart repository username where to locate the requested chart")
f.StringVar(&upgrade.password, "password", "", "Chart repository password where to locate the requested chart")
f.StringVar(&upgrade.certFile, "cert-file", "", "Identify HTTPS client using this SSL certificate file")
f.StringVar(&upgrade.keyFile, "key-file", "", "Identify HTTPS client using this SSL key file")
f.StringVar(&upgrade.caFile, "ca-file", "", "Verify certificates of HTTPS-enabled servers using this CA bundle")
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")
bindOutputFlag(cmd, &upgrade.output)
f.MarkDeprecated("disable-hooks", "Use --no-hooks instead")
// set defaults from environment
settings.InitTLS(f)
@ -307,14 +309,14 @@ func (u *upgradeCmd) run() error {
printRelease(u.out, resp.Release)
}
fmt.Fprintf(u.out, "Release %q has been upgraded. Happy Helming!\n", u.release)
if outputFormat(u.output) == outputTable {
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)
if err != nil {
return prettyError(err)
}
PrintStatus(u.out, status)
return nil
return write(u.out, &statusWriter{status}, outputFormat(u.output))
}

@ -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"})},
},
{

@ -47,7 +47,7 @@ func newVerifyCmd(out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "verify [flags] PATH",
Short: "verify that a chart at the given path has been signed and is valid",
Short: "Verify that a chart at the given path has been signed and is valid",
Long: verifyDesc,
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) == 0 {
@ -59,7 +59,7 @@ func newVerifyCmd(out io.Writer) *cobra.Command {
}
f := cmd.Flags()
f.StringVar(&vc.keyring, "keyring", defaultKeyring(), "keyring containing public keys")
f.StringVar(&vc.keyring, "keyring", defaultKeyring(), "Keyring containing public keys")
return cmd
}

@ -66,7 +66,7 @@ func newVersionCmd(c helm.Interface, out io.Writer) *cobra.Command {
cmd := &cobra.Command{
Use: "version",
Short: "print the client/server version information",
Short: "Print the client/server version information",
Long: versionDesc,
RunE: func(cmd *cobra.Command, args []string) error {
// If neither is explicitly set, show both.
@ -78,10 +78,10 @@ func newVersionCmd(c helm.Interface, out io.Writer) *cobra.Command {
}
f := cmd.Flags()
settings.AddFlagsTLS(f)
f.BoolVarP(&version.showClient, "client", "c", false, "client version only")
f.BoolVarP(&version.showServer, "server", "s", false, "server version only")
f.BoolVar(&version.short, "short", false, "print the version number")
f.StringVar(&version.template, "template", "", "template for version string format")
f.BoolVarP(&version.showClient, "client", "c", false, "Client version only")
f.BoolVarP(&version.showServer, "server", "s", false, "Server version only")
f.BoolVar(&version.short, "short", false, "Print the version number")
f.StringVar(&version.template, "template", "", "Template for version string format")
// set defaults from environment
settings.InitTLS(f)
@ -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)
}

@ -66,6 +66,7 @@ const (
storageMemory = "memory"
storageConfigMap = "configmap"
storageSecret = "secret"
storageSQL = "sql"
traceAddr = ":44136"
@ -74,11 +75,17 @@ const (
)
var (
grpcAddr = flag.String("listen", ":44134", "address:port to listen on")
probeAddr = flag.String("probe-listen", ":44135", "address:port to listen on for probes")
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")
enableProbing = flag.Bool("probe", true, "enable probing over http")
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")
@ -143,6 +150,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 {
@ -187,7 +206,9 @@ func start() {
logger.Printf("Starting Tiller %s (tls=%t)", version.GetVersion(), *tlsEnable || *tlsVerify)
logger.Printf("GRPC listening on %s", *grpcAddr)
if *enableProbing {
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)
@ -207,6 +228,10 @@ func start() {
}()
go func() {
if !*enableProbing {
return
}
mux := newProbesMux()
// Register gRPC server to prometheus to initialized matrix

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

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

@ -38,7 +38,7 @@ Building synchronization state...
Starting synchronization
Would copy file://fantastic-charts/alpine-0.1.0.tgz to gs://fantastic-charts/alpine-0.1.0.tgz
Would copy file://fantastic-charts/index.yaml to gs://fantastic-charts/index.yaml
Are you sure you would like to continue with these changes?? [y/N]} y
Are you sure you would like to continue with these changes? [y/N]} y
Building synchronization state...
Starting synchronization
Copying file://fantastic-charts/alpine-0.1.0.tgz [Content-Type=application/x-tar]...

@ -30,19 +30,19 @@ With those caveats behind, let's write a template that reads three files into ou
`config1.toml`:
```toml
message = Hello from config 1
message = "Hello from config 1"
```
`config2.toml`:
```toml
message = This is config 2
message = "This is config 2"
```
`config3.toml`:
```toml
message = Goodbye from config 3
message = "Goodbye from config 3"
```
Each of these is a simple TOML file (think old-school Windows INI files). We know the names of these files, so we can use a `range` function to loop through them and inject their contents into our ConfigMap.
@ -54,13 +54,13 @@ metadata:
name: {{ .Release.Name }}-configmap
data:
{{- $files := .Files }}
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
{{- range list "config1.toml" "config2.toml" "config3.toml" }}
{{ . }}: |-
{{ $files.Get . }}
{{- end }}
```
This config map uses several of the techniques discussed in previous sections. For example, we create a `$files` variable to hold a reference to the `.Files` object. We also use the `tuple` function to create a list of files that we loop through. Then we print each file name (`{{.}}: |-`) followed by the contents of the file `{{ $files.Get . }}`.
This config map uses several of the techniques discussed in previous sections. For example, we create a `$files` variable to hold a reference to the `.Files` object. We also use the `list` function to create a list of files that we loop through. Then we print each file name (`{{.}}: |-`) followed by the contents of the file `{{ $files.Get . }}`.
Running this template will produce a single ConfigMap with the contents of all three files:
@ -72,13 +72,13 @@ metadata:
name: quieting-giraf-configmap
data:
config1.toml: |-
message = Hello from config 1
message = "Hello from config 1"
config2.toml: |-
message = This is config 2
message = "This is config 2"
config3.toml: |-
message = Goodbye from config 3
message = "Goodbye from config 3"
```
## Path helpers
@ -129,6 +129,7 @@ You have multiple options with Globs:
Or
```yaml
{{ $root := . }}
{{ range $path, $bytes := .Files.Glob "foo/*" }}
{{ base $path }}: '{{ $root.Files.Get $path | b64enc }}'
{{ end }}

@ -1,6 +1,6 @@
# Built-in Objects
Objects are passed into a template from the template engine. And your code can pass objects around (we'll see examples when we look at the `with` and `range` statements). There are even a few ways to create new objects within your templates, like with the `tuple` function we'll see later.
Objects are passed into a template from the template engine. And your code can pass objects around (we'll see examples when we look at the `with` and `range` statements). There are even a few ways to create new objects within your templates, like with the `list` function we'll see later.
Objects can be simple, and have just one value. Or they can contain other objects or functions. For example. the `Release` object contains several objects (like `Release.Name`) and the `Files` object has a few functions.
@ -22,7 +22,7 @@ In the previous section, we use `{{.Release.Name}}` to insert the name of a rele
- `Files.GetBytes` is a function for getting the contents of a file as an array of bytes instead of as a string. This is useful for things like images.
- `Capabilities`: This provides information about what capabilities the Kubernetes cluster supports.
- `Capabilities.APIVersions` is a set of versions.
- `Capabilities.APIVersions.Has $version` indicates whether a version (`batch/v1`) is enabled on the cluster.
- `Capabilities.APIVersions.Has $version` indicates whether a version (e.g., `batch/v1`) or resource (e.g., `apps/v1/Deployment`) is available on the cluster. Note, resources were not available before Helm v2.15.
- `Capabilities.KubeVersion` provides a way to look up the Kubernetes version. It has the following values: `Major`, `Minor`, `GitVersion`, `GitCommit`, `GitTreeState`, `BuildDate`, `GoVersion`, `Compiler`, and `Platform`.
- `Capabilities.TillerVersion` provides a way to look up the Tiller version. It has the following values: `SemVer`, `GitCommit`, and `GitTreeState`.
- `Template`: Contains information about the current template that is being executed

@ -333,11 +333,11 @@ Now, in this example we've done something tricky. The `toppings: |-` line is dec
> 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.
Sometimes it's useful to be able to quickly make a list inside of your template, and then iterate over that list. Helm templates have a function to make this easy: `tuple`. In computer science, a tuple is a list-like collection of fixed size, but with arbitrary data types. This roughly conveys the way a `tuple` is used.
Sometimes it's useful to be able to quickly make a list inside of your template, and then iterate over that list. Helm templates have a function that's called just that: `list`.
```yaml
sizes: |-
{{- range tuple "small" "medium" "large" }}
{{- range list "small" "medium" "large" }}
- {{ . }}
{{- end }}
```
@ -351,4 +351,4 @@ The above will produce this:
- large
```
In addition to lists and tuples, `range` can be used to iterate over collections that have a key and a value (like a `map` or `dict`). We'll see how to do that in the next section when we introduce template variables.
In addition to lists, `range` can be used to iterate over collections that have a key and a value (like a `map` or `dict`). We'll see how to do that in the next section when we introduce template variables.

@ -159,7 +159,7 @@ Operators are implemented as functions that return a boolean value. To use `eq`,
{{ end }}
{{/* do not include the body of this if statement because unset variables evaluate to false and .Values.setVariable was negated with the not function. */}}
{{/* include the body of this if statement when the variable .Values.anUnsetVariable is set or .values.aSetVariable is not set */}}
{{ if or .Values.anUnsetVariable (not .Values.aSetVariable) }}
{{ ... }}
{{ end }}

@ -187,10 +187,10 @@ instead of `mychart-configmap`.
You can run `helm get manifest clunky-serval` to see the entire generated YAML.
At this point, we've seen templates at their most basic: YAML files that have template directives embedded in `{{` and `}}`. In the next part, we'll take a deeper look into templates. But before moving on, there's one quick trick that can make building templates faster: When you want to test the template rendering, but not actually install anything, you can use `helm install --debug --dry-run ./mychart`. This will send the chart to the Tiller server, which will render the templates. But instead of installing the chart, it will return the rendered template to you so you can see the output:
At this point, we've seen templates at their most basic: YAML files that have template directives embedded in `{{` and `}}`. In the next part, we'll take a deeper look into templates. But before moving on, there's one quick trick that can make building templates faster: When you want to test the template rendering, but not actually install anything, you can use `helm install ./mychart --debug --dry-run`. This will send the chart to the Tiller server, which will render the templates. But instead of installing the chart, it will return the rendered template to you so you can see the output:
```console
$ helm install --debug --dry-run ./mychart
$ helm install ./mychart --debug --dry-run
SERVER: "localhost:44134"
CHART PATH: /Users/mattbutcher/Code/Go/src/k8s.io/helm/_scratch/mychart
NAME: goodly-guppy

@ -1,6 +1,6 @@
# Values Files
In the previous section we looked at the built-in objects that Helm templates offer. One of the four built-in objects is `Values`. This object provides access to values passed into the chart. Its contents come from four sources:
In the previous section we looked at the built-in objects that Helm templates offer. One of these built-in objects is `Values`. This object provides access to values passed into the chart. Its contents come from four sources:
- The `values.yaml` file in the chart
- If this is a subchart, the `values.yaml` file of a parent chart

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

@ -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
@ -258,9 +258,11 @@ All charts are loaded by default. If `tags` or `condition` fields are present,
they will be evaluated and used to control loading for the chart(s) they are applied to.
Condition - The condition field holds one or more YAML paths (delimited by commas).
If this path exists in the top parent's values and resolves to a boolean value,
If this path exists in the parent's values and resolves to a boolean value,
the chart will be enabled or disabled based on that boolean value. Only the first
valid path found in the list is evaluated and if no paths exist then the condition has no effect.
valid path found in the list is evaluated and if no paths exist then the condition
has no effect. For multiple level dependencies the condition is prependend by the
path to the parent chart.
Tags - The tags field is a YAML list of labels to associate with this chart.
In the top parent's values, all charts with tags can be enabled or disabled by
@ -272,7 +274,7 @@ dependencies:
- name: subchart1
repository: http://localhost:10191
version: 0.1.0
condition: subchart1.enabled,global.subchart1.enabled
condition: subchart1.enabled
tags:
- front-end
- subchart1
@ -280,11 +282,19 @@ dependencies:
- name: subchart2
repository: http://localhost:10191
version: 0.1.0
condition: subchart2.enabled,global.subchart2.enabled
condition: subchart2.enabled
tags:
- back-end
- subchart2
```
```yaml
# subchart2/requirements.yaml
dependencies:
- name: subsubchart
repository: http://localhost:10191
version: 0.1.0
condition: subsubchart.enabled
```
```yaml
@ -292,6 +302,9 @@ dependencies:
subchart1:
enabled: true
subchart2:
subsubchart:
enabled: false
tags:
front-end: false
back-end: true
@ -302,9 +315,12 @@ In the above example all charts with the tag `front-end` would be disabled but s
`front-end` tag and `subchart1` will be enabled.
Since `subchart2` is tagged with `back-end` and that tag evaluates to `true`, `subchart2` will be
enabled. Also notes that although `subchart2` has a condition specified in `requirements.yaml`, there
enabled. Also note that although `subchart2` has a condition specified in `requirements.yaml`, there
is no corresponding path and value in the parent's values so that condition has no effect.
`subsubchart` is disabled by default but can be enabled by setting `subchart2.subsubchart.enabled=true`.
Hint: disabling `subchart2` via tag will also disable all sub-charts (even if overriding the value `subchart2.subsubchart.enabled=true`).
##### Using the CLI with Tags and Conditions
The `--set` parameter can be used as usual to alter tag and condition values.
@ -479,7 +495,7 @@ Furthermore, A is dependent on chart B that creates objects
- replicaset "B-ReplicaSet"
- service "B-Service"
After installation/upgrade of chart A a single Helm release is created/modified. The release will
After installation/upgrade of chart A, a single Helm release is created/modified. The release will
create/update all of the above Kubernetes objects in the following order:
- A-Namespace

@ -49,6 +49,10 @@ The following hooks are defined:
have been modified.
- crd-install: Adds CRD resources before any other checks are run. This is used
only on CRD definitions that are used by other manifests in the chart.
- test-success: Executes when running `helm test` and expects the pod to
return successfully (return code == 0).
- test-failure: Executes when running `helm test` and expects the pod to
fail (return code != 0).
## Hooks and the Release Lifecycle
@ -129,6 +133,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
@ -198,6 +203,10 @@ You can choose one or more defined annotation values:
* `"hook-failed"` specifies Tiller should delete the hook if the hook failed during execution.
* `"before-hook-creation"` specifies Tiller should delete the previous hook before the new hook is launched.
By default Tiller will wait for 60 seconds for a deleted hook to no longer exist in the API server before timing out. This
behavior can be changed using the `helm.sh/hook-delete-timeout` annotation. The value is the number of seconds Tiller
should wait for the hook to be fully deleted. A value of 0 means Tiller does not wait at all.
### Defining a CRD with the `crd-install` Hook
Custom Resource Definitions (CRDs) are a special kind in Kubernetes. They provide

@ -103,7 +103,7 @@ helm init --canary-image
For developing on Tiller, it is sometimes more expedient to run Tiller locally
instead of packaging it into an image and running it in-cluster. You can do
this by telling the Helm client to us a local instance.
this by telling the Helm client to use a local instance.
```console
$ make build

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

@ -1,4 +1,4 @@
apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
# This uses a "fullname" template (see _helpers)
@ -19,6 +19,10 @@ metadata:
app.kubernetes.io/name: {{ template "nginx.name" . }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: {{ template "nginx.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
template:
metadata:
{{- if .Values.podAnnotations }}

@ -15,68 +15,68 @@ It will also set up any necessary local configuration.
Common actions from this point include:
- helm search: search for charts
- helm fetch: download a chart to your local directory to view
- helm install: upload the chart to Kubernetes
- helm list: list releases of charts
- helm search: Search for charts
- helm fetch: Download a chart to your local directory to view
- helm install: Upload the chart to Kubernetes
- 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_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
- $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
### Options
```
--debug enable verbose output
--debug Enable verbose output
-h, --help help for helm
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm completion](helm_completion.md) - Generate autocompletions script for the specified shell (bash or zsh)
* [helm create](helm_create.md) - create a new chart with the given name
* [helm delete](helm_delete.md) - given a release name, delete the release from Kubernetes
* [helm dependency](helm_dependency.md) - manage a chart's dependencies
* [helm fetch](helm_fetch.md) - download a chart from a repository and (optionally) unpack it in local directory
* [helm get](helm_get.md) - download a named release
* [helm history](helm_history.md) - fetch release history
* [helm home](helm_home.md) - displays the location of HELM_HOME
* [helm init](helm_init.md) - initialize Helm on both client and server
* [helm inspect](helm_inspect.md) - inspect a chart
* [helm install](helm_install.md) - install a chart archive
* [helm lint](helm_lint.md) - examines a chart for possible issues
* [helm list](helm_list.md) - list releases
* [helm package](helm_package.md) - package a chart directory into a chart archive
* [helm plugin](helm_plugin.md) - add, list, or remove Helm plugins
* [helm repo](helm_repo.md) - add, list, remove, update, and index chart repositories
* [helm reset](helm_reset.md) - uninstalls Tiller from a cluster
* [helm rollback](helm_rollback.md) - roll back a release to a previous revision
* [helm search](helm_search.md) - search for a keyword in charts
* [helm serve](helm_serve.md) - start a local http web server
* [helm status](helm_status.md) - displays the status of the named release
* [helm template](helm_template.md) - locally render templates
* [helm test](helm_test.md) - test a release
* [helm upgrade](helm_upgrade.md) - upgrade a release
* [helm verify](helm_verify.md) - verify that a chart at the given path has been signed and is valid
* [helm version](helm_version.md) - print the client/server version information
###### Auto generated by spf13/cobra on 4-Feb-2019
* [helm create](helm_create.md) - Create a new chart with the given name
* [helm delete](helm_delete.md) - Given a release name, delete the release from Kubernetes
* [helm dependency](helm_dependency.md) - Manage a chart's dependencies
* [helm fetch](helm_fetch.md) - Download a chart from a repository and (optionally) unpack it in local directory
* [helm get](helm_get.md) - Download a named release
* [helm history](helm_history.md) - Fetch release history
* [helm home](helm_home.md) - Displays the location of HELM_HOME
* [helm init](helm_init.md) - Initialize Helm on both client and server
* [helm inspect](helm_inspect.md) - Inspect a chart
* [helm install](helm_install.md) - Install a chart archive
* [helm lint](helm_lint.md) - Examines a chart for possible issues
* [helm list](helm_list.md) - List releases
* [helm package](helm_package.md) - Package a chart directory into a chart archive
* [helm plugin](helm_plugin.md) - Add, list, or remove Helm plugins
* [helm repo](helm_repo.md) - Add, list, remove, update, and index chart repositories
* [helm reset](helm_reset.md) - Uninstalls Tiller from a cluster
* [helm rollback](helm_rollback.md) - Rollback a release to a previous revision
* [helm search](helm_search.md) - Search for a keyword in charts
* [helm serve](helm_serve.md) - Start a local http web server
* [helm status](helm_status.md) - Displays the status of the named release
* [helm template](helm_template.md) - Locally render templates
* [helm test](helm_test.md) - Test a release
* [helm upgrade](helm_upgrade.md) - Upgrade a release
* [helm verify](helm_verify.md) - Verify that a chart at the given path has been signed and is valid
* [helm version](helm_version.md) - Print the client/server version information
###### Auto generated by spf13/cobra on 16-May-2019

@ -29,17 +29,17 @@ helm completion SHELL [flags]
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,12 +1,13 @@
## helm create
create a new chart with the given name
Create a new chart with the given name
### Synopsis
This command creates a chart directory along with the common files and
directories used in a chart.
directories used in a chart. It provides a basic example and is not
meant to cover all Kubernetes resources.
For example, 'helm create foo' will create a directory structure that looks
something like this:
@ -30,6 +31,10 @@ do not exist, Helm will attempt to create them as it goes. If the given
destination exists and there are files in that directory, conflicting files
will be overwritten, but other files will be left alone.
The chart that is created by invoking this command contains a Deployment, Ingress
and a Service. To use other Kubernetes resources with your chart, refer to
[The Chart Template Developer's Guide](https://helm.sh/docs/chart_template_guide).
```
helm create NAME [flags]
@ -39,23 +44,23 @@ helm create NAME [flags]
```
-h, --help help for create
-p, --starter string the named Helm starter scaffold
-p, --starter string The name or absolute path to Helm starter scaffold
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 18-Sep-2018
###### Auto generated by spf13/cobra on 7-Jul-2019

@ -1,6 +1,6 @@
## helm delete
given a release name, delete the release from Kubernetes
Given a release name, delete the release from Kubernetes
### Synopsis
@ -19,34 +19,34 @@ helm delete [flags] RELEASE_NAME [...]
### Options
```
--description string specify a description for the release
--dry-run simulate a delete
--description string Specify a description for the release
--dry-run Simulate a delete
-h, --help help for delete
--no-hooks prevent hooks from running during deletion
--purge remove the release from the store and make its name free for later use
--timeout int time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks) (default 300)
--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")
--tls-hostname string the server name used to verify the hostname on the returned certificates from the server
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote
--no-hooks Prevent hooks from running during deletion
--purge Remove the release from the store and make its name free for later use
--timeout int Time in seconds to wait for any individual Kubernetes operation (like Jobs for hooks) (default 300)
--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")
--tls-hostname string The server name used to verify the hostname on the returned certificates from the server
--tls-key string Path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify Enable TLS for request and verify remote
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 10-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,6 +1,6 @@
## helm dependency
manage a chart's dependencies
Manage a chart's dependencies
### Synopsis
@ -62,20 +62,20 @@ for this case.
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
* [helm dependency build](helm_dependency_build.md) - rebuild the charts/ directory based on the requirements.lock file
* [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
* [helm dependency build](helm_dependency_build.md) - Rebuild the charts/ directory based on the requirements.lock file
* [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 26-Mar-2019
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,6 +1,6 @@
## helm dependency build
rebuild the charts/ directory based on the requirements.lock file
Rebuild the charts/ directory based on the requirements.lock file
### Synopsis
@ -14,6 +14,7 @@ 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
```
@ -22,24 +23,24 @@ helm dependency build [flags] CHART
```
-h, --help help for build
--keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg")
--verify verify the packages against signatures
--keyring string Keyring containing public keys (default "~/.gnupg/pubring.gpg")
--verify Verify the packages against signatures
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm dependency](helm_dependency.md) - manage a chart's dependencies
* [helm dependency](helm_dependency.md) - Manage a chart's dependencies
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,6 +1,6 @@
## helm dependency list
list the dependencies for the given chart
List the dependencies for the given chart
### Synopsis
@ -27,17 +27,17 @@ helm dependency list [flags] CHART
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm dependency](helm_dependency.md) - manage a chart's dependencies
* [helm dependency](helm_dependency.md) - Manage a chart's dependencies
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,6 +1,6 @@
## helm dependency update
update charts/ based on the contents of requirements.yaml
Update charts/ based on the contents of requirements.yaml
### Synopsis
@ -27,25 +27,25 @@ helm dependency update [flags] CHART
```
-h, --help help for update
--keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg")
--skip-refresh do not refresh the local repository cache
--verify verify the packages against signatures
--keyring string Keyring containing public keys (default "~/.gnupg/pubring.gpg")
--skip-refresh Do not refresh the local repository cache
--verify Verify the packages against signatures
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm dependency](helm_dependency.md) - manage a chart's dependencies
* [helm dependency](helm_dependency.md) - Manage a chart's dependencies
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,6 +1,6 @@
## helm fetch
download a chart from a repository and (optionally) unpack it in local directory
Download a chart from a repository and (optionally) unpack it in local directory
### Synopsis
@ -26,37 +26,37 @@ helm fetch [flags] [chart URL | repo/chartname] [...]
### Options
```
--ca-file string verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string identify HTTPS client using this SSL certificate file
-d, --destination string location to write the chart. If this and tardir are specified, tardir is appended to this (default ".")
--devel use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
--ca-file string Verify certificates of HTTPS-enabled servers using this CA bundle
--cert-file string Identify HTTPS client using this SSL certificate file
-d, --destination string Location to write the chart. If this and tardir are specified, tardir is appended to this (default ".")
--devel Use development versions, too. Equivalent to version '>0.0.0-0'. If --version is set, this is ignored.
-h, --help help for fetch
--key-file string identify HTTPS client using this SSL key file
--keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg")
--password string chart repository password
--prov fetch the provenance file, but don't perform verification
--repo string chart repository url where to locate the requested chart
--untar if set to true, will untar the chart after downloading it
--untardir string if untar is specified, this flag specifies the name of the directory into which the chart is expanded (default ".")
--username string chart repository username
--verify verify the package against its signature
--version string specific version of a chart. Without this, the latest version is fetched
--key-file string Identify HTTPS client using this SSL key file
--keyring string Keyring containing public keys (default "~/.gnupg/pubring.gpg")
--password string Chart repository password
--prov Fetch the provenance file, but don't perform verification
--repo string Chart repository url where to locate the requested chart
--untar If set to true, will untar the chart after downloading it
--untardir string If untar is specified, this flag specifies the name of the directory into which the chart is expanded (default ".")
--username string Chart repository username
--verify Verify the package against its signature
--version string Specific version of a chart. Without this, the latest version is fetched
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
###### Auto generated by spf13/cobra on 1-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

@ -1,6 +1,6 @@
## helm get
download a named release
Download a named release
### Synopsis
@ -25,33 +25,34 @@ helm get [flags] RELEASE_NAME
```
-h, --help help for get
--revision int32 get the named release with revision
--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")
--tls-hostname string the server name used to verify the hostname on the returned certificates from the server
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote
--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")
--tls-hostname string The server name used to verify the hostname on the returned certificates from the server
--tls-key string Path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify Enable TLS for request and verify remote
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm](helm.md) - The Helm package manager for Kubernetes.
* [helm get hooks](helm_get_hooks.md) - download all hooks for a named release
* [helm get manifest](helm_get_manifest.md) - download the manifest for a named release
* [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
* [helm get hooks](helm_get_hooks.md) - Download all hooks for a named release
* [helm get manifest](helm_get_manifest.md) - Download the manifest for a named release
* [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 16-May-2019

@ -1,6 +1,6 @@
## helm get hooks
download all hooks for a named release
Download all hooks for a named release
### Synopsis
@ -18,29 +18,29 @@ helm get hooks [flags] RELEASE_NAME
```
-h, --help help for hooks
--revision int32 get the named release with revision
--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")
--tls-hostname string the server name used to verify the hostname on the returned certificates from the server
--tls-key string path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify enable TLS for request and verify remote
--revision int32 Get the named release with revision
--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")
--tls-hostname string The server name used to verify the hostname on the returned certificates from the server
--tls-key string Path to TLS key file (default "$HELM_HOME/key.pem")
--tls-verify Enable TLS for request and verify remote
```
### Options inherited from parent commands
```
--debug enable verbose output
--home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string address of Tiller. Overrides $HELM_HOST
--kube-context string name of the kubeconfig context to use
--kubeconfig string absolute path to the kubeconfig file to use
--tiller-connection-timeout int the duration (in seconds) Helm will wait to establish a connection to tiller (default 300)
--tiller-namespace string namespace of Tiller (default "kube-system")
--debug Enable verbose output
--home string Location of your Helm config. Overrides $HELM_HOME (default "~/.helm")
--host string Address of Tiller. Overrides $HELM_HOST
--kube-context string Name of the kubeconfig context to use
--kubeconfig string Absolute path of the kubeconfig file to be used
--tiller-connection-timeout int The duration (in seconds) Helm will wait to establish a connection to Tiller (default 300)
--tiller-namespace string Namespace of Tiller (default "kube-system")
```
### SEE ALSO
* [helm get](helm_get.md) - download a named release
* [helm get](helm_get.md) - Download a named release
###### Auto generated by spf13/cobra on 10-Aug-2018
###### Auto generated by spf13/cobra on 16-May-2019

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

Loading…
Cancel
Save