diff --git a/.gitignore b/.gitignore index 12b7ab7bc..b94f87b26 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ _dist/ _proto/*.pb.go bin/ rootfs/tiller +rootfs/rudder vendor/ *.exe .idea/ diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5811e2005..000000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: go - -go: - - '1.7.x' - -go_import_path: k8s.io/helm - -before_install: - - make bootstrap - -script: - - make test diff --git a/Makefile b/Makefile index d9d869a3b..2070fd4d0 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,10 @@ -DOCKER_REGISTRY ?= gcr.io -IMAGE_PREFIX ?= kubernetes-helm -SHORT_NAME ?= tiller -TARGETS = darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64 -DIST_DIRS = find * -type d -exec -APP = helm +DOCKER_REGISTRY ?= gcr.io +IMAGE_PREFIX ?= kubernetes-helm +SHORT_NAME ?= tiller +SHORT_NAME_RUDDER ?= rudder +TARGETS = darwin/amd64 linux/amd64 linux/386 linux/arm linux/arm64 linux/ppc64le windows/amd64 +DIST_DIRS = find * -type d -exec +APP = helm # go option GO ?= go @@ -66,6 +67,19 @@ docker-build: check-docker docker-binary docker build --rm -t ${IMAGE} rootfs docker tag ${IMAGE} ${MUTABLE_IMAGE} +.PHONY: docker-binary-rudder +docker-binary-rudder: BINDIR = ./rootfs +docker-binary-rudder: GOFLAGS += -a -installsuffix cgo +docker-binary-rudder: + GOOS=linux GOARCH=amd64 CGO_ENABLED=0 $(GO) build -o $(BINDIR)/rudder $(GOFLAGS) -tags '$(TAGS)' -ldflags '$(LDFLAGS)' k8s.io/helm/cmd/rudder + +.PHONY: docker-build-experimental +docker-build-experimental: check-docker docker-binary docker-binary-rudder + docker build --rm -t ${IMAGE} rootfs -f rootfs/Dockerfile.experimental + docker tag ${IMAGE} ${MUTABLE_IMAGE} + docker build --rm -t ${IMAGE_RUDDER} rootfs -f rootfs/Dockerfile.rudder + docker tag ${IMAGE_RUDDER} ${MUTABLE_IMAGE_RUDDER} + .PHONY: test test: build test: TESTFLAGS += -race -v @@ -74,6 +88,8 @@ test: test-unit .PHONY: test-unit test-unit: + @echo + @echo "==> Running unit tests <==" HELM_HOME=/no/such/dir $(GO) test $(GOFLAGS) -run $(TESTS) $(PKG) $(TESTFLAGS) .PHONY: test-style diff --git a/README.md b/README.md index 08351149e..4972e7b5b 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ Think of it like apt/yum/homebrew for Kubernetes. Binary downloads of the Helm client can be found at the following links: -- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.3.0-darwin-amd64.tar.gz) -- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.3.0-linux-amd64.tar.gz) -- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.3.0-linux-386.tar.gz) +- [OSX](https://kubernetes-helm.storage.googleapis.com/helm-v2.4.2-darwin-amd64.tar.gz) +- [Linux](https://kubernetes-helm.storage.googleapis.com/helm-v2.4.2-linux-amd64.tar.gz) +- [Linux 32-bit](https://kubernetes-helm.storage.googleapis.com/helm-v2.4.2-linux-386.tar.gz) Unpack the `helm` binary and add it to your PATH and you are good to go! macOS/[homebrew](https://brew.sh/) users can also use `brew install kubernetes-helm`. @@ -45,7 +45,6 @@ To rapidly get Helm up and running, start with the [Quick Start Guide](docs/quic See the [installation guide](docs/install.md) for more options, including installing pre-releases. - ## Docs Get started with the [Quick Start guide](docs/quickstart.md) or plunge into the [complete documentation](docs/index.md) diff --git a/_proto/Makefile b/_proto/Makefile index 078514a9c..39f72d441 100644 --- a/_proto/Makefile +++ b/_proto/Makefile @@ -20,6 +20,10 @@ services_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(services_ services_pbs = $(sort $(wildcard hapi/services/*.proto)) services_pkg = services +rudder_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(rudder_pkg),$(addprefix M,$(rudder_pbs)))) +rudder_pbs = $(sort $(wildcard hapi/rudder/*.proto)) +rudder_pkg = rudder + version_ias = $(subst $(space),$(comma),$(addsuffix =$(import_path)/$(version_pkg),$(addprefix M,$(version_pbs)))) version_pbs = $(sort $(wildcard hapi/version/*.proto)) version_pkg = version @@ -27,7 +31,7 @@ version_pkg = version google_deps = Mgoogle/protobuf/timestamp.proto=github.com/golang/protobuf/ptypes/timestamp,Mgoogle/protobuf/any.proto=github.com/golang/protobuf/ptypes/any .PHONY: all -all: chart release services version +all: chart release services rudder version .PHONY: chart chart: @@ -41,6 +45,10 @@ release: services: PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps),$(chart_ias),$(version_ias),$(release_ias):$(dst) $(services_pbs) +.PHONY: rudder +rudder: + PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps),$(chart_ias),$(version_ias),$(release_ias):$(dst) $(rudder_pbs) + .PHONY: version version: PATH=../bin:$(PATH) protoc --$(target)_out=plugins=$(plugins),$(google_deps):$(dst) $(version_pbs) diff --git a/_proto/hapi/release/test_run.proto b/_proto/hapi/release/test_run.proto index a441e729f..60734ae03 100644 --- a/_proto/hapi/release/test_run.proto +++ b/_proto/hapi/release/test_run.proto @@ -26,6 +26,7 @@ message TestRun { UNKNOWN = 0; SUCCESS = 1; FAILURE = 2; + RUNNING = 3; } string name = 1; diff --git a/_proto/hapi/rudder/rudder.proto b/_proto/hapi/rudder/rudder.proto new file mode 100644 index 000000000..3a37c9e99 --- /dev/null +++ b/_proto/hapi/rudder/rudder.proto @@ -0,0 +1,120 @@ +// Copyright 2017 The Kubernetes Authors All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package hapi.services.rudder; + +import "hapi/release/info.proto"; +import "hapi/release/release.proto"; + +option go_package = "rudder"; + +service ReleaseModuleService { + rpc Version(VersionReleaseRequest) returns (VersionReleaseResponse) { + } + + // InstallRelease requests installation of a chart as a new release. + rpc InstallRelease(InstallReleaseRequest) returns (InstallReleaseResponse) { + } + + // DeleteRelease requests deletion of a named release. + rpc DeleteRelease(DeleteReleaseRequest) returns (DeleteReleaseResponse) { + } + + // RollbackRelease rolls back a release to a previous version. + rpc RollbackRelease(RollbackReleaseRequest) returns (RollbackReleaseResponse) { + } + + // UpgradeRelease updates release content. + rpc UpgradeRelease(UpgradeReleaseRequest) returns (UpgradeReleaseResponse) { + } + + // ReleaseStatus retrieves release status. + rpc ReleaseStatus(ReleaseStatusRequest) returns (ReleaseStatusResponse) { + } +} + +message Result { + enum Status { + // No status set + UNKNOWN = 0; + // Operation was successful + SUCCESS = 1; + // Operation had no results (e.g. upgrade identical, rollback to same, delete non-existent) + UNCHANGED = 2; + // Operation failed + ERROR = 3; + } + string info = 1; + repeated string log = 2; +} + +message VersionReleaseRequest { +} + +message VersionReleaseResponse { + string name = 1; // The canonical name of the release module + string version = 2; // The version of the release module +} + +message InstallReleaseRequest { + hapi.release.Release release = 1; +} +message InstallReleaseResponse { + hapi.release.Release release = 1; + Result result = 2; +} + +message DeleteReleaseRequest { + hapi.release.Release release = 1; +} +message DeleteReleaseResponse { + hapi.release.Release release = 1; + Result result = 2; +} + +message UpgradeReleaseRequest{ + hapi.release.Release current = 1; + hapi.release.Release target = 2; + int64 Timeout = 3; + bool Wait = 4; + bool Recreate = 5; + bool Force = 6; +} +message UpgradeReleaseResponse{ + hapi.release.Release release = 1; + Result result = 2; +} + +message RollbackReleaseRequest{ + hapi.release.Release current = 1; + hapi.release.Release target = 2; + int64 Timeout = 3; + bool Wait = 4; + bool Recreate = 5; + bool Force = 6; +} +message RollbackReleaseResponse{ + hapi.release.Release release = 1; + Result result = 2; +} + +message ReleaseStatusRequest{ + hapi.release.Release release = 1; +} +message ReleaseStatusResponse{ + hapi.release.Release release = 1; + hapi.release.Info info = 2; +} diff --git a/_proto/hapi/services/tiller.proto b/_proto/hapi/services/tiller.proto index f16d68238..1fb6a86e9 100644 --- a/_proto/hapi/services/tiller.proto +++ b/_proto/hapi/services/tiller.proto @@ -20,6 +20,7 @@ import "hapi/chart/chart.proto"; import "hapi/chart/config.proto"; import "hapi/release/release.proto"; import "hapi/release/info.proto"; +import "hapi/release/test_run.proto"; import "hapi/release/status.proto"; import "hapi/version/version.proto"; @@ -206,6 +207,8 @@ message UpdateReleaseRequest { // ReuseValues will cause Tiller to reuse the values from the last release. // This is ignored if reset_values is set. bool reuse_values = 10; + // Force resource update through delete/recreate if needed. + bool force = 11; } // UpdateReleaseResponse is the response to an update request. @@ -229,6 +232,8 @@ message RollbackReleaseRequest { // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state // before marking the release as successful. It will wait for as long as timeout bool wait = 7; + // Force resource update through delete/recreate if needed. + bool force = 8; } // RollbackReleaseResponse is the response to an update request. @@ -327,4 +332,6 @@ message TestReleaseRequest { // TestReleaseResponse represents a message from executing a test message TestReleaseResponse { string msg = 1; + hapi.release.TestRun.Status status = 2; + } diff --git a/circle.yml b/circle.yml index 46b1bee3d..5b30c8be4 100644 --- a/circle.yml +++ b/circle.yml @@ -3,7 +3,7 @@ machine: - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0 environment: - GOVERSION: "1.7.5" + GOVERSION: "1.8.3" GOPATH: "${HOME}/.go_workspace" WORKDIR: "${GOPATH}/src/k8s.io/helm" PROJECT_NAME: "kubernetes-helm" diff --git a/cmd/helm/completion.go b/cmd/helm/completion.go index ddb8f06c7..b1cd04140 100644 --- a/cmd/helm/completion.go +++ b/cmd/helm/completion.go @@ -16,32 +16,214 @@ limitations under the License. package main import ( + "bytes" + "fmt" "io" "github.com/spf13/cobra" ) const completionDesc = ` -Generate bash autocompletions script for Helm. +Generate autocompletions script for Helm for the specified shell (bash or zsh). -This command can generate shell autocompletions. +This command can generate shell autocompletions. e.g. - $ helm completion + $ helm completion bash Can be sourced as such - $ source <(helm completion) + $ source <(helm completion bash) ` +var ( + completionShells = map[string]func(out io.Writer, cmd *cobra.Command) error{ + "bash": runCompletionBash, + "zsh": runCompletionZsh, + } +) + func newCompletionCmd(out io.Writer) *cobra.Command { + shells := []string{} + for s := range completionShells { + shells = append(shells, s) + } + cmd := &cobra.Command{ - Use: "completion", - Short: "Generate bash autocompletions script", - Long: completionDesc, - Hidden: false, - RunE: func(cmd *cobra.Command, _ []string) error { - return cmd.Root().GenBashCompletion(out) + Use: "completion SHELL", + Short: "Generate autocompletions script for the specified shell (bash or zsh)", + Long: completionDesc, + RunE: func(cmd *cobra.Command, args []string) error { + return runCompletion(out, cmd, args) }, + ValidArgs: shells, } + return cmd } + +func runCompletion(out io.Writer, cmd *cobra.Command, args []string) error { + if len(args) == 0 { + return fmt.Errorf("shell not specified") + } + if len(args) > 1 { + return fmt.Errorf("too many arguments, expected only the shell type") + } + run, found := completionShells[args[0]] + if !found { + return fmt.Errorf("unsupported shell type %q", args[0]) + } + + return run(out, cmd) +} + +func runCompletionBash(out io.Writer, cmd *cobra.Command) error { + return cmd.Root().GenBashCompletion(out) +} + +func runCompletionZsh(out io.Writer, cmd *cobra.Command) error { + zshInitialization := ` +__helm_bash_source() { + alias shopt=':' + alias _expand=_bash_expand + alias _complete=_bash_comp + emulate -L sh + setopt kshglob noshglob braceexpand + source "$@" +} +__helm_type() { + # -t is not supported by zsh + if [ "$1" == "-t" ]; then + shift + # fake Bash 4 to disable "complete -o nospace". Instead + # "compopt +-o nospace" is used in the code to toggle trailing + # spaces. We don't support that, but leave trailing spaces on + # all the time + if [ "$1" = "__helm_compopt" ]; then + echo builtin + return 0 + fi + fi + type "$@" +} +__helm_compgen() { + local completions w + completions=( $(compgen "$@") ) || return $? + # filter by given word as prefix + while [[ "$1" = -* && "$1" != -- ]]; do + shift + shift + done + if [[ "$1" == -- ]]; then + shift + fi + for w in "${completions[@]}"; do + if [[ "${w}" = "$1"* ]]; then + echo "${w}" + fi + done +} +__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 + # Remove colon-word prefix from COMPREPLY items + local colon_word=${1%${1##*:}} + local i=${#COMPREPLY[*]} + while [[ $((--i)) -ge 0 ]]; do + COMPREPLY[$i]=${COMPREPLY[$i]#"$colon_word"} + done + fi +} +__helm_get_comp_words_by_ref() { + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[${COMP_CWORD}-1]}" + words=("${COMP_WORDS[@]}") + cword=("${COMP_CWORD[@]}") +} +__helm_filedir() { + local RET OLD_IFS w qw + __debug "_filedir $@ cur=$cur" + if [[ "$1" = \~* ]]; then + # somehow does not work. Maybe, zsh does not call this at all + eval echo "$1" + return 0 + fi + OLD_IFS="$IFS" + IFS=$'\n' + if [ "$1" = "-d" ]; then + shift + RET=( $(compgen -d) ) + else + RET=( $(compgen -f) ) + fi + IFS="$OLD_IFS" + IFS="," __debug "RET=${RET[@]} len=${#RET[@]}" + for w in ${RET[@]}; do + if [[ ! "${w}" = "${cur}"* ]]; then + continue + fi + if eval "[[ \"\${w}\" = *.$1 || -d \"\${w}\" ]]"; then + qw="$(__helm_quote "${w}")" + if [ -d "${w}" ]; then + COMPREPLY+=("${qw}/") + else + COMPREPLY+=("${qw}") + fi + fi + done +} +__helm_quote() { + if [[ $1 == \'* || $1 == \"* ]]; then + # Leave out first character + printf %q "${1:1}" + else + printf %q "$1" + fi +} +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 + LWORD='\<' + RWORD='\>' +fi +__helm_convert_bash_to_zsh() { + sed \ + -e 's/declare -F/whence -w/' \ + -e 's/_get_comp_words_by_ref "\$@"/_get_comp_words_by_ref "\$*"/' \ + -e 's/local \([a-zA-Z0-9_]*\)=/local \1; \1=/' \ + -e 's/flags+=("\(--.*\)=")/flags+=("\1"); two_word_flags+=("\1")/' \ + -e 's/must_have_one_flag+=("\(--.*\)=")/must_have_one_flag+=("\1")/' \ + -e "s/${LWORD}_filedir${RWORD}/__helm_filedir/g" \ + -e "s/${LWORD}_get_comp_words_by_ref${RWORD}/__helm_get_comp_words_by_ref/g" \ + -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/\\\$(type${RWORD}/\$(__helm_type/g" \ + <<'BASH_COMPLETION_EOF' +` + out.Write([]byte(zshInitialization)) + + buf := new(bytes.Buffer) + cmd.Root().GenBashCompletion(buf) + out.Write(buf.Bytes()) + + zshTail := ` +BASH_COMPLETION_EOF +} +__helm_bash_source <(__helm_convert_bash_to_zsh) +` + out.Write([]byte(zshTail)) + return nil +} diff --git a/cmd/helm/create_test.go b/cmd/helm/create_test.go index a49d3cfa9..d45843eda 100644 --- a/cmd/helm/create_test.go +++ b/cmd/helm/create_test.go @@ -87,7 +87,7 @@ func TestCreateStarterCmd(t *testing.T) { if err != nil { t.Fatal(err) } - old := helmpath.Home(environment.DefaultHelmHome()) + old := helmpath.Home(environment.DefaultHelmHome) settings.Home = thome defer func() { settings.Home = old diff --git a/cmd/helm/delete.go b/cmd/helm/delete.go index 2390573d9..67ab4379c 100755 --- a/cmd/helm/delete.go +++ b/cmd/helm/delete.go @@ -52,12 +52,12 @@ func newDeleteCmd(c helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "delete [flags] RELEASE_NAME [...]", - Aliases: []string{"del"}, - SuggestFor: []string{"remove", "rm"}, - Short: "given a release name, delete the release from Kubernetes", - Long: deleteDesc, - PersistentPreRunE: setupConnection, + Use: "delete [flags] RELEASE_NAME [...]", + Aliases: []string{"del"}, + SuggestFor: []string{"remove", "rm"}, + Short: "given a release name, delete the release from Kubernetes", + Long: deleteDesc, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errors.New("command 'delete' requires a release name") diff --git a/cmd/helm/dependency_build.go b/cmd/helm/dependency_build.go index 3a92edd07..1168a2ed2 100644 --- a/cmd/helm/dependency_build.go +++ b/cmd/helm/dependency_build.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/downloader" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" ) @@ -77,7 +77,7 @@ func (d *dependencyBuildCmd) run() error { ChartPath: d.chartpath, HelmHome: d.helmhome, Keyring: d.keyring, - Getters: defaultgetters.Get(settings), + Getters: getter.All(settings), } if d.verify { man.Verify = downloader.VerifyIfPossible diff --git a/cmd/helm/dependency_update.go b/cmd/helm/dependency_update.go index 1838acb0a..188f21e88 100644 --- a/cmd/helm/dependency_update.go +++ b/cmd/helm/dependency_update.go @@ -21,7 +21,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/downloader" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" ) @@ -95,7 +95,7 @@ func (d *dependencyUpdateCmd) run() error { HelmHome: d.helmhome, Keyring: d.keyring, SkipUpdate: d.skipRefresh, - Getters: defaultgetters.Get(settings), + Getters: getter.All(settings), } if d.verify { man.Verify = downloader.VerifyIfPossible diff --git a/cmd/helm/fetch.go b/cmd/helm/fetch.go index 3dbf645cc..a639e7f02 100644 --- a/cmd/helm/fetch.go +++ b/cmd/helm/fetch.go @@ -26,7 +26,8 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/downloader" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" + "k8s.io/helm/pkg/repo" ) const fetchDesc = ` @@ -50,11 +51,18 @@ type fetchCmd struct { chartRef string destdir string version string + repoURL string verify bool verifyLater bool keyring string + certFile string + keyFile string + caFile string + + devel bool + out io.Writer } @@ -67,8 +75,14 @@ func newFetchCmd(out io.Writer) *cobra.Command { Long: fetchDesc, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { - return fmt.Errorf("This command needs at least one argument, url or repo/name of the chart.") + return fmt.Errorf("need at least one argument, url or repo/name of the chart") } + + if fch.version == "" && fch.devel { + debug("setting version to >0.0.0-a") + fch.version = ">0.0.0-a" + } + for i := 0; i < len(args); i++ { fch.chartRef = args[i] if err := fch.run(); err != nil { @@ -87,6 +101,11 @@ func newFetchCmd(out io.Writer) *cobra.Command { 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-a'. If --version is set, this is ignored.") return cmd } @@ -97,7 +116,7 @@ func (f *fetchCmd) run() error { Out: f.out, Keyring: f.keyring, Verify: downloader.VerifyNever, - Getters: defaultgetters.Get(settings), + Getters: getter.All(settings), } if f.verify { @@ -118,6 +137,14 @@ func (f *fetchCmd) run() error { defer os.RemoveAll(dest) } + if f.repoURL != "" { + chartURL, err := repo.FindChartInRepoURL(f.repoURL, f.chartRef, f.version, f.certFile, f.keyFile, f.caFile, getter.All(settings)) + if err != nil { + return err + } + f.chartRef = chartURL + } + saved, v, err := c.DownloadTo(f.chartRef, f.version, dest) if err != nil { return err diff --git a/cmd/helm/fetch_test.go b/cmd/helm/fetch_test.go index 8c1f2a74b..4d81ed38e 100644 --- a/cmd/helm/fetch_test.go +++ b/cmd/helm/fetch_test.go @@ -34,12 +34,14 @@ func TestFetchCmd(t *testing.T) { if err != nil { t.Fatal(err) } - old := helmpath.Home(environment.DefaultHelmHome()) + old := helmpath.Home(environment.DefaultHelmHome) settings.Home = hh defer func() { settings.Home = old os.RemoveAll(hh.String()) }() + srv := repotest.NewServer(hh.String()) + defer srv.Stop() // all flags will get "--home=TMDIR -d outdir" appended. tests := []struct { @@ -105,11 +107,34 @@ func TestFetchCmd(t *testing.T) { expectDir: true, expectVerify: true, }, + { + name: "Chart fetch using repo URL", + chart: "signtest", + expectFile: "./signtest-0.1.0.tgz", + flags: []string{"--repo", srv.URL()}, + }, + { + name: "Fail fetching non-existent chart on repo URL", + chart: "someChart", + flags: []string{"--repo", srv.URL()}, + failExpect: "Failed to fetch chart", + fail: true, + }, + { + name: "Specific version chart fetch using repo URL", + chart: "signtest", + expectFile: "./signtest-0.1.0.tgz", + flags: []string{"--repo", srv.URL(), "--version", "0.1.0"}, + }, + { + name: "Specific version chart fetch using repo URL", + chart: "signtest", + flags: []string{"--repo", srv.URL(), "--version", "0.2.0"}, + failExpect: "Failed to fetch chart version", + fail: true, + }, } - srv := repotest.NewServer(hh.String()) - defer srv.Stop() - if _, err := srv.CopyCharts("testdata/testcharts/*.tgz*"); err != nil { t.Fatal(err) } diff --git a/cmd/helm/get.go b/cmd/helm/get.go index b540d24fe..fc5871f46 100644 --- a/cmd/helm/get.go +++ b/cmd/helm/get.go @@ -54,10 +54,10 @@ func newGetCmd(client helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "get [flags] RELEASE_NAME", - Short: "download a named release", - Long: getHelp, - PersistentPreRunE: setupConnection, + Use: "get [flags] RELEASE_NAME", + Short: "download a named release", + Long: getHelp, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errReleaseRequired diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index ced336ac4..096a989f6 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -19,7 +19,6 @@ package main // import "k8s.io/helm/cmd/helm" import ( "errors" "fmt" - "io" "io/ioutil" "log" "os" @@ -33,30 +32,22 @@ import ( "k8s.io/helm/pkg/helm" helm_env "k8s.io/helm/pkg/helm/environment" - "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/helm/portforwarder" "k8s.io/helm/pkg/kube" tiller_env "k8s.io/helm/pkg/tiller/environment" "k8s.io/helm/pkg/tlsutil" ) -const ( - localRepoIndexFilePath = "index.yaml" -) - var ( tlsCaCertFile string // path to TLS CA certificate file tlsCertFile string // path to TLS certificate file tlsKeyFile string // path to TLS key file tlsVerify bool // enable TLS and verify remote certificates tlsEnable bool // enable TLS -) -var ( - kubeContext string - settings helm_env.EnvSettings - // TODO refactor out this global var + kubeContext string tillerTunnel *kube.Tunnel + settings helm_env.EnvSettings ) var globalUsage = `The Kubernetes package manager @@ -83,37 +74,58 @@ Environment: $KUBECONFIG set an alternative Kubernetes configuration file (default "~/.kube/config") ` -func newRootCmd(out io.Writer) *cobra.Command { - var helmHomeTemp string +func setFlagFromEnv(name, envar string, cmd *cobra.Command) { + if cmd.Flags().Changed(name) { + return + } + if v, ok := os.LookupEnv(envar); ok { + cmd.Flags().Set(name, v) + } +} + +func setFlagsFromEnv(flags map[string]string, cmd *cobra.Command) { + for name, envar := range flags { + setFlagFromEnv(name, envar, cmd) + } +} + +func addRootFlags(cmd *cobra.Command) { + pf := cmd.PersistentFlags() + pf.StringVar((*string)(&settings.Home), "home", helm_env.DefaultHelmHome, "location of your Helm config. Overrides $HELM_HOME") + pf.StringVar(&settings.TillerHost, "host", "", "address of tiller. Overrides $HELM_HOST") + pf.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use") + pf.BoolVar(&settings.Debug, "debug", false, "enable verbose output") + pf.StringVar(&settings.TillerNamespace, "tiller-namespace", tiller_env.DefaultTillerNamespace, "namespace of tiller") +} +func initRootFlags(cmd *cobra.Command) { + setFlagsFromEnv(map[string]string{ + "debug": helm_env.DebugEnvVar, + "home": helm_env.HomeEnvVar, + "host": helm_env.HostEnvVar, + "tiller-namespace": tiller_env.TillerNamespaceEnvVar, + }, cmd.Root()) + + tlsCaCertFile = os.ExpandEnv(tlsCaCertFile) + tlsCertFile = os.ExpandEnv(tlsCertFile) + tlsKeyFile = os.ExpandEnv(tlsKeyFile) +} + +func newRootCmd() *cobra.Command { cmd := &cobra.Command{ Use: "helm", Short: "The Helm package manager for Kubernetes.", Long: globalUsage, SilenceUsage: true, - PersistentPreRun: func(cmd *cobra.Command, args []string) { - tlsCaCertFile = os.ExpandEnv(tlsCaCertFile) - tlsCertFile = os.ExpandEnv(tlsCertFile) - tlsKeyFile = os.ExpandEnv(tlsKeyFile) + PersistentPreRun: func(cmd *cobra.Command, _ []string) { + initRootFlags(cmd) }, - PersistentPostRun: func(cmd *cobra.Command, args []string) { + PersistentPostRun: func(*cobra.Command, []string) { teardown() }, } - p := cmd.PersistentFlags() - p.StringVar(&helmHomeTemp, "home", helm_env.DefaultHelmHome(), "location of your Helm config. Overrides $HELM_HOME") - settings.Home = helmpath.Home(helmHomeTemp) - p.StringVar(&settings.TillerHost, "host", helm_env.DefaultHelmHost(), "address of tiller. Overrides $HELM_HOST") - p.StringVar(&kubeContext, "kube-context", "", "name of the kubeconfig context to use") - p.BoolVar(&settings.Debug, "debug", false, "enable verbose output") - p.StringVar(&settings.TillerNamespace, "tiller-namespace", tiller_env.GetTillerNamespace(), "namespace of tiller") - - if os.Getenv(helm_env.PluginDisableEnvVar) != "1" { - settings.PlugDirs = os.Getenv(helm_env.PluginEnvVar) - if settings.PlugDirs == "" { - settings.PlugDirs = settings.Home.Plugins() - } - } + addRootFlags(cmd) + out := cmd.OutOrStdout() cmd.AddCommand( // chart commands @@ -166,7 +178,7 @@ func init() { } func main() { - cmd := newRootCmd(os.Stdout) + cmd := newRootCmd() if err := cmd.Execute(); err != nil { os.Exit(1) } diff --git a/cmd/helm/helm_test.go b/cmd/helm/helm_test.go index 016b79d6b..d3078dae0 100644 --- a/cmd/helm/helm_test.go +++ b/cmd/helm/helm_test.go @@ -23,7 +23,9 @@ import ( "io/ioutil" "math/rand" "os" + "path/filepath" "regexp" + "sync" "testing" "github.com/golang/protobuf/ptypes/timestamp" @@ -122,8 +124,9 @@ func releaseMock(opts *releaseOptions) *release.Release { } type fakeReleaseClient struct { - rels []*release.Release - err error + rels []*release.Release + responses map[string]release.TestRun_Status + err error } var _ helm.Interface = &fakeReleaseClient{} @@ -198,7 +201,27 @@ func (c *fakeReleaseClient) ReleaseHistory(rlsName string, opts ...helm.HistoryO } func (c *fakeReleaseClient) RunReleaseTest(rlsName string, opts ...helm.ReleaseTestOption) (<-chan *rls.TestReleaseResponse, <-chan error) { - return nil, nil + + results := make(chan *rls.TestReleaseResponse) + errc := make(chan error, 1) + + go func() { + var wg sync.WaitGroup + for m, s := range c.responses { + wg.Add(1) + + go func(msg string, status release.TestRun_Status) { + defer wg.Done() + results <- &rls.TestReleaseResponse{Msg: msg, Status: status} + }(m, s) + } + + wg.Wait() + close(results) + close(errc) + }() + + return results, errc } func (c *fakeReleaseClient) Option(opt ...helm.Option) helm.Interface { @@ -299,7 +322,7 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error { } } - localRepoIndexFile := home.LocalRepository(localRepoIndexFilePath) + localRepoIndexFile := home.LocalRepository(localRepositoryIndexFile) if fi, err := os.Stat(localRepoIndexFile); err != nil { i := repo.NewIndexFile() if err := i.WriteFile(localRepoIndexFile, 0644); err != nil { @@ -315,3 +338,79 @@ func ensureTestHome(home helmpath.Home, t *testing.T) error { t.Logf("$HELM_HOME has been configured at %s.\n", settings.Home.String()) return nil } + +func TestRootCmd(t *testing.T) { + oldhome := os.Getenv("HELM_HOME") + defer os.Setenv("HELM_HOME", oldhome) + + tests := []struct { + name string + args []string + envars map[string]string + home string + }{ + { + name: "defaults", + args: []string{"home"}, + home: filepath.Join(os.Getenv("HOME"), "/.helm"), + }, + { + name: "with --home set", + args: []string{"--home", "/foo"}, + home: "/foo", + }, + { + name: "subcommands with --home set", + args: []string{"home", "--home", "/foo"}, + home: "/foo", + }, + { + name: "with $HELM_HOME set", + args: []string{"home"}, + envars: map[string]string{"HELM_HOME": "/bar"}, + home: "/bar", + }, + { + name: "subcommands with $HELM_HOME set", + args: []string{"home"}, + envars: map[string]string{"HELM_HOME": "/bar"}, + home: "/bar", + }, + { + name: "with $HELM_HOME and --home set", + args: []string{"home", "--home", "/foo"}, + envars: map[string]string{"HELM_HOME": "/bar"}, + home: "/foo", + }, + } + + // ensure not set locally + os.Unsetenv("HELM_HOME") + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + defer os.Unsetenv("HELM_HOME") + + for k, v := range tt.envars { + os.Setenv(k, v) + } + + cmd := newRootCmd() + cmd.SetOutput(ioutil.Discard) + cmd.SetArgs(tt.args) + cmd.Run = func(*cobra.Command, []string) {} + if err := cmd.Execute(); err != nil { + t.Errorf("unexpected error: %s", err) + } + + if settings.Home.String() != tt.home { + t.Errorf("expected home %q, got %q", tt.home, settings.Home) + } + homeFlag := cmd.Flag("home").Value.String() + homeFlag = os.ExpandEnv(homeFlag) + if homeFlag != tt.home { + t.Errorf("expected home %q, got %q", tt.home, homeFlag) + } + }) + } +} diff --git a/cmd/helm/history.go b/cmd/helm/history.go index 21c444dab..08f1656f5 100644 --- a/cmd/helm/history.go +++ b/cmd/helm/history.go @@ -56,11 +56,11 @@ func newHistoryCmd(c helm.Interface, w io.Writer) *cobra.Command { his := &historyCmd{out: w, helmc: c} cmd := &cobra.Command{ - Use: "history [flags] RELEASE_NAME", - Long: historyHelp, - Short: "fetch release history", - Aliases: []string{"hist"}, - PersistentPreRunE: setupConnection, + Use: "history [flags] RELEASE_NAME", + Long: historyHelp, + Short: "fetch release history", + Aliases: []string{"hist"}, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { switch { case len(args) == 0: diff --git a/cmd/helm/home.go b/cmd/helm/home.go index 25809b521..e96edd7a1 100644 --- a/cmd/helm/home.go +++ b/cmd/helm/home.go @@ -35,7 +35,7 @@ func newHomeCmd(out io.Writer) *cobra.Command { Long: longHomeHelp, Run: func(cmd *cobra.Command, args []string) { h := settings.Home - fmt.Fprintf(out, "%s\n", h) + fmt.Fprintln(out, h) if settings.Debug { fmt.Fprintf(out, "Repository: %s\n", h.Repository()) fmt.Fprintf(out, "RepositoryFile: %s\n", h.RepositoryFile()) @@ -47,6 +47,5 @@ func newHomeCmd(out io.Writer) *cobra.Command { } }, } - return cmd } diff --git a/cmd/helm/init.go b/cmd/helm/init.go index a2bec7a65..dab6e0bc7 100644 --- a/cmd/helm/init.go +++ b/cmd/helm/init.go @@ -27,7 +27,7 @@ import ( "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/helm/cmd/helm/installer" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/repo" ) @@ -54,8 +54,9 @@ To dump a manifest containing the Tiller deployment YAML, combine the ` const ( - stableRepository = "stable" - localRepository = "local" + stableRepository = "stable" + localRepository = "local" + localRepositoryIndexFile = "index.yaml" ) var ( @@ -66,17 +67,18 @@ var ( ) type initCmd struct { - image string - clientOnly bool - canary bool - upgrade bool - namespace string - dryRun bool - skipRefresh bool - out io.Writer - home helmpath.Home - opts installer.Options - kubeClient internalclientset.Interface + image string + clientOnly bool + canary bool + upgrade bool + namespace string + dryRun bool + skipRefresh bool + out io.Writer + home helmpath.Home + opts installer.Options + kubeClient internalclientset.Interface + serviceAccount string } func newInitCmd(out io.Writer) *cobra.Command { @@ -116,6 +118,7 @@ func newInitCmd(out io.Writer) *cobra.Command { 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") return cmd } @@ -154,6 +157,7 @@ func (i *initCmd) run() error { i.opts.Namespace = i.namespace i.opts.UseCanary = i.canary i.opts.ImageSpec = i.image + i.opts.ServiceAccount = i.serviceAccount if settings.Debug { writeYAMLManifest := func(apiVersion, kind, body string, first, last bool) error { @@ -293,7 +297,7 @@ func ensureDefaultRepos(home helmpath.Home, out io.Writer, skipRefresh bool) err if err != nil { return err } - lr, err := initLocalRepo(home.LocalRepository(localRepoIndexFilePath), home.CacheIndex("local")) + lr, err := initLocalRepo(home.LocalRepository(localRepositoryIndexFile), home.CacheIndex("local")) if err != nil { return err } @@ -314,7 +318,7 @@ func initStableRepo(cacheFile string, skipRefresh bool) (*repo.Entry, error) { URL: stableRepositoryURL, Cache: cacheFile, } - r, err := repo.NewChartRepository(&c, defaultgetters.Get(settings)) + r, err := repo.NewChartRepository(&c, getter.All(settings)) if err != nil { return nil, err } diff --git a/cmd/helm/init_test.go b/cmd/helm/init_test.go index 4d82c297b..874d9d4dc 100644 --- a/cmd/helm/init_test.go +++ b/cmd/helm/init_test.go @@ -214,7 +214,7 @@ func TestEnsureHome(t *testing.T) { t.Errorf("%s should not be a directory", fi) } - if fi, err := os.Stat(hh.LocalRepository(localRepoIndexFilePath)); err != nil { + if fi, err := os.Stat(hh.LocalRepository(localRepositoryIndexFile)); err != nil { t.Errorf("%s", err) } else if fi.IsDir() { t.Errorf("%s should not be a directory", fi) diff --git a/cmd/helm/inspect.go b/cmd/helm/inspect.go index c3d539b3a..6369b5ddc 100644 --- a/cmd/helm/inspect.go +++ b/cmd/helm/inspect.go @@ -50,6 +50,11 @@ type inspectCmd struct { keyring string out io.Writer version string + repoURL string + + certFile string + keyFile string + caFile string } const ( @@ -72,7 +77,8 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(args[0], insp.version, insp.verify, insp.keyring) + cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err } @@ -90,7 +96,8 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(args[0], insp.version, insp.verify, insp.keyring) + cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err } @@ -108,7 +115,8 @@ func newInspectCmd(out io.Writer) *cobra.Command { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(args[0], insp.version, insp.verify, insp.keyring) + cp, err := locateChartPath(insp.repoURL, args[0], insp.version, insp.verify, insp.keyring, + insp.certFile, insp.keyFile, insp.caFile) if err != nil { return err } @@ -136,6 +144,30 @@ func newInspectCmd(out io.Writer) *cobra.Command { valuesSubCmd.Flags().StringVar(&insp.version, verflag, "", verdesc) chartSubCmd.Flags().StringVar(&insp.version, verflag, "", verdesc) + repoURL := "repo" + repoURLdesc := "chart repository url where to locate the requested chart" + inspectCommand.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc) + valuesSubCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc) + chartSubCmd.Flags().StringVar(&insp.repoURL, repoURL, "", repoURLdesc) + + certFile := "cert-file" + certFiledesc := "verify certificates of HTTPS-enabled servers using this CA bundle" + inspectCommand.Flags().StringVar(&insp.certFile, certFile, "", certFiledesc) + valuesSubCmd.Flags().StringVar(&insp.certFile, certFile, "", certFiledesc) + chartSubCmd.Flags().StringVar(&insp.certFile, certFile, "", certFiledesc) + + keyFile := "key-file" + keyFiledesc := "identify HTTPS client using this SSL key file" + inspectCommand.Flags().StringVar(&insp.keyFile, keyFile, "", keyFiledesc) + valuesSubCmd.Flags().StringVar(&insp.keyFile, keyFile, "", keyFiledesc) + chartSubCmd.Flags().StringVar(&insp.keyFile, keyFile, "", keyFiledesc) + + caFile := "ca-file" + caFiledesc := "chart repository url where to locate the requested chart" + inspectCommand.Flags().StringVar(&insp.caFile, caFile, "", caFiledesc) + valuesSubCmd.Flags().StringVar(&insp.caFile, caFile, "", caFiledesc) + chartSubCmd.Flags().StringVar(&insp.caFile, caFile, "", caFiledesc) + inspectCommand.AddCommand(valuesSubCmd) inspectCommand.AddCommand(chartSubCmd) diff --git a/cmd/helm/install.go b/cmd/helm/install.go index d01ee514a..f438ebe04 100644 --- a/cmd/helm/install.go +++ b/cmd/helm/install.go @@ -34,11 +34,12 @@ import ( "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/downloader" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm" "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/strvals" ) @@ -115,6 +116,12 @@ type installCmd struct { version string timeout int64 wait bool + repoURL string + devel bool + + certFile string + keyFile string + caFile string } type valueFiles []string @@ -141,15 +148,23 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "install [CHART]", - Short: "install a chart archive", - Long: installDesc, - PersistentPreRunE: setupConnection, + Use: "install [CHART]", + Short: "install a chart archive", + Long: installDesc, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if err := checkArgsLength(len(args), "chart name"); err != nil { return err } - cp, err := locateChartPath(args[0], inst.version, inst.verify, inst.keyring) + + debug("Original chart version: %q", inst.version) + if inst.version == "" && inst.devel { + debug("setting version to >0.0.0-a") + inst.version = ">0.0.0-a" + } + + cp, err := locateChartPath(inst.repoURL, args[0], inst.version, inst.verify, inst.keyring, + inst.certFile, inst.keyFile, inst.caFile) if err != nil { return err } @@ -173,6 +188,11 @@ func newInstallCmd(c helm.Interface, out io.Writer) *cobra.Command { 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.StringVar(&inst.repoURL, "repo", "", "chart repository url 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-a'. If --version is set, this is ignored.") return cmd } @@ -206,7 +226,14 @@ func (i *installCmd) run() error { } if req, err := chartutil.LoadRequirements(chartRequested); err == nil { - checkDependencies(chartRequested, req, i.out) + // If checkDependencies returns an error, we have unfullfilled dependencies. + // As of Helm 2.4.0, this is treated as a stopping condition: + // https://github.com/kubernetes/helm/issues/2209 + if err := checkDependencies(chartRequested, req, i.out); err != nil { + return prettyError(err) + } + } else if err != chartutil.ErrRequirementsNotFound { + return fmt.Errorf("cannot load requirements: %v", err) } res, err := i.client.InstallReleaseFromChart( @@ -326,7 +353,8 @@ func (i *installCmd) printRelease(rel *release.Release) { // - URL // // If 'verify' is true, this will attempt to also verify the chart. -func locateChartPath(name, version string, verify bool, keyring string) (string, error) { +func locateChartPath(repoURL, name, version string, verify bool, keyring, + certFile, keyFile, caFile string) (string, error) { name = strings.TrimSpace(name) version = strings.TrimSpace(version) if fi, err := os.Stat(name); err == nil { @@ -357,11 +385,19 @@ func locateChartPath(name, version string, verify bool, keyring string) (string, HelmHome: settings.Home, Out: os.Stdout, Keyring: keyring, - Getters: defaultgetters.Get(settings), + Getters: getter.All(settings), } if verify { dl.Verify = downloader.VerifyAlways } + if repoURL != "" { + chartURL, err := repo.FindChartInRepoURL(repoURL, name, version, + certFile, keyFile, caFile, getter.All(settings)) + if err != nil { + return "", err + } + name = chartURL + } filename, _, err := dl.DownloadTo(name, version, ".") if err == nil { @@ -398,7 +434,9 @@ func defaultNamespace() string { return "default" } -func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements, out io.Writer) { +func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements, out io.Writer) error { + missing := []string{} + deps := ch.GetDependencies() for _, r := range reqs.Dependencies { found := false @@ -409,7 +447,12 @@ func checkDependencies(ch *chart.Chart, reqs *chartutil.Requirements, out io.Wri } } if !found { - fmt.Fprintf(out, "Warning: %s is in requirements.yaml but not in the charts/ directory!\n", r.Name) + missing = append(missing, r.Name) } } + + if len(missing) > 0 { + return fmt.Errorf("found in requirements.yaml, but missing in charts/ directory: %s", strings.Join(missing, ", ")) + } + return nil } diff --git a/cmd/helm/install_test.go b/cmd/helm/install_test.go index ee0ffe0e8..b2e47a1dd 100644 --- a/cmd/helm/install_test.go +++ b/cmd/helm/install_test.go @@ -134,9 +134,15 @@ func TestInstall(t *testing.T) { }, // Install, chart with missing dependencies in /charts { - name: "install chart with missing dependencies", - args: []string{"testdata/testcharts/chart-missing-deps"}, - expected: "Warning: reqsubchart2 is in requirements.yaml but not in the charts/ directory!", + name: "install chart with missing dependencies", + args: []string{"testdata/testcharts/chart-missing-deps"}, + err: true, + }, + // Install, chart with bad requirements.yaml in /charts + { + name: "install chart with bad requirements.yaml", + args: []string{"testdata/testcharts/chart-bad-requirements"}, + err: true, }, } diff --git a/cmd/helm/installer/install.go b/cmd/helm/installer/install.go index 90e832b07..9d6a6aeb1 100644 --- a/cmd/helm/installer/install.go +++ b/cmd/helm/installer/install.go @@ -58,6 +58,7 @@ func Upgrade(client internalclientset.Interface, opts *Options) error { } 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 _, err := client.Extensions().Deployments(opts.Namespace).Update(obj); err != nil { return err } @@ -131,6 +132,7 @@ func generateDeployment(opts *Options) *extensions.Deployment { Labels: labels, }, Spec: api.PodSpec{ + ServiceAccountName: opts.ServiceAccount, Containers: []api.Container{ { Name: "tiller", @@ -164,6 +166,9 @@ func generateDeployment(opts *Options) *extensions.Deployment { }, }, }, + NodeSelector: map[string]string{ + "beta.kubernetes.io/os": "linux", + }, SecurityContext: &api.PodSecurityContext{ HostNetwork: opts.EnableHostNetwork, }, diff --git a/cmd/helm/installer/install_test.go b/cmd/helm/installer/install_test.go index 63f8419b3..22af4d61e 100644 --- a/cmd/helm/installer/install_test.go +++ b/cmd/helm/installer/install_test.go @@ -70,6 +70,34 @@ func TestDeploymentManifest(t *testing.T) { } } +func TestDeploymentManifestForServiceAccount(t *testing.T) { + tests := []struct { + name string + image string + canary bool + expect string + imagePullPolicy api.PullPolicy + serviceAccount string + }{ + {"withSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", "service-account"}, + {"withoutSA", "", false, "gcr.io/kubernetes-helm/tiller:latest", "IfNotPresent", ""}, + } + for _, tt := range tests { + o, err := DeploymentManifest(&Options{Namespace: api.NamespaceDefault, ImageSpec: tt.image, UseCanary: tt.canary, ServiceAccount: tt.serviceAccount}) + if err != nil { + t.Fatalf("%s: error %q", tt.name, err) + } + + var d extensions.Deployment + if err := yaml.Unmarshal([]byte(o), &d); err != nil { + t.Fatalf("%s: error %q", tt.name, err) + } + if got := d.Spec.Template.Spec.ServiceAccountName; got != tt.serviceAccount { + t.Errorf("%s: expected service account value %q, got %q", tt.name, tt.serviceAccount, got) + } + } +} + func TestDeploymentManifest_WithTLS(t *testing.T) { tests := []struct { opts Options @@ -301,11 +329,12 @@ func TestInstall_canary(t *testing.T) { func TestUpgrade(t *testing.T) { image := "gcr.io/kubernetes-helm/tiller:v2.0.0" - + serviceAccount := "newServiceAccount" existingDeployment := deployment(&Options{ - Namespace: api.NamespaceDefault, - ImageSpec: "imageToReplace", - UseCanary: false, + Namespace: api.NamespaceDefault, + ImageSpec: "imageToReplace", + ServiceAccount: "serviceAccountToReplace", + UseCanary: false, }) existingService := service(api.NamespaceDefault) @@ -319,13 +348,17 @@ func TestUpgrade(t *testing.T) { if i != image { t.Errorf("expected image = '%s', got '%s'", image, i) } + sa := obj.Spec.Template.Spec.ServiceAccountName + if sa != serviceAccount { + t.Errorf("expected serviceAccountName = '%s', got '%s'", serviceAccount, sa) + } return true, obj, nil }) fc.AddReactor("get", "services", func(action testcore.Action) (bool, runtime.Object, error) { return true, existingService, nil }) - opts := &Options{Namespace: api.NamespaceDefault, ImageSpec: image} + opts := &Options{Namespace: api.NamespaceDefault, ImageSpec: image, ServiceAccount: serviceAccount} if err := Upgrade(fc, opts); err != nil { t.Errorf("unexpected error: %#+v", err) } diff --git a/cmd/helm/installer/options.go b/cmd/helm/installer/options.go index eb9519f5d..6fb804a46 100644 --- a/cmd/helm/installer/options.go +++ b/cmd/helm/installer/options.go @@ -43,6 +43,9 @@ type Options struct { // Namespace is the kubernetes namespace to use to deploy tiller. Namespace string + // ServiceAccount is the Kubernetes service account to add to tiller + ServiceAccount string + // ImageSpec indentifies the image tiller will use when deployed. // // Valid if and only if UseCanary is false. diff --git a/cmd/helm/list.go b/cmd/helm/list.go index 0919a1c3b..391e83e20 100644 --- a/cmd/helm/list.go +++ b/cmd/helm/list.go @@ -82,11 +82,11 @@ func newListCmd(client helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "list [flags] [FILTER]", - Short: "list releases", - Long: listHelp, - Aliases: []string{"ls"}, - PersistentPreRunE: setupConnection, + Use: "list [flags] [FILTER]", + Short: "list releases", + Long: listHelp, + Aliases: []string{"ls"}, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if len(args) > 0 { list.filter = strings.Join(args, " ") diff --git a/cmd/helm/load_plugins.go b/cmd/helm/load_plugins.go index af4e2f2bf..ee773cb4b 100644 --- a/cmd/helm/load_plugins.go +++ b/cmd/helm/load_plugins.go @@ -25,19 +25,10 @@ import ( "github.com/spf13/cobra" - "k8s.io/helm/pkg/helm/helmpath" + helm_env "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/plugin" ) -const pluginEnvVar = "HELM_PLUGIN" - -func pluginDirs(home helmpath.Home) string { - if dirs := os.Getenv(pluginEnvVar); dirs != "" { - return dirs - } - return home.Plugins() -} - // loadPlugins loads plugins into the command list. // // This follows a different pattern than the other commands because it has @@ -46,16 +37,25 @@ func pluginDirs(home helmpath.Home) string { func loadPlugins(baseCmd *cobra.Command, out io.Writer) { // If HELM_NO_PLUGINS is set to 1, do not load plugins. - if settings.PlugDirs == "" { + if os.Getenv(helm_env.PluginDisableEnvVar) == "1" { return } - found, err := findPlugins(settings.PlugDirs) + found, err := findPlugins(settings.PluginDirs()) if err != nil { fmt.Fprintf(os.Stderr, "failed to load plugins: %s", err) return } + processParent := func(cmd *cobra.Command, args []string) ([]string, error) { + k, u := manuallyProcessArgs(args) + if err := cmd.Parent().ParseFlags(k); err != nil { + return nil, err + } + initRootFlags(cmd) + return u, nil + } + // Now we create commands for all of these. for _, plug := range found { plug := plug @@ -69,9 +69,8 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { Short: md.Usage, Long: md.Description, RunE: func(cmd *cobra.Command, args []string) error { - - k, u := manuallyProcessArgs(args) - if err := cmd.Parent().ParseFlags(k); err != nil { + u, err := processParent(cmd, args) + if err != nil { return err } @@ -99,10 +98,9 @@ func loadPlugins(baseCmd *cobra.Command, out io.Writer) { } if md.UseTunnel { - c.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { + c.PreRunE = func(cmd *cobra.Command, args []string) error { // Parse the parent flag, but not the local flags. - k, _ := manuallyProcessArgs(args) - if err := c.Parent().ParseFlags(k); err != nil { + if _, err := processParent(cmd, args); err != nil { return err } return setupConnection(cmd, args) diff --git a/cmd/helm/package.go b/cmd/helm/package.go index a2da2c338..e5ab3643d 100644 --- a/cmd/helm/package.go +++ b/cmd/helm/package.go @@ -30,6 +30,8 @@ import ( "golang.org/x/crypto/ssh/terminal" "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/downloader" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/provenance" @@ -48,13 +50,14 @@ Versioned chart archives are used by Helm package repositories. ` type packageCmd struct { - save bool - sign bool - path string - key string - keyring string - version string - destination string + save bool + sign bool + path string + key string + keyring string + version string + destination string + dependencyUpdate bool out io.Writer home helmpath.Home @@ -72,7 +75,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { pkg.home = settings.Home if len(args) == 0 { - return fmt.Errorf("This command needs at least one argument, the path to the chart.") + return fmt.Errorf("need at least one argument, the path to the chart") } if pkg.sign { if pkg.key == "" { @@ -99,6 +102,7 @@ func newPackageCmd(out io.Writer) *cobra.Command { 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.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 } @@ -109,6 +113,21 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { return err } + if p.dependencyUpdate { + downloadManager := &downloader.Manager{ + Out: p.out, + ChartPath: path, + HelmHome: settings.Home, + Keyring: p.keyring, + Getters: getter.All(settings), + Debug: settings.Debug, + } + + if err := downloadManager.Update(); err != nil { + return err + } + } + ch, err := chartutil.LoadDir(path) if err != nil { return err @@ -127,7 +146,13 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { } if reqs, err := chartutil.LoadRequirements(ch); err == nil { - checkDependencies(ch, reqs, p.out) + if err := checkDependencies(ch, reqs, p.out); err != nil { + return err + } + } else { + if err != chartutil.ErrRequirementsNotFound { + return err + } } var dest string @@ -144,7 +169,9 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { name, err := chartutil.Save(ch, dest) if err == nil { - debug("Saved %s to current directory\n", name) + fmt.Fprintf(p.out, "Successfully packaged chart and saved it to: %s\n", name) + } else { + return fmt.Errorf("Failed to save: %s", err) } // Save to $HELM_HOME/local directory. This is second, because we don't want @@ -154,7 +181,7 @@ func (p *packageCmd) run(cmd *cobra.Command, args []string) error { if err := repo.AddChartToLocalRepo(ch, lr); err != nil { return err } - debug("Saved %s to %s\n", name, lr) + debug("Successfully saved %s to %s\n", name, lr) } if p.sign { diff --git a/cmd/helm/package_test.go b/cmd/helm/package_test.go index 1d25260d4..2a0f6d9f5 100644 --- a/cmd/helm/package_test.go +++ b/cmd/helm/package_test.go @@ -64,7 +64,7 @@ func TestPackage(t *testing.T) { name: "package without chart path", args: []string{}, flags: map[string]string{}, - expect: "This command needs at least one argument, the path to the chart.", + expect: "need at least one argument, the path to the chart", err: true, }, { @@ -118,8 +118,8 @@ func TestPackage(t *testing.T) { { name: "package testdata/testcharts/chart-missing-deps", args: []string{"testdata/testcharts/chart-missing-deps"}, - expect: "Warning: reqsubchart2 is in requirements.yaml but not in the charts/ directory!\n", hasfile: "chart-missing-deps-0.1.0.tgz", + err: true, }, } diff --git a/cmd/helm/plugin.go b/cmd/helm/plugin.go index 37d9205b7..47e1b361c 100644 --- a/cmd/helm/plugin.go +++ b/cmd/helm/plugin.go @@ -41,6 +41,7 @@ func newPluginCmd(out io.Writer) *cobra.Command { newPluginInstallCmd(out), newPluginListCmd(out), newPluginRemoveCmd(out), + newPluginUpdateCmd(out), ) return cmd } diff --git a/cmd/helm/plugin_list.go b/cmd/helm/plugin_list.go index 0bf9acbc8..e7618f38a 100644 --- a/cmd/helm/plugin_list.go +++ b/cmd/helm/plugin_list.go @@ -44,11 +44,8 @@ func newPluginListCmd(out io.Writer) *cobra.Command { } func (pcmd *pluginListCmd) run() error { - plugdirs := pluginDirs(pcmd.home) - - debug("pluginDirs: %s", plugdirs) - - plugins, err := findPlugins(plugdirs) + debug("pluginDirs: %s", settings.PluginDirs()) + plugins, err := findPlugins(settings.PluginDirs()) if err != nil { return err } diff --git a/cmd/helm/plugin_remove.go b/cmd/helm/plugin_remove.go index da2041f89..83bc863bf 100644 --- a/cmd/helm/plugin_remove.go +++ b/cmd/helm/plugin_remove.go @@ -16,9 +16,11 @@ limitations under the License. package main import ( + "errors" "fmt" "io" "os" + "strings" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/plugin" @@ -48,8 +50,8 @@ func newPluginRemoveCmd(out io.Writer) *cobra.Command { } func (pcmd *pluginRemoveCmd) complete(args []string) error { - if err := checkArgsLength(len(args), "plugin"); err != nil { - return err + if len(args) == 0 { + return errors.New("please provide plugin name to remove") } pcmd.names = args pcmd.home = settings.Home @@ -57,21 +59,26 @@ func (pcmd *pluginRemoveCmd) complete(args []string) error { } func (pcmd *pluginRemoveCmd) run() error { - plugdirs := pluginDirs(pcmd.home) - debug("loading installed plugins from %s", plugdirs) - plugins, err := findPlugins(plugdirs) + debug("loading installed plugins from %s", settings.PluginDirs()) + plugins, err := findPlugins(settings.PluginDirs()) if err != nil { return err } - + var errorPlugins []string for _, name := range pcmd.names { if found := findPlugin(plugins, name); found != nil { if err := removePlugin(found, pcmd.home); err != nil { - return err + errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to remove plugin %s, got error (%v)", name, err)) + } else { + fmt.Fprintf(pcmd.out, "Removed plugin: %s\n", name) } - fmt.Fprintf(pcmd.out, "Removed plugin: %s\n", name) + } else { + errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name)) } } + if len(errorPlugins) > 0 { + return fmt.Errorf(strings.Join(errorPlugins, "\n")) + } return nil } diff --git a/cmd/helm/plugin_test.go b/cmd/helm/plugin_test.go index 095de7063..77ef00c5d 100644 --- a/cmd/helm/plugin_test.go +++ b/cmd/helm/plugin_test.go @@ -23,6 +23,7 @@ import ( "strings" "testing" + helm_env "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/plugin" @@ -71,7 +72,6 @@ func TestLoadPlugins(t *testing.T) { settings.Home = old }() hh := settings.Home - settings.PlugDirs = hh.Plugins() out := bytes.NewBuffer(nil) cmd := &cobra.Command{} @@ -139,12 +139,11 @@ func TestLoadPlugins(t *testing.T) { func TestLoadPlugins_HelmNoPlugins(t *testing.T) { // Set helm home to point to testdata old := settings.Home - oldPlugDirs := settings.PlugDirs settings.Home = "testdata/helmhome" - settings.PlugDirs = "" + os.Setenv(helm_env.PluginDisableEnvVar, "1") defer func() { settings.Home = old - settings.PlugDirs = oldPlugDirs + os.Unsetenv(helm_env.PluginDisableEnvVar) }() out := bytes.NewBuffer(nil) @@ -161,7 +160,6 @@ func TestSetupEnv(t *testing.T) { name := "pequod" settings.Home = helmpath.Home("testdata/helmhome") base := filepath.Join(settings.Home.Plugins(), name) - settings.PlugDirs = settings.Home.Plugins() settings.Debug = true defer func() { settings.Debug = false diff --git a/cmd/helm/plugin_update.go b/cmd/helm/plugin_update.go new file mode 100644 index 000000000..2e19a2175 --- /dev/null +++ b/cmd/helm/plugin_update.go @@ -0,0 +1,113 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "errors" + "fmt" + "io" + "path/filepath" + "strings" + + "k8s.io/helm/pkg/helm/helmpath" + "k8s.io/helm/pkg/plugin" + "k8s.io/helm/pkg/plugin/installer" + + "github.com/spf13/cobra" +) + +type pluginUpdateCmd struct { + names []string + home helmpath.Home + out io.Writer +} + +func newPluginUpdateCmd(out io.Writer) *cobra.Command { + pcmd := &pluginUpdateCmd{out: out} + cmd := &cobra.Command{ + Use: "update ...", + Short: "update one or more Helm plugins", + PreRunE: func(cmd *cobra.Command, args []string) error { + return pcmd.complete(args) + }, + RunE: func(cmd *cobra.Command, args []string) error { + return pcmd.run() + }, + } + return cmd +} + +func (pcmd *pluginUpdateCmd) complete(args []string) error { + if len(args) == 0 { + return errors.New("please provide plugin name to update") + } + pcmd.names = args + pcmd.home = settings.Home + return nil +} + +func (pcmd *pluginUpdateCmd) run() error { + installer.Debug = settings.Debug + debug("loading installed plugins from %s", settings.PluginDirs()) + plugins, err := findPlugins(settings.PluginDirs()) + if err != nil { + return err + } + var errorPlugins []string + + for _, name := range pcmd.names { + if found := findPlugin(plugins, name); found != nil { + if err := updatePlugin(found, pcmd.home); err != nil { + errorPlugins = append(errorPlugins, fmt.Sprintf("Failed to update plugin %s, got error (%v)", name, err)) + } else { + fmt.Fprintf(pcmd.out, "Updated plugin: %s\n", name) + } + } else { + errorPlugins = append(errorPlugins, fmt.Sprintf("Plugin: %s not found", name)) + } + } + if len(errorPlugins) > 0 { + return fmt.Errorf(strings.Join(errorPlugins, "\n")) + } + return nil +} + +func updatePlugin(p *plugin.Plugin, home helmpath.Home) error { + exactLocation, err := filepath.EvalSymlinks(p.Dir) + if err != nil { + return err + } + absExactLocation, err := filepath.Abs(exactLocation) + if err != nil { + return err + } + + i, err := installer.FindSource(absExactLocation, home) + if err != nil { + return err + } + if err := installer.Update(i); err != nil { + return err + } + + debug("loading plugin from %s", i.Path()) + updatedPlugin, err := plugin.LoadDir(i.Path()) + if err != nil { + return err + } + + return runHook(updatedPlugin, plugin.Update, home) +} diff --git a/cmd/helm/release_testing.go b/cmd/helm/release_testing.go index 76a8f40e3..1aadd4de3 100644 --- a/cmd/helm/release_testing.go +++ b/cmd/helm/release_testing.go @@ -23,6 +23,7 @@ import ( "github.com/spf13/cobra" "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" ) const releaseTestDesc = ` @@ -47,10 +48,10 @@ func newReleaseTestCmd(c helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "test [RELEASE]", - Short: "test a release", - Long: releaseTestDesc, - PersistentPreRunE: setupConnection, + Use: "test [RELEASE]", + Short: "test a release", + Long: releaseTestDesc, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if err := checkArgsLength(len(args), "release name"); err != nil { return err @@ -75,16 +76,35 @@ func (t *releaseTestCmd) run() (err error) { helm.ReleaseTestTimeout(t.timeout), helm.ReleaseTestCleanup(t.cleanup), ) + testErr := &testErr{} for { select { case err := <-errc: + if prettyError(err) == nil && testErr.failed > 0 { + return testErr.Error() + } return prettyError(err) case res, ok := <-c: if !ok { break } + + if res.Status == release.TestRun_FAILURE { + testErr.failed++ + } + fmt.Fprintf(t.out, res.Msg+"\n") + } } + +} + +type testErr struct { + failed int +} + +func (err *testErr) Error() error { + return fmt.Errorf("%v test(s) failed", err.failed) } diff --git a/cmd/helm/release_testing_test.go b/cmd/helm/release_testing_test.go new file mode 100644 index 000000000..2c7a5867a --- /dev/null +++ b/cmd/helm/release_testing_test.go @@ -0,0 +1,105 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "bytes" + "testing" + + "k8s.io/helm/pkg/proto/hapi/release" +) + +func TestReleaseTesting(t *testing.T) { + tests := []struct { + name string + args []string + flags []string + responses map[string]release.TestRun_Status + fail bool + }{ + { + name: "basic test", + args: []string{"example-release"}, + flags: []string{}, + responses: map[string]release.TestRun_Status{"PASSED: green lights everywhere": release.TestRun_SUCCESS}, + fail: false, + }, + { + name: "test failure", + args: []string{"example-fail"}, + flags: []string{}, + responses: map[string]release.TestRun_Status{"FAILURE: red lights everywhere": release.TestRun_FAILURE}, + fail: true, + }, + { + name: "test unknown", + args: []string{"example-unknown"}, + flags: []string{}, + responses: map[string]release.TestRun_Status{"UNKNOWN: yellow lights everywhere": release.TestRun_UNKNOWN}, + fail: false, + }, + { + name: "test error", + args: []string{"example-error"}, + flags: []string{}, + responses: map[string]release.TestRun_Status{"ERROR: yellow lights everywhere": release.TestRun_FAILURE}, + fail: true, + }, + { + name: "test running", + args: []string{"example-running"}, + flags: []string{}, + responses: map[string]release.TestRun_Status{"RUNNING: things are happpeningggg": release.TestRun_RUNNING}, + fail: false, + }, + { + name: "multiple tests example", + args: []string{"example-suite"}, + flags: []string{}, + responses: map[string]release.TestRun_Status{ + "RUNNING: things are happpeningggg": release.TestRun_RUNNING, + "PASSED: party time": release.TestRun_SUCCESS, + "RUNNING: things are happening again": release.TestRun_RUNNING, + "FAILURE: good thing u checked :)": release.TestRun_FAILURE, + "RUNNING: things are happpeningggg yet again": release.TestRun_RUNNING, + "PASSED: feel free to party again": release.TestRun_SUCCESS}, + fail: true, + }, + } + + for _, tt := range tests { + c := &fakeReleaseClient{responses: tt.responses} + + buf := bytes.NewBuffer(nil) + cmd := newReleaseTestCmd(c, buf) + cmd.ParseFlags(tt.flags) + + err := cmd.RunE(cmd, tt.args) + if err == nil && tt.fail { + t.Errorf("%q did not fail but should have failed", tt.name) + } + + if err != nil { + if tt.fail { + continue + } else { + t.Errorf("%q reported error: %s", tt.name, err) + } + } + + } +} diff --git a/cmd/helm/repo_add.go b/cmd/helm/repo_add.go index 92bf61107..1bdbfddb1 100644 --- a/cmd/helm/repo_add.go +++ b/cmd/helm/repo_add.go @@ -22,7 +22,7 @@ import ( "github.com/spf13/cobra" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/repo" ) @@ -85,7 +85,7 @@ func addRepository(name, url string, home helmpath.Home, certFile, keyFile, caFi } if noUpdate && f.Has(name) { - return fmt.Errorf("The repository name you provided (%s) already exists. Please specify a different name.", name) + return fmt.Errorf("repository name (%s) already exists, please specify a different name", name) } cif := home.CacheIndex(name) @@ -98,7 +98,7 @@ func addRepository(name, url string, home helmpath.Home, certFile, keyFile, caFi CAFile: caFile, } - r, err := repo.NewChartRepository(&c, defaultgetters.Get(settings)) + r, err := repo.NewChartRepository(&c, getter.All(settings)) if err != nil { return err } diff --git a/cmd/helm/repo_update.go b/cmd/helm/repo_update.go index d45633497..5e710a267 100644 --- a/cmd/helm/repo_update.go +++ b/cmd/helm/repo_update.go @@ -24,7 +24,7 @@ import ( "github.com/spf13/cobra" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/repo" ) @@ -55,7 +55,7 @@ func newRepoUpdateCmd(out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "update", Aliases: []string{"up"}, - Short: "update information on available charts in the 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 @@ -76,7 +76,7 @@ func (u *repoUpdateCmd) run() error { } var repos []*repo.ChartRepository for _, cfg := range f.Repositories { - r, err := repo.NewChartRepository(cfg, defaultgetters.Get(settings)) + r, err := repo.NewChartRepository(cfg, getter.All(settings)) if err != nil { return err } diff --git a/cmd/helm/repo_update_test.go b/cmd/helm/repo_update_test.go index ecd23543d..17eaed60b 100644 --- a/cmd/helm/repo_update_test.go +++ b/cmd/helm/repo_update_test.go @@ -23,7 +23,7 @@ import ( "strings" "testing" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/repo" "k8s.io/helm/pkg/repo/repotest" @@ -85,7 +85,7 @@ func TestUpdateCharts(t *testing.T) { Name: "charts", URL: ts.URL(), Cache: hh.CacheIndex("charts"), - }, defaultgetters.Get(settings)) + }, getter.All(settings)) if err != nil { t.Error(err) } diff --git a/cmd/helm/reset.go b/cmd/helm/reset.go index c37e3d687..7e040ca9e 100644 --- a/cmd/helm/reset.go +++ b/cmd/helm/reset.go @@ -54,10 +54,15 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "reset", - Short: "uninstalls Tiller from a cluster", - Long: resetDesc, - PersistentPreRunE: setupConnection, + Use: "reset", + Short: "uninstalls Tiller from a cluster", + Long: resetDesc, + PreRunE: func(cmd *cobra.Command, args []string) error { + if err := setupConnection(cmd, args); !d.force && err != nil { + return err + } + return nil + }, RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 0 { return errors.New("This command does not accept arguments") @@ -72,7 +77,7 @@ func newResetCmd(client helm.Interface, out io.Writer) *cobra.Command { } f := cmd.Flags() - f.BoolVarP(&d.force, "force", "f", false, "forces Tiller uninstall even if there are releases installed") + f.BoolVarP(&d.force, "force", "f", false, "forces Tiller uninstall even if there are releases installed, or if tiller is not in ready state") f.BoolVar(&d.removeHelmHome, "remove-helm-home", false, "if set deletes $HELM_HOME") return cmd @@ -91,12 +96,12 @@ func (d *resetCmd) run() error { res, err := d.client.ListReleases( helm.ReleaseListStatuses([]release.Status_Code{release.Status_DEPLOYED}), ) - if err != nil { + if !d.force && err != nil { return prettyError(err) } - if len(res.Releases) > 0 && !d.force { - return fmt.Errorf("There are still %d deployed releases (Tip: use --force).", len(res.Releases)) + if !d.force && res != nil && len(res.Releases) > 0 { + return fmt.Errorf("there are still %d deployed releases (Tip: use --force)", len(res.Releases)) } if err := installer.Uninstall(d.kubeClient, &installer.Options{Namespace: d.namespace}); err != nil { diff --git a/cmd/helm/reset_test.go b/cmd/helm/reset_test.go index 855b4c5bd..5df8f2234 100644 --- a/cmd/helm/reset_test.go +++ b/cmd/helm/reset_test.go @@ -120,7 +120,7 @@ func TestReset_deployedReleases(t *testing.T) { namespace: api.NamespaceDefault, } err = cmd.run() - expected := "There are still 1 deployed releases (Tip: use --force)" + expected := "there are still 1 deployed releases (Tip: use --force)" if !strings.Contains(err.Error(), expected) { t.Errorf("unexpected error: %v", err) } diff --git a/cmd/helm/rollback.go b/cmd/helm/rollback.go index bcc780c1d..95c27e2a5 100644 --- a/cmd/helm/rollback.go +++ b/cmd/helm/rollback.go @@ -30,7 +30,7 @@ const rollbackDesc = ` This command rolls back a release to a previous revision. The first argument of the rollback command is the name of a release, and the -second is a revision (version) number. To see revision numbers, run +second is a revision (version) number. To see revision numbers, run 'helm history RELEASE'. ` @@ -39,6 +39,7 @@ type rollbackCmd struct { revision int32 dryRun bool recreate bool + force bool disableHooks bool out io.Writer client helm.Interface @@ -53,10 +54,10 @@ 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", - Long: rollbackDesc, - PersistentPreRunE: setupConnection, + Use: "rollback [flags] [RELEASE] [REVISION]", + Short: "roll back a release to a previous revision", + Long: rollbackDesc, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if err := checkArgsLength(len(args), "release name", "revision number"); err != nil { return err @@ -78,6 +79,7 @@ func newRollbackCmd(c helm.Interface, out io.Writer) *cobra.Command { f := cmd.Flags() 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") @@ -90,6 +92,7 @@ func (r *rollbackCmd) run() error { r.name, helm.RollbackDryRun(r.dryRun), helm.RollbackRecreate(r.recreate), + helm.RollbackForce(r.force), helm.RollbackDisableHooks(r.disableHooks), helm.RollbackVersion(r.revision), helm.RollbackTimeout(r.timeout), diff --git a/cmd/helm/serve.go b/cmd/helm/serve.go index 028af9751..1c9a772fe 100644 --- a/cmd/helm/serve.go +++ b/cmd/helm/serve.go @@ -55,19 +55,29 @@ func newServeCmd(out io.Writer) *cobra.Command { Use: "serve", Short: "start a local http web server", Long: serveDesc, + PreRunE: func(cmd *cobra.Command, args []string) error { + return srv.complete(args) + }, RunE: func(cmd *cobra.Command, args []string) error { return srv.run() }, } f := cmd.Flags() - f.StringVar(&srv.repoPath, "repo-path", settings.Home.LocalRepository(), "local directory path from which to serve charts") + 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 } +func (s *serveCmd) complete(args []string) error { + if s.repoPath == "" { + s.repoPath = settings.Home.LocalRepository() + } + return nil +} + func (s *serveCmd) run() error { repoPath, err := filepath.Abs(s.repoPath) if err != nil { diff --git a/cmd/helm/status.go b/cmd/helm/status.go index b635e6186..36269c4b1 100644 --- a/cmd/helm/status.go +++ b/cmd/helm/status.go @@ -57,10 +57,10 @@ 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", - Long: statusHelp, - PersistentPreRunE: setupConnection, + Use: "status [flags] RELEASE_NAME", + Short: "displays the status of the named release", + Long: statusHelp, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if len(args) == 0 { return errReleaseRequired diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/.helmignore b/cmd/helm/testdata/testcharts/chart-bad-requirements/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml new file mode 100644 index 000000000..02be4c013 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/Chart.yaml @@ -0,0 +1,3 @@ +description: A Helm chart for Kubernetes +name: chart-missing-deps +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/.helmignore b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/.helmignore new file mode 100644 index 000000000..f0c131944 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/.helmignore @@ -0,0 +1,21 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml new file mode 100644 index 000000000..c3813bc8c --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/Chart.yaml @@ -0,0 +1,3 @@ +description: A Helm chart for Kubernetes +name: reqsubchart +version: 0.1.0 diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/values.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/values.yaml new file mode 100644 index 000000000..0f0b63f2a --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/charts/reqsubchart/values.yaml @@ -0,0 +1,4 @@ +# Default values for reqsubchart. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/requirements.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/requirements.yaml new file mode 100644 index 000000000..10c4d6dcb --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/requirements.yaml @@ -0,0 +1,4 @@ +dependencies: + - name: reqsubchart + version: 0.1.0 + repository: "https://example.com/charts" diff --git a/cmd/helm/testdata/testcharts/chart-bad-requirements/values.yaml b/cmd/helm/testdata/testcharts/chart-bad-requirements/values.yaml new file mode 100644 index 000000000..d57f76b07 --- /dev/null +++ b/cmd/helm/testdata/testcharts/chart-bad-requirements/values.yaml @@ -0,0 +1,4 @@ +# Default values for reqtest. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name: value diff --git a/cmd/helm/upgrade.go b/cmd/helm/upgrade.go index ea8d667a1..3be680a9f 100644 --- a/cmd/helm/upgrade.go +++ b/cmd/helm/upgrade.go @@ -62,6 +62,7 @@ type upgradeCmd struct { client helm.Interface dryRun bool recreate bool + force bool disableHooks bool valueFiles valueFiles values []string @@ -74,6 +75,12 @@ type upgradeCmd struct { resetValues bool reuseValues bool wait bool + repoURL string + devel bool + + certFile string + keyFile string + caFile string } func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { @@ -84,15 +91,20 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { } cmd := &cobra.Command{ - Use: "upgrade [RELEASE] [CHART]", - Short: "upgrade a release", - Long: upgradeDesc, - PersistentPreRunE: setupConnection, + Use: "upgrade [RELEASE] [CHART]", + Short: "upgrade a release", + Long: upgradeDesc, + PreRunE: setupConnection, RunE: func(cmd *cobra.Command, args []string) error { if err := checkArgsLength(len(args), "release name", "chart path"); err != nil { return err } + if upgrade.version == "" && upgrade.devel { + debug("setting version to >0.0.0-a") + upgrade.version = ">0.0.0-a" + } + upgrade.release = args[0] upgrade.chart = args[1] upgrade.client = ensureHelmClient(upgrade.client) @@ -105,6 +117,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { f.VarP(&upgrade.valueFiles, "values", "f", "specify values in a YAML file (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.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") @@ -117,6 +130,11 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { 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 new values. 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.StringVar(&upgrade.repoURL, "repo", "", "chart repository url 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-a'. If --version is set, this is ignored.") f.MarkDeprecated("disable-hooks", "use --no-hooks instead") @@ -124,7 +142,7 @@ func newUpgradeCmd(client helm.Interface, out io.Writer) *cobra.Command { } func (u *upgradeCmd) run() error { - chartPath, err := locateChartPath(u.chart, u.version, u.verify, u.keyring) + chartPath, err := locateChartPath(u.repoURL, u.chart, u.version, u.verify, u.keyring, u.certFile, u.keyFile, u.caFile) if err != nil { return err } @@ -166,8 +184,14 @@ func (u *upgradeCmd) run() error { // Check chart requirements to make sure all dependencies are present in /charts if ch, err := chartutil.Load(chartPath); err == nil { if req, err := chartutil.LoadRequirements(ch); err == nil { - checkDependencies(ch, req, u.out) + if err := checkDependencies(ch, req, u.out); err != nil { + return err + } + } else if err != chartutil.ErrRequirementsNotFound { + return fmt.Errorf("cannot load requirements: %v", err) } + } else { + return prettyError(err) } resp, err := u.client.UpdateRelease( @@ -176,6 +200,7 @@ func (u *upgradeCmd) run() error { helm.UpdateValueOverrides(rawVals), helm.UpgradeDryRun(u.dryRun), helm.UpgradeRecreate(u.recreate), + helm.UpgradeForce(u.force), helm.UpgradeDisableHooks(u.disableHooks), helm.UpgradeTimeout(u.timeout), helm.ResetValues(u.resetValues), diff --git a/cmd/helm/upgrade_test.go b/cmd/helm/upgrade_test.go index a913016e2..d9bff45bb 100644 --- a/cmd/helm/upgrade_test.go +++ b/cmd/helm/upgrade_test.go @@ -82,6 +82,7 @@ func TestUpgradeCmd(t *testing.T) { originalDepsPath := filepath.Join("testdata/testcharts/reqtest") missingDepsPath := filepath.Join("testdata/testcharts/chart-missing-deps") + badDepsPath := filepath.Join("testdata/testcharts/chart-bad-requirements") var ch3 *chart.Chart ch3, err = chartutil.Load(originalDepsPath) if err != nil { @@ -138,10 +139,16 @@ func TestUpgradeCmd(t *testing.T) { expected: "Release \"crazy-bunny\" has been upgraded. Happy Helming!\n", }, { - name: "upgrade a release with missing dependencies", - args: []string{"bonkers-bunny", missingDepsPath}, - resp: releaseMock(&releaseOptions{name: "bonkers-bunny", version: 1, chart: ch3}), - expected: "Warning: reqsubchart2 is in requirements.yaml but not in the charts/ directory!", + name: "upgrade a release with missing dependencies", + args: []string{"bonkers-bunny", missingDepsPath}, + resp: releaseMock(&releaseOptions{name: "bonkers-bunny", version: 1, chart: ch3}), + err: true, + }, + { + name: "upgrade a release with bad dependencies", + args: []string{"bonkers-bunny", badDepsPath}, + resp: releaseMock(&releaseOptions{name: "bonkers-bunny", version: 1, chart: ch3}), + err: true, }, } diff --git a/cmd/rudder/rudder.go b/cmd/rudder/rudder.go new file mode 100644 index 000000000..e1ba4736a --- /dev/null +++ b/cmd/rudder/rudder.go @@ -0,0 +1,139 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "bytes" + "fmt" + "net" + + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + + "k8s.io/helm/pkg/kube" + rudderAPI "k8s.io/helm/pkg/proto/hapi/rudder" + "k8s.io/helm/pkg/rudder" + "k8s.io/helm/pkg/tiller" + "k8s.io/helm/pkg/version" +) + +var kubeClient *kube.Client +var clientset internalclientset.Interface + +func main() { + var err error + kubeClient = kube.New(nil) + clientset, err = kubeClient.ClientSet() + if err != nil { + grpclog.Fatalf("Cannot initialize Kubernetes connection: %s", err) + } + + lis, err := net.Listen("tcp", fmt.Sprintf(":%d", rudder.GrpcPort)) + if err != nil { + grpclog.Fatalf("failed to listen: %v", err) + } + grpcServer := grpc.NewServer() + rudderAPI.RegisterReleaseModuleServiceServer(grpcServer, &ReleaseModuleServiceServer{}) + + grpclog.Print("Server starting") + grpcServer.Serve(lis) + grpclog.Print("Server started") +} + +// ReleaseModuleServiceServer provides implementation for rudderAPI.ReleaseModuleServiceServer +type ReleaseModuleServiceServer struct{} + +// Version returns Rudder version based on helm version +func (r *ReleaseModuleServiceServer) Version(ctx context.Context, in *rudderAPI.VersionReleaseRequest) (*rudderAPI.VersionReleaseResponse, error) { + grpclog.Print("version") + return &rudderAPI.VersionReleaseResponse{ + Name: "helm-rudder-native", + Version: version.Version, + }, nil +} + +// InstallRelease creates a release using kubeClient.Create +func (r *ReleaseModuleServiceServer) InstallRelease(ctx context.Context, in *rudderAPI.InstallReleaseRequest) (*rudderAPI.InstallReleaseResponse, error) { + grpclog.Print("install") + b := bytes.NewBufferString(in.Release.Manifest) + err := kubeClient.Create(in.Release.Namespace, b, 500, false) + if err != nil { + grpclog.Printf("error when creating release: %v", err) + } + return &rudderAPI.InstallReleaseResponse{}, err +} + +// DeleteRelease deletes a provided release +func (r *ReleaseModuleServiceServer) DeleteRelease(ctx context.Context, in *rudderAPI.DeleteReleaseRequest) (*rudderAPI.DeleteReleaseResponse, error) { + grpclog.Print("delete") + + resp := &rudderAPI.DeleteReleaseResponse{} + rel := in.Release + vs, err := tiller.GetVersionSet(clientset.Discovery()) + if err != nil { + return resp, fmt.Errorf("Could not get apiVersions from Kubernetes: %v", err) + } + + kept, errs := tiller.DeleteRelease(rel, vs, kubeClient) + rel.Manifest = kept + + allErrors := "" + for _, e := range errs { + allErrors = allErrors + "\n" + e.Error() + } + + if len(allErrors) > 0 { + err = fmt.Errorf(allErrors) + } + + return &rudderAPI.DeleteReleaseResponse{ + Release: rel, + }, err +} + +// RollbackRelease rolls back the release +func (r *ReleaseModuleServiceServer) RollbackRelease(ctx context.Context, in *rudderAPI.RollbackReleaseRequest) (*rudderAPI.RollbackReleaseResponse, error) { + grpclog.Print("rollback") + c := bytes.NewBufferString(in.Current.Manifest) + t := bytes.NewBufferString(in.Target.Manifest) + err := kubeClient.Update(in.Target.Namespace, c, t, in.Force, in.Recreate, in.Timeout, in.Wait) + return &rudderAPI.RollbackReleaseResponse{}, err +} + +// UpgradeRelease upgrades manifests using kubernetes client +func (r *ReleaseModuleServiceServer) UpgradeRelease(ctx context.Context, in *rudderAPI.UpgradeReleaseRequest) (*rudderAPI.UpgradeReleaseResponse, error) { + grpclog.Print("upgrade") + c := bytes.NewBufferString(in.Current.Manifest) + t := bytes.NewBufferString(in.Target.Manifest) + err := kubeClient.Update(in.Target.Namespace, c, t, in.Force, in.Recreate, in.Timeout, in.Wait) + // upgrade response object should be changed to include status + return &rudderAPI.UpgradeReleaseResponse{}, err +} + +// ReleaseStatus retrieves release status +func (r *ReleaseModuleServiceServer) ReleaseStatus(ctx context.Context, in *rudderAPI.ReleaseStatusRequest) (*rudderAPI.ReleaseStatusResponse, error) { + grpclog.Print("status") + + resp, err := kubeClient.Get(in.Release.Namespace, bytes.NewBufferString(in.Release.Manifest)) + in.Release.Info.Status.Resources = resp + return &rudderAPI.ReleaseStatusResponse{ + Release: in.Release, + Info: in.Release.Info, + }, err +} diff --git a/cmd/tiller/tiller.go b/cmd/tiller/tiller.go index 2f193936e..6abd35ec9 100644 --- a/cmd/tiller/tiller.go +++ b/cmd/tiller/tiller.go @@ -29,6 +29,7 @@ import ( goprom "github.com/grpc-ecosystem/go-grpc-prometheus" "github.com/spf13/cobra" + "github.com/spf13/pflag" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -69,12 +70,15 @@ var rootServer *grpc.Server // Any changes to env should be done before rootServer.Serve() is called. var env = environment.New() +var logger *log.Logger + var ( - grpcAddr = ":44134" - probeAddr = ":44135" - traceAddr = ":44136" - enableTracing = false - store = storageConfigMap + grpcAddr = ":44134" + probeAddr = ":44135" + traceAddr = ":44136" + enableTracing = false + store = storageConfigMap + remoteReleaseModules = false ) var ( @@ -92,63 +96,83 @@ Tiller is the server for Helm. It provides in-cluster resource management. By default, Tiller listens for gRPC connections on port 44134. ` -var rootCommand = &cobra.Command{ - Use: "tiller", - Short: "The Kubernetes Helm server.", - Long: globalUsage, - Run: start, +func addFlags(flags *pflag.FlagSet) { + flags.StringVarP(&grpcAddr, "listen", "l", ":44134", "address:port to listen on") + flags.StringVar(&store, "storage", storageConfigMap, "storage driver to use. One of 'configmap' or 'memory'") + flags.BoolVar(&enableTracing, "trace", false, "enable rpc tracing") + flags.BoolVar(&remoteReleaseModules, "experimental-release", false, "enable experimental release modules") + + flags.BoolVar(&tlsEnable, "tls", tlsEnableEnvVarDefault(), "enable TLS") + flags.BoolVar(&tlsVerify, "tls-verify", tlsVerifyEnvVarDefault(), "enable TLS and verify remote certificate") + flags.StringVar(&keyFile, "tls-key", tlsDefaultsFromEnv("tls-key"), "path to TLS private key file") + flags.StringVar(&certFile, "tls-cert", tlsDefaultsFromEnv("tls-cert"), "path to TLS certificate file") + flags.StringVar(&caCertFile, "tls-ca-cert", tlsDefaultsFromEnv("tls-ca-cert"), "trust certificates signed by this CA") } -func init() { - log.SetFlags(log.Flags() | log.Lshortfile) +func initLog() { + if enableTracing { + log.SetFlags(log.Lshortfile) + } + logger = newLogger("main") } func main() { - p := rootCommand.PersistentFlags() - p.StringVarP(&grpcAddr, "listen", "l", ":44134", "address:port to listen on") - p.StringVar(&store, "storage", storageConfigMap, "storage driver to use. One of 'configmap' or 'memory'") - p.BoolVar(&enableTracing, "trace", false, "enable rpc tracing") - - p.BoolVar(&tlsEnable, "tls", tlsEnableEnvVarDefault(), "enable TLS") - p.BoolVar(&tlsVerify, "tls-verify", tlsVerifyEnvVarDefault(), "enable TLS and verify remote certificate") - p.StringVar(&keyFile, "tls-key", tlsDefaultsFromEnv("tls-key"), "path to TLS private key file") - p.StringVar(&certFile, "tls-cert", tlsDefaultsFromEnv("tls-cert"), "path to TLS certificate file") - p.StringVar(&caCertFile, "tls-ca-cert", tlsDefaultsFromEnv("tls-ca-cert"), "trust certificates signed by this CA") - - if err := rootCommand.Execute(); err != nil { - fmt.Fprint(os.Stderr, err) - os.Exit(1) + root := &cobra.Command{ + Use: "tiller", + Short: "The Kubernetes Helm server.", + Long: globalUsage, + Run: start, + PreRun: func(_ *cobra.Command, _ []string) { + initLog() + }, + } + addFlags(root.Flags()) + + if err := root.Execute(); err != nil { + logger.Fatal(err) } } +func newLogger(prefix string) *log.Logger { + if len(prefix) > 0 { + prefix = fmt.Sprintf("[%s] ", prefix) + } + return log.New(os.Stderr, prefix, log.Flags()) +} + func start(c *cobra.Command, args []string) { clientset, err := kube.New(nil).ClientSet() if err != nil { - fmt.Fprintf(os.Stderr, "Cannot initialize Kubernetes connection: %s\n", err) - os.Exit(1) + logger.Fatalf("Cannot initialize Kubernetes connection: %s", err) } switch store { case storageMemory: env.Releases = storage.Init(driver.NewMemory()) case storageConfigMap: - env.Releases = storage.Init(driver.NewConfigMaps(clientset.Core().ConfigMaps(namespace()))) + cfgmaps := driver.NewConfigMaps(clientset.Core().ConfigMaps(namespace())) + cfgmaps.Log = newLogger("storage/driver").Printf + + env.Releases = storage.Init(cfgmaps) + env.Releases.Log = newLogger("storage").Printf } + kubeClient := kube.New(nil) + kubeClient.Log = newLogger("kube").Printf + env.KubeClient = kubeClient + if tlsEnable || tlsVerify { opts := tlsutil.Options{CertFile: certFile, KeyFile: keyFile} if tlsVerify { opts.CaCertFile = caCertFile } - } var opts []grpc.ServerOption if tlsEnable || tlsVerify { cfg, err := tlsutil.ServerConfig(tlsOptions()) if err != nil { - fmt.Fprintf(os.Stderr, "Could not create server TLS configuration: %v\n", err) - os.Exit(1) + logger.Fatalf("Could not create server TLS configuration: %v", err) } opts = append(opts, grpc.Creds(credentials.NewTLS(cfg))) } @@ -157,14 +181,13 @@ func start(c *cobra.Command, args []string) { lstn, err := net.Listen("tcp", grpcAddr) if err != nil { - fmt.Fprintf(os.Stderr, "Server died: %s\n", err) - os.Exit(1) + logger.Fatalf("Server died: %s", err) } - fmt.Printf("Starting Tiller %s (tls=%t)\n", version.GetVersion(), tlsEnable || tlsVerify) - fmt.Printf("GRPC listening on %s\n", grpcAddr) - fmt.Printf("Probes listening on %s\n", probeAddr) - fmt.Printf("Storage driver is %s\n", env.Releases.Name()) + logger.Printf("Starting Tiller %s (tls=%t)", version.GetVersion(), tlsEnable || tlsVerify) + logger.Printf("GRPC listening on %s", grpcAddr) + logger.Printf("Probes listening on %s", probeAddr) + logger.Printf("Storage driver is %s", env.Releases.Name()) if enableTracing { startTracing(traceAddr) @@ -173,7 +196,8 @@ func start(c *cobra.Command, args []string) { srvErrCh := make(chan error) probeErrCh := make(chan error) go func() { - svc := tiller.NewReleaseServer(env, clientset) + svc := tiller.NewReleaseServer(env, clientset, remoteReleaseModules) + svc.Log = newLogger("tiller").Printf services.RegisterReleaseServiceServer(rootServer, svc) if err := rootServer.Serve(lstn); err != nil { srvErrCh <- err @@ -194,10 +218,9 @@ func start(c *cobra.Command, args []string) { select { case err := <-srvErrCh: - fmt.Fprintf(os.Stderr, "Server died: %s\n", err) - os.Exit(1) + logger.Fatalf("Server died: %s", err) case err := <-probeErrCh: - fmt.Fprintf(os.Stderr, "Probes server died: %s\n", err) + logger.Printf("Probes server died: %s", err) } } diff --git a/cmd/tiller/trace.go b/cmd/tiller/trace.go index b9e0583f2..71d7e8f72 100644 --- a/cmd/tiller/trace.go +++ b/cmd/tiller/trace.go @@ -17,8 +17,6 @@ limitations under the License. package main // import "k8s.io/helm/cmd/tiller" import ( - "fmt" - "log" "net/http" _ "net/http/pprof" @@ -27,7 +25,7 @@ import ( ) func startTracing(addr string) { - fmt.Printf("Tracing server is listening on %s\n", addr) + logger.Printf("Tracing server is listening on %s\n", addr) grpc.EnableTracing = true http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { @@ -41,7 +39,7 @@ func startTracing(addr string) { go func() { if err := http.ListenAndServe(addr, nil); err != nil { - log.Printf("tracing error: %s", err) + logger.Printf("tracing error: %s", err) } }() } diff --git a/docs/chart_best_practices/index.md b/docs/chart_best_practices/README.md similarity index 100% rename from docs/chart_best_practices/index.md rename to docs/chart_best_practices/README.md diff --git a/docs/chart_repository_faq.md b/docs/chart_repository_faq.md new file mode 100644 index 000000000..78e47a461 --- /dev/null +++ b/docs/chart_repository_faq.md @@ -0,0 +1,17 @@ +# Chart Repositories: Frequently Asked Questions + +This section tracks some of the more frequently encountered issues with using chart repositories. + +**We'd love your help** making this document better. To add, correct, or remove +information, [file an issue](https://github.com/kubernetes/helm/issues) or +send us a pull request. + +## Fetching + +**Q: Why do I get a `unsupported protocol scheme ""` error when trying to fetch a chart from my custom repo?** + +A: (Helm < 2.5.0) This is likely caused by you creating your chart repo index without specifying the `--url` flag. +Try recreating your `index.yaml` file with a command like `heml repo index --url http://my-repo/charts .`, +and then re-uploading it to your custom charts repo. + +This behavior was changed in Helm 2.5.0. diff --git a/docs/chart_template_guide/control_structures.md b/docs/chart_template_guide/control_structures.md index 8723aacef..7575ebc35 100644 --- a/docs/chart_template_guide/control_structures.md +++ b/docs/chart_template_guide/control_structures.md @@ -170,7 +170,7 @@ data: {{- end}} ``` -Just for the same of making this point clear, let's adjust the above, and substitute an `*` for each whitespace that will be deleted following this rule. an `*` at the end of the line indicates a newline character that would be removed +Just for the sake of making this point clear, let's adjust the above, and substitute an `*` for each whitespace that will be deleted following this rule. an `*` at the end of the line indicates a newline character that would be removed ```yaml apiVersion: v1 diff --git a/docs/chart_template_guide/named_templates.md b/docs/chart_template_guide/named_templates.md index daea98b19..1dc9c7618 100644 --- a/docs/chart_template_guide/named_templates.md +++ b/docs/chart_template_guide/named_templates.md @@ -170,87 +170,6 @@ metadata: Now `{{ .Chart.Name }}` resolves to `mychart`, and `{{ .Chart.Version }}` resolves to `0.1.0`. -## Creating override-able sections with `block` - -Say we want to create a template in our `_helpers.tpl` file, but then override part of its behavior in our template. This is what blocks are for. Sometimes we don't want to just insert a template with `template`, but we want to sketch out a default and let another template override our default. This makes it possible for one chart to define a base template, but allow another chart to strategically override some of its behavior. - -Blocks are declared like this: - -```yaml -{{ block "NAME" PIPELINE }} -{{ end }} -``` - -Here, "NAME" is the name that a `define` block can use to override it, and PIPELINE is the pipeline that will set the scope. So let's rewrite our `labels:` section to use this strategy. We'll create a basic labels section in our `_helpers.tpl` file, but add some extra labels in the `configmap.yaml` template. - -Let's start with `_helpers.tpl`: - -```yaml -{{- define "my_labels" }} - labels: - chart: {{ .Chart.Name }} - version: {{ .Chart.Version }} - {{ block "my_extra_labels" . }}extras: false{{ end }} -{{- end }} -``` - -Inside of our `my_labels` template, we now declare a block called `my_extra_labels`. By default, this section will have one extra label: `extras: false`. If we were to execute this using the same `configmap.yaml` file from last time, we'd get this: - -```yaml -# Source: mychart/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: tinseled-womba-configmap - labels: - chart: mychart - version: 0.1.0 - extras: false -data: - myvalue: "Hello World" - drink: "coffee" - food: "pizza" -``` - -But inside of our `configmap.yaml` template, we can override `my_extra_labels`: - -```yaml -{{- define "my_extra_labels" }}chart: {{ .Chart.Name }}{{ end -}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ .Release.Name }}-configmap - {{- template "my_labels" . }} -data: - myvalue: "Hello World" - {{- range $key, $val := .Values.favorite }} - {{ $key }}: {{ $val | quote }} - {{- end }} -``` - -On the first line, we redefine `my_extra_labels` to include `chart: {{ .Chart.Name }}`. If we -run this, we will get: - -```yaml -# Source: mychart/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: ignorant-scorp-configmap - labels: - chart: mychart - version: 0.1.0 - chart: mychart -data: - myvalue: "Hello World" - drink: "coffee" - food: "pizza" -``` - -Gone is the `extras: false` section, since that part of the template is now overridden by our new template, which placed `chart: mychart` into the output. - -Blocks are not frequently used in Helm charts. But they do provide one mechanism for creating "abstract" charts, and then selectively overriding parts of the abstract template with concrete implementations. - ## The `include` function Say we've defined a simple template that looks like this: diff --git a/docs/chart_template_guide/subcharts_and_globals.md b/docs/chart_template_guide/subcharts_and_globals.md index 69d828c11..26e9a60cb 100644 --- a/docs/chart_template_guide/subcharts_and_globals.md +++ b/docs/chart_template_guide/subcharts_and_globals.md @@ -175,57 +175,33 @@ Globals are useful for passing information like this, though it does take some p ## Sharing Templates with Subcharts -Parent charts and subcharts can share templates. This can become very powerful when coupled with `block`s. For example, we can define a `block` in the `subchart` ConfigMap like this: +Parent charts and subcharts can share templates. Any defined block in any chart is +available to other charts. -```yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ .Release.Name }}-cfgmap2 - {{block "labels" . }}from: mysubchart{{ end }} -data: - dessert: {{ .Values.dessert }} - salad: {{ .Values.global.salad }} -``` - -Running this would produce: - -```yaml -# Source: mychart/charts/mysubchart/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: gaudy-mastiff-cfgmap2 - from: mysubchart -data: - dessert: ice cream - salad: caesar -``` - -Note that the `from:` line says `mysubchart`. In a previous section, we created `mychart/templates/_helpers.tpl`. Let's define a new named template there called `labels` to match the declaration on the block above. +For example, we can define a simple template like this: ```yaml {{- define "labels" }}from: mychart{{ end }} ``` -Recall how the labels on templates are _globally shared_. That means that if we create a block named `labels` in one chart, and then define an override named `labels` in another chart, the override will be applied. +Recall how the labels on templates are _globally shared_. Thus, the `labels` chart +can be included from any other chart. -Now if we do a `helm install --dry-run --debug mychart`, it will override the block: +While chart developers have a choice between `include` and `template`, one advantage +of using `include` is that `include` can dynamically reference templates: ```yaml -# Source: mychart/charts/mysubchart/templates/configmap.yaml -apiVersion: v1 -kind: ConfigMap -metadata: - name: nasal-cheetah-cfgmap2 - from: mychart -data: - dessert: ice cream - salad: caesar +{{ include $mytemplate }} ``` -Now `from:` is set to `mychart` because the block was overridden. +The above will dereference `$mytemplate`. The `template` function, in contrast, +will only accept a string literal. + +## Avoid Using Blocks -Using this method, you can write flexible "base" charts that can be added as subcharts to many different charts, and which will support selective overriding using blocks. +The Go template language provides a `block` keyword that allows developers to provide +a default implementation which is overridden later. In Helm charts, blocks are not +the best tool for overriding because it if multiple implementations of the same block +are provided, the one selected is unpredictable. -This section of the guide has focused on subcharts. We've seen how to inherit values, how to use global values, and how to override templates with blocks. In the next section we will turn to debugging, and learn how to catch errors in templates. +The suggestion is to instead use `include`. diff --git a/docs/chart_tests.md b/docs/chart_tests.md index cb25d5dfc..4311ffaeb 100644 --- a/docs/chart_tests.md +++ b/docs/chart_tests.md @@ -1,8 +1,8 @@ # Chart Tests -A chart contains a number of Kubernetes resources and components that work together. As a chart author, you may want to write some tests that validate that your charts works as expected when it is installed. These tests also help the chart consumer understand what your chart is supposed to do. +A chart contains a number of Kubernetes resources and components that work together. As a chart author, you may want to write some tests that validate that your chart works as expected when it is installed. These tests also help the chart consumer understand what your chart is supposed to do. -A **test** in a helm chart lives under the `templates/` directory and is a pod definition that specifies a container with a given command to run. The container should exist successfully (exit 0) for a test to be considered a success. The pod definiton must contain one of the helm test hook annotations: `helm.sh/hooks: test-success` or `helm.sh/hooks: test-failure`. +A **test** in a helm chart lives under the `templates/` directory and is a pod definition that specifies a container with a given command to run. The container should exit successfully (exit 0) for a test to be considered a success. The pod definiton must contain one of the helm test hook annotations: `helm.sh/hooks: test-success` or `helm.sh/hooks: test-failure`. Example tests: - Validate that your configuration from the values.yaml file was properly injected. @@ -17,12 +17,12 @@ You can run the pre-defined tests in Helm on a release using the command `helm t In Helm, there are two test hooks: `test-success` and `test-failure` -`test-success` indiciates that test pod should complete successfully. In other words, the containers in the pod should exit 0. -`test-failure` is a way to assert that a test pod should not complete successfully. If the containers in the pod do not exit 0, that indiciates success. +`test-success` indicates that test pod should complete successfully. In other words, the containers in the pod should exit 0. +`test-failure` is a way to assert that a test pod should not complete successfully. If the containers in the pod do not exit 0, that indicates success. ## Example Test -Here is an example of a helm test pod definition in an example maraidb chart: +Here is an example of a helm test pod definition in an example mariadb chart: ``` mariadb/ diff --git a/docs/charts.md b/docs/charts.md index b1aa10ad4..071eb4980 100644 --- a/docs/charts.md +++ b/docs/charts.md @@ -229,6 +229,43 @@ Managing charts with `requirements.yaml` is a good way to easily keep charts updated, and also share requirements information throughout a team. +#### Alias field in requirements.yaml + +In addition to the other fields above, each requirements entry may contain +the optional field `alias`. + +Adding an alias for a dependency chart would put +a chart in dependencies using alias as name of new dependency. + +One can use `alias` in cases where they need to access a chart +with other name(s). + +```` +# parentchart/requirements.yaml +dependencies: + - name: subchart + repository: http://localhost:10191 + version: 0.1.0 + alias: new-subchart-1 + - name: subchart + repository: http://localhost:10191 + version: 0.1.0 + alias: new-subchart-2 + - name: subchart + repository: http://localhost:10191 + version: 0.1.0 +```` + +In the above example we will get 3 depenendencies in all for `parentchart` +``` +subchart +new-subchart-1 +new-subchart-2 +``` + +Manual way of achieving this is copy/pasting same chart in +`charts/` directory multiple times with different name. + #### Tags and Condition fields in requirements.yaml In addition to the other fields above, each requirements entry may contain @@ -687,17 +724,6 @@ parent chart. Also, global variables of parent charts take precedence over the global variables from subcharts. -_Global sections are restricted to only simple key/value pairs. They do -not support nesting._ - -For example, the following is **illegal** and will not work: - -```yaml -global: - foo: # It is illegal to nest an object inside of global. - bar: baz -``` - ### References When it comes to writing templates and values files, there are several diff --git a/docs/charts_hooks.md b/docs/charts_hooks.md index a5babc481..6ac9bc2b0 100644 --- a/docs/charts_hooks.md +++ b/docs/charts_hooks.md @@ -79,8 +79,13 @@ Helm client will pause while the Job is run. For all other kinds, as soon as Kubernetes marks the resource as loaded (added or updated), the resource is considered "Ready". When many -resources are declared in a hook, the resources are executed serially, -but the order of their execution is not guaranteed. +resources are declared in a hook, the resources are executed serially. If they +have hook weights (see below), they are executed in weighted order. Otherwise, +ordering is not guaranteed. (In Helm 2.3.0 and after, they are sorted +alphabetically. That behavior, though, is not considered binding and could change +in the future.) It is considered good practice to add a hook weight, and set it +to `0` if weight is not important. + ### Hook resources are unmanaged @@ -150,19 +155,20 @@ One resource can implement multiple hooks: Similarly, there is no limit to the number of different resources that may implement a given hook. For example, one could declare both a secret -and a config map as a pre-install hook. It is important to keep in mind, -though, that there are no ordering guarantees about hooks. +and a config map as a pre-install hook. When subcharts declare hooks, those are also evaluated. There is no way -for a top-level chart to disable the hooks declared by subcharts. And -again, there is no guaranteed ordering. +for a top-level chart to disable the hooks declared by subcharts. -It is also possible to define a weight for a hook which will help build a deterministic executing order. Weights are defined using the following annotation: +It is also possible to define a weight for a hook which will help build a +deterministic executing order. Weights are defined using the following annotation: ``` annotations: "helm.sh/hook-weight": "5" ``` -Hook weights can be positive or negative numbers but must be represented as strings. When Tiller starts the execution cycle of hooks of a particular Kind it will sort those hooks in ascending order. +Hook weights can be positive or negative numbers but must be represented as +strings. When Tiller starts the execution cycle of hooks of a particular Kind it +will sort those hooks in ascending order. diff --git a/docs/charts_tips_and_tricks.md b/docs/charts_tips_and_tricks.md index 01d9ea83c..0ed1ab83e 100644 --- a/docs/charts_tips_and_tricks.md +++ b/docs/charts_tips_and_tricks.md @@ -78,7 +78,7 @@ Go provides a way for setting template options to control behavior when a map is indexed with a key that's not present in the map. This is typically set with template.Options("missingkey=option"), where option can be default, zero, or error. While setting this option to error will -stop execution with an arror, this would apply to every missing key in the +stop execution with an error, this would apply to every missing key in the map. There may be situations where a chart developer wants to enforce this behavior for select values in the values.yml file. diff --git a/docs/developers.md b/docs/developers.md index 163ba1b9d..e0aeb374a 100644 --- a/docs/developers.md +++ b/docs/developers.md @@ -174,6 +174,7 @@ Common commit types: - feat: Add a new feature - docs: Change documentation - test: Improve testing +- ref: refactor existing code Common scopes: diff --git a/docs/examples/alpine/README.md b/docs/examples/alpine/README.md index 3c32de5db..eb4fb9571 100644 --- a/docs/examples/alpine/README.md +++ b/docs/examples/alpine/README.md @@ -1,4 +1,4 @@ -#Alpine: A simple Helm chart +# Alpine: A simple Helm chart Run a single pod of Alpine Linux. diff --git a/docs/helm/helm.md b/docs/helm/helm.md index 3b63cc5f2..4567049da 100644 --- a/docs/helm/helm.md +++ b/docs/helm/helm.md @@ -33,14 +33,14 @@ Environment: ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") ``` ### SEE ALSO -* [helm completion](helm_completion.md) - Generate bash autocompletions script +* [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 @@ -66,4 +66,4 @@ Environment: * [helm verify](helm_verify.md) - verify that a chart at the given path has been signed and is valid * [helm version](helm_version.md) - print the client/server version information -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_completion.md b/docs/helm/helm_completion.md index 0e05b6700..4b7c540a1 100644 --- a/docs/helm/helm_completion.md +++ b/docs/helm/helm_completion.md @@ -1,31 +1,31 @@ ## helm completion -Generate bash autocompletions script +Generate autocompletions script for the specified shell (bash or zsh) ### Synopsis -Generate bash autocompletions script for Helm. +Generate autocompletions script for Helm for the specified shell (bash or zsh). -This command can generate shell autocompletions. +This command can generate shell autocompletions. e.g. - $ helm completion + $ helm completion bash Can be sourced as such - $ source <(helm completion) + $ source <(helm completion bash) ``` -helm completion +helm completion SHELL ``` ### Options inherited from parent commands ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -34,4 +34,4 @@ helm completion ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_create.md b/docs/helm/helm_create.md index 442becc87..8284be8ff 100644 --- a/docs/helm/helm_create.md +++ b/docs/helm/helm_create.md @@ -44,7 +44,7 @@ helm create NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -53,4 +53,4 @@ helm create NAME ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_delete.md b/docs/helm/helm_delete.md index 97620edaa..0d15b9d02 100644 --- a/docs/helm/helm_delete.md +++ b/docs/helm/helm_delete.md @@ -35,7 +35,7 @@ helm delete [flags] RELEASE_NAME [...] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -44,4 +44,4 @@ helm delete [flags] RELEASE_NAME [...] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_dependency.md b/docs/helm/helm_dependency.md index 69417bc24..629a27d38 100644 --- a/docs/helm/helm_dependency.md +++ b/docs/helm/helm_dependency.md @@ -58,7 +58,7 @@ for this case. ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -70,4 +70,4 @@ for this case. * [helm dependency list](helm_dependency_list.md) - list the dependencies for the given chart * [helm dependency update](helm_dependency_update.md) - update charts/ based on the contents of requirements.yaml -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_dependency_build.md b/docs/helm/helm_dependency_build.md index 4cd149444..9c5d6af78 100644 --- a/docs/helm/helm_dependency_build.md +++ b/docs/helm/helm_dependency_build.md @@ -31,7 +31,7 @@ helm dependency build [flags] CHART ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -40,4 +40,4 @@ helm dependency build [flags] CHART ### SEE ALSO * [helm dependency](helm_dependency.md) - manage a chart's dependencies -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_dependency_list.md b/docs/helm/helm_dependency_list.md index e0223c07e..ec5e861fd 100644 --- a/docs/helm/helm_dependency_list.md +++ b/docs/helm/helm_dependency_list.md @@ -23,7 +23,7 @@ helm dependency list [flags] CHART ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -32,4 +32,4 @@ helm dependency list [flags] CHART ### SEE ALSO * [helm dependency](helm_dependency.md) - manage a chart's dependencies -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_dependency_update.md b/docs/helm/helm_dependency_update.md index 553d1f831..8fb8fd2d1 100644 --- a/docs/helm/helm_dependency_update.md +++ b/docs/helm/helm_dependency_update.md @@ -36,7 +36,7 @@ helm dependency update [flags] CHART ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -45,4 +45,4 @@ helm dependency update [flags] CHART ### SEE ALSO * [helm dependency](helm_dependency.md) - manage a chart's dependencies -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_fetch.md b/docs/helm/helm_fetch.md index 904e0577e..a95781997 100644 --- a/docs/helm/helm_fetch.md +++ b/docs/helm/helm_fetch.md @@ -27,9 +27,14 @@ 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-a'. If --version is set, this is ignored. + --key-file string identify HTTPS client using this SSL key file --keyring string keyring containing public keys (default "~/.gnupg/pubring.gpg") --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 ".") --verify verify the package against its signature @@ -40,7 +45,7 @@ helm fetch [flags] [chart URL | repo/chartname] [...] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -49,4 +54,4 @@ helm fetch [flags] [chart URL | repo/chartname] [...] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_get.md b/docs/helm/helm_get.md index d67af8388..efa3c458a 100644 --- a/docs/helm/helm_get.md +++ b/docs/helm/helm_get.md @@ -37,7 +37,7 @@ helm get [flags] RELEASE_NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -49,4 +49,4 @@ helm get [flags] RELEASE_NAME * [helm get manifest](helm_get_manifest.md) - download the manifest for a named release * [helm get values](helm_get_values.md) - download the values file for a named release -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_get_hooks.md b/docs/helm/helm_get_hooks.md index 9e4cbbaca..d2fad8b69 100644 --- a/docs/helm/helm_get_hooks.md +++ b/docs/helm/helm_get_hooks.md @@ -25,7 +25,7 @@ helm get hooks [flags] RELEASE_NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -34,4 +34,4 @@ helm get hooks [flags] RELEASE_NAME ### SEE ALSO * [helm get](helm_get.md) - download a named release -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_get_manifest.md b/docs/helm/helm_get_manifest.md index aa68c4de8..053ef652d 100644 --- a/docs/helm/helm_get_manifest.md +++ b/docs/helm/helm_get_manifest.md @@ -27,7 +27,7 @@ helm get manifest [flags] RELEASE_NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -36,4 +36,4 @@ helm get manifest [flags] RELEASE_NAME ### SEE ALSO * [helm get](helm_get.md) - download a named release -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_get_values.md b/docs/helm/helm_get_values.md index 67568c6a8..5cdcbd7d2 100644 --- a/docs/helm/helm_get_values.md +++ b/docs/helm/helm_get_values.md @@ -24,7 +24,7 @@ helm get values [flags] RELEASE_NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -33,4 +33,4 @@ helm get values [flags] RELEASE_NAME ### SEE ALSO * [helm get](helm_get.md) - download a named release -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_history.md b/docs/helm/helm_history.md index 1852c6616..590c8a684 100644 --- a/docs/helm/helm_history.md +++ b/docs/helm/helm_history.md @@ -40,7 +40,7 @@ helm history [flags] RELEASE_NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -49,4 +49,4 @@ helm history [flags] RELEASE_NAME ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_home.md b/docs/helm/helm_home.md index d86f3ce80..683142f71 100644 --- a/docs/helm/helm_home.md +++ b/docs/helm/helm_home.md @@ -18,7 +18,7 @@ helm home ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -27,4 +27,4 @@ helm home ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_init.md b/docs/helm/helm_init.md index 3fe94b79c..2d498c554 100644 --- a/docs/helm/helm_init.md +++ b/docs/helm/helm_init.md @@ -38,6 +38,7 @@ helm init --dry-run do not install local or remote --local-repo-url string URL for local repository (default "http://127.0.0.1:8879/charts") --net-host install tiller with net=host + --service-account string name of service account --skip-refresh do not refresh (download) the local repository cache --stable-repo-url string URL for stable repository (default "https://kubernetes-charts.storage.googleapis.com") -i, --tiller-image string override tiller image @@ -53,7 +54,7 @@ helm init ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -62,4 +63,4 @@ helm init ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 18-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_inspect.md b/docs/helm/helm_inspect.md index df8b5eb2d..30f09a72f 100644 --- a/docs/helm/helm_inspect.md +++ b/docs/helm/helm_inspect.md @@ -19,16 +19,20 @@ helm inspect [CHART] ### Options ``` - --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") - --verify verify the provenance data for this chart - --version string version of the chart. By default, the newest chart is shown + --ca-file string chart repository url where to locate the requested chart + --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle + --key-file string identify HTTPS client using this SSL key file + --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") + --repo string chart repository url where to locate the requested chart + --verify verify the provenance data for this chart + --version string version of the chart. By default, the newest chart is shown ``` ### Options inherited from parent commands ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -39,4 +43,4 @@ helm inspect [CHART] * [helm inspect chart](helm_inspect_chart.md) - shows inspect chart * [helm inspect values](helm_inspect_values.md) - shows inspect values -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_inspect_chart.md b/docs/helm/helm_inspect_chart.md index 9980b5a46..5e49e282b 100644 --- a/docs/helm/helm_inspect_chart.md +++ b/docs/helm/helm_inspect_chart.md @@ -17,16 +17,20 @@ helm inspect chart [CHART] ### Options ``` - --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") - --verify verify the provenance data for this chart - --version string version of the chart. By default, the newest chart is shown + --ca-file string chart repository url where to locate the requested chart + --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle + --key-file string identify HTTPS client using this SSL key file + --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") + --repo string chart repository url where to locate the requested chart + --verify verify the provenance data for this chart + --version string version of the chart. By default, the newest chart is shown ``` ### Options inherited from parent commands ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -35,4 +39,4 @@ helm inspect chart [CHART] ### SEE ALSO * [helm inspect](helm_inspect.md) - inspect a chart -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_inspect_values.md b/docs/helm/helm_inspect_values.md index 650a64358..f49215975 100644 --- a/docs/helm/helm_inspect_values.md +++ b/docs/helm/helm_inspect_values.md @@ -17,16 +17,20 @@ helm inspect values [CHART] ### Options ``` - --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") - --verify verify the provenance data for this chart - --version string version of the chart. By default, the newest chart is shown + --ca-file string chart repository url where to locate the requested chart + --cert-file string verify certificates of HTTPS-enabled servers using this CA bundle + --key-file string identify HTTPS client using this SSL key file + --keyring string path to the keyring containing public verification keys (default "~/.gnupg/pubring.gpg") + --repo string chart repository url where to locate the requested chart + --verify verify the provenance data for this chart + --version string version of the chart. By default, the newest chart is shown ``` ### Options inherited from parent commands ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -35,4 +39,4 @@ helm inspect values [CHART] ### SEE ALSO * [helm inspect](helm_inspect.md) - inspect a chart -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_install.md b/docs/helm/helm_install.md index 0f5736887..f8e0885ff 100644 --- a/docs/helm/helm_install.md +++ b/docs/helm/helm_install.md @@ -68,13 +68,18 @@ helm install [CHART] ### 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 + --devel use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored. --dry-run simulate an install + --key-file string identify HTTPS client using this SSL key file --keyring string location of public keys used for verification (default "~/.gnupg/pubring.gpg") -n, --name string release name. If unspecified, it will autogenerate one for you --name-template string specify template used to name the release --namespace string namespace to install the release into --no-hooks prevent hooks from running during install --replace re-use the given name, even if that name is already used. This is unsafe in production + --repo string chart repository url where to locate the requested chart --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) --timeout int time in seconds to wait for any individual kubernetes operation (like Jobs for hooks) (default 300) --tls enable TLS for request @@ -92,7 +97,7 @@ helm install [CHART] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -101,4 +106,4 @@ helm install [CHART] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_lint.md b/docs/helm/helm_lint.md index 00f287569..61e6737c4 100644 --- a/docs/helm/helm_lint.md +++ b/docs/helm/helm_lint.md @@ -28,7 +28,7 @@ helm lint [flags] PATH ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -37,4 +37,4 @@ helm lint [flags] PATH ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_list.md b/docs/helm/helm_list.md index 26e4ffcd1..a69f5ba2c 100644 --- a/docs/helm/helm_list.md +++ b/docs/helm/helm_list.md @@ -61,7 +61,7 @@ helm list [flags] [FILTER] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -70,4 +70,4 @@ helm list [flags] [FILTER] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_package.md b/docs/helm/helm_package.md index d7afd118b..595ec5564 100644 --- a/docs/helm/helm_package.md +++ b/docs/helm/helm_package.md @@ -23,6 +23,7 @@ helm package [flags] [CHART_PATH] [...] ### Options ``` + -u, --dependency-update update dependencies from "requirements.yaml" to dir "charts/" before packaging -d, --destination string location to write the chart. (default ".") --key string name of the key to use when signing. Used if --sign is true --keyring string location of a public keyring (default "~/.gnupg/pubring.gpg") @@ -35,7 +36,7 @@ helm package [flags] [CHART_PATH] [...] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -44,4 +45,4 @@ helm package [flags] [CHART_PATH] [...] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 5-Jun-2017 diff --git a/docs/helm/helm_plugin.md b/docs/helm/helm_plugin.md index 96d474dea..b38f3e013 100644 --- a/docs/helm/helm_plugin.md +++ b/docs/helm/helm_plugin.md @@ -13,7 +13,7 @@ Manage client-side Helm plugins. ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -24,5 +24,6 @@ Manage client-side Helm plugins. * [helm plugin install](helm_plugin_install.md) - install one or more Helm plugins * [helm plugin list](helm_plugin_list.md) - list installed Helm plugins * [helm plugin remove](helm_plugin_remove.md) - remove one or more Helm plugins +* [helm plugin update](helm_plugin_update.md) - update one or more Helm plugins -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_plugin_install.md b/docs/helm/helm_plugin_install.md index 8480eb2ed..7b4f8904e 100644 --- a/docs/helm/helm_plugin_install.md +++ b/docs/helm/helm_plugin_install.md @@ -21,7 +21,7 @@ helm plugin install [options] ... ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -30,4 +30,4 @@ helm plugin install [options] ... ### SEE ALSO * [helm plugin](helm_plugin.md) - add, list, or remove Helm plugins -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_plugin_list.md b/docs/helm/helm_plugin_list.md index cfa321706..53f37b8d0 100644 --- a/docs/helm/helm_plugin_list.md +++ b/docs/helm/helm_plugin_list.md @@ -15,7 +15,7 @@ helm plugin list ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -24,4 +24,4 @@ helm plugin list ### SEE ALSO * [helm plugin](helm_plugin.md) - add, list, or remove Helm plugins -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_plugin_remove.md b/docs/helm/helm_plugin_remove.md index 47c16f6e8..0559f395c 100644 --- a/docs/helm/helm_plugin_remove.md +++ b/docs/helm/helm_plugin_remove.md @@ -15,7 +15,7 @@ helm plugin remove ... ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -24,4 +24,4 @@ helm plugin remove ... ### SEE ALSO * [helm plugin](helm_plugin.md) - add, list, or remove Helm plugins -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_plugin_update.md b/docs/helm/helm_plugin_update.md new file mode 100644 index 000000000..c0dac090a --- /dev/null +++ b/docs/helm/helm_plugin_update.md @@ -0,0 +1,27 @@ +## helm plugin update + +update one or more Helm plugins + +### Synopsis + + +update one or more Helm plugins + +``` +helm plugin update ... +``` + +### Options inherited from parent commands + +``` + --debug enable verbose output + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") + --host string address of tiller. Overrides $HELM_HOST + --kube-context string name of the kubeconfig context to use + --tiller-namespace string namespace of tiller (default "kube-system") +``` + +### SEE ALSO +* [helm plugin](helm_plugin.md) - add, list, or remove Helm plugins + +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_repo.md b/docs/helm/helm_repo.md index 8e5d0da57..3378a1850 100644 --- a/docs/helm/helm_repo.md +++ b/docs/helm/helm_repo.md @@ -17,7 +17,7 @@ Example usage: ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -29,6 +29,6 @@ Example usage: * [helm repo index](helm_repo_index.md) - generate an index file given a directory containing packaged charts * [helm repo list](helm_repo_list.md) - list chart repositories * [helm repo remove](helm_repo_remove.md) - remove a chart repository -* [helm repo update](helm_repo_update.md) - update information on available charts in the chart repositories +* [helm repo update](helm_repo_update.md) - update information of available charts locally from chart repositories -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_repo_add.md b/docs/helm/helm_repo_add.md index 5567fe415..f7f3e8727 100644 --- a/docs/helm/helm_repo_add.md +++ b/docs/helm/helm_repo_add.md @@ -24,7 +24,7 @@ helm repo add [flags] [NAME] [URL] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -33,4 +33,4 @@ helm repo add [flags] [NAME] [URL] ### SEE ALSO * [helm repo](helm_repo.md) - add, list, remove, update, and index chart repositories -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_repo_index.md b/docs/helm/helm_repo_index.md index a98ecbffa..79c8ee608 100644 --- a/docs/helm/helm_repo_index.md +++ b/docs/helm/helm_repo_index.md @@ -31,7 +31,7 @@ helm repo index [flags] [DIR] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -40,4 +40,4 @@ helm repo index [flags] [DIR] ### SEE ALSO * [helm repo](helm_repo.md) - add, list, remove, update, and index chart repositories -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_repo_list.md b/docs/helm/helm_repo_list.md index e5138458c..4f8f2e1dd 100644 --- a/docs/helm/helm_repo_list.md +++ b/docs/helm/helm_repo_list.md @@ -15,7 +15,7 @@ helm repo list [flags] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -24,4 +24,4 @@ helm repo list [flags] ### SEE ALSO * [helm repo](helm_repo.md) - add, list, remove, update, and index chart repositories -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_repo_remove.md b/docs/helm/helm_repo_remove.md index 0d1b1d06f..075b15fce 100644 --- a/docs/helm/helm_repo_remove.md +++ b/docs/helm/helm_repo_remove.md @@ -15,7 +15,7 @@ helm repo remove [flags] [NAME] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -24,4 +24,4 @@ helm repo remove [flags] [NAME] ### SEE ALSO * [helm repo](helm_repo.md) - add, list, remove, update, and index chart repositories -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_repo_update.md b/docs/helm/helm_repo_update.md index e3787cd56..3758f822f 100644 --- a/docs/helm/helm_repo_update.md +++ b/docs/helm/helm_repo_update.md @@ -1,6 +1,6 @@ ## helm repo update -update information on available charts in the chart repositories +update information of available charts locally from chart repositories ### Synopsis @@ -21,7 +21,7 @@ helm repo update ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -30,4 +30,4 @@ helm repo update ### SEE ALSO * [helm repo](helm_repo.md) - add, list, remove, update, and index chart repositories -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_reset.md b/docs/helm/helm_reset.md index e07f3a707..bc3299d48 100644 --- a/docs/helm/helm_reset.md +++ b/docs/helm/helm_reset.md @@ -18,7 +18,7 @@ helm reset ### Options ``` - -f, --force forces Tiller uninstall even if there are releases installed + -f, --force forces Tiller uninstall even if there are releases installed, or if tiller is not in ready state --remove-helm-home if set deletes $HELM_HOME --tls enable TLS for request --tls-ca-cert string path to TLS CA certificate file (default "$HELM_HOME/ca.pem") @@ -31,7 +31,7 @@ helm reset ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -40,4 +40,4 @@ helm reset ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_rollback.md b/docs/helm/helm_rollback.md index a5500008c..3a3ed1a24 100644 --- a/docs/helm/helm_rollback.md +++ b/docs/helm/helm_rollback.md @@ -9,7 +9,7 @@ roll back a release to a previous revision This command rolls back a release to a previous revision. The first argument of the rollback command is the name of a release, and the -second is a revision (version) number. To see revision numbers, run +second is a revision (version) number. To see revision numbers, run 'helm history RELEASE'. @@ -21,6 +21,7 @@ helm rollback [flags] [RELEASE] [REVISION] ``` --dry-run simulate a rollback + --force force resource update through delete/recreate if needed --no-hooks prevent hooks from running during rollback --recreate-pods performs pods restart for the resource if applicable --timeout int time in seconds to wait for any individual kubernetes operation (like Jobs for hooks) (default 300) @@ -36,7 +37,7 @@ helm rollback [flags] [RELEASE] [REVISION] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -45,4 +46,4 @@ helm rollback [flags] [RELEASE] [REVISION] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_search.md b/docs/helm/helm_search.md index 6f1f8a645..48579c5b8 100644 --- a/docs/helm/helm_search.md +++ b/docs/helm/helm_search.md @@ -28,7 +28,7 @@ helm search [keyword] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -37,4 +37,4 @@ helm search [keyword] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 18-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_serve.md b/docs/helm/helm_serve.md index 7c928c9a0..1a317e9e4 100644 --- a/docs/helm/helm_serve.md +++ b/docs/helm/helm_serve.md @@ -28,7 +28,7 @@ helm serve ``` --address string address to listen on (default "127.0.0.1:8879") - --repo-path string local directory path from which to serve charts (default "~/.helm/repository/local") + --repo-path string local directory path from which to serve charts --url string external URL of chart repository ``` @@ -36,7 +36,7 @@ helm serve ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -45,4 +45,4 @@ helm serve ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_status.md b/docs/helm/helm_status.md index 893c3325a..a737d7a64 100644 --- a/docs/helm/helm_status.md +++ b/docs/helm/helm_status.md @@ -35,7 +35,7 @@ helm status [flags] RELEASE_NAME ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -44,4 +44,4 @@ helm status [flags] RELEASE_NAME ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_test.md b/docs/helm/helm_test.md index 795a81c66..8d7b39a59 100644 --- a/docs/helm/helm_test.md +++ b/docs/helm/helm_test.md @@ -32,7 +32,7 @@ helm test [RELEASE] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -41,4 +41,4 @@ helm test [RELEASE] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_upgrade.md b/docs/helm/helm_upgrade.md index 931073937..1fbefcba3 100644 --- a/docs/helm/helm_upgrade.md +++ b/docs/helm/helm_upgrade.md @@ -36,12 +36,18 @@ helm upgrade [RELEASE] [CHART] ### 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 + --devel use development versions, too. Equivalent to version '>0.0.0-a'. If --version is set, this is ignored. --dry-run simulate an upgrade + --force force resource update through delete/recreate if needed -i, --install if a release by this name doesn't already exist, run an install + --key-file string identify HTTPS client using this SSL key file --keyring string path to the keyring that contains public signing keys (default "~/.gnupg/pubring.gpg") --namespace string namespace to install the release into (only used if --install is set) (default "default") --no-hooks disable pre/post upgrade hooks --recreate-pods performs pods restart for the resource if applicable + --repo string chart repository url where to locate the requested chart --reset-values when upgrading, reset the values to the ones built into the chart --reuse-values when upgrading, reuse the last release's values, and merge in any new values. If '--reset-values' is specified, this is ignored. --set stringArray set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) @@ -61,7 +67,7 @@ helm upgrade [RELEASE] [CHART] ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -70,4 +76,4 @@ helm upgrade [RELEASE] [CHART] ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_verify.md b/docs/helm/helm_verify.md index 57ff2f191..10ddab059 100644 --- a/docs/helm/helm_verify.md +++ b/docs/helm/helm_verify.md @@ -30,7 +30,7 @@ helm verify [flags] PATH ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -39,4 +39,4 @@ helm verify [flags] PATH ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/helm/helm_version.md b/docs/helm/helm_version.md index de65607b1..16e2f5b92 100644 --- a/docs/helm/helm_version.md +++ b/docs/helm/helm_version.md @@ -44,7 +44,7 @@ helm version ``` --debug enable verbose output - --home string location of your Helm config. Overrides $HELM_HOME (default "~/.helm") + --home string location of your Helm config. Overrides $HELM_HOME (default "$HOME/.helm") --host string address of tiller. Overrides $HELM_HOST --kube-context string name of the kubeconfig context to use --tiller-namespace string namespace of tiller (default "kube-system") @@ -53,4 +53,4 @@ helm version ### SEE ALSO * [helm](helm.md) - The Helm package manager for Kubernetes. -###### Auto generated by spf13/cobra on 16-Apr-2017 +###### Auto generated by spf13/cobra on 29-May-2017 diff --git a/docs/man/man1/helm.1 b/docs/man/man1/helm.1 index 026ca69e4..4128830f4 100644 --- a/docs/man/man1/helm.1 +++ b/docs/man/man1/helm.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -63,7 +63,7 @@ Environment: location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -82,4 +82,4 @@ Environment: .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_completion.1 b/docs/man/man1/helm_completion.1 index 0468bd4f4..83217073f 100644 --- a/docs/man/man1/helm_completion.1 +++ b/docs/man/man1/helm_completion.1 @@ -1,30 +1,30 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l .SH NAME .PP -helm\-completion \- Generate bash autocompletions script +helm\-completion \- Generate autocompletions script for the specified shell (bash or zsh) .SH SYNOPSIS .PP -\fBhelm completion\fP +\fBhelm completion SHELL\fP .SH DESCRIPTION .PP -Generate bash autocompletions script for Helm. +Generate autocompletions script for Helm for the specified shell (bash or zsh). .PP -This command can generate shell autocompletions. +This command can generate shell autocompletions. e.g. .PP .RS .nf -$ helm completion +$ helm completion bash .fi .RE @@ -36,7 +36,7 @@ Can be sourced as such .RS .nf -$ source <(helm completion) +$ source <(helm completion bash) .fi .RE @@ -52,7 +52,7 @@ $ source <(helm completion) location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -71,4 +71,4 @@ $ source <(helm completion) .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_create.1 b/docs/man/man1/helm_create.1 index 4261d753f..3a1f4b26f 100644 --- a/docs/man/man1/helm_create.1 +++ b/docs/man/man1/helm_create.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -64,7 +64,7 @@ will be overwritten, but other files will be left alone. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -83,4 +83,4 @@ will be overwritten, but other files will be left alone. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_delete.1 b/docs/man/man1/helm_delete.1 index 3df4e0ded..ecce68398 100644 --- a/docs/man/man1/helm_delete.1 +++ b/docs/man/man1/helm_delete.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -71,7 +71,7 @@ deleting them. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -90,4 +90,4 @@ deleting them. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_dependency.1 b/docs/man/man1/helm_dependency.1 index 9287dcb1f..fd4fc195e 100644 --- a/docs/man/man1/helm_dependency.1 +++ b/docs/man/man1/helm_dependency.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -95,7 +95,7 @@ for this case. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -114,4 +114,4 @@ for this case. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_dependency_build.1 b/docs/man/man1/helm_dependency_build.1 index a42f44a58..adc225a81 100644 --- a/docs/man/man1/helm_dependency_build.1 +++ b/docs/man/man1/helm_dependency_build.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -47,7 +47,7 @@ of 'helm dependency update'. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -66,4 +66,4 @@ of 'helm dependency update'. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_dependency_list.1 b/docs/man/man1/helm_dependency_list.1 index 9061d3ebe..30a686bb4 100644 --- a/docs/man/man1/helm_dependency_list.1 +++ b/docs/man/man1/helm_dependency_list.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -36,7 +36,7 @@ if it cannot find a requirements.yaml. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -55,4 +55,4 @@ if it cannot find a requirements.yaml. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_dependency_update.1 b/docs/man/man1/helm_dependency_update.1 index eebee95d7..02d9ec94b 100644 --- a/docs/man/man1/helm_dependency_update.1 +++ b/docs/man/man1/helm_dependency_update.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -56,7 +56,7 @@ in the requirements.yaml file, but (b) at the wrong version. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -75,4 +75,4 @@ in the requirements.yaml file, but (b) at the wrong version. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_fetch.1 b/docs/man/man1/helm_fetch.1 index 0de710574..9a8ad185c 100644 --- a/docs/man/man1/helm_fetch.1 +++ b/docs/man/man1/helm_fetch.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -33,10 +33,26 @@ result in an error, and the chart will not be saved locally. .SH OPTIONS +.PP +\fB\-\-ca\-file\fP="" + verify certificates of HTTPS\-enabled servers using this CA bundle + +.PP +\fB\-\-cert\-file\fP="" + identify HTTPS client using this SSL certificate file + .PP \fB\-d\fP, \fB\-\-destination\fP="." location to write the chart. If this and tardir are specified, tardir is appended to this +.PP +\fB\-\-devel\fP[=false] + use development versions, too. Equivalent to version '>0.0.0\-a'. If \-\-version is set, this is ignored. + +.PP +\fB\-\-key\-file\fP="" + identify HTTPS client using this SSL key file + .PP \fB\-\-keyring\fP="~/.gnupg/pubring.gpg" keyring containing public keys @@ -45,6 +61,10 @@ result in an error, and the chart will not be saved locally. \fB\-\-prov\fP[=false] fetch the provenance file, but don't perform verification +.PP +\fB\-\-repo\fP="" + chart repository url where to locate the requested chart + .PP \fB\-\-untar\fP[=false] if set to true, will untar the chart after downloading it @@ -72,7 +92,7 @@ result in an error, and the chart will not be saved locally. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -91,4 +111,4 @@ result in an error, and the chart will not be saved locally. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_get.1 b/docs/man/man1/helm_get.1 index 6a5f96a20..e680f49dc 100644 --- a/docs/man/man1/helm_get.1 +++ b/docs/man/man1/helm_get.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -67,7 +67,7 @@ chart, the supplied values, and the generated manifest file. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -86,4 +86,4 @@ chart, the supplied values, and the generated manifest file. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_get_hooks.1 b/docs/man/man1/helm_get_hooks.1 index 8ba348e54..34e460d19 100644 --- a/docs/man/man1/helm_get_hooks.1 +++ b/docs/man/man1/helm_get_hooks.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -37,7 +37,7 @@ Hooks are formatted in YAML and separated by the YAML '\-\-\-\\n' separator. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -56,4 +56,4 @@ Hooks are formatted in YAML and separated by the YAML '\-\-\-\\n' separator. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_get_manifest.1 b/docs/man/man1/helm_get_manifest.1 index 21fa6363c..7132db38e 100644 --- a/docs/man/man1/helm_get_manifest.1 +++ b/docs/man/man1/helm_get_manifest.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -39,7 +39,7 @@ charts, those resources will also be included in the manifest. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -58,4 +58,4 @@ charts, those resources will also be included in the manifest. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_get_values.1 b/docs/man/man1/helm_get_values.1 index c897e7378..349f97c14 100644 --- a/docs/man/man1/helm_get_values.1 +++ b/docs/man/man1/helm_get_values.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -38,7 +38,7 @@ This command downloads a values file for a given release. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -57,4 +57,4 @@ This command downloads a values file for a given release. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_history.1 b/docs/man/man1/helm_history.1 index cde92fa82..40789ef92 100644 --- a/docs/man/man1/helm_history.1 +++ b/docs/man/man1/helm_history.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -75,7 +75,7 @@ REVISION UPDATED STATUS CHART DESCRIPTIO location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -94,4 +94,4 @@ REVISION UPDATED STATUS CHART DESCRIPTIO .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_home.1 b/docs/man/man1/helm_home.1 index a0887aac4..77024d53e 100644 --- a/docs/man/man1/helm_home.1 +++ b/docs/man/man1/helm_home.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -29,7 +29,7 @@ any helm configuration files live. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -48,4 +48,4 @@ any helm configuration files live. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_init.1 b/docs/man/man1/helm_init.1 index 9fea8ba40..74871ebe8 100644 --- a/docs/man/man1/helm_init.1 +++ b/docs/man/man1/helm_init.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -61,6 +61,10 @@ To dump a manifest containing the Tiller deployment YAML, combine the \fB\-\-net\-host\fP[=false] install tiller with net=host +.PP +\fB\-\-service\-account\fP="" + name of service account + .PP \fB\-\-skip\-refresh\fP[=false] do not refresh (download) the local repository cache @@ -109,7 +113,7 @@ To dump a manifest containing the Tiller deployment YAML, combine the location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -128,4 +132,4 @@ To dump a manifest containing the Tiller deployment YAML, combine the .SH HISTORY .PP -18\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_inspect.1 b/docs/man/man1/helm_inspect.1 index bce08dfcd..0783476c7 100644 --- a/docs/man/man1/helm_inspect.1 +++ b/docs/man/man1/helm_inspect.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -23,10 +23,26 @@ Inspect prints the contents of the Chart.yaml file and the values.yaml file. .SH OPTIONS +.PP +\fB\-\-ca\-file\fP="" + chart repository url where to locate the requested chart + +.PP +\fB\-\-cert\-file\fP="" + verify certificates of HTTPS\-enabled servers using this CA bundle + +.PP +\fB\-\-key\-file\fP="" + identify HTTPS client using this SSL key file + .PP \fB\-\-keyring\fP="~/.gnupg/pubring.gpg" path to the keyring containing public verification keys +.PP +\fB\-\-repo\fP="" + chart repository url where to locate the requested chart + .PP \fB\-\-verify\fP[=false] verify the provenance data for this chart @@ -46,7 +62,7 @@ Inspect prints the contents of the Chart.yaml file and the values.yaml file. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -65,4 +81,4 @@ Inspect prints the contents of the Chart.yaml file and the values.yaml file. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_inspect_chart.1 b/docs/man/man1/helm_inspect_chart.1 index 892a5f6d1..f728df410 100644 --- a/docs/man/man1/helm_inspect_chart.1 +++ b/docs/man/man1/helm_inspect_chart.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -20,10 +20,26 @@ of the Charts.yaml file .SH OPTIONS +.PP +\fB\-\-ca\-file\fP="" + chart repository url where to locate the requested chart + +.PP +\fB\-\-cert\-file\fP="" + verify certificates of HTTPS\-enabled servers using this CA bundle + +.PP +\fB\-\-key\-file\fP="" + identify HTTPS client using this SSL key file + .PP \fB\-\-keyring\fP="~/.gnupg/pubring.gpg" path to the keyring containing public verification keys +.PP +\fB\-\-repo\fP="" + chart repository url where to locate the requested chart + .PP \fB\-\-verify\fP[=false] verify the provenance data for this chart @@ -43,7 +59,7 @@ of the Charts.yaml file location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -62,4 +78,4 @@ of the Charts.yaml file .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_inspect_values.1 b/docs/man/man1/helm_inspect_values.1 index ed46871bd..c87dd9c60 100644 --- a/docs/man/man1/helm_inspect_values.1 +++ b/docs/man/man1/helm_inspect_values.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -20,10 +20,26 @@ of the values.yaml file .SH OPTIONS +.PP +\fB\-\-ca\-file\fP="" + chart repository url where to locate the requested chart + +.PP +\fB\-\-cert\-file\fP="" + verify certificates of HTTPS\-enabled servers using this CA bundle + +.PP +\fB\-\-key\-file\fP="" + identify HTTPS client using this SSL key file + .PP \fB\-\-keyring\fP="~/.gnupg/pubring.gpg" path to the keyring containing public verification keys +.PP +\fB\-\-repo\fP="" + chart repository url where to locate the requested chart + .PP \fB\-\-verify\fP[=false] verify the provenance data for this chart @@ -43,7 +59,7 @@ of the values.yaml file location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -62,4 +78,4 @@ of the values.yaml file .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_install.1 b/docs/man/man1/helm_install.1 index 521680e50..8fe99acb3 100644 --- a/docs/man/man1/helm_install.1 +++ b/docs/man/man1/helm_install.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -114,10 +114,26 @@ charts in a repository, use 'helm search'. .SH OPTIONS +.PP +\fB\-\-ca\-file\fP="" + verify certificates of HTTPS\-enabled servers using this CA bundle + +.PP +\fB\-\-cert\-file\fP="" + identify HTTPS client using this SSL certificate file + +.PP +\fB\-\-devel\fP[=false] + use development versions, too. Equivalent to version '>0.0.0\-a'. If \-\-version is set, this is ignored. + .PP \fB\-\-dry\-run\fP[=false] simulate an install +.PP +\fB\-\-key\-file\fP="" + identify HTTPS client using this SSL key file + .PP \fB\-\-keyring\fP="~/.gnupg/pubring.gpg" location of public keys used for verification @@ -142,6 +158,10 @@ charts in a repository, use 'helm search'. \fB\-\-replace\fP[=false] re\-use the given name, even if that name is already used. This is unsafe in production +.PP +\fB\-\-repo\fP="" + chart repository url where to locate the requested chart + .PP \fB\-\-set\fP=[] set values on the command line (can specify multiple or separate values with commas: key1=val1,key2=val2) @@ -197,7 +217,7 @@ charts in a repository, use 'helm search'. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -216,4 +236,4 @@ charts in a repository, use 'helm search'. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_lint.1 b/docs/man/man1/helm_lint.1 index 0ade88c5a..e0d522685 100644 --- a/docs/man/man1/helm_lint.1 +++ b/docs/man/man1/helm_lint.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -40,7 +40,7 @@ or recommendation, it will emit [WARNING] messages. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -59,4 +59,4 @@ or recommendation, it will emit [WARNING] messages. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_list.1 b/docs/man/man1/helm_list.1 index aef144aa7..d4fccf960 100644 --- a/docs/man/man1/helm_list.1 +++ b/docs/man/man1/helm_list.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -129,7 +129,7 @@ flag with the '\-\-offset' flag allows you to page through results. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -148,4 +148,4 @@ flag with the '\-\-offset' flag allows you to page through results. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_package.1 b/docs/man/man1/helm_package.1 index b50ef10d6..07185a4c2 100644 --- a/docs/man/man1/helm_package.1 +++ b/docs/man/man1/helm_package.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -63,7 +63,7 @@ Versioned chart archives are used by Helm package repositories. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -82,4 +82,4 @@ Versioned chart archives are used by Helm package repositories. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_plugin.1 b/docs/man/man1/helm_plugin.1 index 36bae074f..7af4f39fc 100644 --- a/docs/man/man1/helm_plugin.1 +++ b/docs/man/man1/helm_plugin.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -28,7 +28,7 @@ Manage client\-side Helm plugins. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -42,9 +42,9 @@ Manage client\-side Helm plugins. .SH SEE ALSO .PP -\fBhelm(1)\fP, \fBhelm\-plugin\-install(1)\fP, \fBhelm\-plugin\-list(1)\fP, \fBhelm\-plugin\-remove(1)\fP +\fBhelm(1)\fP, \fBhelm\-plugin\-install(1)\fP, \fBhelm\-plugin\-list(1)\fP, \fBhelm\-plugin\-remove(1)\fP, \fBhelm\-plugin\-update(1)\fP .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_plugin_install.1 b/docs/man/man1/helm_plugin_install.1 index fc4378048..d2a8d1326 100644 --- a/docs/man/man1/helm_plugin_install.1 +++ b/docs/man/man1/helm_plugin_install.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -34,7 +34,7 @@ install one or more Helm plugins location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -53,4 +53,4 @@ install one or more Helm plugins .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_plugin_list.1 b/docs/man/man1/helm_plugin_list.1 index 0db846a2c..5fcebd748 100644 --- a/docs/man/man1/helm_plugin_list.1 +++ b/docs/man/man1/helm_plugin_list.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -28,7 +28,7 @@ list installed Helm plugins location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -47,4 +47,4 @@ list installed Helm plugins .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_plugin_remove.1 b/docs/man/man1/helm_plugin_remove.1 index 9d3a20057..de64220dd 100644 --- a/docs/man/man1/helm_plugin_remove.1 +++ b/docs/man/man1/helm_plugin_remove.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -28,7 +28,7 @@ remove one or more Helm plugins location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -47,4 +47,4 @@ remove one or more Helm plugins .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_plugin_update.1 b/docs/man/man1/helm_plugin_update.1 new file mode 100644 index 000000000..ad7d70a17 --- /dev/null +++ b/docs/man/man1/helm_plugin_update.1 @@ -0,0 +1,50 @@ +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" +.nh +.ad l + + +.SH NAME +.PP +helm\-plugin\-update \- update one or more Helm plugins + + +.SH SYNOPSIS +.PP +\fBhelm plugin update \&...\fP + + +.SH DESCRIPTION +.PP +update one or more Helm plugins + + +.SH OPTIONS INHERITED FROM PARENT COMMANDS +.PP +\fB\-\-debug\fP[=false] + enable verbose output + +.PP +\fB\-\-home\fP="~/.helm" + location of your Helm config. Overrides $HELM\_HOME + +.PP +\fB\-\-host\fP="localhost:44134" + address of tiller. Overrides $HELM\_HOST + +.PP +\fB\-\-kube\-context\fP="" + name of the kubeconfig context to use + +.PP +\fB\-\-tiller\-namespace\fP="kube\-system" + namespace of tiller + + +.SH SEE ALSO +.PP +\fBhelm\-plugin(1)\fP + + +.SH HISTORY +.PP +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_repo.1 b/docs/man/man1/helm_repo.1 index e073dabcf..19b2da8a3 100644 --- a/docs/man/man1/helm_repo.1 +++ b/docs/man/man1/helm_repo.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -33,7 +33,7 @@ Example usage: location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -52,4 +52,4 @@ Example usage: .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_repo_add.1 b/docs/man/man1/helm_repo_add.1 index 19e2606ac..3cd9f790b 100644 --- a/docs/man/man1/helm_repo_add.1 +++ b/docs/man/man1/helm_repo_add.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -46,7 +46,7 @@ add a chart repository location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -65,4 +65,4 @@ add a chart repository .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_repo_index.1 b/docs/man/man1/helm_repo_index.1 index 260fa97f3..edfcda6f5 100644 --- a/docs/man/man1/helm_repo_index.1 +++ b/docs/man/man1/helm_repo_index.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -47,7 +47,7 @@ into the existing index, with local charts taking priority over existing charts. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -66,4 +66,4 @@ into the existing index, with local charts taking priority over existing charts. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_repo_list.1 b/docs/man/man1/helm_repo_list.1 index 33a97d816..7623f73fe 100644 --- a/docs/man/man1/helm_repo_list.1 +++ b/docs/man/man1/helm_repo_list.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -28,7 +28,7 @@ list chart repositories location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -47,4 +47,4 @@ list chart repositories .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_repo_remove.1 b/docs/man/man1/helm_repo_remove.1 index b6d48a50d..cfbf217a4 100644 --- a/docs/man/man1/helm_repo_remove.1 +++ b/docs/man/man1/helm_repo_remove.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -28,7 +28,7 @@ remove a chart repository location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -47,4 +47,4 @@ remove a chart repository .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_repo_update.1 b/docs/man/man1/helm_repo_update.1 index 1a46691e9..42bf511dd 100644 --- a/docs/man/man1/helm_repo_update.1 +++ b/docs/man/man1/helm_repo_update.1 @@ -1,11 +1,11 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l .SH NAME .PP -helm\-repo\-update \- update information on available charts in the chart repositories +helm\-repo\-update \- update information of available charts locally from chart repositories .SH SYNOPSIS @@ -33,7 +33,7 @@ future releases. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -52,4 +52,4 @@ future releases. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_reset.1 b/docs/man/man1/helm_reset.1 index 6d600d1d2..bf735591d 100644 --- a/docs/man/man1/helm_reset.1 +++ b/docs/man/man1/helm_reset.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -60,7 +60,7 @@ $HELM\_HOME (default \~/.helm/) location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -79,4 +79,4 @@ $HELM\_HOME (default \~/.helm/) .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_rollback.1 b/docs/man/man1/helm_rollback.1 index 909573969..d91ab881d 100644 --- a/docs/man/man1/helm_rollback.1 +++ b/docs/man/man1/helm_rollback.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -28,6 +28,10 @@ second is a revision (version) number. To see revision numbers, run \fB\-\-dry\-run\fP[=false] simulate a rollback +.PP +\fB\-\-force\fP[=false] + force resource update through delete/recreate if needed + .PP \fB\-\-no\-hooks\fP[=false] prevent hooks from running during rollback @@ -75,7 +79,7 @@ second is a revision (version) number. To see revision numbers, run location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -94,4 +98,4 @@ second is a revision (version) number. To see revision numbers, run .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_search.1 b/docs/man/man1/helm_search.1 index 7afc38a12..ac2467bf2 100644 --- a/docs/man/man1/helm_search.1 +++ b/docs/man/man1/helm_search.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -46,7 +46,7 @@ Repositories are managed with 'helm repo' commands. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -65,4 +65,4 @@ Repositories are managed with 'helm repo' commands. .SH HISTORY .PP -18\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_serve.1 b/docs/man/man1/helm_serve.1 index a01385ac2..a4a9c51da 100644 --- a/docs/man/man1/helm_serve.1 +++ b/docs/man/man1/helm_serve.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -39,7 +39,7 @@ for more information on hosting chart repositories in a production setting. address to listen on .PP -\fB\-\-repo\-path\fP="~/.helm/repository/local" +\fB\-\-repo\-path\fP="" local directory path from which to serve charts .PP @@ -57,7 +57,7 @@ for more information on hosting chart repositories in a production setting. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -76,4 +76,4 @@ for more information on hosting chart repositories in a production setting. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_status.1 b/docs/man/man1/helm_status.1 index 380ef03a8..8f2366808 100644 --- a/docs/man/man1/helm_status.1 +++ b/docs/man/man1/helm_status.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -61,7 +61,7 @@ The status consists of: location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -80,4 +80,4 @@ The status consists of: .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_test.1 b/docs/man/man1/helm_test.1 index 5936f81ee..6da36b33b 100644 --- a/docs/man/man1/helm_test.1 +++ b/docs/man/man1/helm_test.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -62,7 +62,7 @@ The tests to be run are defined in the chart that was installed. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -81,4 +81,4 @@ The tests to be run are defined in the chart that was installed. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_upgrade.1 b/docs/man/man1/helm_upgrade.1 index 0190285b6..5d5e919f6 100644 --- a/docs/man/man1/helm_upgrade.1 +++ b/docs/man/man1/helm_upgrade.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -57,14 +57,34 @@ $ helm upgrade \-\-set foo=bar \-\-set foo=newbar redis ./redis .SH OPTIONS +.PP +\fB\-\-ca\-file\fP="" + verify certificates of HTTPS\-enabled servers using this CA bundle + +.PP +\fB\-\-cert\-file\fP="" + identify HTTPS client using this SSL certificate file + +.PP +\fB\-\-devel\fP[=false] + use development versions, too. Equivalent to version '>0.0.0\-a'. If \-\-version is set, this is ignored. + .PP \fB\-\-dry\-run\fP[=false] simulate an upgrade +.PP +\fB\-\-force\fP[=false] + force resource update through delete/recreate if needed + .PP \fB\-i\fP, \fB\-\-install\fP[=false] if a release by this name doesn't already exist, run an install +.PP +\fB\-\-key\-file\fP="" + identify HTTPS client using this SSL key file + .PP \fB\-\-keyring\fP="~/.gnupg/pubring.gpg" path to the keyring that contains public signing keys @@ -81,6 +101,10 @@ $ helm upgrade \-\-set foo=bar \-\-set foo=newbar redis ./redis \fB\-\-recreate\-pods\fP[=false] performs pods restart for the resource if applicable +.PP +\fB\-\-repo\fP="" + chart repository url where to locate the requested chart + .PP \fB\-\-reset\-values\fP[=false] when upgrading, reset the values to the ones built into the chart @@ -144,7 +168,7 @@ $ helm upgrade \-\-set foo=bar \-\-set foo=newbar redis ./redis location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -163,4 +187,4 @@ $ helm upgrade \-\-set foo=bar \-\-set foo=newbar redis ./redis .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_verify.1 b/docs/man/man1/helm_verify.1 index dcd18e7fd..5297924ae 100644 --- a/docs/man/man1/helm_verify.1 +++ b/docs/man/man1/helm_verify.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -43,7 +43,7 @@ the 'helm package \-\-sign' command. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -62,4 +62,4 @@ the 'helm package \-\-sign' command. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/man/man1/helm_version.1 b/docs/man/man1/helm_version.1 index f97acf6da..1f1bf600d 100644 --- a/docs/man/man1/helm_version.1 +++ b/docs/man/man1/helm_version.1 @@ -1,4 +1,4 @@ -.TH "HELM" "1" "Apr 2017" "Auto generated by spf13/cobra" "" +.TH "HELM" "1" "May 2017" "Auto generated by spf13/cobra" "" .nh .ad l @@ -81,7 +81,7 @@ use '\-\-server'. location of your Helm config. Overrides $HELM\_HOME .PP -\fB\-\-host\fP="" +\fB\-\-host\fP="localhost:44134" address of tiller. Overrides $HELM\_HOST .PP @@ -100,4 +100,4 @@ use '\-\-server'. .SH HISTORY .PP -16\-Apr\-2017 Auto generated by spf13/cobra +19\-May\-2017 Auto generated by spf13/cobra diff --git a/docs/related.md b/docs/related.md index e471c653b..4787bc7cd 100644 --- a/docs/related.md +++ b/docs/related.md @@ -34,6 +34,7 @@ or [pull request](https://github.com/kubernetes/helm/pulls). - [helm-last](https://github.com/adamreese/helm-last) - Plugin to show the latest release - [helm-nuke](https://github.com/adamreese/helm-nuke) - Plugin to destroy all releases - [App Registry](https://github.com/app-registry/helm-plugin) - Plugin to manage charts via the [App Registry specification](https://github.com/app-registry/spec) +- [helm-secrets](https://github.com/futuresimple/helm-secrets) - Plugin to manage and store secrets safely We also encourage GitHub authors to use the [helm-plugin](https://github.com/search?q=topic%3Ahelm-plugin&type=Repositories) tag on their plugin repositories. @@ -53,11 +54,13 @@ Tools layered on top of Helm or Tiller. - [Cog](https://github.com/ohaiwalt/cog-helm) - Helm chart to deploy Cog on Kubernetes - [Monocular](https://github.com/helm/monocular) - Web UI for Helm Chart repositories - [Helm Chart Publisher](https://github.com/luizbafilho/helm-chart-publisher) - HTTP API for publishing Helm Charts in an easy way +- [Armada](https://github.com/att-comdev/armada) - Manage prefixed releases throughout various Kubernetes namespaces, and removes completed jobs for complex deployments. Used by the [Openstack-Helm](https://github.com/openstack/openstack-helm) team. ## Helm Included Platforms, distributions, and services that include Helm support. +- [Kubernetic](https://kubernetic.com/) - Kubernetes Desktop Client - [Cabin](http://www.skippbox.com/cabin/) - Mobile App for Managing Kubernetes - [Qstack](https://qstack.com) - [Fabric8](https://fabric8.io) - Integrated development platform for Kubernetes diff --git a/docs/using_helm.md b/docs/using_helm.md index 00793e8e5..e6b1b2b65 100755 --- a/docs/using_helm.md +++ b/docs/using_helm.md @@ -215,12 +215,13 @@ You can then override any of these settings in a YAML formatted file, and then pass that file during installation. ```console -$ echo 'mariadbUser: user0` > config.yaml +$ echo '{mariadbUser: user0, mariadbDatabase: user0db}' > config.yaml $ helm install -f config.yaml stable/mariadb ``` -The above will set the default MariaDB user to `user0`, but accept all -the rest of the defaults for that chart. +The above will create a default MariaDB user with the name `user0`, and +grant this user access to a newly created `user0db` database, but will +accept all the rest of the defaults for that chart. There are two ways to pass configuration data during install: @@ -355,7 +356,7 @@ is not a full list of cli flags. To see a description of all flags, just run This defaults to 300 (5 minutes) - `--wait`: Waits until all Pods are in a ready state, PVCs are bound, Deployments have minimum (`Desired` minus `maxUnavailable`) Pods in ready state and - Services have and IP address (and Ingress if a `LoadBalancer`) before + Services have an IP address (and Ingress if a `LoadBalancer`) before marking the release as successful. It will wait for as long as the `--timeout` value. If timeout is reached, the release will be marked as `FAILED`. @@ -473,6 +474,15 @@ Note: The `stable` repository is managed on the [Kubernetes Charts GitHub repository](https://github.com/kubernetes/charts). That project accepts chart source code, and (after audit) packages those for you. +## Tiller, Namespaces and RBAC +In some cases you may wish to scope Tiller or deploy multiple Tillers to a single cluster. Here are some best practices when operating in those circumstances. + +1. Tiller can be [installed](install.md) into any namespace. By default, it is installed into kube-system. You can run multiple Tillers provided they each run in their own namespace. +2. Limiting Tiller to only be able to install into specific namespaces and/or resource types is controlled by Kubernetes [RBAC](https://kubernetes.io/docs/admin/authorization/rbac/) roles and rolebindings. +3. Release names are unique PER TILLER INSTANCE. +4. Charts should only contain resources that exist in a single namespace. +5. It is not recommended to have multiple Tillers configured to manage resources in the same namespace. + ## Conclusion This chapter has covered the basic usage patterns of the `helm` client, diff --git a/glide.lock b/glide.lock index 3524e3dcd..efefa3de1 100644 --- a/glide.lock +++ b/glide.lock @@ -1,5 +1,5 @@ -hash: e9366bddb36a7120a832959c89f72f75d56b9f10f84820420795c668d9cb0987 -updated: 2017-04-27T14:23:27.66000871-06:00 +hash: f705b8873fe2e7fb0cf436e2485edf218f558ae726015e6d59490d53464ae536 +updated: 2017-05-17T12:47:01.838888143-06:00 imports: - name: bitbucket.org/ww/goautoneg version: 75cd24fc2f2c2a2088577d12123ddee5f54e0675 @@ -20,6 +20,8 @@ imports: version: 3ac7bf7a47d159a033b107610db8a1b6575507a4 subpackages: - quantile +- name: github.com/BurntSushi/toml + version: b26d9c308763d68093482582cea63d69be07a0f0 - name: github.com/chai2010/gettext-go version: bf70f2a70fb1b1f36d90d671a72795984eab0fcb - name: github.com/coreos/go-oidc @@ -47,7 +49,7 @@ imports: - name: github.com/dgrijalva/jwt-go version: 01aeca54ebda6e0fbfafd0a524d234159c05ec20 - name: github.com/docker/distribution - version: cd27f179f2c10c5d300e6d09025b538c475b0d51 + version: 03efb43768979f4d2ea5187bef995656441829e5 subpackages: - digest - reference @@ -100,6 +102,8 @@ imports: version: ba18e35c5c1b36ef6334cad706eb681153d2d379 - name: github.com/exponent-io/jsonpath version: d6023ce2651d8eafb5c75bb0c7167536102ec9f5 +- name: github.com/facebookgo/atomicfile + version: 2de1f203e7d5e386a6833233882782932729f27e - name: github.com/facebookgo/symwalk version: 42004b9f322246749dd73ad71008b1f3160c0052 - name: github.com/ghodss/yaml @@ -150,6 +154,8 @@ imports: version: 34abd90a014618f61222a1b0a7b7eb834a2d0dc3 - name: github.com/howeyc/gopass version: 3ca23474a7c7203e0a0a070fd33508f6efdb9b3d +- name: github.com/huandu/xstrings + version: 3959339b333561bf62a38b424fd41517c2c90f40 - name: github.com/imdario/mergo version: 6633656539c1639d9d78127b7d47c622b5d7b6dc - name: github.com/inconshreveable/mousetrap @@ -167,9 +173,9 @@ imports: - name: github.com/Masterminds/semver version: 3f0ab6d4ab4bed1c61caf056b63a6e62190c7801 - name: github.com/Masterminds/sprig - version: 23597e5f6ad0e4d590e71314bfd0251a4a3cf849 + version: 9526be0327b26ad31aa70296a7b10704883976d5 - name: github.com/Masterminds/vcs - version: 795e20f901c3d561de52811fb3488a2cb2c8588b + version: 3084677c2c188840777bff30054f2b553729d329 - name: github.com/mattn/go-runewidth version: d6bea18f789704b5f83375793155289da36a3c7f - name: github.com/matttproud/golang_protobuf_extensions @@ -180,10 +186,6 @@ imports: version: ad45545899c7b13c020ea92b2072220eefad42b8 - name: github.com/naoina/go-stringutil version: 6b638e95a32d0c1131db0e7fe83775cbea4a0d0b -- name: github.com/naoina/toml - version: 751171607256bb66e64c9f0220c00662420c38e9 - subpackages: - - ast - name: github.com/pborman/uuid version: ca53cad383cad2479bbba7f7a1a05797ec1386e4 - name: github.com/prometheus/client_golang @@ -471,7 +473,7 @@ imports: - util/integer - util/jsonpath - name: k8s.io/kubernetes - version: 477efc3cbe6a7effca06bd1452fa356e2201e1ee + version: 0480917b552be33e2dba47386e51decb1a211df6 subpackages: - federation/apis/federation - federation/apis/federation/install @@ -623,6 +625,8 @@ imports: - pkg/watch/json - name: vbom.ml/util version: db5cfe13f5cc80a4990d98e2e1b0707a4d1a5394 + repo: https://github.com/fvbommel/util.git + vcs: git subpackages: - sortorder testImports: diff --git a/glide.yaml b/glide.yaml index 62a0b58bd..7b298a29e 100644 --- a/glide.yaml +++ b/glide.yaml @@ -10,8 +10,12 @@ import: version: 9ff6c6923cfffbcd502984b8e0c80539a94968b7 - package: github.com/Masterminds/vcs version: ~1.11.0 + + # Pin version of mergo that is compatible with both sprig and Kubernetes +- package: github.com/imdario/mergo + version: 6633656539c1639d9d78127b7d47c622b5d7b6dc - package: github.com/Masterminds/sprig - version: ^2.10 + version: ^2.12 - package: github.com/ghodss/yaml - package: github.com/Masterminds/semver version: ~1.2.3 @@ -37,14 +41,20 @@ import: - package: github.com/gobwas/glob version: ^0.2.1 - package: github.com/evanphx/json-patch +- package: github.com/facebookgo/atomicfile - package: github.com/facebookgo/symwalk -- package: github.com/naoina/toml - version: ~0.1.0 +- package: github.com/BurntSushi/toml + version: ~0.3.0 - package: github.com/naoina/go-stringutil version: ~0.1.0 - package: github.com/chai2010/gettext-go - package: github.com/prometheus/client_golang version: v0.8.0 +- package: vbom.ml/util + repo: https://github.com/fvbommel/util.git + vcs: git +- package: github.com/docker/distribution + version: ~v2.4.0 testImports: - package: github.com/stretchr/testify version: ^1.1.4 diff --git a/pkg/chartutil/create.go b/pkg/chartutil/create.go index 9db26ff83..8ab05e896 100644 --- a/pkg/chartutil/create.go +++ b/pkg/chartutil/create.go @@ -195,7 +195,7 @@ const defaultNotes = `1. Get the application URL by running these commands: {{- else if contains "NodePort" .Values.service.type }} export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "fullname" . }}) export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") - echo http://$NODE_IP:$NODE_PORT/login + echo http://$NODE_IP:$NODE_PORT {{- else if contains "LoadBalancer" .Values.service.type }} NOTE: It may take a few minutes for the LoadBalancer IP to be available. You can watch the status of by running 'kubectl get svc -w {{ template "fullname" . }}' diff --git a/pkg/chartutil/files.go b/pkg/chartutil/files.go index 6d6f17ef5..470ed711e 100644 --- a/pkg/chartutil/files.go +++ b/pkg/chartutil/files.go @@ -16,16 +16,17 @@ limitations under the License. package chartutil import ( + "bytes" "encoding/base64" "encoding/json" "path" "strings" - yaml "gopkg.in/yaml.v2" + "github.com/ghodss/yaml" + "github.com/BurntSushi/toml" "github.com/gobwas/glob" "github.com/golang/protobuf/ptypes/any" - "github.com/naoina/toml" ) // Files is a map of files in a chart that can be accessed from a template. @@ -197,12 +198,13 @@ func FromYaml(str string) map[string]interface{} { // // This is designed to be called from a template. func ToToml(v interface{}) string { - data, err := toml.Marshal(v) + b := bytes.NewBuffer(nil) + e := toml.NewEncoder(b) + err := e.Encode(v) if err != nil { - // Swallow errors inside of a template. - return "" + return err.Error() } - return string(data) + return b.String() } // ToJson takes an interface, marshals it to json, and returns a string. It will diff --git a/pkg/chartutil/files_test.go b/pkg/chartutil/files_test.go index e87fb7187..731c82e6f 100644 --- a/pkg/chartutil/files_test.go +++ b/pkg/chartutil/files_test.go @@ -112,9 +112,9 @@ func TestToYaml(t *testing.T) { } func TestToToml(t *testing.T) { - expect := "foo=\"bar\"\n" + expect := "foo = \"bar\"\n" v := struct { - Foo string `json:"foo"` + Foo string `toml:"foo"` }{ Foo: "bar", } @@ -122,6 +122,18 @@ func TestToToml(t *testing.T) { if got := ToToml(v); got != expect { t.Errorf("Expected %q, got %q", expect, got) } + + // Regression for https://github.com/kubernetes/helm/issues/2271 + dict := map[string]map[string]string{ + "mast": { + "sail": "white", + }, + } + got := ToToml(dict) + expect = "[mast]\n sail = \"white\"\n" + if got != expect { + t.Errorf("Expected:\n%s\nGot\n%s\n", expect, got) + } } func TestFromYaml(t *testing.T) { diff --git a/pkg/chartutil/requirements.go b/pkg/chartutil/requirements.go index 5f9fa3b3d..fbd686b91 100644 --- a/pkg/chartutil/requirements.go +++ b/pkg/chartutil/requirements.go @@ -65,6 +65,8 @@ type Dependency struct { // ImportValues holds the mapping of source values to parent key to be imported. Each item can be a // string or pair of child/parent sublist items. ImportValues []interface{} `json:"import-values"` + // Alias usable alias to be used for the chart + Alias string `json:"alias"` } // ErrNoRequirementsFile to detect error condition @@ -216,6 +218,32 @@ func ProcessRequirementsTags(reqs *Requirements, cvals Values) { } +func getAliasDependency(charts []*chart.Chart, aliasChart *Dependency) *chart.Chart { + var chartFound chart.Chart + for _, existingChart := range charts { + if existingChart == nil { + continue + } + if existingChart.Metadata == nil { + continue + } + if existingChart.Metadata.Name != aliasChart.Name { + continue + } + if existingChart.Metadata.Version != aliasChart.Version { + continue + } + chartFound = *existingChart + newMetadata := *existingChart.Metadata + if aliasChart.Alias != "" { + newMetadata.Name = aliasChart.Alias + } + chartFound.Metadata = &newMetadata + return &chartFound + } + return nil +} + // ProcessRequirementsEnabled removes disabled charts from dependencies func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { reqs, err := LoadRequirements(c) @@ -228,6 +256,18 @@ func ProcessRequirementsEnabled(c *chart.Chart, v *chart.Config) error { // no requirements to process return nil } + + var chartDependencies []*chart.Chart + for _, req := range reqs.Dependencies { + if chartDependency := getAliasDependency(c.Dependencies, req); chartDependency != nil { + chartDependencies = append(chartDependencies, chartDependency) + } + if req.Alias != "" { + req.Name = req.Alias + } + } + c.Dependencies = chartDependencies + // set all to true for _, lr := range reqs.Dependencies { lr.Enabled = true @@ -322,22 +362,17 @@ func getParents(c *chart.Chart, out []*chart.Chart) []*chart.Chart { } // processImportValues merges values from child to parent based on the chart's dependencies' ImportValues field. -func processImportValues(c *chart.Chart, v *chart.Config) error { +func processImportValues(c *chart.Chart) error { reqs, err := LoadRequirements(c) if err != nil { return err } - // combine chart values and its dependencies' values - cvals, err := CoalesceValues(c, v) + // combine chart values and empty config to get Values + cvals, err := CoalesceValues(c, &chart.Config{}) if err != nil { return err } - nv := v.GetValues() - b := make(map[string]interface{}, len(nv)) - // convert values to map - for kk, vvv := range nv { - b[kk] = vvv - } + b := make(map[string]interface{}, 0) // import values from each dependency if specified in import-values for _, r := range reqs.Dependencies { if len(r.ImportValues) > 0 { @@ -392,10 +427,10 @@ func processImportValues(c *chart.Chart, v *chart.Config) error { } // ProcessRequirementsImportValues imports specified chart values from child to parent. -func ProcessRequirementsImportValues(c *chart.Chart, v *chart.Config) error { +func ProcessRequirementsImportValues(c *chart.Chart) error { pc := getParents(c, nil) for i := len(pc) - 1; i >= 0; i-- { - processImportValues(pc[i], v) + processImportValues(pc[i]) } return nil diff --git a/pkg/chartutil/requirements_test.go b/pkg/chartutil/requirements_test.go index b5dbe35dc..65d59e52f 100644 --- a/pkg/chartutil/requirements_test.go +++ b/pkg/chartutil/requirements_test.go @@ -282,7 +282,7 @@ func TestProcessRequirementsImportValues(t *testing.T) { } func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Config, e map[string]string) { - err := ProcessRequirementsImportValues(c, v) + err := ProcessRequirementsImportValues(c) if err != nil { t.Errorf("Error processing import values requirements %v", err) } @@ -320,3 +320,73 @@ func verifyRequirementsImportValues(t *testing.T, c *chart.Chart, v *chart.Confi } } + +func TestGetAliasDependency(t *testing.T) { + c, err := Load("testdata/frobnitz") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + req, err := LoadRequirements(c) + if err != nil { + t.Fatalf("Failed to load requirement for testdata: %s", err) + } + if len(req.Dependencies) == 0 { + t.Fatalf("There are no requirements to test") + } + + // Success case + aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]) + if aliasChart == nil { + t.Fatalf("Failed to get dependency chart for alias %s", req.Dependencies[0].Name) + } + if req.Dependencies[0].Alias != "" { + if aliasChart.Metadata.Name != req.Dependencies[0].Alias { + t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Alias, aliasChart.Metadata.Name) + } + } else if aliasChart.Metadata.Name != req.Dependencies[0].Name { + t.Fatalf("Dependency chart name should be %s but got %s", req.Dependencies[0].Name, aliasChart.Metadata.Name) + } + + // Failure case + req.Dependencies[0].Name = "something-else" + if aliasChart := getAliasDependency(c.Dependencies, req.Dependencies[0]); aliasChart != nil { + t.Fatalf("expected no chart but got %s", aliasChart.Metadata.Name) + } +} + +func TestDependentChartAliases(t *testing.T) { + c, err := Load("testdata/dependent-chart-alias") + if err != nil { + t.Fatalf("Failed to load testdata: %s", err) + } + + if len(c.Dependencies) == 0 { + t.Fatal("There are no dependencies to run this test") + } + + origLength := len(c.Dependencies) + if err := ProcessRequirementsEnabled(c, c.Values); err != nil { + t.Fatalf("Expected no errors but got %q", err) + } + + if len(c.Dependencies) == origLength { + t.Fatal("Expected alias dependencies to be added, but did not got that") + } + + reqmts, err := LoadRequirements(c) + if err != nil { + t.Fatalf("Cannot load requirements for test chart, %v", err) + } + + // var expectedDependencyCharts int + // for _, reqmt := range reqmts.Dependencies { + // expectedDependencyCharts++ + // if len(reqmt.Alias) >= 0 { + // expectedDependencyCharts += len(reqmt.Alias) + // } + // } + if len(c.Dependencies) != len(reqmts.Dependencies) { + t.Fatalf("Expected number of chart dependencies %d, but got %d", len(reqmts.Dependencies), len(c.Dependencies)) + } + +} diff --git a/pkg/chartutil/testdata/dependent-chart-alias/.helmignore b/pkg/chartutil/testdata/dependent-chart-alias/.helmignore new file mode 100644 index 000000000..9973a57b8 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/.helmignore @@ -0,0 +1 @@ +ignore/ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml new file mode 100644 index 000000000..7c071c27b --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +name: frobnitz +description: This is a frobnitz. +version: "1.2.3" +keywords: + - frobnitz + - sprocket + - dodad +maintainers: + - name: The Helm Team + email: helm@example.com + - name: Someone Else + email: nobody@example.com +sources: + - https://example.com/foo/bar +home: http://example.com +icon: https://example.com/64x64.png diff --git a/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt b/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt new file mode 100644 index 000000000..2010438c2 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/INSTALL.txt @@ -0,0 +1 @@ +This is an install document. The client may display this. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/LICENSE b/pkg/chartutil/testdata/dependent-chart-alias/LICENSE new file mode 100644 index 000000000..6121943b1 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/LICENSE @@ -0,0 +1 @@ +LICENSE placeholder. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/README.md b/pkg/chartutil/testdata/dependent-chart-alias/README.md new file mode 100644 index 000000000..8cf4cc3d7 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/README.md @@ -0,0 +1,11 @@ +# Frobnitz + +This is an example chart. + +## Usage + +This is an example. It has no usage. + +## Development + +For developer info, see the top-level repository. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me b/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me new file mode 100644 index 000000000..2cecca682 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/_ignore_me @@ -0,0 +1 @@ +This should be ignored by the loader, but may be included in a chart. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml new file mode 100644 index 000000000..38a4aaa54 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/Chart.yaml @@ -0,0 +1,4 @@ +name: alpine +description: Deploy a basic Alpine Linux pod +version: 0.1.0 +home: https://k8s.io/helm diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md new file mode 100644 index 000000000..a7c84fc41 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/README.md @@ -0,0 +1,9 @@ +This example was generated using the command `helm create alpine`. + +The `templates/` directory contains a very simple pod resource with a +couple of parameters. + +The `values.toml` file contains the default values for the +`alpine-pod.yaml` template. + +You can install this example using `helm install docs/examples/alpine`. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml new file mode 100644 index 000000000..171e36156 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/Chart.yaml @@ -0,0 +1,4 @@ +name: mast1 +description: A Helm chart for Kubernetes +version: 0.1.0 +home: "" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml new file mode 100644 index 000000000..42c39c262 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast1/values.yaml @@ -0,0 +1,4 @@ +# Default values for mast1. +# This is a YAML-formatted file. +# Declare name/value pairs to be passed into your templates. +# name = "value" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz new file mode 100644 index 000000000..ced5a4a6a Binary files /dev/null and b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/charts/mast2-0.1.0.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml new file mode 100644 index 000000000..08cf3c2c1 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/templates/alpine-pod.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Pod +metadata: + name: {{.Release.Name}}-{{.Chart.Name}} + labels: + heritage: {{.Release.Service}} + chartName: {{.Chart.Name}} + chartVersion: {{.Chart.Version | quote}} + annotations: + "helm.sh/created": "{{.Release.Time.Seconds}}" +spec: + restartPolicy: {{default "Never" .restart_policy}} + containers: + - name: waiter + image: "alpine:3.3" + command: ["/bin/sleep","9000"] diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml new file mode 100644 index 000000000..6c2aab7ba --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/charts/alpine/values.yaml @@ -0,0 +1,2 @@ +# The pod name +name: "my-alpine" diff --git a/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz b/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz new file mode 100644 index 000000000..3af333e76 Binary files /dev/null and b/pkg/chartutil/testdata/dependent-chart-alias/charts/mariner-4.3.2.tgz differ diff --git a/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md b/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md new file mode 100644 index 000000000..d40747caf --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/docs/README.md @@ -0,0 +1 @@ +This is a placeholder for documentation. diff --git a/pkg/chartutil/testdata/dependent-chart-alias/icon.svg b/pkg/chartutil/testdata/dependent-chart-alias/icon.svg new file mode 100644 index 000000000..892130606 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/icon.svg @@ -0,0 +1,8 @@ + + + Example icon + + + diff --git a/pkg/chartutil/testdata/dependent-chart-alias/ignore/me.txt b/pkg/chartutil/testdata/dependent-chart-alias/ignore/me.txt new file mode 100644 index 000000000..e69de29bb diff --git a/pkg/chartutil/testdata/dependent-chart-alias/requirements.lock b/pkg/chartutil/testdata/dependent-chart-alias/requirements.lock new file mode 100644 index 000000000..6fcc2ed9f --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/requirements.lock @@ -0,0 +1,8 @@ +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts +digest: invalid diff --git a/pkg/chartutil/testdata/dependent-chart-alias/requirements.yaml b/pkg/chartutil/testdata/dependent-chart-alias/requirements.yaml new file mode 100644 index 000000000..aab6cddf7 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/requirements.yaml @@ -0,0 +1,12 @@ +dependencies: + - name: alpine + version: "0.1.0" + repository: https://example.com/charts + - name: mariner + version: "4.3.2" + repository: https://example.com/charts + alias: mariners2 + - name: mariner + version: "4.3.2" + repository: https://example.com/charts + alias: mariners1 diff --git a/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl b/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl new file mode 100644 index 000000000..c651ee6a0 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/templates/template.tpl @@ -0,0 +1 @@ +Hello {{.Name | default "world"}} diff --git a/pkg/chartutil/testdata/dependent-chart-alias/values.yaml b/pkg/chartutil/testdata/dependent-chart-alias/values.yaml new file mode 100644 index 000000000..61f501258 --- /dev/null +++ b/pkg/chartutil/testdata/dependent-chart-alias/values.yaml @@ -0,0 +1,6 @@ +# A values file contains configuration. + +name: "Some Name" + +section: + name: "Name in a section" diff --git a/pkg/downloader/chart_downloader.go b/pkg/downloader/chart_downloader.go index 1a4c3a7a9..15e97f6a1 100644 --- a/pkg/downloader/chart_downloader.go +++ b/pkg/downloader/chart_downloader.go @@ -66,7 +66,7 @@ type ChartDownloader struct { // HelmHome is the $HELM_HOME. HelmHome helmpath.Home // Getter collection for the operation - Getters []getter.Prop + Getters getter.Providers } // DownloadTo retrieves a chart. Depending on the settings, it may also download a provenance file. @@ -161,7 +161,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge // If there is no special config, return the default HTTP client and // swallow the error. if err == ErrNoOwnerRepo { - getterConstructor, err := getter.ConstructorByScheme(c.Getters, u.Scheme) + getterConstructor, err := c.Getters.ByScheme(u.Scheme) if err != nil { return u, nil, err } @@ -202,7 +202,7 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge cv, err := i.Get(chartName, version) if err != nil { - return u, r.Client, fmt.Errorf("chart %q not found in %s index. (try 'helm repo update'). %s", chartName, r.Config.Name, err) + return u, r.Client, fmt.Errorf("chart %q matching %s not found in %s index. (try 'helm repo update'). %s", chartName, version, r.Config.Name, err) } if len(cv.URLs) == 0 { @@ -215,6 +215,12 @@ func (c *ChartDownloader) ResolveChartVersion(ref, version string) (*url.URL, ge return u, r.Client, fmt.Errorf("invalid chart URL format: %s", ref) } + // If the URL is relative (no scheme), prepend the chart repo's base URL + if !u.IsAbs() { + u, err = url.Parse(rc.URL + "/" + u.Path) + return u, r.Client, err + } + return u, r.Client, nil } diff --git a/pkg/downloader/chart_downloader_test.go b/pkg/downloader/chart_downloader_test.go index 17a66145b..4049b7979 100644 --- a/pkg/downloader/chart_downloader_test.go +++ b/pkg/downloader/chart_downloader_test.go @@ -25,8 +25,7 @@ import ( "path/filepath" "testing" - "k8s.io/helm/pkg/getter/defaultgetters" - "k8s.io/helm/pkg/getter/http" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/helm/helmpath" "k8s.io/helm/pkg/repo" @@ -43,6 +42,7 @@ func TestResolveChartRef(t *testing.T) { {name: "full URL, with authentication", ref: "http://username:password@example.com/foo-1.2.3.tgz", expect: "http://username:password@example.com/foo-1.2.3.tgz"}, {name: "reference, testing repo", ref: "testing/alpine", expect: "http://example.com/alpine-1.2.3.tgz"}, {name: "reference, version, testing repo", ref: "testing/alpine", version: "0.2.0", expect: "http://example.com/alpine-0.2.0.tgz"}, + {name: "reference, version, malformed repo", ref: "malformed/alpine", version: "1.2.3", expect: "http://dl.example.com/alpine-1.2.3.tgz"}, {name: "full URL, HTTPS, irrelevant version", ref: "https://example.com/foo-1.2.3.tgz", version: "0.1.0", expect: "https://example.com/foo-1.2.3.tgz", fail: true}, {name: "full URL, file", ref: "file:///foo-1.2.3.tgz", fail: true}, {name: "invalid", ref: "invalid-1.2.3", fail: true}, @@ -52,7 +52,7 @@ func TestResolveChartRef(t *testing.T) { c := ChartDownloader{ HelmHome: helmpath.Home("testdata/helmhome"), Out: os.Stderr, - Getters: defaultgetters.Get(environment.EnvSettings{}), + Getters: getter.All(environment.EnvSettings{}), } for _, tt := range tests { @@ -89,7 +89,12 @@ func TestDownload(t *testing.T) { })) defer srv.Close() - getter, err := httpgetter.New(srv.URL, "", "", "") + provider, err := getter.ByScheme("http", environment.EnvSettings{}) + if err != nil { + t.Fatal("No http provider found") + } + + getter, err := provider.New(srv.URL, "", "", "") if err != nil { t.Fatal(err) } @@ -182,7 +187,7 @@ func TestDownloadTo(t *testing.T) { Out: os.Stderr, Verify: VerifyAlways, Keyring: "testdata/helm-test-key.pub", - Getters: defaultgetters.Get(environment.EnvSettings{}), + Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" where, v, err := c.DownloadTo(srv.URL()+cname, "", dest) @@ -245,7 +250,7 @@ func TestDownloadTo_VerifyLater(t *testing.T) { HelmHome: hh, Out: os.Stderr, Verify: VerifyLater, - Getters: defaultgetters.Get(environment.EnvSettings{}), + Getters: getter.All(environment.EnvSettings{}), } cname := "/signtest-0.1.0.tgz" where, _, err := c.DownloadTo(srv.URL()+cname, "", dest) @@ -274,7 +279,7 @@ func TestScanReposForURL(t *testing.T) { HelmHome: hh, Out: os.Stderr, Verify: VerifyLater, - Getters: defaultgetters.Get(environment.EnvSettings{}), + Getters: getter.All(environment.EnvSettings{}), } u := "http://example.com/alpine-0.2.0.tgz" diff --git a/pkg/downloader/manager.go b/pkg/downloader/manager.go index 4f881591a..aea489edf 100644 --- a/pkg/downloader/manager.go +++ b/pkg/downloader/manager.go @@ -56,7 +56,7 @@ type Manager struct { // SkipUpdate indicates that the repository should not be updated first. SkipUpdate bool // Getter collection for the operation - Getters []getter.Prop + Getters []getter.Provider } // Build rebuilds a local charts directory from a lockfile. @@ -572,5 +572,5 @@ func tarFromLocalDir(chartpath string, name string, repo string, version string) return ch.Metadata.Version, err } - return "", fmt.Errorf("Can't get a valid version for dependency %s.", name) + return "", fmt.Errorf("can't get a valid version for dependency %s", name) } diff --git a/pkg/downloader/testdata/helmhome/repository/cache/malformed-index.yaml b/pkg/downloader/testdata/helmhome/repository/cache/malformed-index.yaml new file mode 100644 index 000000000..1956e9f83 --- /dev/null +++ b/pkg/downloader/testdata/helmhome/repository/cache/malformed-index.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +entries: + alpine: + - name: alpine + urls: + - alpine-1.2.3.tgz + checksum: 0e6661f193211d7a5206918d42f5c2a9470b737d + home: https://k8s.io/helm + sources: + - https://github.com/kubernetes/helm + version: 1.2.3 + description: Deploy a basic Alpine Linux pod + keywords: [] + maintainers: [] + engine: "" + icon: "" diff --git a/pkg/downloader/testdata/helmhome/repository/repositories.yaml b/pkg/downloader/testdata/helmhome/repository/repositories.yaml index 200f370bd..9b0cfe972 100644 --- a/pkg/downloader/testdata/helmhome/repository/repositories.yaml +++ b/pkg/downloader/testdata/helmhome/repository/repositories.yaml @@ -8,3 +8,5 @@ repositories: url: "http://username:password@example.com" - name: kubernetes-charts url: "http://example.com/charts" + - name: malformed + url: "http://dl.example.com" diff --git a/pkg/engine/engine.go b/pkg/engine/engine.go index a987fb3b8..595ec2c19 100644 --- a/pkg/engine/engine.go +++ b/pkg/engine/engine.go @@ -20,6 +20,7 @@ import ( "bytes" "fmt" "path" + "sort" "strings" "text/template" @@ -143,12 +144,12 @@ func (e *Engine) alterFuncMap(t *template.Template) template.FuncMap { } // Add the 'include' function here so we can close over t. - funcMap["include"] = func(name string, data interface{}) string { + funcMap["include"] = func(name string, data interface{}) (string, error) { buf := bytes.NewBuffer(nil) if err := t.ExecuteTemplate(buf, name, data); err != nil { - buf.WriteString(err.Error()) + return "", err } - return buf.String() + return buf.String(), nil } // Add the 'required' function here @@ -203,9 +204,14 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { funcMap := e.alterFuncMap(t) + // We want to parse the templates in a predictable order. The order favors + // higher-level (in file system) templates over deeply nested templates. + keys := sortTemplates(tpls) + files := []string{} - for fname, r := range tpls { + for _, fname := range keys { + r := tpls[fname] t = t.New(fname).Funcs(funcMap) if _, err := t.Parse(r.tpl); err != nil { return map[string]string{}, fmt.Errorf("parse error in %q: %s", fname, err) @@ -227,6 +233,11 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { rendered := make(map[string]string, len(files)) var buf bytes.Buffer for _, file := range files { + // Don't render partials. We don't care out the direct output of partials. + // They are only included from other templates. + if strings.HasPrefix(path.Base(file), "_") { + continue + } // At render time, add information about the template that is being rendered. vals := tpls[file].vals vals["Template"] = map[string]interface{}{"Name": file, "BasePath": tpls[file].basePath} @@ -244,6 +255,30 @@ func (e *Engine) render(tpls map[string]renderable) (map[string]string, error) { return rendered, nil } +func sortTemplates(tpls map[string]renderable) []string { + keys := make([]string, len(tpls)) + i := 0 + for key := range tpls { + keys[i] = key + i++ + } + sort.Sort(sort.Reverse(byPathLen(keys))) + return keys +} + +type byPathLen []string + +func (p byPathLen) Len() int { return len(p) } +func (p byPathLen) Swap(i, j int) { p[j], p[i] = p[i], p[j] } +func (p byPathLen) Less(i, j int) bool { + a, b := p[i], p[j] + ca, cb := strings.Count(a, "/"), strings.Count(b, "/") + if ca == cb { + return strings.Compare(a, b) == -1 + } + return ca < cb +} + // allTemplates returns all templates for a chart and its dependencies. // // As it goes, it also prepares the values in a scope-sensitive manner. diff --git a/pkg/engine/engine_test.go b/pkg/engine/engine_test.go index fcf9c1c64..7da4a9103 100644 --- a/pkg/engine/engine_test.go +++ b/pkg/engine/engine_test.go @@ -27,6 +27,37 @@ import ( "github.com/golang/protobuf/ptypes/any" ) +func TestSortTemplates(t *testing.T) { + tpls := map[string]renderable{ + "/mychart/templates/foo.tpl": {}, + "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl": {}, + "/mychart/templates/bar.tpl": {}, + "/mychart/templates/charts/foo/templates/bar.tpl": {}, + "/mychart/templates/_foo.tpl": {}, + "/mychart/templates/charts/foo/templates/foo.tpl": {}, + "/mychart/templates/charts/bar/templates/foo.tpl": {}, + } + got := sortTemplates(tpls) + if len(got) != len(tpls) { + t.Fatal("Sorted results are missing templates") + } + + expect := []string{ + "/mychart/templates/charts/foo/charts/bar/templates/foo.tpl", + "/mychart/templates/charts/foo/templates/foo.tpl", + "/mychart/templates/charts/foo/templates/bar.tpl", + "/mychart/templates/charts/bar/templates/foo.tpl", + "/mychart/templates/foo.tpl", + "/mychart/templates/bar.tpl", + "/mychart/templates/_foo.tpl", + } + for i, e := range expect { + if got[i] != e { + t.Errorf("expected %q, got %q at index %d\n\tExp: %#v\n\tGot: %#v", e, got[i], i, expect, got) + } + } +} + func TestEngine(t *testing.T) { e := New() diff --git a/pkg/getter/defaultgetters/default.go b/pkg/getter/defaultgetters/default.go deleted file mode 100644 index 8e5f0c862..000000000 --- a/pkg/getter/defaultgetters/default.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2016 The Kubernetes Authors All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package defaultgetters - -import ( - "os" - - "k8s.io/helm/pkg/getter" - "k8s.io/helm/pkg/getter/http" - "k8s.io/helm/pkg/getter/plugin" - "k8s.io/helm/pkg/helm/environment" - "k8s.io/helm/pkg/helm/helmpath" - "k8s.io/helm/pkg/plugin" -) - -// Get gathers the getter constructors for the downloaders. -// Currently the build-in http getter and the discovered -// plugins with downloader notations are collected. -func Get(settings environment.EnvSettings) []getter.Prop { - result := []getter.Prop{ - { - Schemes: getter.Schemes{"http", "https"}, - Constructor: httpgetter.New, - }, - } - pluginDownloaders, _ := collectPlugins(settings) - result = append(result, pluginDownloaders...) - return result -} - -func collectPlugins(settings environment.EnvSettings) ([]getter.Prop, error) { - plugdirs := os.Getenv(environment.PluginEnvVar) - if plugdirs == "" { - home := helmpath.Home(os.Getenv(environment.HomeEnvVar)) - plugdirs = home.Plugins() - } - - plugins, err := plugin.FindPlugins(plugdirs) - if err != nil { - return nil, err - } - var result []getter.Prop - for _, plugin := range plugins { - for _, downloader := range plugin.Metadata.Downloaders { - result = append(result, getter.Prop{ - Schemes: downloader.Protocols, - Constructor: plugingetter.ConstructNew( - downloader.Command, - settings, - plugin.Metadata.Name, - plugin.Dir, - ), - }) - } - } - return result, nil -} diff --git a/pkg/getter/doc.go b/pkg/getter/doc.go new file mode 100644 index 000000000..fe51e4967 --- /dev/null +++ b/pkg/getter/doc.go @@ -0,0 +1,21 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/*Package getter provides a generalize tool for fetching data by scheme. + +This provides a method by which the plugin system can load arbitrary protocol +handlers based upon a URL scheme. +*/ +package getter diff --git a/pkg/getter/getter.go b/pkg/getter/getter.go index c8b131f31..ca018884a 100644 --- a/pkg/getter/getter.go +++ b/pkg/getter/getter.go @@ -19,6 +19,8 @@ package getter import ( "bytes" "fmt" + + "k8s.io/helm/pkg/helm/environment" ) // Getter is an interface to support GET to the specified URL. @@ -27,27 +29,70 @@ type Getter interface { Get(url string) (*bytes.Buffer, error) } -//Schemes is the list to represent a specific Getter's protocol capabilities -type Schemes []string - -//Constructor is the function for every getter which creates a specific instance -//according to the configuration +// Constructor is the function for every getter which creates a specific instance +// according to the configuration type Constructor func(URL, CertFile, KeyFile, CAFile string) (Getter, error) -//Prop represents any getter and its capability -type Prop struct { - Schemes Schemes - Constructor Constructor +// Provider represents any getter and the schemes that it supports. +// +// For example, an HTTP provider may provide one getter that handles both +// 'http' and 'https' schemes. +type Provider struct { + Schemes []string + New Constructor +} + +// Provides returns true if the given scheme is supported by this Provider. +func (p Provider) Provides(scheme string) bool { + for _, i := range p.Schemes { + if i == scheme { + return true + } + } + return false +} + +// Providers is a collection of Provider objects. +type Providers []Provider + +// ByScheme returns a Provider that handles the given scheme. +// +// If no provider handles this scheme, this will return an error. +func (p Providers) ByScheme(scheme string) (Constructor, error) { + for _, pp := range p { + if pp.Provides(scheme) { + return pp.New, nil + } + } + return nil, fmt.Errorf("scheme %q not supported", scheme) +} + +// All finds all of the registered getters as a list of Provider instances. +// Currently the build-in http/https getter and the discovered +// plugins with downloader notations are collected. +func All(settings environment.EnvSettings) Providers { + result := Providers{ + { + Schemes: []string{"http", "https"}, + New: newHTTPGetter, + }, + } + pluginDownloaders, _ := collectPlugins(settings) + result = append(result, pluginDownloaders...) + return result } -//ConstructorByScheme returns a contstructor based on the required scheme -func ConstructorByScheme(props []Prop, requiredScheme string) (Constructor, error) { - for _, item := range props { - for _, itemScheme := range item.Schemes { - if itemScheme == requiredScheme { - return item.Constructor, nil - } +// ByScheme returns a getter for the given scheme. +// +// If the scheme is not supported, this will return an error. +func ByScheme(scheme string, settings environment.EnvSettings) (Provider, error) { + // Q: What do you call a scheme string who's the boss? + // A: Bruce Schemestring, of course. + a := All(settings) + for _, p := range a { + if p.Provides(scheme) { + return p, nil } } - return nil, fmt.Errorf("Getter not found") + return Provider{}, fmt.Errorf("scheme %q not supported", scheme) } diff --git a/pkg/getter/getter_test.go b/pkg/getter/getter_test.go new file mode 100644 index 000000000..6d38a0d28 --- /dev/null +++ b/pkg/getter/getter_test.go @@ -0,0 +1,81 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package getter + +import ( + "os" + "testing" +) + +func TestProvider(t *testing.T) { + p := Provider{ + []string{"one", "three"}, + func(h, e, l, m string) (Getter, error) { return nil, nil }, + } + + if !p.Provides("three") { + t.Error("Expected provider to provide three") + } +} + +func TestProviders(t *testing.T) { + ps := Providers{ + {[]string{"one", "three"}, func(h, e, l, m string) (Getter, error) { return nil, nil }}, + {[]string{"two", "four"}, func(h, e, l, m string) (Getter, error) { return nil, nil }}, + } + + if _, err := ps.ByScheme("one"); err != nil { + t.Error(err) + } + if _, err := ps.ByScheme("four"); err != nil { + t.Error(err) + } + + if _, err := ps.ByScheme("five"); err == nil { + t.Error("Did not expect handler for five") + } +} + +func TestAll(t *testing.T) { + oldhh := os.Getenv("HELM_HOME") + defer os.Setenv("HELM_HOME", oldhh) + os.Setenv("HELM_HOME", "") + + env := hh(false) + + all := All(env) + if len(all) != 3 { + t.Errorf("expected 3 providers (default plus two plugins), got %d", len(all)) + } + + if _, err := all.ByScheme("test2"); err != nil { + t.Error(err) + } +} + +func TestByScheme(t *testing.T) { + oldhh := os.Getenv("HELM_HOME") + defer os.Setenv("HELM_HOME", oldhh) + os.Setenv("HELM_HOME", "") + + env := hh(false) + if _, err := ByScheme("test", env); err != nil { + t.Error(err) + } + if _, err := ByScheme("https", env); err != nil { + t.Error(err) + } +} diff --git a/pkg/getter/http/http_getter.go b/pkg/getter/httpgetter.go similarity index 80% rename from pkg/getter/http/http_getter.go rename to pkg/getter/httpgetter.go index 57f6c742f..1dfffb6d4 100644 --- a/pkg/getter/http/http_getter.go +++ b/pkg/getter/httpgetter.go @@ -1,11 +1,10 @@ /* Copyright 2016 The Kubernetes Authors All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, @@ -14,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package httpgetter +package getter import ( "bytes" @@ -22,18 +21,17 @@ import ( "io" "net/http" - "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/tlsutil" "k8s.io/helm/pkg/urlutil" ) -//HTTPGetter is the efault HTTP(/S) backend handler -type HTTPGetter struct { +//httpGetter is the efault HTTP(/S) backend handler +type httpGetter struct { client *http.Client } //Get performs a Get from repo.Getter and returns the body. -func (g *HTTPGetter) Get(href string) (*bytes.Buffer, error) { +func (g *httpGetter) Get(href string) (*bytes.Buffer, error) { buf := bytes.NewBuffer(nil) resp, err := g.client.Get(href) @@ -49,9 +47,9 @@ func (g *HTTPGetter) Get(href string) (*bytes.Buffer, error) { return buf, err } -//New constructs a valid http/https client as Getter -func New(URL, CertFile, KeyFile, CAFile string) (getter.Getter, error) { - var client HTTPGetter +// newHTTPGetter constructs a valid http/https client as Getter +func newHTTPGetter(URL, CertFile, KeyFile, CAFile string) (Getter, error) { + var client httpGetter if CertFile != "" && KeyFile != "" && CAFile != "" { tlsConf, err := tlsutil.NewClientTLS(CertFile, KeyFile, CAFile) if err != nil { diff --git a/pkg/getter/httpgetter_test.go b/pkg/getter/httpgetter_test.go new file mode 100644 index 000000000..477e0bc4f --- /dev/null +++ b/pkg/getter/httpgetter_test.go @@ -0,0 +1,48 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package getter + +import ( + "net/http" + "path/filepath" + "testing" +) + +func TestHTTPGetter(t *testing.T) { + g, err := newHTTPGetter("http://example.com", "", "", "") + if err != nil { + t.Fatal(err) + } + + if hg, ok := g.(*httpGetter); !ok { + t.Fatal("Expected newHTTPGetter to produce an httpGetter") + } else if hg.client != http.DefaultClient { + t.Fatal("Expected newHTTPGetter to return a default HTTP client.") + } + + // Test with SSL: + cd := "../../testdata" + join := filepath.Join + ca, pub, priv := join(cd, "ca.pem"), join(cd, "crt.pem"), join(cd, "key.pem") + g, err = newHTTPGetter("http://example.com/", pub, priv, ca) + if err != nil { + t.Fatal(err) + } + + if _, ok := g.(*httpGetter); !ok { + t.Fatal("Expected newHTTPGetter to produce an httpGetter") + } +} diff --git a/pkg/getter/plugin/plugin_getter.go b/pkg/getter/plugingetter.go similarity index 61% rename from pkg/getter/plugin/plugin_getter.go rename to pkg/getter/plugingetter.go index 3ae600591..c747eef7f 100644 --- a/pkg/getter/plugin/plugin_getter.go +++ b/pkg/getter/plugingetter.go @@ -13,7 +13,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package plugingetter +package getter import ( "bytes" @@ -22,14 +22,37 @@ import ( "os/exec" "path/filepath" - "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/plugin" ) -// PluginGetter is a generic type to invoke custom downloaders, +// collectPlugins scans for getter plugins. +// This will load plugins according to the environment. +func collectPlugins(settings environment.EnvSettings) (Providers, error) { + plugins, err := plugin.FindPlugins(settings.PluginDirs()) + if err != nil { + return nil, err + } + var result Providers + for _, plugin := range plugins { + for _, downloader := range plugin.Metadata.Downloaders { + result = append(result, Provider{ + Schemes: downloader.Protocols, + New: newPluginGetter( + downloader.Command, + settings, + plugin.Metadata.Name, + plugin.Dir, + ), + }) + } + } + return result, nil +} + +// pluginGetter is a generic type to invoke custom downloaders, // implemented in plugins. -type PluginGetter struct { +type pluginGetter struct { command string certFile, keyFile, cAFile string settings environment.EnvSettings @@ -38,7 +61,7 @@ type PluginGetter struct { } // Get runs downloader plugin command -func (p *PluginGetter) Get(href string) (*bytes.Buffer, error) { +func (p *pluginGetter) Get(href string) (*bytes.Buffer, error) { argv := []string{p.certFile, p.keyFile, p.cAFile, href} prog := exec.Command(filepath.Join(p.base, p.command), argv...) plugin.SetupPluginEnv(p.settings, p.name, p.base) @@ -56,13 +79,10 @@ func (p *PluginGetter) Get(href string) (*bytes.Buffer, error) { return buf, nil } -// ConstructNew constructs a valid plugin getter -func ConstructNew(command string, - settings environment.EnvSettings, - name string, - base string) getter.Constructor { - return func(URL, CertFile, KeyFile, CAFile string) (getter.Getter, error) { - result := &PluginGetter{ +// newPluginGetter constructs a valid plugin getter +func newPluginGetter(command string, settings environment.EnvSettings, name, base string) Constructor { + return func(URL, CertFile, KeyFile, CAFile string) (Getter, error) { + result := &pluginGetter{ command: command, certFile: CertFile, keyFile: KeyFile, diff --git a/pkg/getter/plugingetter_test.go b/pkg/getter/plugingetter_test.go new file mode 100644 index 000000000..f1fe9bf29 --- /dev/null +++ b/pkg/getter/plugingetter_test.go @@ -0,0 +1,91 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package getter + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "k8s.io/helm/pkg/helm/environment" + "k8s.io/helm/pkg/helm/helmpath" +) + +func hh(debug bool) environment.EnvSettings { + apath, err := filepath.Abs("./testdata") + if err != nil { + panic(err) + } + hp := helmpath.Home(apath) + return environment.EnvSettings{ + Home: hp, + Debug: debug, + } +} + +func TestCollectPlugins(t *testing.T) { + // Reset HELM HOME to testdata. + oldhh := os.Getenv("HELM_HOME") + defer os.Setenv("HELM_HOME", oldhh) + os.Setenv("HELM_HOME", "") + + env := hh(false) + p, err := collectPlugins(env) + if err != nil { + t.Fatal(err) + } + + if len(p) != 2 { + t.Errorf("Expected 2 plugins, got %d: %v", len(p), p) + } + + if _, err := p.ByScheme("test2"); err != nil { + t.Error(err) + } + + if _, err := p.ByScheme("test"); err != nil { + t.Error(err) + } + + if _, err := p.ByScheme("nosuchthing"); err == nil { + t.Fatal("did not expect protocol handler for nosuchthing") + } +} + +func TestPluginGetter(t *testing.T) { + oldhh := os.Getenv("HELM_HOME") + defer os.Setenv("HELM_HOME", oldhh) + os.Setenv("HELM_HOME", "") + + env := hh(false) + pg := newPluginGetter("echo", env, "test", ".") + g, err := pg("test://foo/bar", "", "", "") + if err != nil { + t.Fatal(err) + } + + data, err := g.Get("test://foo/bar") + if err != nil { + t.Fatal(err) + } + + expect := "test://foo/bar" + got := strings.TrimSpace(data.String()) + if got != expect { + t.Errorf("Expected %q, got %q", expect, got) + } +} diff --git a/pkg/getter/testdata/plugins/testgetter/get.sh b/pkg/getter/testdata/plugins/testgetter/get.sh new file mode 100755 index 000000000..cdd992369 --- /dev/null +++ b/pkg/getter/testdata/plugins/testgetter/get.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +echo ENVIRONMENT +env + +echo "" +echo ARGUMENTS +echo $@ diff --git a/pkg/getter/testdata/plugins/testgetter/plugin.yaml b/pkg/getter/testdata/plugins/testgetter/plugin.yaml new file mode 100644 index 000000000..d1b929e3f --- /dev/null +++ b/pkg/getter/testdata/plugins/testgetter/plugin.yaml @@ -0,0 +1,15 @@ +name: "testgetter" +version: "0.1.0" +usage: "Fetch a package from a test:// source" +description: |- + Print the environment that the plugin was given, then exit. + + This registers the test:// protocol. + +command: "$HELM_PLUGIN_DIR/get.sh" +ignoreFlags: true +downloaders: +#- command: "$HELM_PLUGIN_DIR/get.sh" +- command: "echo" + protocols: + - "test" diff --git a/pkg/getter/testdata/plugins/testgetter2/get.sh b/pkg/getter/testdata/plugins/testgetter2/get.sh new file mode 100755 index 000000000..cdd992369 --- /dev/null +++ b/pkg/getter/testdata/plugins/testgetter2/get.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +echo ENVIRONMENT +env + +echo "" +echo ARGUMENTS +echo $@ diff --git a/pkg/getter/testdata/plugins/testgetter2/plugin.yaml b/pkg/getter/testdata/plugins/testgetter2/plugin.yaml new file mode 100644 index 000000000..f1a527ef9 --- /dev/null +++ b/pkg/getter/testdata/plugins/testgetter2/plugin.yaml @@ -0,0 +1,10 @@ +name: "testgetter2" +version: "0.1.0" +usage: "Fetch a different package from a test2:// source" +description: "Handle test2 scheme" +command: "$HELM_PLUGIN_DIR/get.sh" +ignoreFlags: true +downloaders: +- command: "echo" + protocols: + - "test2" diff --git a/pkg/getter/testdata/repository/cache/local-index.yaml b/pkg/getter/testdata/repository/cache/local-index.yaml new file mode 120000 index 000000000..ed068e99e --- /dev/null +++ b/pkg/getter/testdata/repository/cache/local-index.yaml @@ -0,0 +1 @@ +repository/local/index.yaml \ No newline at end of file diff --git a/pkg/getter/testdata/repository/cache/stable-index.yaml b/pkg/getter/testdata/repository/cache/stable-index.yaml new file mode 100644 index 000000000..bf187e3df --- /dev/null +++ b/pkg/getter/testdata/repository/cache/stable-index.yaml @@ -0,0 +1,7852 @@ +apiVersion: v1 +entries: + aws-cluster-autoscaler: + - apiVersion: v1 + created: 2017-04-28T00:18:30.071110236Z + description: Scales worker nodes within autoscaling groups. + digest: 291eaabbf54913e71cda39d42a6e9c4c493a3672a5b0b5183ea1991a76d6c317 + engine: gotpl + icon: https://github.com/kubernetes/kubernetes/blob/master/logo/logo.png + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: aws-cluster-autoscaler + sources: + - https://github.com/kubernetes/contrib/tree/master/cluster-autoscaler/cloudprovider/aws + urls: + - https://kubernetes-charts.storage.googleapis.com/aws-cluster-autoscaler-0.2.1.tgz + version: 0.2.1 + - apiVersion: v1 + created: 2017-03-02T18:48:30.418071154Z + description: Scales worker nodes within autoscaling groups. + digest: 52ee58b51619f641d0f6df4c778bcd068242379a1a040a269f0fc93867b79b13 + engine: gotpl + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: aws-cluster-autoscaler + sources: + - https://github.com/kubernetes/contrib/tree/master/cluster-autoscaler/cloudprovider/aws + urls: + - https://kubernetes-charts.storage.googleapis.com/aws-cluster-autoscaler-0.2.0.tgz + version: 0.2.0 + chaoskube: + - created: 2017-04-28T00:18:30.071358212Z + description: Chaoskube periodically kills random pods in your Kubernetes cluster. + digest: c90ff57a6205c725520dc600b439fc8eda120c3d8d4d4b7c9feee60bf62629cb + engine: gotpl + home: https://github.com/linki/chaoskube + maintainers: + - email: linki+kubernetes.io@posteo.de + name: Martin Linkhorst + name: chaoskube + sources: + - https://github.com/linki/chaoskube + urls: + - https://kubernetes-charts.storage.googleapis.com/chaoskube-0.5.0.tgz + version: 0.5.0 + - created: 2017-03-14T23:48:31.878196764Z + description: Chaoskube periodically kills random pods in your Kubernetes cluster. + digest: cc211be37255f2fdf7cc74022f51473c2c4af98c06b0871ab8c9b8341ee9d349 + engine: gotpl + home: https://github.com/linki/chaoskube + maintainers: + - email: linki+kubernetes.io@posteo.de + name: Martin Linkhorst + name: chaoskube + sources: + - https://github.com/linki/chaoskube + urls: + - https://kubernetes-charts.storage.googleapis.com/chaoskube-0.4.0.tgz + version: 0.4.0 + - created: 2017-02-13T04:18:31.525717582Z + description: A Helm chart for chaoskube + digest: 6e6466b2a7294853fbad6a1dc5526e7fd6eb75bafd035748259511ccf49f9c47 + engine: gotpl + home: https://github.com/linki/chaoskube + maintainers: + - email: linki+helm.sh@posteo.de + name: Martin Linkhorst + name: chaoskube + sources: + - https://github.com/linki/chaoskube + urls: + - https://kubernetes-charts.storage.googleapis.com/chaoskube-0.3.1.tgz + version: 0.3.1 + - created: 2017-02-13T21:18:28.251529085Z + description: Chaoskube periodically kills random pods in your Kubernetes cluster. + digest: 6439a906666fc62e7aeb28186ce2f4a730216941163edd799176cc30616e713a + engine: gotpl + home: https://github.com/linki/chaoskube + maintainers: + - email: linki+kubernetes.io@posteo.de + name: Martin Linkhorst + name: chaoskube + sources: + - https://github.com/linki/chaoskube + urls: + - https://kubernetes-charts.storage.googleapis.com/chaoskube-0.3.1-1.tgz + version: 0.3.1-1 + chronograf: + - created: 2017-04-28T00:18:30.071786276Z + description: Open-source web application written in Go and React.js that provides + the tools to visualize your monitoring data and easily create alerting and automation + rules. + digest: 5f6c0ada37696c624ebdfad1703666b91a84dedea81cbae4335109e7046c9f86 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/chronograf/ + keywords: + - chronograf + - visualizaion + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: chronograf + urls: + - https://kubernetes-charts.storage.googleapis.com/chronograf-0.2.0.tgz + version: 0.2.0 + - created: 2017-03-22T23:03:29.448801697Z + description: Open-source web application written in Go and React.js that provides + the tools to visualize your monitoring data and easily create alerting and automation + rules. + digest: 6bc90a815f7fc513bfa2b4d1a56a03e53444bfd08b742e0d0f1ff9f3b5db52f7 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/chronograf/ + keywords: + - chronograf + - visualizaion + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: chronograf + urls: + - https://kubernetes-charts.storage.googleapis.com/chronograf-0.1.2.tgz + version: 0.1.2 + - created: 2017-02-13T04:18:31.52604543Z + description: Chart for Chronograf + digest: 985fa74feb6ed111220ca7a8c5da529accd42def9d75f56c0c82902631bcf15f + engine: gotpl + home: https://www.influxdata.com/time-series-platform/chronograf/ + keywords: + - chronograf + - visualizaion + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: chronograf + urls: + - https://kubernetes-charts.storage.googleapis.com/chronograf-0.1.1.tgz + version: 0.1.1 + - created: 2017-01-28T00:33:31.060189495Z + description: Chart for Chronograf + digest: ea03695da15a018e84d05e0fd97d581f3fd348d8461aa098cd221b5010176a35 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/chronograf/ + keywords: + - chronograf + - visualizaion + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: chronograf + urls: + - https://kubernetes-charts.storage.googleapis.com/chronograf-0.1.0.tgz + version: 0.1.0 + cockroachdb: + - created: 2017-04-28T00:18:30.07217685Z + description: CockroachDB is a scalable, survivable, strongly-consistent SQL database. + digest: c51bf6c3d07067b80ca8661ff8460b2bb7d25d242f153045668ba95566fc8069 + home: https://www.cockroachlabs.com + icon: https://raw.githubusercontent.com/cockroachdb/cockroach/master/docs/media/cockroach_db.png + maintainers: + - email: alex@cockroachlabs.com + name: Alex Robinson + name: cockroachdb + sources: + - https://github.com/cockroachdb/cockroach + urls: + - https://kubernetes-charts.storage.googleapis.com/cockroachdb-0.2.2.tgz + version: 0.2.2 + - created: 2017-02-13T04:18:31.526409785Z + description: CockroachDB Helm chart for Kubernetes. + digest: 0eec741613e00f7092ec81469f919cd79fec52a22e8685063c0b0d8fd6570c37 + home: https://www.cockroachlabs.com + maintainers: + - email: alex@cockroachlabs.com + name: Alex Robinson + name: cockroachdb + sources: + - https://github.com/cockroachdb/cockroach + urls: + - https://kubernetes-charts.storage.googleapis.com/cockroachdb-0.2.1.tgz + version: 0.2.1 + - created: 2017-01-27T21:48:32.059935168Z + description: CockroachDB Helm chart for Kubernetes. + digest: 7b41add319a997fd3d862d6649b707313f59ac6fa137842db65230342baff619 + home: https://www.cockroachlabs.com + maintainers: + - email: alex@cockroachlabs.com + name: Alex Robinson + name: cockroachdb + sources: + - https://github.com/cockroachdb/cockroach + urls: + - https://kubernetes-charts.storage.googleapis.com/cockroachdb-0.2.0.tgz + version: 0.2.0 + concourse: + - created: 2017-04-28T00:18:30.074578589Z + description: Concourse is a simple and scalable CI system. + digest: b7ea57e289002deba839f52acf6a5919870ab99910f12bcc6edadd4ae5651826 + engine: gotpl + home: https://concourse.ci/ + icon: https://avatars1.githubusercontent.com/u/7809479 + keywords: + - ci + - concourse + - concourse.ci + maintainers: + - email: frodenas@gmail.com + name: Ferran Rodenas + name: concourse + sources: + - https://github.com/concourse/bin + - https://github.com/kubernetes/charts + urls: + - https://kubernetes-charts.storage.googleapis.com/concourse-0.1.3.tgz + version: 0.1.3 + - created: 2017-02-14T19:03:29.77100439Z + description: Concourse is a simple and scalable CI system. + digest: 5f8ed4eb5b0acf8da7b34772714154322405b796553a33fc6ffd779e0a556003 + engine: gotpl + home: https://concourse.ci/ + icon: https://avatars1.githubusercontent.com/u/7809479 + keywords: + - ci + - concourse + - concourse.ci + maintainers: + - email: frodenas@gmail.com + name: Ferran Rodenas + name: concourse + sources: + - https://github.com/concourse/bin + - https://github.com/kubernetes/charts + urls: + - https://kubernetes-charts.storage.googleapis.com/concourse-0.1.2.tgz + version: 0.1.2 + - created: 2017-02-13T21:18:28.254273427Z + description: Concourse is a simple and scalable CI system. + digest: cba53dadd21dbd85b31a1a522a5eaeb49cadfa595ba0762c02dca04905d35f20 + engine: gotpl + home: https://concourse.ci/ + icon: https://avatars1.githubusercontent.com/u/7809479 + keywords: + - ci + - concourse + - concourse.ci + maintainers: + - email: frodenas@gmail.com + name: Ferran Rodenas + name: concourse + sources: + - https://github.com/concourse/bin + - https://github.com/kubernetes/charts + urls: + - https://kubernetes-charts.storage.googleapis.com/concourse-0.1.1.tgz + version: 0.1.1 + - created: 2017-02-10T23:18:26.003782261Z + description: Concourse Helm chart for Kubernetes. + digest: 08e6b4c56357ce15dfd66b0fad8c2df37664877b16b4a0afcbaeb301ddcc7432 + engine: gotpl + home: https://concourse.ci/ + keywords: + - ci + - concourse + - concourse.ci + maintainers: + - email: frodenas@gmail.com + name: Ferran Rodenas + name: concourse + sources: + - https://github.com/concourse/bin + - https://github.com/kubernetes/charts + urls: + - https://kubernetes-charts.storage.googleapis.com/concourse-0.1.0.tgz + version: 0.1.0 + consul: + - created: 2017-04-28T00:18:30.075038045Z + description: Highly available and distributed service discovery and key-value + store designed with support for the modern data center to make distributed systems + and configuration easy. + digest: 7e0093709abc7a2c475e4e8c14e856d9834f88683b4a9e8c6099f4e0ad7e434e + home: https://github.com/hashicorp/consul + icon: https://www.consul.io/assets/images/logo_large-475cebb0.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + name: consul + sources: + - https://github.com/kelseyhightower/consul-on-kubernetes + urls: + - https://kubernetes-charts.storage.googleapis.com/consul-0.2.2.tgz + version: 0.2.2 + - created: 2017-04-26T14:48:28.225526691Z + description: Highly available and distributed service discovery and key-value + store designed with support for the modern data center to make distributed systems + and configuration easy. + digest: 03daa04827bf93627b7087001c1d2424337d268a5875a51d8db4bb4e53bbc7db + home: https://github.com/hashicorp/consul + icon: https://www.consul.io/assets/images/logo_large-475cebb0.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + name: consul + sources: + - https://github.com/kelseyhightower/consul-on-kubernetes + urls: + - https://kubernetes-charts.storage.googleapis.com/consul-0.2.0.tgz + version: 0.2.0 + coredns: + - created: 2017-04-28T00:18:30.075393511Z + description: CoreDNS is a DNS server that chains middleware and provides Kubernetes + DNS Services + digest: adbdc4a8895f7c2e7cca64c2dcf36ddfffeff1115a3ade32011ec82b60be119b + home: https://coredns.io + icon: https://raw.githubusercontent.com/coredns/logo/master/Icon/CoreDNS_Colour_Icon.svg + keywords: + - coredns + - dns + - kubedns + maintainers: + - email: hello@acale.ph + name: Acaleph + - email: shashidhara.huawei@gmail.com + name: Shashidhara TD + name: coredns + sources: + - https://github.com/coredns/coredns + urls: + - https://kubernetes-charts.storage.googleapis.com/coredns-0.1.0.tgz + version: 0.1.0 + datadog: + - created: 2017-04-28T00:18:30.075800677Z + description: DataDog Agent + digest: bc559f013b738704089c4964a268c447b22e82181e9fa9e8219d46d40b388709 + home: https://www.datadoghq.com + icon: https://datadog-live.imgix.net/img/dd_logo_70x75.png + keywords: + - monitoring + - alerting + - metric + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: datadog + sources: + - https://app.datadoghq.com/account/settings#agent/kubernetes + - https://github.com/DataDog/docker-dd-agent + urls: + - https://kubernetes-charts.storage.googleapis.com/datadog-0.3.0.tgz + version: 0.3.0 + - created: 2017-04-06T11:33:26.056402381Z + description: DataDog Agent + digest: b32c28e76004eedf5c160936ccf35adca3a150ae1d0b491df52d017725b5ee90 + home: https://www.datadoghq.com + icon: https://datadog-live.imgix.net/img/dd_logo_70x75.png + keywords: + - monitoring + - alerting + - metric + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: datadog + sources: + - https://app.datadoghq.com/account/settings#agent/kubernetes + - https://github.com/DataDog/docker-dd-agent + urls: + - https://kubernetes-charts.storage.googleapis.com/datadog-0.2.1.tgz + version: 0.2.1 + - created: 2017-02-11T03:18:26.518137684Z + description: DataDog Agent + digest: d534bdcf4644d88ebfa70c58e57aafed41b75da4264042d4975f70d091e2b493 + keywords: + - monitoring + - alerting + - metric + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: datadog + sources: + - https://app.datadoghq.com/account/settings#agent/kubernetes + - https://github.com/DataDog/docker-dd-agent + urls: + - https://kubernetes-charts.storage.googleapis.com/datadog-0.2.0.tgz + version: 0.2.0 + - created: 2017-01-04T00:48:19.731877862Z + description: DataDog Agent + digest: 694c1d036d92c8bb60638f7bd66144a991a807dc879bedacf8a5d32e9d72081a + keywords: + - monitoring + - alerting + - metric + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: datadog + sources: + - https://app.datadoghq.com/account/settings#agent/kubernetes + - https://github.com/DataDog/docker-dd-agent + urls: + - https://kubernetes-charts.storage.googleapis.com/datadog-0.1.0.tgz + version: 0.1.0 + dokuwiki: + - created: 2017-04-28T00:18:30.076184541Z + description: DokuWiki is a standards-compliant, simple to use wiki optimized for + creating documentation. It is targeted at developer teams, workgroups, and small + companies. All data is stored in plain text files, so no database is required. + digest: 5cfff9542341a391abf9029dd9b42e7c44813c520ef0301ce62e9c08586ceca2 + engine: gotpl + home: http://www.dokuwiki.org/ + icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png + keywords: + - dokuwiki + - wiki + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: dokuwiki + sources: + - https://github.com/bitnami/bitnami-docker-dokuwiki + urls: + - https://kubernetes-charts.storage.googleapis.com/dokuwiki-0.1.4.tgz + version: 0.1.4 + - created: 2017-04-13T05:18:28.897680481Z + description: DokuWiki is a standards-compliant, simple to use wiki optimized for + creating documentation. It is targeted at developer teams, workgroups, and small + companies. All data is stored in plain text files, so no database is required. + digest: 3c46f9d9196bbf975711b2bb7c889fd3df1976cc57c3c120c7374d721da0e240 + engine: gotpl + home: http://www.dokuwiki.org/ + icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png + keywords: + - dokuwiki + - wiki + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: dokuwiki + sources: + - https://github.com/bitnami/bitnami-docker-dokuwiki + urls: + - https://kubernetes-charts.storage.googleapis.com/dokuwiki-0.1.3.tgz + version: 0.1.3 + - created: 2017-03-02T19:33:28.170205427Z + description: DokuWiki is a standards-compliant, simple to use wiki optimized for + creating documentation. It is targeted at developer teams, workgroups, and small + companies. All data is stored in plain text files, so no database is required. + digest: f533bc20e08179a49cca77b175f897087dc3f2c57e6c89ecbd7264ab5975d66a + engine: gotpl + home: http://www.dokuwiki.org/ + icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png + keywords: + - dokuwiki + - wiki + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: dokuwiki + sources: + - https://github.com/bitnami/bitnami-docker-dokuwiki + urls: + - https://kubernetes-charts.storage.googleapis.com/dokuwiki-0.1.2.tgz + version: 0.1.2 + - created: 2017-02-01T02:18:29.116555882Z + description: DokuWiki is a standards-compliant, simple to use wiki optimized for + creating documentation. It is targeted at developer teams, workgroups, and small + companies. All data is stored in plain text files, so no database is required. + digest: 34a926398cfafbf426ff468167ef49577252e260ebce5df33380e6e67b79fe59 + engine: gotpl + home: http://www.dokuwiki.org/ + icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png + keywords: + - dokuwiki + - wiki + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: dokuwiki + sources: + - https://github.com/bitnami/bitnami-docker-dokuwiki + urls: + - https://kubernetes-charts.storage.googleapis.com/dokuwiki-0.1.1.tgz + version: 0.1.1 + - created: 2017-01-28T00:33:31.06436596Z + description: DokuWiki is a standards-compliant, simple to use wiki optimized for + creating documentation. It is targeted at developer teams, workgroups, and small + companies. All data is stored in plain text files, so no database is required. + digest: 6825fbacb709cf05901985561be10ba9473a379488d99b71d1590d33f5a81374 + engine: gotpl + home: http://www.dokuwiki.org/ + icon: https://bitnami.com/assets/stacks/dokuwiki/img/dokuwiki-stack-110x117.png + keywords: + - dokuwiki + - wiki + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: dokuwiki + sources: + - https://github.com/bitnami/bitnami-docker-dokuwiki + urls: + - https://kubernetes-charts.storage.googleapis.com/dokuwiki-0.1.0.tgz + version: 0.1.0 + drupal: + - created: 2017-04-28T00:18:30.076853626Z + description: One of the most versatile open source content management systems. + digest: db95c255b19164c5051eb75a6860f3775a1011399a62b27e474cd9ebee0cb578 + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.5.1.tgz + version: 0.5.1 + - created: 2017-04-24T19:18:29.642780033Z + description: One of the most versatile open source content management systems. + digest: 84c13154a9aeb7215dc0d98e9825207207e69ca870f3d54b273bfc2d34699f61 + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.5.0.tgz + version: 0.5.0 + - created: 2017-04-11T17:18:28.883487907Z + description: One of the most versatile open source content management systems. + digest: 17d0bfdcdf5a1a650941343c76b6b928d76d3332fece127c502e91f9597f419e + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-23T01:48:29.309045867Z + description: One of the most versatile open source content management systems. + digest: 317674c89762e0b54156b734203ee93638dd7a25df35120c5cab45546814d89b + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-16T13:33:30.828606332Z + description: One of the most versatile open source content management systems. + digest: 24c4f187b50c0e961cc2cacf6e6b2ce6d6b225c73637c578e001bebd9b3f5d48 + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-02T19:33:28.170963773Z + description: One of the most versatile open source content management systems. + digest: 7fcea4684a3d520454aeaa10beb9f9b1789c09c097680fc484954084f283feb3 + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-27T17:03:27.765392204Z + description: One of the most versatile open source content management systems. + digest: adb23bc71125b9691b407a47dadf4298de3516805218813b56067967e39db7d8 + engine: gotpl + home: http://www.drupal.org/ + icon: https://bitnami.com/assets/stacks/drupal/img/drupal-stack-220x234.png + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.2.tgz + version: 0.4.2 + - created: 2017-02-11T00:18:52.26723498Z + description: One of the most versatile open source content management systems. + digest: 5de529e25767e8a37b8d6f413daa0fe99f5c304e48ddcfa8adb4d8c7a0496aa7 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-28T00:33:31.065139372Z + description: One of the most versatile open source content management systems. + digest: a35dbf9d470972cc2461de3e0a8fcf2fec8d0adc04f5a0f1e924505f22c714d7 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.4.0.tgz + version: 0.4.0 + - created: 2017-01-04T00:48:19.73297256Z + description: One of the most versatile open source content management systems. + digest: a62d686d6bd47643dfa489e395dda89286954f785123a43a88db7ef34f3ea48d + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.10.tgz + version: 0.3.10 + - created: 2016-12-15T00:48:24.005322531Z + description: One of the most versatile open source content management systems. + digest: 2c189424bda94eeebb7e6370e96884f09bdfa81498cb93ac4723d24c92a3938e + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.9.tgz + version: 0.3.9 + - created: 2016-12-09T18:48:20.182097412Z + description: One of the most versatile open source content management systems. + digest: 3596f47c5dcaa7a975d1c4cb7bf7ef6790c9ad8dda41a5a329e30c1ea8a40d11 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.8.tgz + version: 0.3.8 + - created: 2016-11-21T19:48:21.904806991Z + description: One of the most versatile open source content management systems. + digest: 78b2bb3717be63dccb02ea06b711ca7cf7869848b296b880099c6264e86d86d3 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.7.tgz + version: 0.3.7 + - created: 2016-11-08T15:03:20.739400722Z + description: One of the most versatile open source content management systems. + digest: 5508b29e20a5d609f76319869774f008dcc4bed13bbbc7ed40546bc9af8c7cd7 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.6.tgz + version: 0.3.6 + - created: 2016-11-03T19:33:29.11780736Z + description: One of the most versatile open source content management systems. + digest: 023a282c93f8411fb81bb4fff7820c1337aad0586ccf7dae55bdbed515ad8b05 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.5.tgz + version: 0.3.5 + - created: 2016-10-21T19:18:18.619010562Z + description: One of the most versatile open source content management systems. + digest: 9bdaa53f7a9e82c9b32c7ac9b34b84fd142671732a54423a2dcdc893c4162801 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.4.tgz + version: 0.3.4 + - created: 2016-10-19T00:03:14.027652488Z + description: One of the most versatile open source content management systems. + digest: 25650526abc1036398dbb314d77a0062cbb644b2c5791a258fb863fdaad5093d + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.3.tgz + version: 0.3.3 + - created: 2016-10-19T00:03:14.027073479Z + description: One of the most versatile open source content management systems. + digest: 13d5d32d316c08359221d230004dd2adc0152362e87abcc0d61ea191241fa69f + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.2.tgz + version: 0.3.2 + - created: 2016-10-19T00:03:14.025451665Z + description: One of the most versatile open source content management systems. + digest: b3f09ecd191f8c06275c96d9af4d77a97c94355525864201e9baf151b17bd5a7 + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.1.tgz + version: 0.3.1 + - created: 2016-10-19T00:03:14.024557743Z + description: One of the most versatile open source content management systems. + digest: c56fc55b93b0dead65af7b81bbd54befd5115860698ca475baa5acb178a12e5a + engine: gotpl + home: http://www.drupal.org/ + keywords: + - drupal + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: drupal + sources: + - https://github.com/bitnami/bitnami-docker-drupal + urls: + - https://kubernetes-charts.storage.googleapis.com/drupal-0.3.0.tgz + version: 0.3.0 + etcd-operator: + - apiVersion: v1 + created: 2017-04-28T00:18:30.077181321Z + description: CoreOS etcd-operator Helm chart for Kubernetes + digest: 1eb39b2f0ca26762eb13fc8cb577be741f7bb9d3162ab9c4547bb5176383bc2b + home: https://github.com/coreos/etcd-operator + icon: https://raw.githubusercontent.com/coreos/etcd/master/logos/etcd-horizontal-color.png + maintainers: + - email: chance.zibolski@coreos.com + name: Chance Zibolski + - email: lachlan@deis.com + name: Lachlan Evenson + name: etcd-operator + sources: + - https://github.com/coreos/etcd-operator + urls: + - https://kubernetes-charts.storage.googleapis.com/etcd-operator-0.2.0.tgz + version: 0.2.0 + - apiVersion: v1 + created: 2017-03-08T19:18:29.84391993Z + description: CoreOS etcd-operator Helm chart for Kubernetes + digest: 4a65fe6c61e731b373395e524f160eb4ced32a22cbfb3ff5d406b1c8bc3ae3c7 + home: https://github.com/coreos/etcd-operator + icon: https://raw.githubusercontent.com/coreos/etcd/master/logos/etcd-horizontal-color.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + name: etcd-operator + sources: + - https://github.com/coreos/etcd-operator + urls: + - https://kubernetes-charts.storage.googleapis.com/etcd-operator-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: 2017-02-11T03:18:26.519796919Z + description: CoreOS etcd-operator Helm chart for Kubernetes + digest: 9bf72369082c4bad154171b3b68ad435331d6d07ae6d00e79e859f2a1599017b + icon: https://github.com/coreos/etcd/blob/master/logos/etcd-horizontal-color.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + name: etcd-operator + sources: + - https://github.com/coreos/etcd-operator + urls: + - https://kubernetes-charts.storage.googleapis.com/etcd-operator-0.1.0.tgz + version: 0.1.0 + factorio: + - created: 2017-04-28T00:18:30.077542134Z + description: Factorio dedicated server. + digest: cdc44bc00d42020a7a4df154cdc5cc7ae148aa8d2a3f97b1e18ac1fc42853dc1 + home: https://www.factorio.com/ + icon: https://us1.factorio.com/assets/img/factorio-logo.png + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: factorio + sources: + - https://github.com/games-on-k8s/docker-factorio + urls: + - https://kubernetes-charts.storage.googleapis.com/factorio-0.2.0.tgz + version: 0.2.0 + - created: 2017-03-15T11:48:36.74226142Z + description: Factorio dedicated server. + digest: 5a60defdd8ac6f2276950662ba75f65d20c9e37edc85149012a2c84146eb6cff + home: https://www.factorio.com/ + icon: https://us1.factorio.com/assets/img/factorio-logo.png + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: factorio + sources: + - https://github.com/games-on-k8s/docker-factorio + urls: + - https://kubernetes-charts.storage.googleapis.com/factorio-0.1.4.tgz + version: 0.1.4 + - created: 2017-02-13T04:18:31.530714209Z + description: Factorio dedicated server. + digest: 26244a5e1b5f992cdbef73ef9c7ffcaa52af538912fa299210f72275f2c756c9 + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: factorio + sources: + - https://github.com/games-on-k8s/docker-factorio + urls: + - https://kubernetes-charts.storage.googleapis.com/factorio-0.1.3.tgz + version: 0.1.3 + - created: 2017-01-28T00:33:31.066072163Z + description: Factorio dedicated server. + digest: d6f4e42b82713c2da69e1ddcfce4a04fad31d4af915629d8e83e54b90a822f9a + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: factorio + sources: + - https://github.com/games-on-k8s/docker-factorio + urls: + - https://kubernetes-charts.storage.googleapis.com/factorio-0.1.2.tgz + version: 0.1.2 + - created: 2016-12-02T09:03:20.175832035Z + description: Factorio dedicated server. + digest: 3cc1f83669fd1d97eb96e76ba4821e8664350a4c310d3a14e62be18cc09e59b7 + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: factorio + sources: + - https://github.com/games-on-k8s/docker-factorio + urls: + - https://kubernetes-charts.storage.googleapis.com/factorio-0.1.1.tgz + version: 0.1.1 + - created: 2016-11-07T18:33:21.243890443Z + description: Factorio dedicated server. + digest: 8edc1340cd99225a769b5843a677896c0d54a079133f9759211a1839bc7bb3eb + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: factorio + sources: + - https://github.com/games-on-k8s/docker-factorio + urls: + - https://kubernetes-charts.storage.googleapis.com/factorio-0.1.0.tgz + version: 0.1.0 + gcloud-endpoints: + - created: 2017-04-28T00:18:30.077956448Z + description: Develop, deploy, protect and monitor your APIs with Google Cloud + Endpoints. + digest: 172b56d0343c560f06e18858038e2096c910b37075a90f4303f77a79342b56fd + engine: gotpl + home: https://cloud.google.com/endpoints/ + keywords: + - google + - endpoints + - nginx + - gcloud + - proxy + - authentication + - monitoring + - api + - swagger + - open api + maintainers: + - email: mtucker@deis.com + name: Matt Tucker + name: gcloud-endpoints + urls: + - https://kubernetes-charts.storage.googleapis.com/gcloud-endpoints-0.1.0.tgz + version: 0.1.0 + gcloud-sqlproxy: + - created: 2017-04-28T00:18:30.078355277Z + description: Google Cloud SQL Proxy + digest: 1115afe0958f1ed01e568ec4c35b48ac0094704165b8808634ea5331c0d6da7d + engine: gotpl + home: https://cloud.google.com/sql/docs/postgres/sql-proxy + keywords: + - google + - cloud + - postgresql + - mysql + - sql + - sqlproxy + maintainers: + - email: rmocius@gmail.com + name: Rimas Mocevicius + name: gcloud-sqlproxy + sources: + - https://github.com/rimusz/charts + urls: + - https://kubernetes-charts.storage.googleapis.com/gcloud-sqlproxy-0.1.0.tgz + version: 0.1.0 + ghost: + - created: 2017-04-28T00:18:30.079159648Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: 91d195c99e00b2801eafef5c23fcf9ced218bb29c7097f08139e2bdc80e38a0f + engine: gotpl + home: http://www.ghost.org/ + icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.7.tgz + version: 0.4.7 + - created: 2017-04-06T10:48:26.261677382Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: 6342a95aeef40690430c2e80b167fbb116a632746cdca49cfac4cbd535eadb22 + engine: gotpl + home: http://www.ghost.org/ + icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-08T19:03:31.719494672Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: 8998a9a4e75e777edb6f06c05b45d461daebba09021161af3bef523efd193b15 + engine: gotpl + home: http://www.ghost.org/ + icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.5.tgz + version: 0.4.5 + - created: 2017-02-27T17:03:27.767416735Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: e44c9a53355086ded1832f769dca515b863337ad118ba618ef97f37b3ef84030 + engine: gotpl + home: http://www.ghost.org/ + icon: https://bitnami.com/assets/stacks/ghost/img/ghost-stack-220x234.png + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-11T00:18:52.2688126Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: b0c94a93c88fde68bb4fc78e92691d46cd2eb4d32cbac011e034565fbd35d46b + engine: gotpl + home: http://www.ghost.org/ + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.3.tgz + version: 0.4.3 + - created: 2017-01-29T22:48:35.944809746Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: 791ccb42b62d56d50c72b37db3282eb3f2af75d667a25542d76c7991004eb822 + engine: gotpl + home: http://www.ghost.org/ + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.342008382Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: 331a2b145bfdb39b626313cda7dc539f32dbda5149893957589c5406317fca53 + engine: gotpl + home: http://www.ghost.org/ + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-09T18:48:20.183434341Z + description: A simple, powerful publishing platform that allows you to share your + stories with the world + digest: 804227af037082a0f5c3c579feb9e24eb3682449e78876971c93a109bc716f40 + engine: gotpl + home: http://www.ghost.org/ + keywords: + - ghost + - blog + - http + - web + - application + - nodejs + - javascript + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: ghost + sources: + - https://github.com/bitnami/bitnami-docker-ghost + urls: + - https://kubernetes-charts.storage.googleapis.com/ghost-0.4.0.tgz + version: 0.4.0 + gitlab-ce: + - created: 2017-04-28T00:18:30.080184798Z + description: GitLab Community Edition + digest: df5e36c64bf1b8e2b77609c1cd9c717df47c290777a005ebf0edbe42d1f0ac70 + home: https://about.gitlab.com + icon: https://gitlab.com/uploads/group/avatar/6543/gitlab-logo-square.png + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.7.tgz + version: 0.1.7 + - created: 2017-04-11T16:33:29.173232912Z + description: GitLab Community Edition + digest: 80094520d1bee55c7e25ad740bbfe3740814f389e6221b2fa536f77aabba9b37 + home: https://about.gitlab.com + icon: https://gitlab.com/uploads/group/avatar/6543/gitlab-logo-square.png + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.6.tgz + version: 0.1.6 + - created: 2017-03-23T20:48:32.107763732Z + description: GitLab Community Edition + digest: 20c0895dd3c5c1edbc0e3be4687f0d0874599cd0c53e49560f9f0a91506957ce + home: https://about.gitlab.com + icon: https://gitlab.com/uploads/group/avatar/6543/gitlab-logo-square.png + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.5.tgz + version: 0.1.5 + - created: 2017-02-14T02:48:28.88667289Z + description: GitLab Community Edition + digest: e05d4de559597760d59ca684fab107abcc0968b3260a77b16099d31e0b00cd74 + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.4.tgz + version: 0.1.4 + - created: 2017-02-13T20:18:28.097071087Z + description: GitLab Community Edition + digest: a2fef3fd8d3187f8a4242d66435488bce4193ae3f2db8d3ec14da1c4373c2519 + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.3.tgz + version: 0.1.3 + - created: 2017-01-26T03:18:35.336711333Z + description: GitLab Community Edition + digest: 3ca821c4e3bec2fe7541b95f94503875c508517e2f1200368268511e254df360 + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.2.tgz + version: 0.1.2 + - created: 2017-01-13T20:48:31.520266166Z + description: GitLab Community Edition + digest: d78cfc8cc2e262c49e1aee3af046509b92b022a5cd4b522778e846ddcd808d9b + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.1.tgz + version: 0.1.1 + - created: 2016-12-15T21:18:24.667256782Z + description: GitLab Community Edition + digest: 2c84a056e3f6d66a6aed763b2f4a26c1f4275eb3f6ca64798962a070809c6272 + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: gitlab-ce + sources: + - https://hub.docker.com/r/gitlab/gitlab-ce/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ce-0.1.0.tgz + version: 0.1.0 + gitlab-ee: + - created: 2017-04-28T00:18:30.081242553Z + description: GitLab Enterprise Edition + digest: 3fa791ba74b0e43c05178b6eb16fb21eede5bb046e9870b854db38654768de09 + home: https://about.gitlab.com + icon: https://gitlab.com/uploads/group/avatar/6543/gitlab-logo-square.png + keywords: + - git + - ci + - deploy + - issue tracker + - code review + - wiki + maintainers: + - email: contact@quent.in + name: Quentin Rousseau + name: gitlab-ee + sources: + - https://hub.docker.com/r/gitlab/gitlab-ee/ + - https://docs.gitlab.com/omnibus/ + urls: + - https://kubernetes-charts.storage.googleapis.com/gitlab-ee-0.1.6.tgz + version: 0.1.6 + grafana: + - created: 2017-04-28T00:18:30.082782593Z + description: The leading tool for querying and visualizing time series and metrics. + digest: 721c85c6407ef534dc0d2366af3092f63229d51158abb124496efbe1a224907b + engine: gotpl + home: https://grafana.net + icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.3.1.tgz + version: 0.3.1 + - created: 2017-04-14T01:18:27.369088748Z + description: The leading tool for querying and visualizing time series and metrics. + digest: 8f1db00e769d13c2435841b9b89b2045653039ca377c4547f46b33ec566f60ef + engine: gotpl + home: https://grafana.net + icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.3.0.tgz + version: 0.3.0 + - created: 2017-04-05T18:03:30.376700685Z + description: The leading tool for querying and visualizing time series and metrics. + digest: e428e33b249f2261882632cc658c36d50df81460c091fd29404a3b3d16886416 + engine: gotpl + home: https://grafana.net + icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.2.5.tgz + version: 0.2.5 + - created: 2017-03-16T23:33:31.578792832Z + description: The leading tool for querying and visualizing time series and metrics. + digest: 2fefc51028c411641e5bf85b799fc8e608c7646c7054cfaa2149d4e83610d60a + engine: gotpl + home: https://grafana.net + icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.2.4.tgz + version: 0.2.4 + - created: 2017-03-14T23:48:31.886602615Z + description: The leading tool for querying and visualizing time series and metrics. + digest: 7fac33dcd25d8ac60071e56968db133ecfa4f796732f92044d1f7714495840fd + engine: gotpl + home: https://grafana.net + icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.2.3.tgz + version: 0.2.3 + - created: 2017-02-13T22:33:28.777518221Z + description: The leading tool for querying and visualizing time series and metrics. + digest: 037158b80733838ab2ad84928c999997ab76b5383cbeb4cce6c174fe5bc5ae8d + engine: gotpl + home: https://grafana.net + icon: https://raw.githubusercontent.com/grafana/grafana/master/public/img/logo_transparent_400x.png + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.2.2.tgz + version: 0.2.2 + - created: 2017-02-13T04:18:31.532928133Z + description: A Helm chart for Kubernetes + digest: e49f08bd26843eb0449304c72fd9be3e65de0d8647457f39807f9aa296ba9b6a + engine: gotpl + home: https://grafana.net + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.2.1.tgz + version: 0.2.1 + - created: 2017-01-29T23:03:26.897254812Z + description: A Helm chart for Kubernetes + digest: 283be1abb811d005b3759f65761e4465e79f2031be8a47b3d87256e88888f047 + engine: gotpl + home: https://grafana.net + maintainers: + - email: zanhsieh@gmail.com + name: Ming Hsieh + name: grafana + sources: + - https://github.com/grafana/grafana + urls: + - https://kubernetes-charts.storage.googleapis.com/grafana-0.2.0.tgz + version: 0.2.0 + influxdb: + - created: 2017-04-28T00:18:30.083302575Z + description: Scalable datastore for metrics, events, and real-time analytics. + digest: c34d6d57a1c4f5cdf1d1eb869231b27d2c080c7d219ab330f43fd9fd795b8d40 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/influxdb/ + keywords: + - influxdb + - database + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: influxdb + sources: + - https://github.com/influxdata/influxdb + urls: + - https://kubernetes-charts.storage.googleapis.com/influxdb-0.4.0.tgz + version: 0.4.0 + - created: 2017-03-23T01:19:01.442621577Z + description: Scalable datastore for metrics, events, and real-time analytics. + digest: 76522d9156e4939669344cc72a9674ce16ccc7049b06c03eaa0822ed4ce59254 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/influxdb/ + keywords: + - influxdb + - database + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: influxdb + sources: + - https://github.com/influxdata/influxdb + urls: + - https://kubernetes-charts.storage.googleapis.com/influxdb-0.3.0.tgz + version: 0.3.0 + - created: 2017-03-17T05:33:29.077307939Z + description: Scalable datastore for metrics, events, and real-time analytics. + digest: 499e87e9a0cfb2452107eaabc5e81bb5382554731b12e20dac791d81d492044e + engine: gotpl + home: https://www.influxdata.com/time-series-platform/influxdb/ + keywords: + - influxdb + - database + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: influxdb + sources: + - https://github.com/influxdata/influxdb + urls: + - https://kubernetes-charts.storage.googleapis.com/influxdb-0.2.1.tgz + version: 0.2.1 + - created: 2017-02-13T17:03:30.109119817Z + description: Scalable datastore for metrics, events, and real-time analytics. + digest: e78ce7b2aac9538e5f6087fc77e5e30ab41a2a54d9bb1381b02830dd3324f0a9 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/influxdb/ + keywords: + - influxdb + - database + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: influxdb + sources: + - https://github.com/influxdata/influxdb + urls: + - https://kubernetes-charts.storage.googleapis.com/influxdb-0.1.2.tgz + version: 0.1.2 + - created: 2017-02-13T04:33:52.294695041Z + description: Chart for InfluxDB + digest: 3341f3cca2d60540b219390688ac600ec605a331975cd402d6489969a0346aec + engine: gotpl + home: https://www.influxdata.com/time-series-platform/influxdb/ + keywords: + - influxdb + - database + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: influxdb + urls: + - https://kubernetes-charts.storage.googleapis.com/influxdb-0.1.1.tgz + version: 0.1.1 + - created: 2017-01-28T00:48:33.328518706Z + description: Chart for InfluxDB + digest: a64fad23b1d02c8f14955f652f60a36ca2bc9f01fb5f4d80cd88bcf9f96a7300 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/influxdb/ + keywords: + - influxdb + - database + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: influxdb + urls: + - https://kubernetes-charts.storage.googleapis.com/influxdb-0.1.0.tgz + version: 0.1.0 + jasperreports: + - created: 2017-04-28T00:18:30.084006378Z + description: The JasperReports server can be used as a stand-alone or embedded + reporting and BI server that offers web-based reporting, analytic tools and + visualization, and a dashboard feature for compiling multiple custom views + digest: 8a649026f55b2fa1e743c93fd331e127e66b49c4d7f20116a2bb06e5937f4828 + engine: gotpl + home: http://community.jaspersoft.com/project/jasperreports-server + icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png + keywords: + - business intelligence + - java + - jasper + - reporting + - analytic + - visualization + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: jasperreports + sources: + - https://github.com/bitnami/bitnami-docker-jasperreports + urls: + - https://kubernetes-charts.storage.googleapis.com/jasperreports-0.1.5.tgz + version: 0.1.5 + - created: 2017-04-06T10:18:27.580953879Z + description: The JasperReports server can be used as a stand-alone or embedded + reporting and BI server that offers web-based reporting, analytic tools and + visualization, and a dashboard feature for compiling multiple custom views + digest: d4a62f7ace55256852e5c650a56ccf671633c4f223180d304cfb03b9cd7993aa + engine: gotpl + home: http://community.jaspersoft.com/project/jasperreports-server + icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png + keywords: + - business intelligence + - java + - jasper + - reporting + - analytic + - visualization + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: jasperreports + sources: + - https://github.com/bitnami/bitnami-docker-jasperreports + urls: + - https://kubernetes-charts.storage.googleapis.com/jasperreports-0.1.4.tgz + version: 0.1.4 + - created: 2017-03-08T19:03:31.722665089Z + description: The JasperReports server can be used as a stand-alone or embedded + reporting and BI server that offers web-based reporting, analytic tools and + visualization, and a dashboard feature for compiling multiple custom views + digest: 99af0fca7ef1c475b239f2c8fc2dee6b040ea76b3c30bba1431f358df873aa49 + engine: gotpl + home: http://community.jaspersoft.com/project/jasperreports-server + icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png + keywords: + - business intelligence + - java + - jasper + - reporting + - analytic + - visualization + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: jasperreports + sources: + - https://github.com/bitnami/bitnami-docker-jasperreports + urls: + - https://kubernetes-charts.storage.googleapis.com/jasperreports-0.1.3.tgz + version: 0.1.3 + - created: 2017-02-13T21:33:27.741967589Z + description: The JasperReports server can be used as a stand-alone or embedded + reporting and BI server that offers web-based reporting, analytic tools and + visualization, and a dashboard feature for compiling multiple custom views + digest: f01e53d1b89c4fb1fcd9702cd5f4e48d77607aed65f249e1f94b8b21f7eef3f4 + engine: gotpl + home: http://community.jaspersoft.com/project/jasperreports-server + icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png + keywords: + - business intelligence + - java + - jasper + - reporting + - analytic + - visualization + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: jasperreports + sources: + - https://github.com/bitnami/bitnami-docker-jasperreports + urls: + - https://kubernetes-charts.storage.googleapis.com/jasperreports-0.1.2.tgz + version: 0.1.2 + - created: 2017-01-28T01:33:32.784491819Z + description: The JasperReports server can be used as a stand-alone or embedded + reporting and BI server that offers web-based reporting, analytic tools and + visualization, and a dashboard feature for compiling multiple custom views + digest: 5cc4af8c88691d7030602c97be2ccbc125ef11129b361da0aa236a127c31b965 + engine: gotpl + home: http://community.jaspersoft.com/project/jasperreports-server + icon: https://bitnami.com/assets/stacks/jasperserver/img/jasperserver-stack-110x117.png + keywords: + - business intelligence + - java + - jasper + - reporting + - analytic + - visualization + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: jasperreports + sources: + - https://github.com/bitnami/bitnami-docker-jasperreports + urls: + - https://kubernetes-charts.storage.googleapis.com/jasperreports-0.1.1.tgz + version: 0.1.1 + jenkins: + - created: 2017-04-28T00:18:30.084552323Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: e8bb6edabe1af4db4f67e2939b88fa45416167612349a3a32bcf786c529a18e7 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.6.0.tgz + version: 0.6.0 + - created: 2017-04-24T21:03:29.315054715Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 0c962935ead654ed9422c75978195b9ec893b1e1909b38cdbc15e3dc24863f7e + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.5.1.tgz + version: 0.5.1 + - created: 2017-04-19T16:48:30.580850385Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 338a9265e567f75469c4e227f583de14b7ab38da137b1d4fd5eee24ddea05724 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.5.0.tgz + version: 0.5.0 + - created: 2017-04-13T19:03:30.206806842Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 66cb4ab56bdac82c98eea4ceaf0fbcd9bb7c5c446f21bb396c3ce4246a76f423 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.4.1.tgz + version: 0.4.1 + - created: 2017-04-13T05:33:26.915479665Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 109c67f85696136b5629126952d5eb4196ca3cf36524976adf7babd3b8631782 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.4.0.tgz + version: 0.4.0 + - created: 2017-04-13T05:18:28.907645759Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: afa4583548e2e89617e21b91ef014285f060ad4a5355741260d72e27bb8eb4d3 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.3.1.tgz + version: 0.3.1 + - created: 2017-03-30T07:48:56.453711055Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: e9c5280a7cc57b16c51e70af9e4bf8b10f6525daf191c22a3a050a1791e5f7c7 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.3.0.tgz + version: 0.3.0 + - created: 2017-03-22T22:18:33.707478335Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: f5cf5bafd797d65bbbb55ff0b31935123d3f7ac283e703ac0b9df73b016edf59 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.2.0.tgz + version: 0.2.0 + - created: 2017-03-15T09:48:31.97803944Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: f8167252e615a10eb155087d6666985e8eb083781faa0485c0413de8c13deeb8 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.15.tgz + version: 0.1.15 + - created: 2017-03-14T13:33:36.105867963Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 6c5039f7ab62e7f74bb902f5804814f66733c535908b4ffae1cf759efa3c6f24 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.14.tgz + version: 0.1.14 + - created: 2017-02-13T22:18:28.949796594Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 90aa0fe9bac319690c871327dff6fee85e7de117e961dfa95ba12abedec4e99b + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.13.tgz + version: 0.1.13 + - created: 2017-02-13T21:48:52.587710548Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 92224a4dcf96772652e91cee6369a08f21f4ba7d1513ee458b5724720f7045ca + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.12.tgz + version: 0.1.12 + - created: 2017-02-13T21:03:28.21318035Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: c75cadf6bc32c90cfc8a61c1f233884004670c8583edefcfcaa43b5573422923 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.10.tgz + version: 0.1.10 + - created: 2017-02-13T17:03:30.11276321Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 47f32f975f942607a4b4ca05bd804ece5e2381840508d049251ca692e83080c0 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.9.tgz + version: 0.1.9 + - created: 2017-02-13T04:18:31.534794405Z + description: Open source continuous integration server. It supports multiple SCM + tools including CVS, Subversion and Git. It can execute Apache Ant and Apache + Maven-based projects as well as arbitrary scripts. + digest: 0764541a4165016e68bef6de1f405964b58ebb2b43b4d738f4bbbbad794b5df8 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.8.tgz + version: 0.1.8 + - created: 2017-01-27T21:48:32.069740444Z + description: A Jenkins Helm chart for Kubernetes. + digest: c29819a435e9474277846492930e910c9f7c0605717421c4efe411ce476283ab + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.7.tgz + version: 0.1.7 + - created: 2017-01-04T00:48:19.7378014Z + description: A Jenkins Helm chart for Kubernetes. + digest: 58ddd6442c8534de79a8d5eaca48263c744ca9fa6e9af7ceb28d1fda9b88ddb5 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.6.tgz + version: 0.1.6 + - created: 2016-12-19T22:03:51.103057889Z + description: A Jenkins Helm chart for Kubernetes. + digest: 2c8dff77b7ad736ac54e5335f5d52bfacaf7ea2ad97558da507b7f5bb9f4d549 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.5.tgz + version: 0.1.5 + - created: 2016-12-09T18:48:20.183887307Z + description: A Jenkins Helm chart for Kubernetes. + digest: 0b7016624acec8d66f6d919611e8f1c9853a5475c9801c3da6e50b7ce044ed57 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.4.tgz + version: 0.1.4 + - created: 2016-12-05T18:33:22.028569484Z + description: A Jenkins Helm chart for Kubernetes. + digest: 82b53a4c90ac2cf71bb41d870939ce1e980192e1407ba8825051c0ed98c04906 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.1.tgz + version: 0.1.1 + - created: 2016-10-19T00:03:14.028672648Z + description: A Jenkins Helm chart for Kubernetes. + digest: ea97fbfeaf9b9701d71dbc2b6fa50bff25a33f0565233d81170a6ac225d22ab4 + home: https://jenkins.io/ + icon: https://wiki.jenkins-ci.org/download/attachments/2916393/logo.png + maintainers: + - email: lachlan@deis.com + name: Lachlan Evenson + - email: viglesias@google.com + name: Vic Iglesias + name: jenkins + sources: + - https://github.com/jenkinsci/jenkins + - https://github.com/jenkinsci/docker-jnlp-slave + urls: + - https://kubernetes-charts.storage.googleapis.com/jenkins-0.1.0.tgz + version: 0.1.0 + joomla: + - created: 2017-04-28T00:18:30.085219154Z + description: PHP content management system (CMS) for publishing web content + digest: 6f9934487533f325515f4877b3af1306c87d64bf3ece9d4bd875289cd73b340d + engine: gotpl + home: http://www.joomla.org/ + icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.6.tgz + version: 0.4.6 + - created: 2017-04-06T11:03:25.397962583Z + description: PHP content management system (CMS) for publishing web content + digest: f9dedab2fc2dbf170cf45b2c230baa6d20aad9a6f8ccfcb09c459602fc5213dc + engine: gotpl + home: http://www.joomla.org/ + icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-02T19:33:28.178324258Z + description: PHP content management system (CMS) for publishing web content + digest: 1e067e459873ae832d54ff516a3420f7f0e16ecd8f72f4c4f02be22e47702077 + engine: gotpl + home: http://www.joomla.org/ + icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-27T16:48:32.159029074Z + description: PHP content management system (CMS) for publishing web content + digest: 9a99b15e83e18955eb364985cd545659f1176ef203ac730876dfe39499edfb18 + engine: gotpl + home: http://www.joomla.org/ + icon: https://bitnami.com/assets/stacks/joomla/img/joomla-stack-220x234.png + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-11T00:18:52.272598223Z + description: PHP content management system (CMS) for publishing web content + digest: 07c3a16eb674ffc74fe5b2b16191b8bb24c63bdae9bce9710bda1999920c46fc + engine: gotpl + home: http://www.joomla.org/ + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.345952551Z + description: PHP content management system (CMS) for publishing web content + digest: 95fbe272015941544609eee90b3bffd5172bfdec10be13636510caa8478a879e + engine: gotpl + home: http://www.joomla.org/ + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-09T18:48:20.18539038Z + description: PHP content management system (CMS) for publishing web content + digest: 0a01ea051ec15274932c8d82076c1a9fd62584b0fb916a81372319bef223c20e + engine: gotpl + home: http://www.joomla.org/ + keywords: + - joomla + - cms + - blog + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: joomla + sources: + - https://github.com/bitnami/bitnami-docker-joomla + urls: + - https://kubernetes-charts.storage.googleapis.com/joomla-0.4.0.tgz + version: 0.4.0 + kapacitor: + - created: 2017-04-28T00:18:30.085544137Z + description: InfluxDB's native data processing engine. It can process both stream + and batch data from InfluxDB. + digest: f484ef5acbc3ad68bb765adb1fd6c74920b9265371d3379e43a47ec266dc8449 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/kapacitor/ + keywords: + - kapacitor + - stream + - etl + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: kapacitor + sources: + - https://github.com/influxdata/kapacitor + urls: + - https://kubernetes-charts.storage.googleapis.com/kapacitor-0.2.2.tgz + version: 0.2.2 + - created: 2017-03-22T21:33:33.841225941Z + description: InfluxDB's native data processing engine. It can process both stream + and batch data from InfluxDB. + digest: c592b3bf2dec92c2273057b984b7289c17032dc578ea0f2ec86aeb5e838a7047 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/kapacitor/ + keywords: + - kapacitor + - stream + - etl + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: kapacitor + sources: + - https://github.com/influxdata/kapacitor + urls: + - https://kubernetes-charts.storage.googleapis.com/kapacitor-0.2.1.tgz + version: 0.2.1 + - created: 2017-02-13T21:48:52.58883187Z + description: InfluxDB's native data processing engine. It can process both stream + and batch data from InfluxDB. + digest: 18971c9c5b3805830f72fdfacd6c1dfc173db4797994f61b3666296b67abe70a + engine: gotpl + home: https://www.influxdata.com/time-series-platform/kapacitor/ + keywords: + - kapacitor + - stream + - etl + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: kapacitor + sources: + - https://github.com/influxdata/kapacitor + urls: + - https://kubernetes-charts.storage.googleapis.com/kapacitor-0.1.2.tgz + version: 0.1.2 + - created: 2017-02-13T21:03:28.215149313Z + description: Chart for Chronograf + digest: e0d29608602df25a9352629afd3fd7615e01a23549e1d1eba8101ee1b725e528 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/kapacitor/ + keywords: + - kapacitor + - stream + - etl + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: kapacitor + urls: + - https://kubernetes-charts.storage.googleapis.com/kapacitor-0.1.1.tgz + version: 0.1.1 + - created: 2017-01-28T01:33:32.786133788Z + description: Chart for Chronograf + digest: efde65696634d3bf1dc4c9caae44e68f7ed8c7599aab809c8cdd0fde7e4f9efc + engine: gotpl + home: https://www.influxdata.com/time-series-platform/kapacitor/ + keywords: + - kapacitor + - stream + - etl + - timeseries + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: kapacitor + urls: + - https://kubernetes-charts.storage.googleapis.com/kapacitor-0.1.0.tgz + version: 0.1.0 + kube-lego: + - apiVersion: v1 + created: 2017-04-28T00:18:30.085897046Z + description: Automatically requests certificates from Let's Encrypt + digest: adc8f0fe1c244e5abda2d918afe9153d47648b2acb779adaf796fa5a7266ea6b + engine: gotpl + keywords: + - kube-lego + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube-lego + sources: + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-lego-0.1.8.tgz + version: 0.1.8 + - apiVersion: v1 + created: 2017-03-20T20:03:37.426644363Z + description: Automatically requests certificates from Let's Encrypt + digest: 0110b8c36f6ad016684ef904b5e514751ffa7a89ba7b88cf4a8376c889e03693 + engine: gotpl + keywords: + - kube-lego + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube-lego + sources: + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-lego-0.1.7.tgz + version: 0.1.7 + - apiVersion: v1 + created: 2017-03-02T19:33:28.179029307Z + description: Automatically requests certificates from Let's Encrypt + digest: 275265fda80ccc62376ebd3a200bbea94b105cbf3481efcc726d6a33d73da8d0 + engine: gotpl + keywords: + - kube-lego + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube-lego + sources: + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-lego-0.1.6.tgz + version: 0.1.6 + - apiVersion: v1 + created: 2017-02-27T17:33:27.264070807Z + description: Automatically requests certificates from Let's Encrypt + digest: 62de296cc21c6b286097de9ac389033e18a4cd3cf414cd97b7a02d4c3be14d6d + engine: gotpl + keywords: + - kube-lego + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube-lego + sources: + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-lego-0.1.5.tgz + version: 0.1.5 + - apiVersion: v1 + created: 2017-02-14T15:48:28.578398517Z + description: Automatically requests certificates from Let's Encrypt + digest: 1bc993b68eb51fb00e4eb18a65df9e2182f895bd46134d6ddfdc0dd3b6432a98 + engine: gotpl + keywords: + - kube-lego + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube-lego + sources: + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-lego-0.1.4.tgz + version: 0.1.4 + - apiVersion: v1 + created: 2017-02-11T00:48:25.354730418Z + description: Automatically requests certificates from Let's Encrypt + digest: d4b3694951c2bb42b356217a8f12870bd3806dac4de3390056279998fd634c34 + engine: gotpl + keywords: + - kube-lego + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube-lego + sources: + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-lego-0.1.3.tgz + version: 0.1.3 + kube-ops-view: + - created: 2017-04-28T00:18:30.086143954Z + description: Kubernetes Operational View - read-only system dashboard for multiple + K8s clusters + digest: 9e7b62f2d781aaacee801e91efe4d6b6ef6df9d511c0d6fb2d6e7977c8d0a06e + home: https://github.com/hjacobs/kube-ops-view + icon: https://raw.githubusercontent.com/hjacobs/kube-ops-view/master/kube-ops-view-logo.png + keywords: + - kubernetes + - dashboard + - operations + maintainers: + - email: henning@jacobs1.de + name: Henning Jacobs + name: kube-ops-view + sources: + - https://github.com/hjacobs/kube-ops-view + urls: + - https://kubernetes-charts.storage.googleapis.com/kube-ops-view-0.2.0.tgz + version: 0.2.0 + kube2iam: + - created: 2017-04-28T00:18:30.086423576Z + description: Provide IAM credentials to pods based on annotations. + digest: 2035d8dfae5733fa475914694cd060e28954b1f5c930b6c4800ee165d23abc46 + engine: gotpl + keywords: + - kube2iam + - aws + - iam + - security + maintainers: + - email: jm.carp@gmail.com + name: Josh Carp + - email: michael.haselton@gmail.com + name: Michael Haselton + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube2iam + sources: + - https://github.com/jtblin/kube2iam + urls: + - https://kubernetes-charts.storage.googleapis.com/kube2iam-0.2.1.tgz + version: 0.2.1 + - created: 2017-03-02T18:48:30.431985789Z + description: Provide IAM credentials to pods based on annotations. + digest: 5d2226fb101b800fc5a6edb5566d329880c604d75f4245da55f47027bcf5546e + engine: gotpl + keywords: + - kube2iam + - aws + - iam + - security + maintainers: + - email: jm.carp@gmail.com + name: Josh Carp + - email: michael.haselton@gmail.com + name: Michael Haselton + - email: mgoodness@gmail.com + name: Michael Goodness + name: kube2iam + sources: + - https://github.com/jtblin/kube2iam + urls: + - https://kubernetes-charts.storage.googleapis.com/kube2iam-0.2.0.tgz + version: 0.2.0 + - created: 2017-02-13T17:03:30.115672844Z + description: Provide IAM credentials to containers running inside a kubernetes + cluster based on annotations. + digest: 63d913fb8f31c287b1f1d45d310517c0b22597ecdaef19d7ad2520bff990969a + engine: gotpl + keywords: + - kube2iam + - aws + - iam + - security + maintainers: + - email: jm.carp@gmail.com + name: Josh Carp + - email: michael.haselton@gmail.com + name: Michael Haselton + name: kube2iam + sources: + - https://github.com/jtblin/kube2iam + urls: + - https://kubernetes-charts.storage.googleapis.com/kube2iam-0.1.1.tgz + version: 0.1.1 + - created: 2017-01-19T00:33:27.789172853Z + description: Provide IAM credentials to containers running inside a kubernetes + cluster based on annotations. + digest: b4de5bddfa0af6737ea91d9b3a9bcfc3a0e5a656045e06038505613b214120fc + engine: gotpl + keywords: + - kube2iam + - aws + - iam + - security + maintainers: + - email: jm.carp@gmail.com + name: Josh Carp + - email: michael.haselton@gmail.com + name: Michael Haselton + name: kube2iam + sources: + - https://github.com/jtblin/kube2iam + urls: + - https://kubernetes-charts.storage.googleapis.com/kube2iam-0.1.0.tgz + version: 0.1.0 + linkerd: + - apiVersion: v1 + created: 2017-04-28T00:18:30.086763621Z + description: Service mesh for cloud native apps + digest: 9341ea67647129999beadb49f1a33131a187569581095907f902daffa73dd73b + home: https://linkerd.io/ + icon: https://pbs.twimg.com/profile_images/690258997237014528/KNgQd9GL_400x400.png + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: linkerd + sources: + - https://github.com/BuoyantIO/linkerd + urls: + - https://kubernetes-charts.storage.googleapis.com/linkerd-0.2.0.tgz + version: 0.2.0 + - apiVersion: v1 + created: 2017-03-27T13:18:34.232799345Z + description: Service mesh for cloud native apps + digest: f9f2287d026c6de3a522bdcffdff061f81a8e64274da4acefdc9d2177b603570 + home: https://linkerd.io/ + icon: https://pbs.twimg.com/profile_images/690258997237014528/KNgQd9GL_400x400.png + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: linkerd + sources: + - https://github.com/BuoyantIO/linkerd + urls: + - https://kubernetes-charts.storage.googleapis.com/linkerd-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: 2017-02-13T04:33:52.299092507Z + description: Service mesh for cloud native apps + digest: 147759e4982f1dffce894e0d4242fb914d21014700a7d9891e8dc352318633fa + home: https://linkerd.io/ + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: linkerd + sources: + - https://github.com/BuoyantIO/linkerd + urls: + - https://kubernetes-charts.storage.googleapis.com/linkerd-0.1.0.tgz + version: 0.1.0 + locust: + - created: 2017-04-28T00:18:30.087097677Z + description: A modern load testing framework + digest: eb91b0e3c0b618cf5ad0f24d2685fe4086bc6f497685e58ad8a64032c4e82b7a + home: http://locust.io + icon: https://pbs.twimg.com/profile_images/1867636195/locust-logo-orignal.png + maintainers: + - email: vincent.drl@gmail.com + name: Vincent De Smet + name: locust + sources: + - https://github.com/honestbee/distributed-load-testing + urls: + - https://kubernetes-charts.storage.googleapis.com/locust-0.1.2.tgz + version: 0.1.2 + - created: 2017-04-20T16:18:33.345004534Z + description: A chart for locust distributed load testing + digest: 40a353b38823eaa4954dcb70ae92858671a16fb8d00ac9677a36912aab6b629a + name: locust + sources: + - https://github.com/honestbee/distributed-load-testing + urls: + - https://kubernetes-charts.storage.googleapis.com/locust-0.1.1.tgz + version: 0.1.1 + - created: 2017-04-11T16:03:29.689097384Z + description: A chart for locust distributed load testing + digest: 2cd175e8c8d66625ca3608046d5764662b53e3760aa260a934d3d2ee35148c49 + name: locust + sources: + - https://github.com/honestbee/distributed-load-testing + urls: + - https://kubernetes-charts.storage.googleapis.com/locust-0.1.0.tgz + version: 0.1.0 + magento: + - created: 2017-04-28T00:18:30.087794886Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: 2d7ebf934ca0e742e248753da6502f05929b51329a42d0d4b3db4e1824381f11 + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-31T19:33:30.42890495Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: a289a051d6d2e8cf833398d7d63fcb1fd953d202f0755800eebe568804fce525 + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-09T19:33:31.142591331Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: 311a98182281b981f60fbd446b42f6af4b86e0bac9445bf2248b1b47dfce5933 + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-02T19:33:28.180428252Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: fccfa89493a977a2f898a5e6d3c6fa4fda15faf0dc0a9e48772744a32c2b1d24 + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-27T16:48:32.161186722Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: fb33cafa4874855419ea3178dc0660a856ed6f8b581b984f38e86701d54e54a6 + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.349108265Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: c597107a5cf512d616e5e4134e562f8fcc6e79ad1f78241e9a40ded77e77ef62 + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-04T00:48:19.74048297Z + description: A feature-rich flexible e-commerce solution. It includes transaction + options, multi-store functionality, loyalty programs, product categorization + and shopper filtering, promotion rules, and more. + digest: 526115583b8ae741f6819c2a0d3e719a6622a63a0a2e9918e56d2ebaa6ad498b + engine: gotpl + home: https://magento.com/ + icon: https://bitnami.com/assets/stacks/magento/img/magento-stack-110x117.png + keywords: + - magento + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: magento + sources: + - https://github.com/bitnami/bitnami-docker-magento + urls: + - https://kubernetes-charts.storage.googleapis.com/magento-0.4.0.tgz + version: 0.4.0 + mariadb: + - created: 2017-04-28T00:18:30.088188975Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: bc13b8aa3728de5595bb83ea8a5c21f9234e169352739d8e72f04d1ed2558508 + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.6.0.tgz + version: 0.6.0 + - created: 2017-04-03T22:33:26.672780802Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: 23b81e8bc8cd8577a47a73578b56f68a2a3c15640e0a0fb0ac7f1646845bc0b0 + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.14.tgz + version: 0.5.14 + - created: 2017-03-23T19:18:29.910295788Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: a4c8e19714e2a3d29eae03c63a63a7343b3e4f62df2956603acca7eb42ab15de + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.13.tgz + version: 0.5.13 + - created: 2017-03-17T22:48:29.250381078Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: 8d5cd0e79d675baa79ab4f999da8e10037ae4c2158e434b7b60d4ff345f1d976 + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.12.tgz + version: 0.5.12 + - created: 2017-03-16T13:33:30.840089178Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: 0841b64dbe2fe499bd3554fa9b2074be0ed6535e9f87d3ea2c13bcfc2616717b + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.11.tgz + version: 0.5.11 + - created: 2017-03-15T23:03:34.130536152Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: 587d7ef318ee1229d14e3b783c3e825f90f274f56b2eb62f1c7126f69fec6fc2 + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.10.tgz + version: 0.5.10 + - created: 2017-03-08T19:03:31.728285865Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. MariaDB Server is intended for mission-critical, heavy-load + production systems as well as for embedding into mass-deployed software. + digest: cfec18e7be68a616a5e4944c21ed0b65dab1701cf7182d0d1bdea83df6a77ab1 + engine: gotpl + home: https://mariadb.org + icon: https://bitnami.com/assets/stacks/mariadb/img/mariadb-stack-220x234.png + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.9.tgz + version: 0.5.9 + - created: 2017-02-11T00:18:52.27620633Z + description: Chart for MariaDB + digest: 33990fb57b2fafb4396498e02a8083f6a476cf0b465a4a52905a67aab73f3f43 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.8.tgz + version: 0.5.8 + - created: 2017-01-31T01:03:26.972704179Z + description: Chart for MariaDB + digest: 177a882b4248d6ebb2e72e125a3be775085bf082e4eb8f4359fee9e8640ede50 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.7.tgz + version: 0.5.7 + - created: 2017-01-27T22:33:30.411626628Z + description: Chart for MariaDB + digest: 730d179ab0d5922494e3c21594ce7b4f118f7fe796071dfcfdbbc2714ada7d15 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.6.tgz + version: 0.5.6 + - created: 2016-12-15T23:18:25.557355931Z + description: Chart for MariaDB + digest: c7811fad8165eb5d57eb55ad4e58d63e949d2f710c0fbbcd28cca11bb6af6de6 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.5.tgz + version: 0.5.5 + - created: 2016-12-09T18:48:20.185787408Z + description: Chart for MariaDB + digest: c6b73c5146d085a202f838741526fe5fdc2fae55b8f5f17c0c42fd195c65311f + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.4.tgz + version: 0.5.4 + - created: 2016-11-23T00:33:20.016012859Z + description: Chart for MariaDB + digest: 38786f4f6fe4fb7a2dcbc38aa0bfea72e40e4d9766e1679942af60e25b5ba9bd + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.3.tgz + version: 0.5.3 + - created: 2016-11-03T19:33:29.120356141Z + description: Chart for MariaDB + digest: 8eb24c65fbdb75711a8ffd8f8f6ed206951cdc3a24d74a355121bc11c6c02f3b + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.2.tgz + version: 0.5.2 + - created: 2016-10-19T00:03:14.031007257Z + description: Chart for MariaDB + digest: 6544dbf62586f570762a3a469f0086fe595379454fb46a53d001206a0e282268 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.1.tgz + version: 0.5.1 + - created: 2016-10-19T00:03:14.030598543Z + description: Chart for MariaDB + digest: c9f67f8140b3e7243d479f819d4ec8b2e992ee462b8fa579b920e86066955312 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.5.0.tgz + version: 0.5.0 + - created: 2016-10-19T00:03:14.03012509Z + description: Chart for MariaDB + digest: fe8adb0730567ad8cd63501ac18b178ec2a9a590d43dd7ad91fd2d5fcf6114be + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.4.0.tgz + version: 0.4.0 + - created: 2016-10-19T00:03:14.029775557Z + description: Chart for MariaDB + digest: 47fe08909187da025e7e86e9534a736df11e2629c5c123178113fe776992e9e7 + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.3.1.tgz + version: 0.3.1 + - created: 2016-10-19T00:03:14.02942396Z + description: Chart for MariaDB + digest: 7354d2672b3983e98fe5c31d96d2c3d9c73edefe642eb5e1981484804315c4bc + engine: gotpl + home: https://mariadb.org + keywords: + - mariadb + - mysql + - database + - sql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mariadb + sources: + - https://github.com/bitnami/bitnami-docker-mariadb + urls: + - https://kubernetes-charts.storage.googleapis.com/mariadb-0.3.0.tgz + version: 0.3.0 + mediawiki: + - created: 2017-04-28T00:18:30.088860566Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: 0e51822c5547895109a5b41ce426c77f62d0434b40f3021afee8471ab976a6f5 + engine: gotpl + home: http://www.mediawiki.org/ + icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.6.tgz + version: 0.4.6 + - created: 2017-04-06T10:18:27.586263816Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: 0e419c2c5d87997f94a32da6597af3f3b52120dc1ec682dcbb6b238fb4825e06 + engine: gotpl + home: http://www.mediawiki.org/ + icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-08T19:03:31.729226516Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: 6f4dde26737f7f1aa63ffda6c259ce388e3a3509225f90f334bfc3f0f7617bc1 + engine: gotpl + home: http://www.mediawiki.org/ + icon: https://bitnami.com/assets/stacks/mediawiki/img/mediawiki-stack-220x234.png + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-13T04:18:31.539427547Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: 0ba52b8c4c9e0bee3eb76fe625d2dc88729a1cdf41ace9d13cd4abc5b477cfb8 + engine: gotpl + home: http://www.mediawiki.org/ + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.3.tgz + version: 0.4.3 + - created: 2017-01-21T00:18:31.350311075Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: f49df3e17f97b238743aad0376eb9db7e4a9bca3829a3a65d7bbb349344a73be + engine: gotpl + home: http://www.mediawiki.org/ + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.2.tgz + version: 0.4.2 + - created: 2016-12-15T21:18:24.670966268Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: 339a90050d5cf4216140409349a356aa7cd8dc95e2cbdca06e4fdd11e87aa963 + engine: gotpl + home: http://www.mediawiki.org/ + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-09T18:48:20.186416136Z + description: Extremely powerful, scalable software and a feature-rich wiki implementation + that uses PHP to process and display data stored in a database. + digest: 9617f13f51f5bb016a072f2a026c627420721a1c5b7cd22f32d6cd0c90f34eda + engine: gotpl + home: http://www.mediawiki.org/ + keywords: + - mediawiki + - wiki + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mediawiki + sources: + - https://github.com/bitnami/bitnami-docker-mediawiki + urls: + - https://kubernetes-charts.storage.googleapis.com/mediawiki-0.4.0.tgz + version: 0.4.0 + memcached: + - created: 2017-04-28T00:18:30.090586195Z + description: Free & open source, high-performance, distributed memory object caching + system. + digest: 36ceb2767094598171b2851ecda54bd43d862b9b81aa4b294f3d8c8d59ddd79c + engine: gotpl + home: http://memcached.org/ + icon: https://upload.wikimedia.org/wikipedia/en/thumb/2/27/Memcached.svg/1024px-Memcached.svg.png + keywords: + - memcached + - cache + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: memcached + sources: + - https://github.com/docker-library/memcached + urls: + - https://kubernetes-charts.storage.googleapis.com/memcached-0.4.1.tgz + version: 0.4.1 + - created: 2017-02-13T04:18:31.539713999Z + description: Chart for Memcached + digest: 2b918dd8129a9d706e58b3de459004e3367c05a162d3e3cdb031cb6818d5f820 + engine: gotpl + home: http://memcached.org/ + keywords: + - memcached + - cache + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: memcached + sources: + - https://github.com/docker-library/memcached + urls: + - https://kubernetes-charts.storage.googleapis.com/memcached-0.4.0.tgz + version: 0.4.0 + minecraft: + - created: 2017-04-28T00:18:30.09103508Z + description: Minecraft server + digest: bc18a8356092c19f39ce94ff34b2160650a7bebca5723fd2e2f4e350c38793c0 + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: minecraft + sources: + - https://hub.docker.com/r/itzg/minecraft-server/~/dockerfile/ + - https://github.com/itzg/dockerfiles + urls: + - https://kubernetes-charts.storage.googleapis.com/minecraft-0.1.3.tgz + version: 0.1.3 + - created: 2017-03-16T23:33:31.588714127Z + description: Minecraft server + digest: 5a42451d7c2f69b7b77c40e91ef60ca284798bcab8aa5b97b1f3f2559612d443 + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: minecraft + sources: + - https://hub.docker.com/r/itzg/minecraft-server/~/dockerfile/ + - https://github.com/itzg/dockerfiles + urls: + - https://kubernetes-charts.storage.googleapis.com/minecraft-0.1.2.tgz + version: 0.1.2 + - created: 2017-01-30T23:33:28.437640114Z + description: Minecraft server + digest: 87c2ef91c8feaee680bb26ba16362c6b366429e0f54119c40dbf7a5ce3f22553 + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: minecraft + sources: + - https://hub.docker.com/r/itzg/minecraft-server/~/dockerfile/ + - https://github.com/itzg/dockerfiles + urls: + - https://kubernetes-charts.storage.googleapis.com/minecraft-0.1.1.tgz + version: 0.1.1 + - created: 2016-12-05T21:03:20.22190695Z + description: Minecraft server + digest: 2cd423a28eb8a0186fba171cc17dcbe6f5a53d1a99e4078f9713afed3b61cce6 + keywords: + - game + - server + maintainers: + - email: gtaylor@gc-taylor.com + name: Greg Taylor + name: minecraft + sources: + - https://hub.docker.com/r/itzg/minecraft-server/~/dockerfile/ + - https://github.com/itzg/dockerfiles + urls: + - https://kubernetes-charts.storage.googleapis.com/minecraft-0.1.0.tgz + version: 0.1.0 + minio: + - apiVersion: v1 + created: 2017-04-28T00:18:30.091526674Z + description: Distributed object storage server built for cloud applications and + devops. + digest: 12eb77d0d25971c86a4df25297eea8d579b4b5dc6599d16f340f553977c53914 + home: https://minio.io + icon: https://www.minio.io/logo/img/logo-dark.svg + keywords: + - storage + - object-storage + - S3 + maintainers: + - email: hello@acale.ph + name: Acaleph + - email: hello@minio.io + name: Minio + name: minio + sources: + - https://github.com/minio/minio + urls: + - https://kubernetes-charts.storage.googleapis.com/minio-0.1.0.tgz + version: 0.1.0 + - apiVersion: v1 + created: 2017-03-16T23:48:28.876000842Z + description: Distributed object storage server built for cloud applications and + devops. + digest: d19b721fcb5f0566cce4a259e296b957ba982e87343947e1cbdbe979c770378d + home: https://minio.io + icon: https://www.minio.io/logo/img/logo-dark.svg + keywords: + - storage + - object-storage + - S3 + maintainers: + - email: hello@acale.ph + name: Acaleph + - email: hello@minio.io + name: Minio + name: minio + sources: + - https://github.com/minio/minio + urls: + - https://kubernetes-charts.storage.googleapis.com/minio-0.0.4.tgz + version: 0.0.4 + - apiVersion: v1 + created: 2017-02-13T04:33:52.302413618Z + description: A Minio Helm chart for Kubernetes + digest: cd58148df0776329fe5f518c542759565cab29dbefbc43f6b0ffdac86c9b31a9 + home: https://minio.io + keywords: + - storage + - object-storage + - S3 + maintainers: + - email: hello@acale.ph + name: Acaleph + - email: hello@minio.io + name: Minio + name: minio + sources: + - https://github.com/minio/minio + urls: + - https://kubernetes-charts.storage.googleapis.com/minio-0.0.3.tgz + version: 0.0.3 + - apiVersion: v1 + created: 2017-02-03T20:18:29.15659791Z + description: A Minio Helm chart for Kubernetes + digest: 699003bf2ef4cbb570580887da980412c1b9d6d173636de5def6053c7ba29a2a + home: https://minio.io + keywords: + - storage + - object-storage + - S3 + maintainers: + - email: hello@acale.ph + name: Acaleph + - email: hello@minio.io + name: Minio + name: minio + sources: + - https://github.com/minio/minio + urls: + - https://kubernetes-charts.storage.googleapis.com/minio-0.0.2.tgz + version: 0.0.2 + - apiVersion: v1 + created: 2017-01-12T02:36:05.500400572Z + description: A Minio Helm chart for Kubernetes + digest: aed17de3622988f8366126e158c740535c8d3bc55a0a85a3dcfabf07ac1395e9 + home: https://minio.io + keywords: + - storage + - object-storage + - S3 + maintainers: + - email: hello@acale.ph + name: Acaleph + name: minio + sources: + - https://github.com/minio/minio + urls: + - https://kubernetes-charts.storage.googleapis.com/minio-0.0.1.tgz + version: 0.0.1 + mongodb: + - created: 2017-04-28T00:18:30.091865909Z + description: NoSQL document-oriented database that stores JSON-like documents + with dynamic schemas, simplifying the integration of data in content-driven + applications. + digest: 979d36b208be9b266c70860d4fe1f9e5130d9d60b3bcbd893132452648dfe27f + engine: gotpl + home: https://mongodb.org + icon: https://bitnami.com/assets/stacks/mongodb/img/mongodb-stack-220x234.png + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.9.tgz + version: 0.4.9 + - created: 2017-04-06T10:33:26.324740815Z + description: NoSQL document-oriented database that stores JSON-like documents + with dynamic schemas, simplifying the integration of data in content-driven + applications. + digest: cdc9fc28ff9139fcc6be015177e481f9d3765d6af284dce1999fc334cd7ef3a4 + engine: gotpl + home: https://mongodb.org + icon: https://bitnami.com/assets/stacks/mongodb/img/mongodb-stack-220x234.png + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.8.tgz + version: 0.4.8 + - created: 2017-03-08T19:03:31.731176002Z + description: NoSQL document-oriented database that stores JSON-like documents + with dynamic schemas, simplifying the integration of data in content-driven + applications. + digest: 7d334e12acf9327f58f9a890e884a61ad760da2b1081d4a79b5680bee055cdbd + engine: gotpl + home: https://mongodb.org + icon: https://bitnami.com/assets/stacks/mongodb/img/mongodb-stack-220x234.png + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.7.tgz + version: 0.4.7 + - created: 2017-02-14T03:48:27.566728756Z + description: NoSQL document-oriented database that stores JSON-like documents + with dynamic schemas, simplifying the integration of data in content-driven + applications. + digest: 27f9071cb81e9d3745776861f453db80b6ab6bf4507809f2e5c59e8a34d44939 + engine: gotpl + home: https://mongodb.org + icon: https://bitnami.com/assets/stacks/mongodb/img/mongodb-stack-220x234.png + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.6.tgz + version: 0.4.6 + - created: 2017-02-10T23:18:26.017406698Z + description: Chart for MongoDB + digest: 27a78b0c6300f4567975af18a3ca145940a716a53de42ed89c75872788d2848b + engine: gotpl + home: https://mongodb.org + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.5.tgz + version: 0.4.5 + - created: 2017-01-30T23:33:28.438574863Z + description: Chart for MongoDB + digest: 9bcc0e2aa1d7d8f8c2ce43ef9284af39e5794214d185577ed1baff07c1a019f4 + engine: gotpl + home: https://mongodb.org + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.4.tgz + version: 0.4.4 + - created: 2017-01-13T20:48:31.52900213Z + description: Chart for MongoDB + digest: be548ec183b8c44f031504864b85b7a0d48d745fa450bf733f89646c9cbbda44 + engine: gotpl + home: https://mongodb.org + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.3.tgz + version: 0.4.3 + - created: 2017-01-03T17:48:20.740456601Z + description: Chart for MongoDB + digest: 027dd50ff545309506daa0636a62d633071379040f8be080a403cb6d67399b0d + engine: gotpl + home: https://mongodb.org + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.2.tgz + version: 0.4.2 + - created: 2016-12-15T00:48:24.012807516Z + description: Chart for MongoDB + digest: 42e8e56c715ea3bd2b8f9c188f5a9aec48a87654fb5215c35728ddf6c33c7437 + engine: gotpl + home: https://mongodb.org + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-09T18:48:20.187403991Z + description: Chart for MongoDB + digest: d5eabbe99b03b4f7f71c461580564e3d965c2602bfd1be4dd09f1c54c8e7e9db + engine: gotpl + home: https://mongodb.org + keywords: + - mongodb + - database + - nosql + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: mongodb + sources: + - https://github.com/bitnami/bitnami-docker-mongodb + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-0.4.0.tgz + version: 0.4.0 + mongodb-replicaset: + - created: 2017-04-28T00:18:30.092344824Z + description: NoSQL document-oriented database that stores JSON-like documents + with dynamic schemas, simplifying the integration of data in content-driven + applications. + digest: 98592fbb471eb98d3c59deb83ed9bd2a808e8df972b21ff183d04fa4659e9a39 + home: https://github.com/mongodb/mongo + icon: https://webassets.mongodb.com/_com_assets/cms/mongodb-logo-rgb-j6w271g1xn.jpg + maintainers: + - email: ramanathana@google.com + name: Anirudh Ramanathan + name: mongodb-replicaset + sources: + - https://github.com/mongodb/mongo + urls: + - https://kubernetes-charts.storage.googleapis.com/mongodb-replicaset-1.0.0.tgz + version: 1.0.0 + moodle: + - created: 2017-04-28T00:18:30.093014917Z + description: Moodle is a learning platform designed to provide educators, administrators + and learners with a single robust, secure and integrated system to create personalised + learning environments + digest: 386bff8ce61cf61961daf8ed6d68a76cd3a360560a08c1fca80bcbd897f11270 + engine: gotpl + home: http://www.moodle.org/ + icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png + keywords: + - moodle + - learning + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: moodle + sources: + - https://github.com/bitnami/bitnami-docker-moodle + urls: + - https://kubernetes-charts.storage.googleapis.com/moodle-0.1.4.tgz + version: 0.1.4 + - created: 2017-03-31T19:33:30.433327839Z + description: Moodle is a learning platform designed to provide educators, administrators + and learners with a single robust, secure and integrated system to create personalised + learning environments + digest: bd85420a7cefd82e9d96088591601f832ecc60016d6389dbcde51a2050327a66 + engine: gotpl + home: http://www.moodle.org/ + icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png + keywords: + - moodle + - learning + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: moodle + sources: + - https://github.com/bitnami/bitnami-docker-moodle + urls: + - https://kubernetes-charts.storage.googleapis.com/moodle-0.1.3.tgz + version: 0.1.3 + - created: 2017-03-26T18:03:33.791615833Z + description: Moodle is a learning platform designed to provide educators, administrators + and learners with a single robust, secure and integrated system to create personalised + learning environments + digest: 8656c544a71fa8cc4ac23380e999e072740ec8e481a22aff86517d8362e70121 + engine: gotpl + home: http://www.moodle.org/ + icon: https://bitnami.com/assets/stacks/moodle/img/moodle-stack-110x117.png + keywords: + - moodle + - learning + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: moodle + sources: + - https://github.com/bitnami/bitnami-docker-moodle + urls: + - https://kubernetes-charts.storage.googleapis.com/moodle-0.1.2.tgz + version: 0.1.2 + mysql: + - created: 2017-04-28T00:18:30.093353908Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. + digest: 2de7736158326da89faed2e5af952b4c7288f61bd96377ef345a99b4b271a3e7 + engine: gotpl + home: https://www.mysql.com/ + icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.6.tgz + version: 0.2.6 + - created: 2017-04-13T05:18:28.91694371Z + description: Fast, reliable, scalable, and easy to use open-source relational + database system. + digest: 66b9a6787a36c72095c24499a45f2d3bfc700e31420a8926d09c029604ac4c98 + engine: gotpl + home: https://www.mysql.com/ + icon: https://www.mysql.com/common/logos/logo-mysql-170x115.png + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.5.tgz + version: 0.2.5 + - created: 2017-02-13T04:18:31.541320579Z + description: Chart for MySQL + digest: 3653a2d111ca60287ffbf10cbbb3c268dcb8666422bf5518d37adb9b2f131f7c + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.4.tgz + version: 0.2.4 + - created: 2017-01-27T22:33:30.41494884Z + description: Chart for MySQL + digest: 1244814f3490d23172a923e52339ad8b126f3037483db462591405863884e7ce + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.3.tgz + version: 0.2.3 + - created: 2016-12-21T19:33:19.335178436Z + description: Chart for MySQL + digest: 5dfdd6301aa5c7424c5ecc649ac3038ea72afd0e25d4c350c79e8ba84fe15faf + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.2.tgz + version: 0.2.2 + - created: 2016-12-09T18:48:20.187750412Z + description: Chart for MySQL + digest: 3dd20c2ed2faf64b9f940e813b18d7fa1c18c1b86101de453c7c836940e05680 + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.1.tgz + version: 0.2.1 + - created: 2016-11-04T14:18:20.013918541Z + description: Chart for MySQL + digest: 9723417e4d71713ed87b701eff52a1a74abea64c3cf852833b1e9bb96ff6fd55 + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.2.0.tgz + version: 0.2.0 + - created: 2016-11-03T03:18:19.715713217Z + description: Chart for MySQL + digest: fef93423760265f8bb3aedf9a922ed0169e018071ea467f22c17527006ae6a60 + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.1.2.tgz + version: 0.1.2 + - created: 2016-10-19T18:33:14.866729383Z + description: Chart for MySQL + digest: 3cb4495336e12d4fea28a1f7e3f02bbb18249582227866088cf6ac89587a2527 + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.1.1.tgz + version: 0.1.1 + - created: 2016-10-19T00:03:14.031801762Z + description: Chart for MySQL + digest: cba3eff1710520dbfbbeca2b0f9a754e0ddc172eb83ce51211606387955a6572 + engine: gotpl + home: https://www.mysql.com/ + keywords: + - mysql + - database + - sql + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: mysql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/mysql-0.1.0.tgz + version: 0.1.0 + namerd: + - apiVersion: v1 + created: 2017-04-28T00:18:30.093626863Z + description: Service that manages routing for multiple linkerd instances + digest: 52164352b00a196135a608198d12748d063965cfde016dc8a4fdc9021587bbeb + home: https://linkerd.io/in-depth/namerd/ + icon: https://pbs.twimg.com/profile_images/690258997237014528/KNgQd9GL_400x400.png + maintainers: + - email: knoxville@gmail.com + name: Sean Knox + name: namerd + sources: + - https://github.com/linkerd/linkerd + urls: + - https://kubernetes-charts.storage.googleapis.com/namerd-0.1.0.tgz + version: 0.1.0 + nginx-ingress: + - created: 2017-04-28T00:18:30.094151336Z + description: An nginx Ingress controller that uses ConfigMap to store the nginx + configuration. + digest: 2d6183d368b3993b4fdfd7108531067b928083b3db6dc30eab3f9c3a8212df9b + engine: gotpl + icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png + keywords: + - ingress + - nginx + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + - email: chance.zibolski@coreos.com + name: Chance Zibolski + name: nginx-ingress + sources: + - https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-ingress-0.3.2.tgz + version: 0.3.2 + - created: 2017-04-03T22:33:26.678796256Z + description: An nginx Ingress controller that uses ConfigMap to store the nginx + configuration. + digest: 4e4632273eb5db95e525a8899b9f6f1697db241c2ff1ccb7456e0fc916bb75c2 + engine: gotpl + icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png + keywords: + - ingress + - nginx + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + - email: chance.zibolski@coreos.com + name: Chance Zibolski + name: nginx-ingress + sources: + - https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-ingress-0.3.1.tgz + version: 0.3.1 + - created: 2017-03-10T17:03:37.208481141Z + description: An nginx Ingress controller that uses ConfigMap to store the nginx + configuration. + digest: 731823c88a849f945f405c192d92daf27afad37af76befb1eb92251240de29d7 + engine: gotpl + icon: https://upload.wikimedia.org/wikipedia/commons/thumb/c/c5/Nginx_logo.svg/500px-Nginx_logo.svg.png + keywords: + - ingress + - nginx + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + - email: mgoodness@gmail.com + name: Michael Goodness + name: nginx-ingress + sources: + - https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-ingress-0.3.0.tgz + version: 0.3.0 + nginx-lego: + - created: 2017-04-28T00:18:30.094506147Z + description: Chart for nginx-ingress-controller and kube-lego + digest: da173cc1a9313ea0b11f5fb7aa67a20a2ac797b2f129a079c06284e8a9765f21 + engine: gotpl + keywords: + - kube-lego + - nginx-ingress-controller + - nginx + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + name: nginx-lego + sources: + - https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-lego-0.2.1.tgz + version: 0.2.1 + - created: 2017-03-02T18:48:30.43738595Z + description: Chart for nginx-ingress-controller and kube-lego + digest: 9a8ea81371900d2c7931b0143f991ef0fde41038d26a968628008aef14ec08ef + engine: gotpl + keywords: + - kube-lego + - nginx-ingress-controller + - nginx + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + name: nginx-lego + sources: + - https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-lego-0.2.0.tgz + version: 0.2.0 + - created: 2017-01-12T02:52:37.835917781Z + description: Chart for nginx-ingress-controller and kube-lego + digest: 2dee942e546940beb8301a94a1a51e22c7a38b4c5592701528149dfaa83e1bb6 + engine: gotpl + keywords: + - kube-lego + - nginx-ingress-controller + - nginx + - letsencrypt + maintainers: + - email: jack.zampolin@gmail.com + name: Jack Zampolin + name: nginx-lego + sources: + - https://github.com/kubernetes/contrib/tree/master/ingress/controllers/nginx + - https://github.com/jetstack/kube-lego/tree/master/examples/nginx + urls: + - https://kubernetes-charts.storage.googleapis.com/nginx-lego-0.1.0.tgz + version: 0.1.0 + odoo: + - created: 2017-04-28T00:18:30.095144247Z + description: A suite of web based open source business apps. + digest: 439b9160c3226a0cf6b848a5ac921307e5100ffa36e03e3697619a6c968eec3b + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.5.0.tgz + version: 0.5.0 + - created: 2017-04-20T11:48:30.777572861Z + description: A suite of web based open source business apps. + digest: 4aeba5cf600c9552d3a0ace00b4880aa27d8b2c2aec1b7dc9ebad04f2152d165 + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.4.5.tgz + version: 0.4.5 + - created: 2017-04-19T19:48:30.479799003Z + description: A suite of web based open source business apps. + digest: 74dff526f84c50445e0cc0fb602679c68941e9b93d9410ce66a6f3713a71f39d + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-23T21:18:31.859026332Z + description: A suite of web based open source business apps. + digest: 6bdd86f83c41f079218105162f1e86cf572d005174c0688a2201c59d20cf1e55 + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.4.3.tgz + version: 0.4.3 + - created: 2017-03-23T00:18:30.533565303Z + description: A suite of web based open source business apps. + digest: 9a7723d94d17ff18347074d5a1d6b42530466a8a4882981178f4856138f44072 + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.4.2.tgz + version: 0.4.2 + - created: 2017-03-17T22:48:29.256461745Z + description: A suite of web based open source business apps. + digest: 1339b6efbc6b4df849c460ddbcade9f81ede3a6a800e9566f54acc1c52dccb4b + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.4.1.tgz + version: 0.4.1 + - created: 2017-03-08T19:03:31.734764598Z + description: A suite of web based open source business apps. + digest: 15b1d5339086427990fb3d0ee8f65e768b398195f2db63b0356e6c79f4d4b981 + engine: gotpl + home: https://www.odoo.com/ + icon: https://bitnami.com/assets/stacks/odoo/img/odoo-stack-110x117.png + keywords: + - odoo + - crm + - www + - http + - web + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: odoo + sources: + - https://github.com/bitnami/bitnami-docker-odoo + urls: + - https://kubernetes-charts.storage.googleapis.com/odoo-0.4.0.tgz + version: 0.4.0 + opencart: + - created: 2017-04-28T00:18:30.095906073Z + description: A free and open source e-commerce platform for online merchants. + It provides a professional and reliable foundation for a successful online store. + digest: a22a7ee46a419b2bc4dd0ba66c3fa294e15f86722944a7bc3cc56cb24f3fa737 + engine: gotpl + home: https://opencart.com/ + icon: https://bitnami.com/assets/stacks/opencart/img/opencart-stack-110x117.png + keywords: + - opencart + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: opencart + sources: + - https://github.com/bitnami/bitnami-docker-opencart + urls: + - https://kubernetes-charts.storage.googleapis.com/opencart-0.4.5.tgz + version: 0.4.5 + - created: 2017-04-06T11:18:27.44256999Z + description: A free and open source e-commerce platform for online merchants. + It provides a professional and reliable foundation for a successful online store. + digest: 0abb5c30e0eef2a06b36b83b0c11f4bcf3df14ea416c5e49cb821c78e6783bce + engine: gotpl + home: https://opencart.com/ + icon: https://bitnami.com/assets/stacks/opencart/img/opencart-stack-110x117.png + keywords: + - opencart + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: opencart + sources: + - https://github.com/bitnami/bitnami-docker-opencart + urls: + - https://kubernetes-charts.storage.googleapis.com/opencart-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-02T19:33:28.187180475Z + description: A free and open source e-commerce platform for online merchants. + It provides a professional and reliable foundation for a successful online store. + digest: 1a80dfcec98c190b8710d7db47c22c3882a05a046b3d767c84094cef983a1556 + engine: gotpl + home: https://opencart.com/ + icon: https://bitnami.com/assets/stacks/opencart/img/opencart-stack-110x117.png + keywords: + - opencart + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: opencart + sources: + - https://github.com/bitnami/bitnami-docker-opencart + urls: + - https://kubernetes-charts.storage.googleapis.com/opencart-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-23T02:33:30.963491381Z + description: A free and open source e-commerce platform for online merchants. + It provides a professional and reliable foundation for a successful online store. + digest: d3092c4ed82db569937e435d3dc6bcddce420540bf340dd54a554a57b62c6aaa + engine: gotpl + home: https://opencart.com/ + icon: https://bitnami.com/assets/stacks/opencart/img/opencart-stack-110x117.png + keywords: + - opencart + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: opencart + sources: + - https://github.com/bitnami/bitnami-docker-opencart + urls: + - https://kubernetes-charts.storage.googleapis.com/opencart-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.354317974Z + description: A free and open source e-commerce platform for online merchants. + It provides a professional and reliable foundation for a successful online store. + digest: afbaa2517e811990bc4b31495a4634b6399615493cf344215a5658de2f33575b + engine: gotpl + home: https://opencart.com/ + icon: https://bitnami.com/assets/stacks/opencart/img/opencart-stack-110x117.png + keywords: + - opencart + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: opencart + sources: + - https://github.com/bitnami/bitnami-docker-opencart + urls: + - https://kubernetes-charts.storage.googleapis.com/opencart-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-04T00:48:19.745672724Z + description: A free and open source e-commerce platform for online merchants. + It provides a professional and reliable foundation for a successful online store. + digest: f0e46cf67f8594c6d92f02fad4a23fdf7aa94bdb145bfde39436e17f0a8930f5 + engine: gotpl + home: https://opencart.com/ + icon: https://bitnami.com/assets/stacks/opencart/img/opencart-stack-110x117.png + keywords: + - opencart + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: opencart + sources: + - https://github.com/bitnami/bitnami-docker-opencart + urls: + - https://kubernetes-charts.storage.googleapis.com/opencart-0.4.0.tgz + version: 0.4.0 + openvpn: + - apiVersion: v1 + created: 2017-04-28T00:18:30.096266269Z + description: A Helm chart to install an openvpn server inside a kubernetes cluster. Certificate + generation is also part of the deployment, and this chart will generate client + keys as needed. + digest: 403a80ed3f204442afe4236e275035bf39f9e1f6af8d48153d6698f51efeb16d + home: https://openvpn.net/index.php/open-source.html + icon: https://forums.openvpn.net/styles/openvpn/theme/images/ovpnlogo.png + keywords: + - openvpn + - vpn + - tunnel + - network + - service + - connectivity + - encryption + maintainers: + - email: john.felten@gmail.com + name: John Felten + name: openvpn + urls: + - https://kubernetes-charts.storage.googleapis.com/openvpn-1.0.2.tgz + version: 1.0.2 + - apiVersion: v1 + created: 2017-04-06T10:18:27.592699587Z + description: A Helm chart to install an openvpn server inside a kubernetes cluster. Certificate + generation is also part of the deployment, and this chart will generate client + keys as needed. + digest: c92674b7c6391e412864a3aa73af1a2d89f3b4e768bb459118b8e75b9750fc10 + home: https://openvpn.net/index.php/open-source.html + icon: https://forums.openvpn.net/styles/openvpn/theme/images/ovpnlogo.png + keywords: + - openvpn + - vpn + - tunnel + - network + - service + - connectivity + - encryption + maintainers: + - email: john.felten@gmail.com + name: John Felten + name: openvpn + urls: + - https://kubernetes-charts.storage.googleapis.com/openvpn-1.0.1.tgz + version: 1.0.1 + - apiVersion: v1 + created: 2017-01-27T21:48:32.078827588Z + description: A Helm chart to install an openvpn server inside a kubernetes cluster. Certificate + generation is also part of the deployment, and this chart will generate client + keys as needed. + digest: a12cfddce900c8a4ef6cea0cef5426d5fb23c657cdab433f9307f6d048273f6b + home: https://openvpn.net/index.php/open-source.html + icon: https://forums.openvpn.net/styles/openvpn/theme/images/ovpnlogo.png + keywords: + - openvpn + - vpn + - tunnel + - network + - service + - connectivity + - encryption + maintainers: + - email: john.felten@gmail.com + name: John Felten + name: openvpn + urls: + - https://kubernetes-charts.storage.googleapis.com/openvpn-1.0.0.tgz + version: 1.0.0 + orangehrm: + - created: 2017-04-28T00:18:30.097427186Z + description: OrangeHRM is a free HR management system that offers a wealth of + modules to suit the needs of your business. + digest: d796649de3610a41a9f92b0ee3f5ce327a250d1b86d7a4c76fd46a31144c59e5 + engine: gotpl + home: https://www.orangehrm.com + icon: https://bitnami.com/assets/stacks/orangehrm/img/orangehrm-stack-110x117.png + keywords: + - orangehrm + - http + - https + - web + - application + - php + - human resources + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: orangehrm + sources: + - https://github.com/bitnami/bitnami-docker-orangehrm + urls: + - https://kubernetes-charts.storage.googleapis.com/orangehrm-0.4.5.tgz + version: 0.4.5 + - created: 2017-04-12T21:03:28.668395677Z + description: OrangeHRM is a free HR management system that offers a wealth of + modules to suit the needs of your business. + digest: 9c954806dfd415ee07cbcc6ee5e82eeeebeb765ecfe173ba80c0003b1d0be504 + engine: gotpl + home: https://www.orangehrm.com + icon: https://bitnami.com/assets/stacks/orangehrm/img/orangehrm-stack-110x117.png + keywords: + - orangehrm + - http + - https + - web + - application + - php + - human resources + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: orangehrm + sources: + - https://github.com/bitnami/bitnami-docker-orangehrm + urls: + - https://kubernetes-charts.storage.googleapis.com/orangehrm-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-18T18:33:36.171180792Z + description: OrangeHRM is a free HR management system that offers a wealth of + modules to suit the needs of your business. + digest: 87483fe0c39f63c61549a85103b9423b30ac4bfe2b19a7af083eaf7dd3491ce2 + engine: gotpl + home: https://www.orangehrm.com + icon: https://bitnami.com/assets/stacks/orangehrm/img/orangehrm-stack-110x117.png + keywords: + - orangehrm + - http + - https + - web + - application + - php + - human resources + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: orangehrm + sources: + - https://github.com/bitnami/bitnami-docker-orangehrm + urls: + - https://kubernetes-charts.storage.googleapis.com/orangehrm-0.4.3.tgz + version: 0.4.3 + - created: 2017-03-02T19:33:28.188778055Z + description: OrangeHRM is a free HR management system that offers a wealth of + modules to suit the needs of your business. + digest: d03e9f261dd50212f6c2c0ecc3a84cbabda89493d8bfc28ad95c6c9a6a195af9 + engine: gotpl + home: https://www.orangehrm.com + icon: https://bitnami.com/assets/stacks/orangehrm/img/orangehrm-stack-110x117.png + keywords: + - orangehrm + - http + - https + - web + - application + - php + - human resources + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: orangehrm + sources: + - https://github.com/bitnami/bitnami-docker-orangehrm + urls: + - https://kubernetes-charts.storage.googleapis.com/orangehrm-0.4.2.tgz + version: 0.4.2 + - created: 2017-02-23T02:33:30.964778113Z + description: OrangeHRM is a free HR management system that offers a wealth of + modules to suit the needs of your business. + digest: b5e5ed301b5321a99a7147173ec6fa7cb471879a585b9af0e8ac2741f0409e48 + engine: gotpl + home: https://www.orangehrm.com + icon: https://bitnami.com/assets/stacks/orangehrm/img/orangehrm-stack-110x117.png + keywords: + - orangehrm + - http + - https + - web + - application + - php + - human resources + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: orangehrm + sources: + - https://github.com/bitnami/bitnami-docker-orangehrm + urls: + - https://kubernetes-charts.storage.googleapis.com/orangehrm-0.4.1.tgz + version: 0.4.1 + - created: 2017-02-01T00:48:29.581953712Z + description: OrangeHRM is a free HR management system that offers a wealth of + modules to suit the needs of your business. + digest: 4ca6ccafc5bc408cb9e61139b8914468e0d8eeed7bffb62eea51c6e40697e8dd + engine: gotpl + home: https://www.orangehrm.com + icon: https://bitnami.com/assets/stacks/orangehrm/img/orangehrm-stack-110x117.png + keywords: + - orangehrm + - http + - https + - web + - application + - php + - human resources + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: orangehrm + sources: + - https://github.com/bitnami/bitnami-docker-orangehrm + urls: + - https://kubernetes-charts.storage.googleapis.com/orangehrm-0.4.0.tgz + version: 0.4.0 + osclass: + - created: 2017-04-28T00:18:30.098620601Z + description: Osclass is a php script that allows you to quickly create and manage + your own free classifieds site. + digest: 23472bb37c094dad3122a96f6fd6a3967f1d41547121ecf2622b557f2fc5da8b + engine: gotpl + home: https://osclass.org/ + icon: https://bitnami.com/assets/stacks/osclass/img/osclass-stack-110x117.png + keywords: + - osclass + - classifieds + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: osclass + sources: + - https://github.com/bitnami/bitnami-docker-osclass + urls: + - https://kubernetes-charts.storage.googleapis.com/osclass-0.4.1.tgz + version: 0.4.1 + - created: 2017-02-23T02:33:30.965596248Z + description: Osclass is a php script that allows you to quickly create and manage + your own free classifieds site. + digest: 7c21cef0b281e3da5b3c9b60940c1b010c34fe866c95615b7d0afd10c480178c + engine: gotpl + home: https://osclass.org/ + icon: https://bitnami.com/assets/stacks/osclass/img/osclass-stack-110x117.png + keywords: + - osclass + - classifieds + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: osclass + sources: + - https://github.com/bitnami/bitnami-docker-osclass + urls: + - https://kubernetes-charts.storage.googleapis.com/osclass-0.4.0.tgz + version: 0.4.0 + owncloud: + - created: 2017-04-28T00:18:30.099329236Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: 9beee22ffa4b4eae5e6f8da5734e08a9455a063299b33d4350dea91050ae9a25 + engine: gotpl + home: https://owncloud.org/ + icon: https://bitnami.com/assets/stacks/owncloud/img/owncloud-stack-220x234.png + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.7.tgz + version: 0.4.7 + - created: 2017-04-20T16:18:33.358391215Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: 97b3574369c1d690ce8787588668070451c559ac06b6b69942e9905cb6ec94ad + engine: gotpl + home: https://owncloud.org/ + icon: https://bitnami.com/assets/stacks/owncloud/img/owncloud-stack-220x234.png + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-23T21:18:31.86258413Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: fb78c2ea47996c6781ddfe2dca6aea87cbb8b895e1fc5fea404aa28735d4a2ec + engine: gotpl + home: https://owncloud.org/ + icon: https://bitnami.com/assets/stacks/owncloud/img/owncloud-stack-220x234.png + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-08T19:03:31.738784117Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: 07e18bccf9dc0a8fcff42e97c422520530c2c5057c2a9c14767842609bcaa7ac + engine: gotpl + home: https://owncloud.org/ + icon: https://bitnami.com/assets/stacks/owncloud/img/owncloud-stack-220x234.png + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-11T03:18:26.536480213Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: 4e534bdc4be184c59fbcbcff4641f2bfa341cd7f4ed3668912cd47c1336f5dea + engine: gotpl + home: https://owncloud.org/ + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.3.tgz + version: 0.4.3 + - created: 2017-01-30T23:48:29.172608299Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: b41eb1fc1eea6311f3ad15b459f99b77e96b944b81ea50ebccc941191d496141 + engine: gotpl + home: https://owncloud.org/ + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.35624854Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: 89901fc256d6a4f878d77b51bf23eb77358dedb040052b34a09cbe9ca607487f + engine: gotpl + home: https://owncloud.org/ + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-21T18:33:20.278724682Z + description: A file sharing server that puts the control and security of your + own data back into your hands. + digest: 7ec2dfe27bf42e8ee2e0672964decc2fbec94ec4fb60af2f3e9e91e13fdae08a + engine: gotpl + home: https://owncloud.org/ + keywords: + - owncloud + - storage + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: owncloud + sources: + - https://github.com/bitnami/bitnami-docker-owncloud + urls: + - https://kubernetes-charts.storage.googleapis.com/owncloud-0.4.0.tgz + version: 0.4.0 + percona: + - created: 2017-04-28T00:18:30.099763589Z + description: free, fully compatible, enhanced, open source drop-in replacement + for MySQL + digest: b10837a3de2492c2826aaf9ab740314ffa3f8c6110568b0a88e142791abb0e96 + engine: gotpl + home: https://www.percona.com/ + icon: https://www.percona.com/sites/all/themes/percona2015/logo.png + keywords: + - mysql + - percona + - database + - sql + maintainers: + - email: patg@patg.net + name: Patrick Galbraith + name: percona + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/percona + urls: + - https://kubernetes-charts.storage.googleapis.com/percona-0.1.0.tgz + version: 0.1.0 + phabricator: + - created: 2017-04-28T00:18:30.100508372Z + description: Collection of open source web applications that help software companies + build better software. + digest: 6c53fef9ac9c246983a26d2e8b9e8f032b87706c09667edb9f9bb62dfa822e88 + engine: gotpl + home: https://www.phacility.com/phabricator/ + icon: https://bitnami.com/assets/stacks/phabricator/img/phabricator-stack-110x117.png + keywords: + - phabricator + - http + - https + - web + - application + - collaboration + - project management + - bug tracking + - code review + - wiki + - git + - mercurial + - subversion + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phabricator + sources: + - https://github.com/bitnami/bitnami-docker-phabricator + urls: + - https://kubernetes-charts.storage.googleapis.com/phabricator-0.4.5.tgz + version: 0.4.5 + - created: 2017-04-06T10:33:26.333625406Z + description: Collection of open source web applications that help software companies + build better software. + digest: ef626563185a4b41ff7da03a9bd5548903061c6140d2d65300e4b1c1d39a2f28 + engine: gotpl + home: https://www.phacility.com/phabricator/ + icon: https://bitnami.com/assets/stacks/phabricator/img/phabricator-stack-110x117.png + keywords: + - phabricator + - http + - https + - web + - application + - collaboration + - project management + - bug tracking + - code review + - wiki + - git + - mercurial + - subversion + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phabricator + sources: + - https://github.com/bitnami/bitnami-docker-phabricator + urls: + - https://kubernetes-charts.storage.googleapis.com/phabricator-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-02T19:33:28.191377992Z + description: Collection of open source web applications that help software companies + build better software. + digest: 544373e984918da349351f05fdb7a8cf0c695726890bcd4cc348e786e1c55d57 + engine: gotpl + home: https://www.phacility.com/phabricator/ + icon: https://bitnami.com/assets/stacks/phabricator/img/phabricator-stack-110x117.png + keywords: + - phabricator + - http + - https + - web + - application + - collaboration + - project management + - bug tracking + - code review + - wiki + - git + - mercurial + - subversion + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phabricator + sources: + - https://github.com/bitnami/bitnami-docker-phabricator + urls: + - https://kubernetes-charts.storage.googleapis.com/phabricator-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-23T02:33:30.967143259Z + description: Collection of open source web applications that help software companies + build better software. + digest: 657b118031c1513b95268915ef4f68f5453f2b21ca5a919e9a370facb439dfe5 + engine: gotpl + home: https://www.phacility.com/phabricator/ + icon: https://bitnami.com/assets/stacks/phabricator/img/phabricator-stack-110x117.png + keywords: + - phabricator + - http + - https + - web + - application + - collaboration + - project management + - bug tracking + - code review + - wiki + - git + - mercurial + - subversion + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phabricator + sources: + - https://github.com/bitnami/bitnami-docker-phabricator + urls: + - https://kubernetes-charts.storage.googleapis.com/phabricator-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.358403062Z + description: Collection of open source web applications that help software companies + build better software. + digest: 77603dd383c0674e829fa4aa2ca0d33ce61884df6a3ce68f9e165e7802e7eacc + engine: gotpl + home: https://www.phacility.com/phabricator/ + icon: https://bitnami.com/assets/stacks/phabricator/img/phabricator-stack-110x117.png + keywords: + - phabricator + - http + - https + - web + - application + - collaboration + - project management + - bug tracking + - code review + - wiki + - git + - mercurial + - subversion + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phabricator + sources: + - https://github.com/bitnami/bitnami-docker-phabricator + urls: + - https://kubernetes-charts.storage.googleapis.com/phabricator-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-04T00:48:19.747640048Z + description: Collection of open source web applications that help software companies + build better software. + digest: e4f0f5322bac2a2bed822eb8065eec6e29716310af1623975f1ad0ef2b448c66 + engine: gotpl + home: https://www.phacility.com/phabricator/ + icon: https://bitnami.com/assets/stacks/phabricator/img/phabricator-stack-110x117.png + keywords: + - phabricator + - http + - https + - web + - application + - collaboration + - project management + - bug tracking + - code review + - wiki + - git + - mercurial + - subversion + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phabricator + sources: + - https://github.com/bitnami/bitnami-docker-phabricator + urls: + - https://kubernetes-charts.storage.googleapis.com/phabricator-0.4.0.tgz + version: 0.4.0 + phpbb: + - created: 2017-04-28T00:18:30.10119319Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: d4e97de68d28acd69cb655c7fa44cb8829367ce3a7b20ecbf151f1bb2b10d1cb + engine: gotpl + home: https://www.phpbb.com/ + icon: https://bitnami.com/assets/stacks/phpbb/img/phpbb-stack-220x234.png + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.7.tgz + version: 0.4.7 + - created: 2017-04-13T05:18:28.926003844Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: 30732d5c200ef312a50f5a89a222a959c99b0a08af87f0c312c3699bb2c56082 + engine: gotpl + home: https://www.phpbb.com/ + icon: https://bitnami.com/assets/stacks/phpbb/img/phpbb-stack-220x234.png + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-02T19:33:28.19218227Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: ed994a70f454f325cf677bdcc0a09aa66b01c7343a624a3304b88148eafa0dd4 + engine: gotpl + home: https://www.phpbb.com/ + icon: https://bitnami.com/assets/stacks/phpbb/img/phpbb-stack-220x234.png + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.5.tgz + version: 0.4.5 + - created: 2017-02-23T02:33:30.967854461Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: f50e4b1eb4bf8b2e6815b664e3cfff4bfd7979b650bf1efa709b79c92a8e5dd0 + engine: gotpl + home: https://www.phpbb.com/ + icon: https://bitnami.com/assets/stacks/phpbb/img/phpbb-stack-220x234.png + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-13T17:03:30.134235119Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: c41705e34490d1941adda128b166ef1a327cceb7a447113d2633652f3c85363a + engine: gotpl + home: https://www.phpbb.com/ + icon: https://bitnami.com/assets/stacks/phpbb/img/phpbb-stack-220x234.png + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-13T04:18:31.547850917Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: 77f6dfa8f277b003ba0b8e2ca6d046f6b6526ee7db05e304cbb9924c9fd7d194 + engine: gotpl + home: https://www.phpbb.com/ + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-31T00:03:26.18262303Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: cd6dde525898d91f5f39541ee205e79ba5b8eb7a5b99585141cc88c3a63f5aed + engine: gotpl + home: https://www.phpbb.com/ + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-09T18:48:20.188802837Z + description: Community forum that supports the notion of users and groups, file + attachments, full-text search, notifications and more. + digest: 7e5e8828fdbba9ebc83c95baac4937e569e881d8b68bc79974a0f5d8a2883e13 + engine: gotpl + home: https://www.phpbb.com/ + keywords: + - phpbb + - forum + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: phpbb + sources: + - https://github.com/bitnami/bitnami-docker-phpbb + urls: + - https://kubernetes-charts.storage.googleapis.com/phpbb-0.4.0.tgz + version: 0.4.0 + postgresql: + - created: 2017-04-28T00:18:30.101575321Z + description: Object-relational database management system (ORDBMS) with an emphasis + on extensibility and on standards-compliance. + digest: 60224116f1e803bbf744cb2b3c2d621cfadef709ef0a8c6ed08cbed0f5415915 + engine: gotpl + home: https://www.postgresql.org/ + icon: https://www.postgresql.org/media/img/about/press/elephant.png + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.6.0.tgz + version: 0.6.0 + - created: 2017-03-16T23:33:31.598678756Z + description: Object-relational database management system (ORDBMS) with an emphasis + on extensibility and on standards-compliance. + digest: abfc2c516cce613f9042eb5d202b01f6766240f54bb3632df9378be033711b30 + engine: gotpl + home: https://www.postgresql.org/ + icon: https://www.postgresql.org/media/img/about/press/elephant.png + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.5.1.tgz + version: 0.5.1 + - created: 2017-03-15T11:18:31.676836517Z + description: Object-relational database management system (ORDBMS) with an emphasis + on extensibility and on standards-compliance. + digest: 29a90e1b52577e04a42da3ac7464b1b1cf9572b359d7825a375aad122659db55 + engine: gotpl + home: https://www.postgresql.org/ + icon: https://www.postgresql.org/media/img/about/press/elephant.png + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.5.0.tgz + version: 0.5.0 + - created: 2017-03-15T09:48:31.994004661Z + description: Object-relational database management system (ORDBMS) with an emphasis + on extensibility and on standards-compliance. + digest: 0ca09fbfd539d5258026fcaf7b640f88295271f88773acfa540dcd55cfaf6036 + engine: gotpl + home: https://www.postgresql.org/ + icon: https://www.postgresql.org/media/img/about/press/elephant.png + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.4.0.tgz + version: 0.4.0 + - created: 2017-02-13T21:33:27.762374795Z + description: Object-relational database management system (ORDBMS) with an emphasis + on extensibility and on standards-compliance. + digest: b07b7c12f13731ebc3019c11601351863030ad3933f72fb7925f3c0de39e23f4 + engine: gotpl + home: https://www.postgresql.org/ + icon: https://www.postgresql.org/media/img/about/press/elephant.png + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.3.4.tgz + version: 0.3.4 + - created: 2017-02-13T04:18:31.548242286Z + description: Chart for PostgreSQL + digest: 80685774a539b9efa27ea82337e9dd9fccd436688a84e2609d59a68a336d8f18 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.3.3.tgz + version: 0.3.3 + - created: 2017-02-06T17:48:28.059659169Z + description: Chart for PostgreSQL + digest: d8306a710181a440672795d0b5850e6851ae28b3ecb4cf5f92126c9f533700d5 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.3.2.tgz + version: 0.3.2 + - created: 2017-01-28T00:18:32.756495622Z + description: Chart for PostgreSQL + digest: 7b4e2b838ccb2e96c26f0b18d75b2cba224a634925abacaeee1b053a3db40f72 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.3.1.tgz + version: 0.3.1 + - created: 2017-01-26T17:18:33.808053693Z + description: Chart for PostgreSQL + digest: b8e412ddd2f2648efbaa84f85c924e5b94cba0393fb93ea607fdcab3132b7d2e + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + - name: databus23 + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.3.0.tgz + version: 0.3.0 + - created: 2016-12-21T18:33:20.280731983Z + description: Chart for PostgreSQL + digest: a0516b4e5b83d9dd4af936859582878683183c6f72e9dddb73c9ff0fd280b972 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.2.2.tgz + version: 0.2.2 + - created: 2016-12-19T22:48:20.03810957Z + description: Chart for PostgreSQL + digest: 8d79ed44bb8477cdbbf8a3eb5a08eef787a147334b30d35a81f8ee43d07eb9ee + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.2.1.tgz + version: 0.2.1 + - created: 2016-12-05T20:48:21.242600076Z + description: Chart for PostgreSQL + digest: 713d7ee250af7f914188d889ba3cb3065b4c36565b6f68ca8f55cd5340bda213 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.2.0.tgz + version: 0.2.0 + - created: 2016-11-08T15:03:20.745475633Z + description: Chart for PostgreSQL + digest: c79411d63ad872d0f6d034de9818cef565dbde3ac5f018ee8349305f286031a8 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.1.1.tgz + version: 0.1.1 + - created: 2016-11-04T14:18:20.014925806Z + description: Chart for PostgreSQL + digest: c984e3efb97da1b841c6a07b1e8fd32638b59de42e5b062690b8c9956be58959 + engine: gotpl + home: https://www.postgresql.org/ + keywords: + - postgresql + - postgres + - database + - sql + maintainers: + - name: swordbeta + name: postgresql + sources: + - https://github.com/kubernetes/charts + - https://github.com/docker-library/postgres + urls: + - https://kubernetes-charts.storage.googleapis.com/postgresql-0.1.0.tgz + version: 0.1.0 + prestashop: + - created: 2017-04-28T00:18:30.102289986Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: fd575ef671455aa2c85ed91643419219bad86f8766b5775c1e869837cfbef6e5 + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.6.tgz + version: 0.4.6 + - created: 2017-04-06T11:03:25.414976585Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: 17eb689a1b04c31c1c78fa1b411f938eaad74bbd1ae78e4eecd0755770156043 + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-23T01:19:01.462007867Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: ff84426385c8abb40d0f4f7e2cfc7fec70b0ae38ca32de832ccbb2b26f74787b + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-08T19:03:31.741573757Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: 4beeb9d17c713b8065f9167d11672862fe2d1eeb5fded4d4ecd395a379825bbc + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.3.tgz + version: 0.4.3 + - created: 2017-02-13T17:03:30.136404798Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: fd8c2be6fd9348d2c5bda428ac61f0745974ebd7a4b58ad5d975d8d21888f1c4 + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.2.tgz + version: 0.4.2 + - created: 2017-01-21T00:18:31.360326923Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: eba227b32cb9f503c4fd41609d705019372f7936c69febaf2c4200735910e333 + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-04T00:48:19.749415831Z + description: A popular open source ecommerce solution. Professional tools are + easily accessible to increase online sales including instant guest checkout, + abandoned cart reminders and automated Email marketing. + digest: dd80da4612c962f5d02e2f52f7e0facf42a661d6d6f5eec36d7a3980e2a685d3 + engine: gotpl + home: https://prestashop.com/ + icon: https://bitnami.com/assets/stacks/prestashop/img/prestashop-stack-110x117.png + keywords: + - prestashop + - e-commerce + - http + - web + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: prestashop + sources: + - https://github.com/bitnami/bitnami-docker-prestashop + urls: + - https://kubernetes-charts.storage.googleapis.com/prestashop-0.4.0.tgz + version: 0.4.0 + prometheus: + - created: 2017-04-28T00:18:30.103009703Z + description: Prometheus is a monitoring system and time series database. + digest: 54cd97a780b0af5cd86d84455a29ec2f30765cfbf5c56f9eedf0c30539e325ec + engine: gotpl + home: https://prometheus.io/ + icon: https://raw.githubusercontent.com/prometheus/prometheus.github.io/master/assets/prometheus_logo-cb55bb5c346.png + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + - https://github.com/kubernetes/kube-state-metrics + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-3.0.2.tgz + version: 3.0.2 + - created: 2017-04-14T01:18:27.389282119Z + description: Prometheus is a monitoring system and time series database. + digest: 8240b1319bb4b1382f69e3ae753fce599185a7f21a399c7ed15108138cb6e793 + engine: gotpl + home: https://prometheus.io/ + icon: https://raw.githubusercontent.com/prometheus/prometheus.github.io/master/assets/prometheus_logo-cb55bb5c346.png + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + - https://github.com/kubernetes/kube-state-metrics + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-3.0.1.tgz + version: 3.0.1 + - created: 2017-03-23T19:03:32.048982259Z + description: Prometheus is a monitoring system and time series database. + digest: a6a49f3aaf7768dd84acfec96f95cab74a4ed1b68f95f23a9248d6926e8a2ba7 + engine: gotpl + home: https://prometheus.io/ + icon: https://raw.githubusercontent.com/prometheus/prometheus.github.io/master/assets/prometheus_logo-cb55bb5c346.png + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + - https://github.com/kubernetes/kube-state-metrics + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-3.0.0.tgz + version: 3.0.0 + - created: 2017-03-20T20:18:33.636286259Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: 0bbefe8b7732b400320007e4f8898518ffcafd3371114be24ca8ded424fe60b3 + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-2.0.4.tgz + version: 2.0.4 + - created: 2017-02-13T04:18:31.549501389Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: 0f54774b8b258a8e126f09a66749b15c0691e0a330b65f397d58418b0fa0210c + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-2.0.3.tgz + version: 2.0.3 + - created: 2017-02-03T20:18:29.168136057Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: f1f457be370a944f3c703ceecc35664aa00f7a243730ca9e110bc18f1ed3ab9a + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-2.0.2.tgz + version: 2.0.2 + - created: 2017-02-01T02:18:29.14318599Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: eebea40b9e86c6dfb92048b0f63d47b11021ab0df437e2b13bc0fd1fc121e8d6 + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-2.0.1.tgz + version: 2.0.1 + - created: 2017-01-19T19:18:27.372125459Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: 0ae9e1c2ec6e3a6e2148f01e174bbbdd02a5797b4136e5de784383bca9bff938 + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-2.0.0.tgz + version: 2.0.0 + - created: 2017-01-12T02:52:37.843602967Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: cd8d763bdfe5d7c3c0e9a38f9741a2ef5de1c7c57a0c43a4407e70e2f6232dc9 + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-1.4.2.tgz + version: 1.4.2 + - created: 2017-01-04T00:48:19.750279814Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: 7209dafe19488487a8a151129deff24fe174d9734ea2c1629dd52bee183a8ad2 + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-1.4.1.tgz + version: 1.4.1 + - created: 2016-12-09T18:48:20.189907856Z + description: A Prometheus Helm chart for Kubernetes. Prometheus is a monitoring + system and time series database. + digest: f6b4c948e408471b51ff6361e0d0f5afc801ee8141aae5002111ffbc12c68895 + engine: gotpl + home: https://prometheus.io/ + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: prometheus + sources: + - https://github.com/prometheus/alertmanager + - https://github.com/prometheus/prometheus + urls: + - https://kubernetes-charts.storage.googleapis.com/prometheus-1.3.1.tgz + version: 1.3.1 + rabbitmq: + - created: 2017-04-28T00:18:30.103417484Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 1b4339d3f866cdc57072ce60fc3a579a5f4233aa6a4cb81254dcb9ddc0fabc33 + engine: gotpl + home: https://www.rabbitmq.com + icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.5.1.tgz + version: 0.5.1 + - created: 2017-04-06T10:33:26.336868116Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 60c103b6cd7c57d5bf25588c0d1fcbbe0790ad50968d1947ce11e4b7ae1b2e6e + engine: gotpl + home: https://www.rabbitmq.com + icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.5.0.tgz + version: 0.5.0 + - created: 2017-04-03T22:33:26.690098498Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 2f9b4afaffbe72c99c640de71b33e161e9d11386c287bab9028af19d1548bfa8 + engine: gotpl + home: https://www.rabbitmq.com + icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-18T18:33:36.178174406Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 833f7c8b0db5eeee50005fe88db8f1aaa899e5cc7875e18473a77162425756be + engine: gotpl + home: https://www.rabbitmq.com + icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.4.4.tgz + version: 0.4.4 + - created: 2017-03-16T13:33:30.85508759Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: bcfcbfa446f75dc1ca93f9a7d2ccc36731ef41f1dd5615e251344af0d6a1ba83 + engine: gotpl + home: https://www.rabbitmq.com + icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.4.3.tgz + version: 0.4.3 + - created: 2017-03-08T19:03:31.743135112Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 7ad8ba0ff9d1b57778ffe60812be9087ad4fac27e8696fad4c9eba9a2529fdba + engine: gotpl + home: https://www.rabbitmq.com + icon: https://bitnami.com/assets/stacks/rabbitmq/img/rabbitmq-stack-220x234.png + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.4.2.tgz + version: 0.4.2 + - created: 2017-02-13T04:18:31.549853753Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 323ca950152028ecfa421b78ba0b9282265f39b934b07649b239be4d9f2dc10a + engine: gotpl + home: https://www.rabbitmq.com + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-31T00:03:26.184733426Z + description: Open source message broker software that implements the Advanced + Message Queuing Protocol (AMQP) + digest: 9bd9655f974dc3b2666c141718b65c7786e91c533ffee1784428a6d48cb458ca + engine: gotpl + home: https://www.rabbitmq.com + keywords: + - rabbitmq + - message queue + - AMQP + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: rabbitmq + sources: + - https://github.com/bitnami/bitnami-docker-rabbitmq + urls: + - https://kubernetes-charts.storage.googleapis.com/rabbitmq-0.4.0.tgz + version: 0.4.0 + redis: + - created: 2017-04-28T00:18:30.103747837Z + description: Open source, advanced key-value store. It is often referred to as + a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. + digest: 72af23bdc2aee61a6cc75e6b7fe3677d1f08ec98afbf8b6fccb3922c9bde47aa + engine: gotpl + home: http://redis.io/ + icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.5.1.tgz + version: 0.5.1 + - created: 2017-04-03T22:18:27.809077961Z + description: Open source, advanced key-value store. It is often referred to as + a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. + digest: 84fdd07b6f56e7771696e7a707456808cb925f2a6311bf85e1bd027e5b2d364d + engine: gotpl + home: http://redis.io/ + icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.5.0.tgz + version: 0.5.0 + - created: 2017-03-23T15:48:30.415372004Z + description: Open source, advanced key-value store. It is often referred to as + a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. + digest: 83ace7583e93e763b781d74c940d0966d7317d2b1665eaed35be9ca73dcace5e + engine: gotpl + home: http://redis.io/ + icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-02T19:33:28.196397881Z + description: Open source, advanced key-value store. It is often referred to as + a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. + digest: 7891aef2647fd00ca93cd6894720a6307d3fdd275f912eb6a05fcbb6b7009c13 + engine: gotpl + home: http://redis.io/ + icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.5.tgz + version: 0.4.5 + - created: 2017-02-13T20:18:28.115940614Z + description: Open source, advanced key-value store. It is often referred to as + a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. + digest: e8cf2f96a6931397adf372857a6a0da161e7e9eb0cf91f565399d20b26144be1 + engine: gotpl + home: http://redis.io/ + icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-13T04:18:31.550184203Z + description: Chart for Redis + digest: b4cb9b2e0811a83ce269dc06c25a05fe31deb799018eba418232b2c3f4b18b12 + engine: gotpl + home: http://redis.io/ + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.3.tgz + version: 0.4.3 + - created: 2017-01-28T02:03:32.495597111Z + description: Chart for Redis + digest: 160dab504021716867790c3b1ea5c6e4afcaf865d9b8569707e123bc4d1536dc + engine: gotpl + home: http://redis.io/ + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.2.tgz + version: 0.4.2 + - created: 2016-12-09T18:48:20.191261198Z + description: Chart for Redis + digest: 7132d9ddecaf4a890e5177c401228fa031f52e927393063f8d6c5a0881b281a3 + engine: gotpl + home: http://redis.io/ + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.1.tgz + version: 0.4.1 + - created: 2016-10-31T16:33:19.644247028Z + description: Chart for Redis + digest: cef59b98a3607bf0f40560c724fd36a84e5f29498031a36c0f2f80369c35d9c4 + engine: gotpl + home: http://redis.io/ + keywords: + - redis + - keyvalue + - database + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redis + sources: + - https://github.com/bitnami/bitnami-docker-redis + urls: + - https://kubernetes-charts.storage.googleapis.com/redis-0.4.0.tgz + version: 0.4.0 + redmine: + - created: 2017-04-28T00:18:30.104404263Z + description: A flexible project management web application. + digest: 65419e8af0bff73d1c4da279cbb4d2abdaf99a714dabae01b27f67b917d4efc1 + engine: gotpl + home: http://www.redmine.org/ + icon: https://bitnami.com/assets/stacks/redmine/img/redmine-stack-220x234.png + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.4.2.tgz + version: 0.4.2 + - created: 2017-04-13T05:18:28.930142135Z + description: A flexible project management web application. + digest: 3edad0332b7aa4ff2c33729f0e49e7c58c0ad06108d66774d1f1583b8e4ccddf + engine: gotpl + home: http://www.redmine.org/ + icon: https://bitnami.com/assets/stacks/redmine/img/redmine-stack-220x234.png + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.4.1.tgz + version: 0.4.1 + - created: 2017-03-31T19:33:30.446728632Z + description: A flexible project management web application. + digest: facd552d60b39c5f6d37e8cf88f453bcae418a08eee0c401f15d15872e1e3601 + engine: gotpl + home: http://www.redmine.org/ + icon: https://bitnami.com/assets/stacks/redmine/img/redmine-stack-220x234.png + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.4.0.tgz + version: 0.4.0 + - created: 2017-03-14T23:48:31.907843022Z + description: A flexible project management web application. + digest: a21733ee877ad579f8b5be03d5a35008816d64dd56e0ca6482a7c0686fcdfe09 + engine: gotpl + home: http://www.redmine.org/ + icon: https://bitnami.com/assets/stacks/redmine/img/redmine-stack-220x234.png + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.11.tgz + version: 0.3.11 + - created: 2017-03-08T19:03:31.745197966Z + description: A flexible project management web application. + digest: 30253b618b47801a076c6cdd8a9ff93e1e4401e0189e88576553802b224e2775 + engine: gotpl + home: http://www.redmine.org/ + icon: https://bitnami.com/assets/stacks/redmine/img/redmine-stack-220x234.png + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.10.tgz + version: 0.3.10 + - created: 2017-02-13T21:33:27.767502945Z + description: A flexible project management web application. + digest: aa8a3b1be968e99c7a61ad0b7c1d13934562b9c30eeec0b3a3683063b9d38c7b + engine: gotpl + home: http://www.redmine.org/ + icon: https://bitnami.com/assets/stacks/redmine/img/redmine-stack-220x234.png + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.9.tgz + version: 0.3.9 + - created: 2017-02-10T23:18:26.028438027Z + description: A flexible project management web application. + digest: 51f4e834b5d2eb4ab66468e6996419bb20aa4d96ebe35a3663bc8b2c494694e6 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.8.tgz + version: 0.3.8 + - created: 2017-01-31T00:18:28.517014253Z + description: A flexible project management web application. + digest: d9e7c4c47c853413107330d4fc0ad44e9bc3be90057ca722d28042b73f244fe5 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.7.tgz + version: 0.3.7 + - created: 2016-12-15T21:18:24.678305914Z + description: A flexible project management web application. + digest: 98d9c8c7f241a9418bed6862f7c82295d5d8158cd1702907ced7150e46530768 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.6.tgz + version: 0.3.6 + - created: 2016-12-05T21:03:20.228049572Z + description: A flexible project management web application. + digest: ae1c2ced129d05cdae28e1fe9c2bed53ded35cd77d96fc1b26f810d334c601e3 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.5.tgz + version: 0.3.5 + - created: 2016-11-03T19:33:29.122956769Z + description: A flexible project management web application. + digest: c591dea135ef93f4af1a05961333125167ae551cf2b666363fe76b5a7ad9f806 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.4.tgz + version: 0.3.4 + - created: 2016-10-21T19:18:18.621573514Z + description: A flexible project management web application. + digest: da6a8cb8c355a93ae11d9312be9eca51966d2288eafe96b6724e6154d000b8c3 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.3.tgz + version: 0.3.3 + - created: 2016-10-19T00:03:14.035726608Z + description: A flexible project management web application. + digest: 052a0a97ff279db43f06c5ceeabfc5bd26f2e5f4f7ce7c24fdbcf761f97af84e + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.2.tgz + version: 0.3.2 + - created: 2016-10-19T00:03:14.034750035Z + description: A flexible project management web application. + digest: 88cf358644be274866ec5e88199c257e18a35fc8bbe97417658b9a0ea1e4a260 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.1.tgz + version: 0.3.1 + - created: 2016-10-19T00:03:14.033766322Z + description: A flexible project management web application. + digest: f4815d35cbf9f8bb72c051ee528958b9c6f48b1f3bf8b3fdceaadd90d1b88068 + engine: gotpl + home: http://www.redmine.org/ + keywords: + - redmine + - project management + - www + - http + - web + - application + - ruby + - rails + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: redmine + sources: + - https://github.com/bitnami/bitnami-docker-redmine + urls: + - https://kubernetes-charts.storage.googleapis.com/redmine-0.3.0.tgz + version: 0.3.0 + sapho: + - apiVersion: v1 + created: 2017-04-28T00:18:30.105942339Z + description: A micro application development and integration platform that enables + organizations to create and deliver secure micro applications that tie into + existing business systems and track changes to key business data. + digest: 6c499f9875c07b508d23b081ffd991a5737a0acaf1c75def55dbb2dc07bf40ea + engine: gotpl + home: http://www.sapho.com + icon: https://www.sapho.com/wp-content/uploads/2016/04/sapho-logotype.svg + maintainers: + - email: support@sapho.com + name: Sapho + name: sapho + sources: + - https://bitbucket.org/sapho/ops-docker-tomcat/src + - https://hub.docker.com/r/sapho/ops-docker-tomcat + - https://github.com/kubernetes/charts/tree/master/stable/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/sapho-0.1.5.tgz + version: 0.1.5 + - apiVersion: v1 + created: 2017-02-13T04:33:52.314506112Z + description: A micro application development and integration platform that enables + organizations to create and deliver secure micro applications that tie into + existing business systems and track changes to key business data. + digest: abe8e15b8e51369d6d05033177efb524139d3352794e201003d2e3fce3d0669d + engine: gotpl + maintainers: + - email: support@sapho.com + name: Sapho + name: sapho + sources: + - https://bitbucket.org/sapho/ops-docker-tomcat/src + - https://hub.docker.com/r/sapho/ops-docker-tomcat + - https://github.com/kubernetes/charts/tree/master/stable/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/sapho-0.1.4.tgz + version: 0.1.4 + - apiVersion: v1 + created: 2017-01-31T00:18:28.518904Z + description: A micro application development and integration platform that enables + organizations to create and deliver secure micro applications that tie into + existing business systems and track changes to key business data. + digest: d93ff20d61a35de8ab23d5d118c177184a6b8b0578a39ba7d101f818a8742851 + engine: gotpl + maintainers: + - email: support@sapho.com + name: Sapho + name: sapho + sources: + - https://bitbucket.org/sapho/ops-docker-tomcat/src + - https://hub.docker.com/r/sapho/ops-docker-tomcat + - https://github.com/kubernetes/charts/tree/master/stable/mysql + urls: + - https://kubernetes-charts.storage.googleapis.com/sapho-0.1.3.tgz + version: 0.1.3 + selenium: + - created: 2017-04-28T00:18:30.106455441Z + description: Chart for selenium grid + digest: 0e03cf36738e83b3e6ae7384c0ffdeb4ee4b694f0c0a025eb15106acb189b8d2 + engine: gotpl + home: http://www.seleniumhq.org/ + icon: http://docs.seleniumhq.org/images/big-logo.png + keywords: + - qa + maintainers: + - email: techops@adaptly.com + name: Philip Champon (flah00) + name: selenium + sources: + - https://github.com/SeleniumHQ/docker-selenium + urls: + - https://kubernetes-charts.storage.googleapis.com/selenium-0.1.0.tgz + version: 0.1.0 + sensu: + - apiVersion: v1 + created: 2017-04-28T00:18:30.107064065Z + description: Sensu monitoring framework backed by the Redis transport + digest: 56c74a8de76074cfb021057112cf46d11d8b77f9ef5f6ec5d0877698c9931dfa + engine: gotpl + home: https://sensuapp.org/ + icon: https://raw.githubusercontent.com/sensu/sensu/master/sensu-logo.png + keywords: + - sensu + - monitoring + maintainers: + - email: shane.starcher@gmail.com + name: Shane Starcher + name: sensu + sources: + - https://github.com/kubernetes/charts + - https://github.com/sstarcher/docker-sensu + - https://github.com/sensu/sensu + urls: + - https://kubernetes-charts.storage.googleapis.com/sensu-0.1.2.tgz + version: 0.1.2 + - apiVersion: v1 + created: 2017-03-17T05:18:29.12808256Z + description: Sensu monitoring framework backed by the Redis transport + digest: bb8781a9693f3b6df9389b3098a6298658127df2e86ad8156788602f541f33c3 + engine: gotpl + home: https://sensuapp.org/ + icon: https://raw.githubusercontent.com/sensu/sensu/master/sensu-logo.png + keywords: + - sensu + - monitoring + maintainers: + - email: shane.starcher@gmail.com + name: Shane Starcher + name: sensu + sources: + - https://github.com/kubernetes/charts + - https://github.com/sstarcher/docker-sensu + - https://github.com/sensu/sensu + urls: + - https://kubernetes-charts.storage.googleapis.com/sensu-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: 2016-12-21T23:33:22.277352049Z + description: Sensu monitoring framework backed by the Redis transport + digest: 4592387df52c4110a3a313820dbea81e8bf0252845e8c08ad7c71bce9a92831c + engine: gotpl + home: https://sensuapp.org/ + icon: https://raw.githubusercontent.com/sensu/sensu/master/sensu-logo.png + keywords: + - sensu + - monitoring + maintainers: + - email: shane.starcher@gmail.com + name: Shane Starcher + name: sensu + sources: + - https://github.com/kubernetes/charts + - https://github.com/sstarcher/docker-sensu + - https://github.com/sensu/sensu + urls: + - https://kubernetes-charts.storage.googleapis.com/sensu-0.1.0.tgz + version: 0.1.0 + spark: + - created: 2017-04-28T00:18:30.107369986Z + description: Fast and general-purpose cluster computing system. + digest: d37ec7d7530a5836eeeb5ff54110d594efe188ce8175a7c2e3b50e5d9f5af9bc + home: http://spark.apache.org + icon: http://spark.apache.org/images/spark-logo-trademark.png + maintainers: + - email: lachlan.evenson@gmail.com + name: Lachlan Evenson + name: spark + sources: + - https://github.com/kubernetes/kubernetes/tree/master/examples/spark + - https://github.com/apache/spark + urls: + - https://kubernetes-charts.storage.googleapis.com/spark-0.1.4.tgz + version: 0.1.4 + - created: 2017-03-09T19:03:32.57258203Z + description: Fast and general-purpose cluster computing system. + digest: 1cea71eb812c7ea6d566ad34247ad8d1c7b2a460b908748372618a94f035d974 + home: http://spark.apache.org + icon: http://spark.apache.org/images/spark-logo-trademark.png + maintainers: + - email: lachlan.evenson@gmail.com + name: Lachlan Evenson + name: spark + sources: + - https://github.com/kubernetes/kubernetes/tree/master/examples/spark + - https://github.com/apache/spark + urls: + - https://kubernetes-charts.storage.googleapis.com/spark-0.1.3.tgz + version: 0.1.3 + - created: 2017-02-13T04:33:52.317122021Z + description: A Apache Spark Helm chart for Kubernetes. Apache Spark is a fast + and general-purpose cluster computing system + digest: fd5559299116691e56c85f60be46e3b1d1a647973f4dfd6c0d87d0b0274a349b + home: http://spark.apache.org/ + maintainers: + - email: lachlan.evenson@gmail.com + name: Lachlan Evenson + name: spark + sources: + - https://github.com/kubernetes/kubernetes/tree/master/examples/spark + - https://github.com/apache/spark + urls: + - https://kubernetes-charts.storage.googleapis.com/spark-0.1.2.tgz + version: 0.1.2 + - created: 2017-01-27T21:48:32.088621169Z + description: A Apache Spark Helm chart for Kubernetes. Apache Spark is a fast + and general-purpose cluster computing system + digest: 884cc07e4710011476db63017b48504cc00b00faf461cdfe83aac40f0fd33e49 + home: http://spark.apache.org/ + maintainers: + - email: lachlan.evenson@gmail.com + name: Lachlan Evenson + name: spark + sources: + - https://github.com/kubernetes/kubernetes/tree/master/examples/spark + - https://github.com/apache/spark + urls: + - https://kubernetes-charts.storage.googleapis.com/spark-0.1.1.tgz + version: 0.1.1 + spartakus: + - created: 2017-04-28T00:18:30.107681212Z + description: Collect information about Kubernetes clusters to help improve the + project. + digest: 7db8a6ac7280c8d112b533b2653cfa8ed43d8517a4cf31d28e24d5761d8c6b80 + engine: gotpl + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: spartakus + sources: + - https://github.com/kubernetes-incubator/spartakus + urls: + - https://kubernetes-charts.storage.googleapis.com/spartakus-1.1.1.tgz + version: 1.1.1 + - created: 2017-03-02T18:48:30.451198217Z + description: Collect information about Kubernetes clusters to help improve the + project. + digest: 84720960919addcce5b608717eca0218b7f6cd9edbf77a52ddc0747e51037936 + engine: gotpl + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: spartakus + sources: + - https://github.com/kubernetes-incubator/spartakus + urls: + - https://kubernetes-charts.storage.googleapis.com/spartakus-1.1.0.tgz + version: 1.1.0 + - created: 2017-02-13T17:03:30.144830851Z + description: A Spartakus Helm chart for Kubernetes. Spartakus aims to collect + information about Kubernetes clusters. + digest: 1c202628cd57e01cb324ee6e9457b52d1e1a5fd665f99d4bb25bd17c92c438e9 + engine: gotpl + maintainers: + - email: mgoodness@gmail.com + name: Michael Goodness + name: spartakus + sources: + - https://github.com/kubernetes-incubator/spartakus + urls: + - https://kubernetes-charts.storage.googleapis.com/spartakus-1.0.0.tgz + version: 1.0.0 + spinnaker: + - apiVersion: v1 + created: 2017-04-28T00:18:30.109150773Z + description: Open source, multi-cloud continuous delivery platform for releasing + software changes with high velocity and confidence. + digest: a06ae1d7452e19824110cbb3270c5b7bfc4acf10af23e072e442b81fe26b1dc5 + home: http://spinnaker.io/ + icon: https://pbs.twimg.com/profile_images/669205226994319362/O7OjwPrh_400x400.png + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: spinnaker + sources: + - https://github.com/spinnaker + - https://github.com/viglesiasce/images + urls: + - https://kubernetes-charts.storage.googleapis.com/spinnaker-0.1.1.tgz + version: 0.1.1 + - apiVersion: v1 + created: 2017-02-13T20:48:27.29021219Z + description: A Helm chart for Kubernetes + digest: cc44efeace9d645b2ea824b017986d86b6b3a50fcd94e86199e0e6849eb02731 + home: http://spinnaker.io/ + maintainers: + - email: viglesias@google.com + name: Vic Iglesias + name: spinnaker + sources: + - https://github.com/spinnaker + - https://github.com/viglesiasce/images + urls: + - https://kubernetes-charts.storage.googleapis.com/spinnaker-0.1.0.tgz + version: 0.1.0 + sumokube: + - created: 2017-04-28T00:18:30.109952763Z + description: Sumologic Log Collector + digest: 2f4f5cfc4c1d40cd24085497041fd701f72d4f15cb55241bfb998da82b05c7b9 + keywords: + - monitoring + - logging + maintainers: + - email: jdumars+github@gmail.com + name: Jason DuMars + - email: knoxville+github@gmail.com + name: Sean Knox + name: sumokube + sources: + - https://github.com/SumoLogic/sumologic-collector-docker + urls: + - https://kubernetes-charts.storage.googleapis.com/sumokube-0.1.1.tgz + version: 0.1.1 + - created: 2017-01-27T21:48:32.092039665Z + description: Sumologic Log Collector + digest: 5b173be9b7dc0e1d48a7cd11015b9c405666a40420a290c5fb54e4f8718b4fc0 + keywords: + - monitoring + - logging + maintainers: + - email: jdumars+github@gmail.com + name: Jason DuMars + - email: knoxville+github@gmail.com + name: Sean Knox + name: sumokube + sources: + - https://github.com/SumoLogic/sumologic-collector-docker + urls: + - https://kubernetes-charts.storage.googleapis.com/sumokube-0.1.0.tgz + version: 0.1.0 + telegraf: + - created: 2017-04-28T00:18:30.110460492Z + description: Telegraf is an agent written in Go for collecting, processing, aggregating, + and writing metrics. + digest: 850b4b7543a3dd7f5d33ba65d9098fe4f361981f49452a40ce9774850b4285e3 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/telegraf/ + keywords: + - telegraf + - collector + - timeseries + - influxdata + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: telegraf + urls: + - https://kubernetes-charts.storage.googleapis.com/telegraf-0.2.0.tgz + version: 0.2.0 + - created: 2017-02-13T21:48:52.617397285Z + description: Telegraf is an agent written in Go for collecting, processing, aggregating, + and writing metrics. + digest: 1f74106455808d45d16742f6d7d02164eb328a40dd9699dfa4511b33efaf14e9 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/telegraf/ + keywords: + - telegraf + - collector + - timeseries + - influxdata + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: telegraf + urls: + - https://kubernetes-charts.storage.googleapis.com/telegraf-0.1.1.tgz + version: 0.1.1 + - created: 2017-02-11T03:18:26.54678474Z + description: Chart for Telegraf Kubernetes deployments + digest: 52fa68fd948ee675a5d1a5ffff22d98e293ee37569a8fa56a4022f51e9507184 + engine: gotpl + home: https://www.influxdata.com/time-series-platform/telegraf/ + keywords: + - telegraf + - collector + - timeseries + - influxdata + maintainers: + - email: jack@influxdb.com + name: Jack Zampolin + name: telegraf + urls: + - https://kubernetes-charts.storage.googleapis.com/telegraf-0.1.0.tgz + version: 0.1.0 + testlink: + - created: 2017-04-28T00:18:30.111130012Z + description: Web-based test management system that facilitates software quality + assurance. + digest: 8cffc761a9e6618bc015cec3721964192e909dfaae92a9bb79c4471424c74128 + engine: gotpl + home: http://www.testlink.org/ + icon: https://bitnami.com/assets/stacks/testlink/img/testlink-stack-220x234.png + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.6.tgz + version: 0.4.6 + - created: 2017-03-23T18:18:30.028234531Z + description: Web-based test management system that facilitates software quality + assurance. + digest: 08f7104671364ff6bd43270659733ea97a4adc06181f8a5c3027ac3d0078e51c + engine: gotpl + home: http://www.testlink.org/ + icon: https://bitnami.com/assets/stacks/testlink/img/testlink-stack-220x234.png + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.5.tgz + version: 0.4.5 + - created: 2017-03-08T19:03:31.751542723Z + description: Web-based test management system that facilitates software quality + assurance. + digest: 7861921ff159f1be6834acfc3e5c139382a8c6461b20a45c4b1561985827c865 + engine: gotpl + home: http://www.testlink.org/ + icon: https://bitnami.com/assets/stacks/testlink/img/testlink-stack-220x234.png + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.4.tgz + version: 0.4.4 + - created: 2017-02-11T03:18:26.547570032Z + description: Web-based test management system that facilitates software quality + assurance. + digest: 2c7188d5f1a9fb03c71b2e2d693dfbef9a739ae8889d9eb38854900cf066077b + engine: gotpl + home: http://www.testlink.org/ + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.3.tgz + version: 0.4.3 + - created: 2017-01-21T00:18:31.369288453Z + description: Web-based test management system that facilitates software quality + assurance. + digest: 78f6a9cfe1843b8ea99489d8b4c801f84271ee25827ad044989ed0df21ac086b + engine: gotpl + home: http://www.testlink.org/ + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.2.tgz + version: 0.4.2 + - created: 2016-12-15T21:18:24.679744308Z + description: Web-based test management system that facilitates software quality + assurance. + digest: 9edb2777c6db4794885a2c7531a28436774edc248aad3a26007bca4076058143 + engine: gotpl + home: http://www.testlink.org/ + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.1.tgz + version: 0.4.1 + - created: 2016-12-09T18:48:20.193151472Z + description: Web-based test management system that facilitates software quality + assurance. + digest: df216a31082cdf15867ee9a17b107e4006e9e0a20b79425889b695c4c46fb0c1 + engine: gotpl + home: http://www.testlink.org/ + keywords: + - testlink + - testing + - http + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: testlink + sources: + - https://github.com/bitnami/bitnami-docker-testlink + urls: + - https://kubernetes-charts.storage.googleapis.com/testlink-0.4.0.tgz + version: 0.4.0 + traefik: + - apiVersion: v1 + created: 2017-04-28T00:18:30.111646123Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 4019610a5fb1defcc5bc90532cb19c986999114f7de4aef3f0272e6c7ed1b914 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.2.1-a.tgz + version: 1.2.1-a + - apiVersion: v1 + created: 2017-03-31T19:33:30.456523182Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: ba0ade25b34f419ad0790b220fb7277a046d48bc76e1c726f66ba535b51d4f63 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-h.tgz + version: 1.1.2-h + - apiVersion: v1 + created: 2017-03-16T23:33:31.610346236Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 9aa401aee6da3b4afc5cc3f8be7ff9f74bf424743ca72a7a7b91a7105d9781b6 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-g.tgz + version: 1.1.2-g + - apiVersion: v1 + created: 2017-02-27T17:18:28.185706737Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 5bb7b98b962098808e3b73f604592bc4c6e6245e0074fa0c99308fc04bf766b8 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-f.tgz + version: 1.1.2-f + - apiVersion: v1 + created: 2017-02-13T22:18:28.973464794Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: ae467c4bee7364d17de2583d33031d0eeb2ef55e7962a7db0245d692e65479e1 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-e.tgz + version: 1.1.2-e + - apiVersion: v1 + created: 2017-02-13T21:33:27.776086791Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 399d74bcd8ab26f2de10894d83b59d413752797789b9fe9568e17f7b564f5f75 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-d.tgz + version: 1.1.2-d + - apiVersion: v1 + created: 2017-02-03T19:33:30.806247527Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: e225511060509d9cf3e38eaafd93af9ee994f8ed99c40a25500f4a1d06851841 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-c.tgz + version: 1.1.2-c + - apiVersion: v1 + created: 2017-02-01T02:18:29.153394653Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 9cc02b2e43c901c92aa560b4f85e325f04635d052035418f3b27b06bdd571ae9 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-b.tgz + version: 1.1.2-b + - apiVersion: v1 + created: 2017-01-28T00:18:32.767314879Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 9bae960964d5062dd4c412ad7daf6f6f9e8dd070264aa3f44c831c817fc26b7d + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.2-a.tgz + version: 1.1.2-a + - apiVersion: v1 + created: 2017-01-03T17:48:20.753425335Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: e8ab4576505091785b27084e4f4e4f02f1ee3f1744d9842ec086457baabe8b85 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.1-a.tgz + version: 1.1.1-a + - apiVersion: v1 + created: 2016-11-23T00:33:20.024479934Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 6664534aab03a22531602a415ca14a72e932b08fe1feab8866cc55ba18b77dc8 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.0-rc3-a.tgz + version: 1.1.0-rc3-a + - apiVersion: v1 + created: 2016-11-30T22:03:20.721274307Z + description: A Traefik based Kubernetes ingress controller with Let's Encrypt + support + digest: 2828d7284839baee1fb36f823ce4e2574a4b675b7f4f74e921a4685f4cee28c2 + engine: gotpl + home: http://traefik.io/ + icon: http://traefik.io/traefik.logo.png + keywords: + - traefik + - ingress + - acme + - letsencrypt + maintainers: + - email: engineering@deis.com + name: Deis + name: traefik + sources: + - https://github.com/containous/traefik + - https://github.com/krancour/charts/tree/master/traefik + urls: + - https://kubernetes-charts.storage.googleapis.com/traefik-1.1.0-a.tgz + version: 1.1.0-a + uchiwa: + - apiVersion: v1 + created: 2017-04-28T00:18:30.112528335Z + description: Dashboard for the Sensu monitoring framework + digest: b9b7186c2e53d4049c4b0ef9ba9c89ded7de36bf920653b63f6ea725253354d6 + engine: gotpl + home: https://uchiwa.io/ + icon: https://uchiwa.io/img/favicon.png + keywords: + - uchiwa + - sensu + - monitoring + maintainers: + - email: shane.starcher@gmail.com + name: Shane Starcher + name: uchiwa + sources: + - https://github.com/kubernetes/charts + - https://github.com/sstarcher/docker-uchiwa + - https://github.com/sensu/uchiwa + urls: + - https://kubernetes-charts.storage.googleapis.com/uchiwa-0.2.1.tgz + version: 0.2.1 + - apiVersion: v1 + created: 2017-03-17T06:03:29.101091523Z + description: Dashboard for the Sensu monitoring framework + digest: 9bee21cd61e56e08f58c1ba130e0a4af1a1d62a8d7921f9408509bd501494403 + engine: gotpl + home: https://uchiwa.io/ + icon: https://uchiwa.io/img/favicon.png + keywords: + - uchiwa + - sensu + - monitoring + maintainers: + - email: shane.starcher@gmail.com + name: Shane Starcher + name: uchiwa + sources: + - https://github.com/kubernetes/charts + - https://github.com/sstarcher/docker-uchiwa + - https://github.com/sensu/uchiwa + urls: + - https://kubernetes-charts.storage.googleapis.com/uchiwa-0.2.0.tgz + version: 0.2.0 + - apiVersion: v1 + created: 2017-01-18T23:03:27.817024829Z + description: Dashboard for the Sensu monitoring framework + digest: 868d7e58adb2fead4ed9e4be17e2017c2d1c55d265b2a579625e787e6f15f4d5 + engine: gotpl + home: https://uchiwa.io/ + icon: https://uchiwa.io/img/favicon.png + keywords: + - uchiwa + - sensu + - monitoring + maintainers: + - email: shane.starcher@gmail.com + name: Shane Starcher + name: uchiwa + sources: + - https://github.com/kubernetes/charts + - https://github.com/sstarcher/docker-uchiwa + - https://github.com/sensu/uchiwa + urls: + - https://kubernetes-charts.storage.googleapis.com/uchiwa-0.1.0.tgz + version: 0.1.0 + wordpress: + - created: 2017-04-28T00:18:30.114169329Z + description: Web publishing platform for building blogs and websites. + digest: 8df4b37c471d43b5b3955ecadcc0da1dad31ba28a93ae0b74be5fc94debf2876 + engine: gotpl + home: http://www.wordpress.com/ + icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.6.0.tgz + version: 0.6.0 + - created: 2017-04-03T22:33:26.700088102Z + description: Web publishing platform for building blogs and websites. + digest: 4413a17258eaca753252174a219ba9081283a406375d8ae49e5c1f3313c6619a + engine: gotpl + home: http://www.wordpress.com/ + icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.5.2.tgz + version: 0.5.2 + - created: 2017-03-23T21:18:31.877594706Z + description: Web publishing platform for building blogs and websites. + digest: 3e408baaa5110edfd730603bd5d49d7a8c222f49c7e9de1bd168b564463d57d9 + engine: gotpl + home: http://www.wordpress.com/ + icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.5.1.tgz + version: 0.5.1 + - created: 2017-03-16T13:33:30.866725941Z + description: Web publishing platform for building blogs and websites. + digest: 40c767b4b2b7d494ea6da7a20a9fe58e76896a0bdad7c6c569f9d8cdab71f2e3 + engine: gotpl + home: http://www.wordpress.com/ + icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.5.0.tgz + version: 0.5.0 + - created: 2017-03-14T23:48:31.917245657Z + description: Web publishing platform for building blogs and websites. + digest: 306220e3c19f1360644eade517a2a8ca422e8f9ec6ea9c65181ce8fc9797772f + engine: gotpl + home: http://www.wordpress.com/ + icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.4.3.tgz + version: 0.4.3 + - created: 2017-03-08T19:03:31.755452536Z + description: Web publishing platform for building blogs and websites. + digest: 0689b452d3c9a9bee6e5c84b48172c68de6eedc253223b96ab6500ad88a5de40 + engine: gotpl + home: http://www.wordpress.com/ + icon: https://bitnami.com/assets/stacks/wordpress/img/wordpress-stack-220x234.png + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.4.2.tgz + version: 0.4.2 + - created: 2017-02-13T04:33:52.323397093Z + description: Web publishing platform for building blogs and websites. + digest: 0fc412dea55069b368183afefb74342001a91a7f3a0e9126a921581d7740d61c + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.4.1.tgz + version: 0.4.1 + - created: 2017-01-28T00:18:32.769124587Z + description: Web publishing platform for building blogs and websites. + digest: 2f4a5d65350b36a6481c4c3d619f713835f091821d3f56c38c718061628ff712 + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.4.0.tgz + version: 0.4.0 + - created: 2017-01-04T00:48:19.757447587Z + description: Web publishing platform for building blogs and websites. + digest: f62b6f1728a33c5d59dd24dc6fb984f13d2dffac2bc6eec01724501e66ffc6a0 + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.3.4.tgz + version: 0.3.4 + - created: 2016-12-15T00:48:24.021239603Z + description: Web publishing platform for building blogs and websites. + digest: 0c86b7cec5877a3c3c55d919b2f02ae52340c953afd9dc541ae0280bc23fe9aa + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.3.3.tgz + version: 0.3.3 + - created: 2016-12-09T18:48:20.19465733Z + description: Web publishing platform for building blogs and websites. + digest: 589e49370cb09f6d9ddb3ceba3b21f52697570cd4b40aff891a660c5daaa9bec + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.3.2.tgz + version: 0.3.2 + - created: 2016-10-21T19:18:18.622178432Z + description: Web publishing platform for building blogs and websites. + digest: e70a072dcbb7252becc8899f54de8cb5977ceaea47197919c3990a6896adc350 + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.3.1.tgz + version: 0.3.1 + - created: 2016-10-19T00:03:14.037631856Z + description: Web publishing platform for building blogs and websites. + digest: 1c44515f02fb34b722dce1d8cf5fed0dfbbd2f8c03d63b335211b7bcb12b6dea + engine: gotpl + home: http://www.wordpress.com/ + keywords: + - wordpress + - cms + - blog + - http + - web + - application + - php + maintainers: + - email: containers@bitnami.com + name: Bitnami + name: wordpress + sources: + - https://github.com/bitnami/bitnami-docker-wordpress + urls: + - https://kubernetes-charts.storage.googleapis.com/wordpress-0.3.0.tgz + version: 0.3.0 +generated: 2017-04-28T00:18:30.070608132Z diff --git a/pkg/getter/testdata/repository/local/index.yaml b/pkg/getter/testdata/repository/local/index.yaml new file mode 100644 index 000000000..efcf30c21 --- /dev/null +++ b/pkg/getter/testdata/repository/local/index.yaml @@ -0,0 +1,3 @@ +apiVersion: v1 +entries: {} +generated: 2017-04-28T12:34:38.900985501-06:00 diff --git a/pkg/getter/testdata/repository/repositories.yaml b/pkg/getter/testdata/repository/repositories.yaml new file mode 100644 index 000000000..1d884a0c7 --- /dev/null +++ b/pkg/getter/testdata/repository/repositories.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +generated: 2017-04-28T12:34:38.551693035-06:00 +repositories: +- caFile: "" + cache: repository/cache/stable-index.yaml + certFile: "" + keyFile: "" + name: stable + url: https://kubernetes-charts.storage.googleapis.com +- caFile: "" + cache: repository/cache/local-index.yaml + certFile: "" + keyFile: "" + name: local + url: http://127.0.0.1:8879/charts diff --git a/pkg/helm/client.go b/pkg/helm/client.go index 5b12047c8..7e41b89bc 100644 --- a/pkg/helm/client.go +++ b/pkg/helm/client.go @@ -98,7 +98,7 @@ func (h *Client) InstallReleaseFromChart(chart *chart.Chart, ns string, opts ... if err != nil { return nil, err } - err = chartutil.ProcessRequirementsImportValues(req.Chart, req.Values) + err = chartutil.ProcessRequirementsImportValues(req.Chart) if err != nil { return nil, err } @@ -159,6 +159,7 @@ func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts req.Name = rlsName req.DisableHooks = h.opts.disableHooks req.Recreate = h.opts.recreate + req.Force = h.opts.force req.ResetValues = h.opts.resetValues req.ReuseValues = h.opts.reuseValues ctx := NewContext() @@ -172,7 +173,7 @@ func (h *Client) UpdateReleaseFromChart(rlsName string, chart *chart.Chart, opts if err != nil { return nil, err } - err = chartutil.ProcessRequirementsImportValues(req.Chart, req.Values) + err = chartutil.ProcessRequirementsImportValues(req.Chart) if err != nil { return nil, err } @@ -202,6 +203,8 @@ func (h *Client) RollbackRelease(rlsName string, opts ...RollbackOption) (*rls.R opt(&h.opts) } req := &h.opts.rollbackReq + req.Recreate = h.opts.recreate + req.Force = h.opts.force req.DisableHooks = h.opts.disableHooks req.DryRun = h.opts.dryRun req.Name = rlsName diff --git a/pkg/helm/environment/environment.go b/pkg/helm/environment/environment.go index 22fb43ae7..2092450e1 100644 --- a/pkg/helm/environment/environment.go +++ b/pkg/helm/environment/environment.go @@ -30,27 +30,37 @@ import ( ) const ( - HomeEnvVar = "HELM_HOME" - PluginEnvVar = "HELM_PLUGIN" + // HomeEnvVar is the HELM_HOME environment variable key. + HomeEnvVar = "HELM_HOME" + // PluginEnvVar is the HELM_PLUGIN environment variable key. + PluginEnvVar = "HELM_PLUGIN" + // PluginDisableEnvVar is the HELM_NO_PLUGINS environment variable key. PluginDisableEnvVar = "HELM_NO_PLUGINS" - HostEnvVar = "HELM_HOST" + // HostEnvVar is the HELM_HOST environment variable key. + HostEnvVar = "HELM_HOST" + // DebugEnvVar is the HELM_DEBUG environment variable key. + DebugEnvVar = "HELM_DEBUG" ) -func DefaultHelmHome() string { - if home := os.Getenv(HomeEnvVar); home != "" { - return home - } - return filepath.Join(os.Getenv("HOME"), ".helm") -} - -func DefaultHelmHost() string { - return os.Getenv(HostEnvVar) -} +// DefaultHelmHome is the default HELM_HOME. +var DefaultHelmHome = filepath.Join("$HOME", ".helm") +// EnvSettings describes all of the environment settings. type EnvSettings struct { - TillerHost string + // TillerHost is the host and port of Tiller. + TillerHost string + // TillerNamespace is the namespace in which Tiller runs. TillerNamespace string - Home helmpath.Home - PlugDirs string - Debug bool + // Home is the local path to the Helm home directory. + Home helmpath.Home + // Debug indicates whether or not Helm is running in Debug mode. + Debug bool +} + +// PluginDirs is the path to the plugin directories. +func (s EnvSettings) PluginDirs() string { + if d := os.Getenv(PluginEnvVar); d != "" { + return d + } + return s.Home.Plugins() } diff --git a/pkg/helm/helmpath/helmhome.go b/pkg/helm/helmpath/helmhome.go index d3023dcba..c9aad70c6 100644 --- a/pkg/helm/helmpath/helmhome.go +++ b/pkg/helm/helmpath/helmhome.go @@ -17,6 +17,7 @@ package helmpath import ( "fmt" + "os" "path/filepath" ) @@ -29,12 +30,12 @@ type Home string // // Implements fmt.Stringer. func (h Home) String() string { - return string(h) + return os.ExpandEnv(string(h)) } // Path returns Home with elements appended. func (h Home) Path(elem ...string) string { - p := []string{string(h)} + p := []string{h.String()} p = append(p, elem...) return filepath.Join(p...) } diff --git a/pkg/helm/helmpath/helmhome_unix_test.go b/pkg/helm/helmpath/helmhome_unix_test.go index cca303d29..9c31a9c07 100644 --- a/pkg/helm/helmpath/helmhome_unix_test.go +++ b/pkg/helm/helmpath/helmhome_unix_test.go @@ -38,3 +38,9 @@ func TestHelmHome(t *testing.T) { isEq(t, hh.CacheIndex("t"), "/r/repository/cache/t-index.yaml") isEq(t, hh.Starters(), "/r/starters") } + +func TestHelmHome_expand(t *testing.T) { + if Home("$HOME").String() == "$HOME" { + t.Error("expected variable expansion") + } +} diff --git a/pkg/helm/option.go b/pkg/helm/option.go index b4f6631c4..2b30cd3c5 100644 --- a/pkg/helm/option.go +++ b/pkg/helm/option.go @@ -46,6 +46,8 @@ type options struct { reuseName bool // if set, performs pod restart during upgrade/rollback recreate bool + // if set, force resource update through delete/recreate if needed + force bool // if set, skip running hooks disableHooks bool // name of release @@ -311,6 +313,13 @@ func RollbackRecreate(recreate bool) RollbackOption { } } +// RollbackForce will (if true) force resource update through delete/recreate if needed +func RollbackForce(force bool) RollbackOption { + return func(opts *options) { + opts.force = force + } +} + // RollbackVersion sets the version of the release to deploy. func RollbackVersion(ver int32) RollbackOption { return func(opts *options) { @@ -353,6 +362,13 @@ func UpgradeRecreate(recreate bool) UpdateOption { } } +// UpgradeForce will (if true) force resource update through delete/recreate if needed +func UpgradeForce(force bool) UpdateOption { + return func(opts *options) { + opts.force = force + } +} + // ContentOption allows setting optional attributes when // performing a GetReleaseContent tiller rpc. type ContentOption func(*options) @@ -408,7 +424,7 @@ func WithMaxHistory(max int32) HistoryOption { // NewContext creates a versioned context. func NewContext() context.Context { - md := metadata.Pairs("x-helm-api-client", version.Version) + md := metadata.Pairs("x-helm-api-client", version.GetVersion()) return metadata.NewContext(context.TODO(), md) } diff --git a/pkg/kube/client.go b/pkg/kube/client.go index 333e3ab37..04e28e816 100644 --- a/pkg/kube/client.go +++ b/pkg/kube/client.go @@ -59,6 +59,8 @@ type Client struct { cmdutil.Factory // SchemaCacheDir is the path for loading cached schema. SchemaCacheDir string + + Log func(string, ...interface{}) } // New create a new Client @@ -66,6 +68,7 @@ func New(config clientcmd.ClientConfig) *Client { return &Client{ Factory: cmdutil.NewFactory(config), SchemaCacheDir: clientcmd.RecommendedSchemaFile, + Log: func(_ string, _ ...interface{}) {}, } } @@ -83,10 +86,12 @@ func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shoul if err := ensureNamespace(client, namespace); err != nil { return err } + c.Log("building resources from manifest") infos, buildErr := c.BuildUnstructured(namespace, reader) if buildErr != nil { return buildErr } + c.Log("creating %d resource(s)", len(infos)) if err := perform(infos, createResource); err != nil { return err } @@ -99,7 +104,7 @@ func (c *Client) Create(namespace string, reader io.Reader, timeout int64, shoul func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result { schema, err := c.Validator(true, c.SchemaCacheDir) if err != nil { - log.Printf("warning: failed to load schema: %s", err) + c.Log("warning: failed to load schema: %s", err) } return c.NewBuilder(). ContinueOnError(). @@ -115,12 +120,12 @@ func (c *Client) newBuilder(namespace string, reader io.Reader) *resource.Result func (c *Client) BuildUnstructured(namespace string, reader io.Reader) (Result, error) { schema, err := c.Validator(true, c.SchemaCacheDir) if err != nil { - log.Printf("warning: failed to load schema: %s", err) + c.Log("warning: failed to load schema: %s", err) } mapper, typer, err := c.UnstructuredObject() if err != nil { - log.Printf("failed to load mapper: %s", err) + c.Log("failed to load mapper: %s", err) return nil, err } var result Result @@ -155,9 +160,9 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { } missing := []string{} err = perform(infos, func(info *resource.Info) error { - log.Printf("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name) + c.Log("Doing get for %s: %q", info.Mapping.GroupVersionKind.Kind, info.Name) if err := info.Get(); err != nil { - log.Printf("WARNING: Failed Get for resource %q: %s", info.Name, err) + c.Log("WARNING: Failed Get for resource %q: %s", info.Name, err) missing = append(missing, fmt.Sprintf("%v\t\t%s", info.Mapping.Resource, info.Name)) return nil } @@ -185,7 +190,7 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { } for _, o := range ot { if err := p.PrintObj(o, buf); err != nil { - log.Printf("failed to print object type %s, object: %q :\n %v", t, o, err) + c.Log("failed to print object type %s, object: %q :\n %v", t, o, err) return "", err } } @@ -208,12 +213,13 @@ func (c *Client) Get(namespace string, reader io.Reader) (string, error) { // not present in the target configuration // // Namespace will set the namespaces -func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { +func (c *Client) Update(namespace string, originalReader, targetReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { original, err := c.BuildUnstructured(namespace, originalReader) if err != nil { return fmt.Errorf("failed decoding reader into objects: %s", err) } + c.Log("building resources from updated manifest") target, err := c.BuildUnstructured(namespace, targetReader) if err != nil { return fmt.Errorf("failed decoding reader into objects: %s", err) @@ -221,6 +227,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader updateErrors := []string{} + c.Log("checking %d resources for changes", len(target)) err = target.Visit(func(info *resource.Info, err error) error { if err != nil { return err @@ -229,7 +236,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader helper := resource.NewHelper(info.Client, info.Mapping) if _, err := helper.Get(info.Namespace, info.Name, info.Export); err != nil { if !errors.IsNotFound(err) { - return fmt.Errorf("Could not get information about the resource: err: %s", err) + return fmt.Errorf("Could not get information about the resource: %s", err) } // Since the resource does not exist, create it. @@ -238,7 +245,7 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader } kind := info.Mapping.GroupVersionKind.Kind - log.Printf("Created a new %s called %q\n", kind, info.Name) + c.Log("Created a new %s called %q\n", kind, info.Name) return nil } @@ -247,8 +254,8 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader return fmt.Errorf("no resource with the name %q found", info.Name) } - if err := updateResource(c, info, originalInfo.Object, recreate); err != nil { - log.Printf("error updating the resource %q:\n\t %v", info.Name, err) + if err := updateResource(c, info, originalInfo.Object, force, recreate); err != nil { + c.Log("error updating the resource %q:\n\t %v", info.Name, err) updateErrors = append(updateErrors, err.Error()) } @@ -263,9 +270,9 @@ func (c *Client) Update(namespace string, originalReader, targetReader io.Reader } for _, info := range original.Difference(target) { - log.Printf("Deleting %q in %s...", info.Name, info.Namespace) + c.Log("Deleting %q in %s...", info.Name, info.Namespace) if err := deleteResource(c, info); err != nil { - log.Printf("Failed to delete %q, err: %s", info.Name, err) + c.Log("Failed to delete %q, err: %s", info.Name, err) } } if shouldWait { @@ -283,23 +290,23 @@ func (c *Client) Delete(namespace string, reader io.Reader) error { return err } return perform(infos, func(info *resource.Info) error { - log.Printf("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) + c.Log("Starting delete for %q %s", info.Name, info.Mapping.GroupVersionKind.Kind) err := deleteResource(c, info) - return skipIfNotFound(err) + return c.skipIfNotFound(err) }) } -func skipIfNotFound(err error) error { +func (c *Client) skipIfNotFound(err error) error { if errors.IsNotFound(err) { - log.Printf("%v", err) + c.Log("%v", err) return nil } return err } -func watchTimeout(t time.Duration) ResourceActorFunc { +func (c *Client) watchTimeout(t time.Duration) ResourceActorFunc { return func(info *resource.Info) error { - return watchUntilReady(t, info) + return c.watchUntilReady(t, info) } } @@ -322,7 +329,7 @@ func (c *Client) WatchUntilReady(namespace string, reader io.Reader, timeout int } // For jobs, there's also the option to do poll c.Jobs(namespace).Get(): // https://github.com/adamreese/kubernetes/blob/master/test/e2e/job.go#L291-L300 - return perform(infos, watchTimeout(time.Duration(timeout)*time.Second)) + return perform(infos, c.watchTimeout(time.Duration(timeout)*time.Second)) } func perform(infos Result, fn ResourceActorFunc) error { @@ -355,7 +362,7 @@ func deleteResource(c *Client, info *resource.Info) error { } return err } - log.Printf("Using reaper for deleting %q", info.Name) + c.Log("Using reaper for deleting %q", info.Name) return reaper.Stop(info.Namespace, info.Name, 0, nil) } @@ -383,19 +390,18 @@ func createPatch(mapping *meta.RESTMapping, target, current runtime.Object) ([]b case err != nil: return nil, types.StrategicMergePatchType, fmt.Errorf("failed to get versionedObject: %s", err) default: - log.Printf("generating strategic merge patch for %T", target) patch, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, versionedObject) return patch, types.StrategicMergePatchType, err } } -func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, recreate bool) error { +func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, force bool, recreate bool) error { patch, patchType, err := createPatch(target.Mapping, target.Object, currentObj) if err != nil { return fmt.Errorf("failed to create patch: %s", err) } if patch == nil { - log.Printf("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) + c.Log("Looks like there are no changes for %s %q", target.Mapping.GroupVersionKind.Kind, target.Name) // This needs to happen to make sure that tiller has the latest info from the API // Otherwise there will be no labels and other functions that use labels will panic if err := target.Get(); err != nil { @@ -406,12 +412,36 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, // send patch to server helper := resource.NewHelper(target.Client, target.Mapping) + obj, err := helper.Patch(target.Namespace, target.Name, patchType, patch) if err != nil { - return err - } + kind := target.Mapping.GroupVersionKind.Kind + log.Printf("Cannot patch %s: %q (%v)", kind, target.Name, err) + + if force { + // Attempt to delete... + if err := deleteResource(c, target); err != nil { + return err + } + log.Printf("Deleted %s: %q", kind, target.Name) + + // ... and recreate + if err := createResource(target); err != nil { + return fmt.Errorf("Failed to recreate resource: %s", err) + } + log.Printf("Created a new %s called %q\n", kind, target.Name) - target.Refresh(obj, true) + // No need to refresh the target, as we recreated the resource based + // on it. In addition, it might not exist yet and a call to `Refresh` + // may fail. + } else { + log.Print("Use --force to force recreation of the resource") + return err + } + } else { + // When patch succeeds without needing to recreate, refresh target. + target.Refresh(obj, true) + } if !recreate { return nil @@ -429,7 +459,11 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, if err != nil { return nil } - client, _ := c.ClientSet() + + client, err := c.ClientSet() + if err != nil { + return err + } pods, err := client.Core().Pods(target.Namespace).List(metav1.ListOptions{ FieldSelector: fields.Everything().String(), @@ -441,7 +475,7 @@ func updateResource(c *Client, target *resource.Info, currentObj runtime.Object, // Restart pods for _, pod := range pods.Items { - log.Printf("Restarting pod: %v/%v", pod.Namespace, pod.Name) + c.Log("Restarting pod: %v/%v", pod.Namespace, pod.Name) // Delete each pod for get them restarted with changed spec. if err := client.Core().Pods(pod.Namespace).Delete(pod.Name, metav1.NewPreconditionDeleteOptions(string(pod.UID))); err != nil { @@ -470,14 +504,14 @@ func getSelectorFromObject(obj runtime.Object) (map[string]string, error) { } } -func watchUntilReady(timeout time.Duration, info *resource.Info) error { +func (c *Client) watchUntilReady(timeout time.Duration, info *resource.Info) error { w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) if err != nil { return err } kind := info.Mapping.GroupVersionKind.Kind - log.Printf("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout) + c.Log("Watching for changes to %s %s with timeout of %v", kind, info.Name, timeout) // What we watch for depends on the Kind. // - For a Job, we watch for completion. @@ -492,17 +526,17 @@ func watchUntilReady(timeout time.Duration, info *resource.Info) error { // we get. We care mostly about jobs, where what we want to see is // the status go into a good state. For other types, like ReplicaSet // we don't really do anything to support these as hooks. - log.Printf("Add/Modify event for %s: %v", info.Name, e.Type) + c.Log("Add/Modify event for %s: %v", info.Name, e.Type) if kind == "Job" { - return waitForJob(e, info.Name) + return c.waitForJob(e, info.Name) } return true, nil case watch.Deleted: - log.Printf("Deleted event for %s", info.Name) + c.Log("Deleted event for %s", info.Name) return true, nil case watch.Error: // Handle error and return with an error. - log.Printf("Error event for %s", info.Name) + c.Log("Error event for %s", info.Name) return true, fmt.Errorf("Failed to deploy %s", info.Name) default: return false, nil @@ -525,7 +559,7 @@ func (c *Client) AsVersionedObject(obj runtime.Object) (runtime.Object, error) { // waitForJob is a helper that waits for a job to complete. // // This operates on an event returned from a watcher. -func waitForJob(e watch.Event, name string) (bool, error) { +func (c *Client) waitForJob(e watch.Event, name string) (bool, error) { o, ok := e.Object.(*batchinternal.Job) if !ok { return true, fmt.Errorf("Expected %s to be a *batch.Job, got %T", name, e.Object) @@ -539,7 +573,7 @@ func waitForJob(e watch.Event, name string) (bool, error) { } } - log.Printf("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded) + c.Log("%s: Jobs active: %d, jobs failed: %d, jobs succeeded: %d", name, o.Status.Active, o.Status.Failed, o.Status.Succeeded) return false, nil } @@ -570,7 +604,7 @@ func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, return api.PodUnknown, fmt.Errorf("%s is not a Pod", info.Name) } - if err := watchPodUntilComplete(timeout, info); err != nil { + if err := c.watchPodUntilComplete(timeout, info); err != nil { return api.PodUnknown, err } @@ -582,13 +616,13 @@ func (c *Client) WaitAndGetCompletedPodPhase(namespace string, reader io.Reader, return status, nil } -func watchPodUntilComplete(timeout time.Duration, info *resource.Info) error { +func (c *Client) watchPodUntilComplete(timeout time.Duration, info *resource.Info) error { w, err := resource.NewHelper(info.Client, info.Mapping).WatchSingle(info.Namespace, info.Name, info.ResourceVersion) if err != nil { return err } - log.Printf("Watching pod %s for completion with timeout of %v", info.Name, timeout) + c.Log("Watching pod %s for completion with timeout of %v", info.Name, timeout) _, err = watch.Until(timeout, w, func(e watch.Event) (bool, error) { return conditions.PodCompleted(e) }) diff --git a/pkg/kube/client_test.go b/pkg/kube/client_test.go index 1f2032bb2..647a50652 100644 --- a/pkg/kube/client_test.go +++ b/pkg/kube/client_test.go @@ -138,6 +138,12 @@ func encodeAndMarshalEvent(e *watch.Event) ([]byte, error) { return json.Marshal(encodedEvent) } +func newTestClient(f cmdutil.Factory) *Client { + c := New(nil) + c.Factory = f + return c +} + func TestUpdate(t *testing.T) { listA := newPodList("starfish", "otter", "squid") listB := newPodList("starfish", "otter", "dolphin") @@ -186,8 +192,8 @@ func TestUpdate(t *testing.T) { reaper := &fakeReaper{} rf := &fakeReaperFactory{Factory: f, reaper: reaper} - c := &Client{Factory: rf} - if err := c.Update(api.NamespaceDefault, objBody(codec, &listA), objBody(codec, &listB), false, 0, false); err != nil { + c := newTestClient(rf) + if err := c.Update(api.NamespaceDefault, objBody(codec, &listA), objBody(codec, &listB), false, false, 0, false); err != nil { t.Fatal(err) } // TODO: Find a way to test methods that use Client Set @@ -251,7 +257,7 @@ func TestBuild(t *testing.T) { for _, tt := range tests { f, tf, _, _ := cmdtesting.NewAPIFactory() - c := &Client{Factory: f} + c := newTestClient(f) if tt.swaggerFile != "" { data, err := ioutil.ReadFile(tt.swaggerFile) if err != nil { @@ -320,7 +326,7 @@ func TestGet(t *testing.T) { } }), } - c := &Client{Factory: f} + c := newTestClient(f) // Test Success data := strings.NewReader("kind: Pod\napiVersion: v1\nmetadata:\n name: otter") @@ -380,7 +386,7 @@ func TestPerform(t *testing.T) { } f, tf, _, _ := cmdtesting.NewAPIFactory() - c := &Client{Factory: f} + c := newTestClient(f) if tt.swaggerFile != "" { data, err := ioutil.ReadFile(tt.swaggerFile) if err != nil { @@ -464,7 +470,7 @@ func TestWaitAndGetCompletedPodPhase(t *testing.T) { }), } - c := &Client{Factory: f} + c := newTestClient(f) phase, err := c.WaitAndGetCompletedPodPhase("test", objBody(codec, &testPodList), 1*time.Second) if (err != nil) != tt.err { diff --git a/pkg/kube/wait.go b/pkg/kube/wait.go index bef366b4f..d917d79ed 100644 --- a/pkg/kube/wait.go +++ b/pkg/kube/wait.go @@ -17,7 +17,6 @@ limitations under the License. package kube // import "k8s.io/helm/pkg/kube" import ( - "log" "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -44,15 +43,17 @@ type deployment struct { // waitForResources polls to get the current status of all pods, PVCs, and Services // until all are ready or a timeout is reached func (c *Client) waitForResources(timeout time.Duration, created Result) error { - log.Printf("beginning wait for resources with timeout of %v", timeout) + c.Log("beginning wait for %d resources with timeout of %v", len(created), timeout) - cs, _ := c.ClientSet() + cs, err := c.ClientSet() + if err != nil { + return err + } client := versionedClientsetForDeployment(cs) return wait.Poll(2*time.Second, timeout, func() (bool, error) { pods := []v1.Pod{} services := []v1.Service{} pvc := []v1.PersistentVolumeClaim{} - replicaSets := []*extensions.ReplicaSet{} deployments := []deployment{} for _, v := range created { obj, err := c.AsVersionedObject(v.Object) @@ -73,26 +74,13 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { } pods = append(pods, *pod) case (*extensions.Deployment): - // Get the RS children first - rs, err := client.Extensions().ReplicaSets(value.Namespace).List(metav1.ListOptions{ - FieldSelector: fields.Everything().String(), - LabelSelector: labels.Set(value.Spec.Selector.MatchLabels).AsSelector().String(), - }) - if err != nil { - return false, err - } - - for _, i := range rs.Items { - replicaSets = append(replicaSets, &i) - } - currentDeployment, err := client.Extensions().Deployments(value.Namespace).Get(value.Name, metav1.GetOptions{}) if err != nil { return false, err } // Find RS associated with deployment - newReplicaSet, err := deploymentutil.FindNewReplicaSet(currentDeployment, replicaSets) - if err != nil { + newReplicaSet, err := deploymentutil.GetNewReplicaSet(currentDeployment, client) + if err != nil || newReplicaSet == nil { return false, err } newDeployment := deployment{ @@ -132,7 +120,9 @@ func (c *Client) waitForResources(timeout time.Duration, created Result) error { services = append(services, *svc) } } - return podsReady(pods) && servicesReady(services) && volumesReady(pvc) && deploymentsReady(deployments), nil + isReady := podsReady(pods) && servicesReady(services) && volumesReady(pvc) && deploymentsReady(deployments) + c.Log("resources ready: %v", isReady) + return isReady, nil }) } @@ -147,6 +137,11 @@ func podsReady(pods []v1.Pod) bool { func servicesReady(svc []v1.Service) bool { for _, s := range svc { + // ExternalName Services are external to cluster so helm shouldn't be checking to see if they're 'ready' (i.e. have an IP Set) + if s.Spec.Type == v1.ServiceTypeExternalName { + continue + } + // Make sure the service is not explicitly set to "None" before checking the IP if s.Spec.ClusterIP != v1.ClusterIPNone && !v1.IsServiceIPSet(&s) { return false diff --git a/pkg/lint/rules/template.go b/pkg/lint/rules/template.go index 5b98d4886..73206b80c 100644 --- a/pkg/lint/rules/template.go +++ b/pkg/lint/rules/template.go @@ -21,12 +21,15 @@ import ( "fmt" "os" "path/filepath" + "runtime" "github.com/ghodss/yaml" + "k8s.io/apimachinery/pkg/version" "k8s.io/helm/pkg/chartutil" "k8s.io/helm/pkg/engine" "k8s.io/helm/pkg/lint/support" "k8s.io/helm/pkg/timeconv" + tversion "k8s.io/helm/pkg/version" ) // Templates lints the templates in the Linter. @@ -51,7 +54,17 @@ func Templates(linter *support.Linter) { } options := chartutil.ReleaseOptions{Name: "testRelease", Time: timeconv.Now(), Namespace: "testNamespace"} - caps := &chartutil.Capabilities{APIVersions: chartutil.DefaultVersionSet} + caps := &chartutil.Capabilities{ + APIVersions: chartutil.DefaultVersionSet, + KubeVersion: &version.Info{ + Major: "1", + Minor: "6", + GoVersion: runtime.Version(), + Compiler: runtime.Compiler, + Platform: fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH), + }, + TillerVersion: tversion.GetVersionProto(), + } valuesToRender, err := chartutil.ToRenderValuesCaps(chart, chart.Values, options, caps) if err != nil { // FIXME: This seems to generate a duplicate, but I can't find where the first diff --git a/pkg/lint/rules/testdata/albatross/templates/svc.yaml b/pkg/lint/rules/testdata/albatross/templates/svc.yaml index 2c44ea2c6..167148112 100644 --- a/pkg/lint/rules/testdata/albatross/templates/svc.yaml +++ b/pkg/lint/rules/testdata/albatross/templates/svc.yaml @@ -8,6 +8,8 @@ metadata: heritage: {{ .Release.Service | quote }} release: {{ .Release.Name | quote }} chart: "{{.Chart.Name}}-{{.Chart.Version}}" + kubeVersion: {{ .Capabilities.KubeVersion.Major }} + tillerVersion: {{ .Capabilities.TillerVersion }} spec: ports: - port: {{default 80 .Values.httpPort | quote}} diff --git a/pkg/plugin/hooks.go b/pkg/plugin/hooks.go index 1f435f9f8..b5ca032ac 100644 --- a/pkg/plugin/hooks.go +++ b/pkg/plugin/hooks.go @@ -21,6 +21,8 @@ const ( Install = "install" // Delete is executed after the plugin is removed. Delete = "delete" + // Update is executed after the plugin is updated. + Update = "update" ) // Hooks is a map of events to commands. diff --git a/pkg/plugin/installer/installer.go b/pkg/plugin/installer/installer.go index 31ef9ae53..6ecec980a 100644 --- a/pkg/plugin/installer/installer.go +++ b/pkg/plugin/installer/installer.go @@ -19,6 +19,7 @@ import ( "errors" "fmt" "os" + "path" "path/filepath" "k8s.io/helm/pkg/helm/helmpath" @@ -36,13 +37,32 @@ type Installer interface { Install() error // Path is the directory of the installed plugin. Path() string + // Update updates a plugin to $HELM_HOME. + Update() error } // Install installs a plugin to $HELM_HOME. func Install(i Installer) error { + if _, pathErr := os.Stat(path.Dir(i.Path())); os.IsNotExist(pathErr) { + return errors.New(`plugin home "$HELM_HOME/plugins" does not exist`) + } + + if _, pathErr := os.Stat(i.Path()); !os.IsNotExist(pathErr) { + return errors.New("plugin already exists") + } + return i.Install() } +// Update updates a plugin in $HELM_HOME. +func Update(i Installer) error { + if _, pathErr := os.Stat(i.Path()); os.IsNotExist(pathErr) { + return errors.New("plugin does not exist") + } + + return i.Update() +} + // NewForSource determines the correct Installer for the given source. func NewForSource(source, version string, home helmpath.Home) (Installer, error) { // Check if source is a local directory @@ -52,6 +72,15 @@ func NewForSource(source, version string, home helmpath.Home) (Installer, error) return NewVCSInstaller(source, version, home) } +// FindSource determines the correct Installer for the given source. +func FindSource(location string, home helmpath.Home) (Installer, error) { + installer, err := existingVCSRepo(location, home) + if err != nil && err.Error() == "Cannot detect VCS" { + return installer, errors.New("cannot get information about plugin source") + } + return installer, err +} + // isLocalReference checks if the source exists on the filesystem. func isLocalReference(source string) bool { _, err := os.Stat(source) diff --git a/pkg/plugin/installer/local_installer.go b/pkg/plugin/installer/local_installer.go index 7ab588d60..18011f8de 100644 --- a/pkg/plugin/installer/local_installer.go +++ b/pkg/plugin/installer/local_installer.go @@ -47,3 +47,9 @@ func (i *LocalInstaller) Install() error { } return i.link(src) } + +// Update updates a local repository +func (i *LocalInstaller) Update() error { + debug("local repository is auto-updated") + return nil +} diff --git a/pkg/plugin/installer/local_installer_test.go b/pkg/plugin/installer/local_installer_test.go index a2a1b541c..6a7c957d6 100644 --- a/pkg/plugin/installer/local_installer_test.go +++ b/pkg/plugin/installer/local_installer_test.go @@ -31,7 +31,7 @@ func TestLocalInstaller(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(hh) + defer os.RemoveAll(hh) home := helmpath.Home(hh) if err := os.MkdirAll(home.Plugins(), 0755); err != nil { @@ -43,7 +43,7 @@ func TestLocalInstaller(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(tdir) + defer os.RemoveAll(tdir) if err := ioutil.WriteFile(filepath.Join(tdir, "plugin.yaml"), []byte{}, 0644); err != nil { t.Fatal(err) } diff --git a/pkg/plugin/installer/vcs_installer.go b/pkg/plugin/installer/vcs_installer.go index ce7a93b83..d2ba3aa31 100644 --- a/pkg/plugin/installer/vcs_installer.go +++ b/pkg/plugin/installer/vcs_installer.go @@ -16,6 +16,8 @@ limitations under the License. package installer // import "k8s.io/helm/pkg/plugin/installer" import ( + "errors" + "fmt" "os" "sort" @@ -33,6 +35,18 @@ type VCSInstaller struct { base } +func existingVCSRepo(location string, home helmpath.Home) (Installer, error) { + repo, err := vcs.NewRepo("", location) + if err != nil { + return nil, err + } + i := &VCSInstaller{ + Repo: repo, + base: newBase(repo.Remote(), home), + } + return i, err +} + // NewVCSInstaller creates a new VCSInstaller. func NewVCSInstaller(source, version string, home helmpath.Home) (*VCSInstaller, error) { key, err := cache.Key(source) @@ -76,6 +90,21 @@ func (i *VCSInstaller) Install() error { return i.link(i.Repo.LocalPath()) } +// Update updates a remote repository +func (i *VCSInstaller) Update() error { + debug("updating %s", i.Repo.Remote()) + if i.Repo.IsDirty() { + return errors.New("plugin repo was modified") + } + if err := i.Repo.Update(); err != nil { + return err + } + if !isPlugin(i.Repo.LocalPath()) { + return ErrMissingMetadata + } + return nil +} + func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { if i.Version == "" { return "", nil @@ -112,7 +141,8 @@ func (i *VCSInstaller) solveVersion(repo vcs.Repo) (string, error) { return ver, nil } } - return "", nil + + return "", fmt.Errorf("requested version %q does not exist for plugin %q", i.Version, i.Repo.Remote()) } // setVersion attempts to checkout the version diff --git a/pkg/plugin/installer/vcs_installer_test.go b/pkg/plugin/installer/vcs_installer_test.go index e340bbe5a..4abfc4c09 100644 --- a/pkg/plugin/installer/vcs_installer_test.go +++ b/pkg/plugin/installer/vcs_installer_test.go @@ -16,8 +16,10 @@ limitations under the License. package installer // import "k8s.io/helm/pkg/plugin/installer" import ( + "fmt" "io/ioutil" "os" + "path/filepath" "testing" "k8s.io/helm/pkg/helm/helmpath" @@ -51,7 +53,7 @@ func TestVCSInstaller(t *testing.T) { if err != nil { t.Fatal(err) } - defer os.Remove(hh) + defer os.RemoveAll(hh) home := helmpath.Home(hh) if err := os.MkdirAll(home.Plugins(), 0755); err != nil { @@ -59,8 +61,9 @@ func TestVCSInstaller(t *testing.T) { } source := "https://github.com/adamreese/helm-env" + testRepoPath, _ := filepath.Abs("../testdata/plugdir/echo") repo := &testRepo{ - local: "../testdata/plugdir/echo", + local: testRepoPath, tags: []string{"0.1.0", "0.1.1"}, } @@ -87,4 +90,114 @@ func TestVCSInstaller(t *testing.T) { if i.Path() != home.Path("plugins", "helm-env") { t.Errorf("expected path '$HELM_HOME/plugins/helm-env', got %q", i.Path()) } + + // Install again to test plugin exists error + if err := Install(i); err == nil { + t.Error("expected error for plugin exists, got none") + } else if err.Error() != "plugin already exists" { + t.Errorf("expected error for plugin exists, got (%v)", err) + } + + //Testing FindSource method, expect error because plugin code is not a cloned repository + if _, err := FindSource(i.Path(), home); err == nil { + t.Error("expected error for inability to find plugin source, got none") + } else if err.Error() != "cannot get information about plugin source" { + t.Errorf("expected error for inability to find plugin source, got (%v)", err) + } +} + +func TestVCSInstallerNonExistentVersion(t *testing.T) { + hh, err := ioutil.TempDir("", "helm-home-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(hh) + + home := helmpath.Home(hh) + if err := os.MkdirAll(home.Plugins(), 0755); err != nil { + t.Fatalf("Could not create %s: %s", home.Plugins(), err) + } + + source := "https://github.com/adamreese/helm-env" + version := "0.2.0" + + i, err := NewForSource(source, version, home) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + // ensure a VCSInstaller was returned + _, ok := i.(*VCSInstaller) + if !ok { + t.Error("expected a VCSInstaller") + } + + if err := Install(i); err == nil { + t.Error("expected error for version does not exists, got none") + } else if err.Error() != fmt.Sprintf("requested version %q does not exist for plugin %q", version, source) { + t.Errorf("expected error for version does not exists, got (%v)", err) + } +} +func TestVCSInstallerUpdate(t *testing.T) { + + hh, err := ioutil.TempDir("", "helm-home-") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(hh) + + home := helmpath.Home(hh) + if err := os.MkdirAll(home.Plugins(), 0755); err != nil { + t.Fatalf("Could not create %s: %s", home.Plugins(), err) + } + + source := "https://github.com/adamreese/helm-env" + + i, err := NewForSource(source, "", home) + if err != nil { + t.Errorf("unexpected error: %s", err) + } + + // ensure a VCSInstaller was returned + _, ok := i.(*VCSInstaller) + if !ok { + t.Error("expected a VCSInstaller") + } + + if err := Update(i); err == nil { + t.Error("expected error for plugin does not exist, got none") + } else if err.Error() != "plugin does not exist" { + t.Errorf("expected error for plugin does not exist, got (%v)", err) + } + + // Install plugin before update + if err := Install(i); err != nil { + t.Error(err) + } + + // Test FindSource method for positive result + pluginInfo, err := FindSource(i.Path(), home) + if err != nil { + t.Error(err) + } + + repoRemote := pluginInfo.(*VCSInstaller).Repo.Remote() + if repoRemote != source { + t.Errorf("invalid source found, expected %q got %q", source, repoRemote) + } + + // Update plugin + if err := Update(i); err != nil { + t.Error(err) + } + + // Test update failure + os.Remove(filepath.Join(i.Path(), "plugin.yaml")) + // Testing update for error + if err := Update(i); err == nil { + t.Error("expected error for plugin modified, got none") + } else if err.Error() != "plugin repo was modified" { + t.Errorf("expected error for plugin modified, got (%v)", err) + } + } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 8bde6ad24..75d808701 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -179,7 +179,7 @@ func SetupPluginEnv(settings helm_env.EnvSettings, // Set vars that may not have been set, and save client the // trouble of re-parsing. - helm_env.PluginEnvVar: settings.PlugDirs, + helm_env.PluginEnvVar: settings.PluginDirs(), helm_env.HomeEnvVar: settings.Home.String(), // Set vars that convey common information. diff --git a/pkg/proto/hapi/release/test_run.pb.go b/pkg/proto/hapi/release/test_run.pb.go index 79fce4ab4..bd3c0ab2b 100644 --- a/pkg/proto/hapi/release/test_run.pb.go +++ b/pkg/proto/hapi/release/test_run.pb.go @@ -20,17 +20,20 @@ const ( TestRun_UNKNOWN TestRun_Status = 0 TestRun_SUCCESS TestRun_Status = 1 TestRun_FAILURE TestRun_Status = 2 + TestRun_RUNNING TestRun_Status = 3 ) var TestRun_Status_name = map[int32]string{ 0: "UNKNOWN", 1: "SUCCESS", 2: "FAILURE", + 3: "RUNNING", } var TestRun_Status_value = map[string]int32{ "UNKNOWN": 0, "SUCCESS": 1, "FAILURE": 2, + "RUNNING": 3, } func (x TestRun_Status) String() string { @@ -94,22 +97,23 @@ func init() { func init() { proto.RegisterFile("hapi/release/test_run.proto", fileDescriptor4) } var fileDescriptor4 = []byte{ - // 265 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x8f, 0x41, 0x4b, 0xfb, 0x40, - 0x14, 0xc4, 0xff, 0xc9, 0xbf, 0x26, 0x64, 0x53, 0x24, 0xec, 0x29, 0x54, 0xc1, 0xd0, 0x53, 0x4e, - 0xbb, 0x50, 0xbd, 0x78, 0xf0, 0x10, 0x4b, 0x05, 0x51, 0x22, 0x6c, 0x1a, 0x04, 0x2f, 0x65, 0xab, - 0xaf, 0x35, 0x90, 0x64, 0x43, 0xf6, 0xe5, 0x8b, 0xf8, 0x89, 0x65, 0x93, 0xad, 0x78, 0xf3, 0xf6, - 0x86, 0xf9, 0xcd, 0x30, 0x8f, 0x5c, 0x7c, 0xca, 0xae, 0xe2, 0x3d, 0xd4, 0x20, 0x35, 0x70, 0x04, - 0x8d, 0xbb, 0x7e, 0x68, 0x59, 0xd7, 0x2b, 0x54, 0x74, 0x6e, 0x4c, 0x66, 0xcd, 0xc5, 0xd5, 0x51, - 0xa9, 0x63, 0x0d, 0x7c, 0xf4, 0xf6, 0xc3, 0x81, 0x63, 0xd5, 0x80, 0x46, 0xd9, 0x74, 0x13, 0xbe, - 0xfc, 0x72, 0x89, 0xbf, 0x05, 0x8d, 0x62, 0x68, 0x29, 0x25, 0xb3, 0x56, 0x36, 0x10, 0x3b, 0x89, - 0x93, 0x06, 0x62, 0xbc, 0xe9, 0x0d, 0xf1, 0x34, 0x4a, 0x1c, 0x74, 0xec, 0x26, 0x4e, 0x7a, 0xbe, - 0xba, 0x64, 0xbf, 0xfb, 0x99, 0x8d, 0xb2, 0x62, 0x64, 0x84, 0x65, 0x4d, 0x53, 0xd5, 0x1e, 0x54, - 0xfc, 0x7f, 0x6a, 0x32, 0x37, 0xbd, 0x25, 0x44, 0xa3, 0xec, 0x11, 0x3e, 0x76, 0x12, 0xe3, 0x59, - 0xe2, 0xa4, 0xe1, 0x6a, 0xc1, 0xa6, 0x7d, 0xec, 0xb4, 0x8f, 0x6d, 0x4f, 0xfb, 0x44, 0x60, 0xe9, - 0x0c, 0xe9, 0x1d, 0x99, 0xbf, 0xab, 0xa6, 0xab, 0xc1, 0x86, 0xcf, 0xfe, 0x0c, 0x87, 0x3f, 0x7c, - 0x86, 0x4b, 0x4e, 0xbc, 0x69, 0x1f, 0x0d, 0x89, 0x5f, 0xe6, 0x4f, 0xf9, 0xcb, 0x6b, 0x1e, 0xfd, - 0x33, 0xa2, 0x28, 0xd7, 0xeb, 0x4d, 0x51, 0x44, 0x8e, 0x11, 0x0f, 0xd9, 0xe3, 0x73, 0x29, 0x36, - 0x91, 0x7b, 0x1f, 0xbc, 0xf9, 0xf6, 0xc1, 0xbd, 0x37, 0x96, 0x5f, 0x7f, 0x07, 0x00, 0x00, 0xff, - 0xff, 0x8d, 0xb9, 0xce, 0x57, 0x74, 0x01, 0x00, 0x00, + // 274 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x8f, 0xc1, 0x4b, 0xfb, 0x30, + 0x1c, 0xc5, 0x7f, 0xe9, 0xf6, 0x6b, 0x69, 0x3a, 0xa4, 0xe4, 0x54, 0xa6, 0x60, 0xd9, 0xa9, 0xa7, + 0x14, 0xa6, 0x17, 0x41, 0x0f, 0x75, 0x4c, 0x19, 0x4a, 0x84, 0x74, 0x45, 0xf0, 0x32, 0x32, 0xcd, + 0x66, 0xa1, 0x6d, 0x4a, 0xf3, 0xed, 0xdf, 0xe3, 0xbf, 0x2a, 0x69, 0x33, 0xf1, 0xe6, 0xed, 0xfb, + 0x78, 0x9f, 0xf7, 0xf2, 0x82, 0xcf, 0x3f, 0x45, 0x5b, 0xa6, 0x9d, 0xac, 0xa4, 0xd0, 0x32, 0x05, + 0xa9, 0x61, 0xd7, 0xf5, 0x0d, 0x6d, 0x3b, 0x05, 0x8a, 0xcc, 0x8c, 0x49, 0xad, 0x39, 0xbf, 0x3c, + 0x2a, 0x75, 0xac, 0x64, 0x3a, 0x78, 0xfb, 0xfe, 0x90, 0x42, 0x59, 0x4b, 0x0d, 0xa2, 0x6e, 0x47, + 0x7c, 0xf1, 0xe5, 0x60, 0x6f, 0x2b, 0x35, 0xf0, 0xbe, 0x21, 0x04, 0x4f, 0x1b, 0x51, 0xcb, 0x08, + 0xc5, 0x28, 0xf1, 0xf9, 0x70, 0x93, 0x6b, 0xec, 0x6a, 0x10, 0xd0, 0xeb, 0xc8, 0x89, 0x51, 0x72, + 0xb6, 0xbc, 0xa0, 0xbf, 0xfb, 0xa9, 0x8d, 0xd2, 0x7c, 0x60, 0xb8, 0x65, 0x4d, 0x53, 0xd9, 0x1c, + 0x54, 0x34, 0x19, 0x9b, 0xcc, 0x4d, 0x6e, 0x30, 0xd6, 0x20, 0x3a, 0x90, 0x1f, 0x3b, 0x01, 0xd1, + 0x34, 0x46, 0x49, 0xb0, 0x9c, 0xd3, 0x71, 0x1f, 0x3d, 0xed, 0xa3, 0xdb, 0xd3, 0x3e, 0xee, 0x5b, + 0x3a, 0x03, 0x72, 0x87, 0x67, 0xef, 0xaa, 0x6e, 0x2b, 0x69, 0xc3, 0xff, 0xff, 0x0c, 0x07, 0x3f, + 0x7c, 0x06, 0x8b, 0x5b, 0xec, 0x8e, 0xfb, 0x48, 0x80, 0xbd, 0x82, 0x3d, 0xb1, 0x97, 0x57, 0x16, + 0xfe, 0x33, 0x22, 0x2f, 0x56, 0xab, 0x75, 0x9e, 0x87, 0xc8, 0x88, 0x87, 0x6c, 0xf3, 0x5c, 0xf0, + 0x75, 0xe8, 0x18, 0xc1, 0x0b, 0xc6, 0x36, 0xec, 0x31, 0x9c, 0xdc, 0xfb, 0x6f, 0x9e, 0xfd, 0xed, + 0xde, 0x1d, 0x5e, 0xba, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x31, 0x86, 0x46, 0xdb, 0x81, 0x01, + 0x00, 0x00, } diff --git a/pkg/proto/hapi/rudder/rudder.pb.go b/pkg/proto/hapi/rudder/rudder.pb.go new file mode 100644 index 000000000..3f64ba713 --- /dev/null +++ b/pkg/proto/hapi/rudder/rudder.pb.go @@ -0,0 +1,723 @@ +// Code generated by protoc-gen-go. +// source: hapi/rudder/rudder.proto +// DO NOT EDIT! + +/* +Package rudder is a generated protocol buffer package. + +It is generated from these files: + hapi/rudder/rudder.proto + +It has these top-level messages: + Result + VersionReleaseRequest + VersionReleaseResponse + InstallReleaseRequest + InstallReleaseResponse + DeleteReleaseRequest + DeleteReleaseResponse + UpgradeReleaseRequest + UpgradeReleaseResponse + RollbackReleaseRequest + RollbackReleaseResponse + ReleaseStatusRequest + ReleaseStatusResponse +*/ +package rudder + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" +import hapi_release3 "k8s.io/helm/pkg/proto/hapi/release" +import hapi_release5 "k8s.io/helm/pkg/proto/hapi/release" + +import ( + context "golang.org/x/net/context" + grpc "google.golang.org/grpc" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type Result_Status int32 + +const ( + // No status set + Result_UNKNOWN Result_Status = 0 + // Operation was successful + Result_SUCCESS Result_Status = 1 + // Operation had no results (e.g. upgrade identical, rollback to same, delete non-existent) + Result_UNCHANGED Result_Status = 2 + // Operation failed + Result_ERROR Result_Status = 3 +) + +var Result_Status_name = map[int32]string{ + 0: "UNKNOWN", + 1: "SUCCESS", + 2: "UNCHANGED", + 3: "ERROR", +} +var Result_Status_value = map[string]int32{ + "UNKNOWN": 0, + "SUCCESS": 1, + "UNCHANGED": 2, + "ERROR": 3, +} + +func (x Result_Status) String() string { + return proto.EnumName(Result_Status_name, int32(x)) +} +func (Result_Status) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } + +type Result struct { + Info string `protobuf:"bytes,1,opt,name=info" json:"info,omitempty"` + Log []string `protobuf:"bytes,2,rep,name=log" json:"log,omitempty"` +} + +func (m *Result) Reset() { *m = Result{} } +func (m *Result) String() string { return proto.CompactTextString(m) } +func (*Result) ProtoMessage() {} +func (*Result) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Result) GetInfo() string { + if m != nil { + return m.Info + } + return "" +} + +func (m *Result) GetLog() []string { + if m != nil { + return m.Log + } + return nil +} + +type VersionReleaseRequest struct { +} + +func (m *VersionReleaseRequest) Reset() { *m = VersionReleaseRequest{} } +func (m *VersionReleaseRequest) String() string { return proto.CompactTextString(m) } +func (*VersionReleaseRequest) ProtoMessage() {} +func (*VersionReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +type VersionReleaseResponse struct { + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + Version string `protobuf:"bytes,2,opt,name=version" json:"version,omitempty"` +} + +func (m *VersionReleaseResponse) Reset() { *m = VersionReleaseResponse{} } +func (m *VersionReleaseResponse) String() string { return proto.CompactTextString(m) } +func (*VersionReleaseResponse) ProtoMessage() {} +func (*VersionReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +func (m *VersionReleaseResponse) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *VersionReleaseResponse) GetVersion() string { + if m != nil { + return m.Version + } + return "" +} + +type InstallReleaseRequest struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` +} + +func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} } +func (m *InstallReleaseRequest) String() string { return proto.CompactTextString(m) } +func (*InstallReleaseRequest) ProtoMessage() {} +func (*InstallReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func (m *InstallReleaseRequest) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +type InstallReleaseResponse struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` + Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` +} + +func (m *InstallReleaseResponse) Reset() { *m = InstallReleaseResponse{} } +func (m *InstallReleaseResponse) String() string { return proto.CompactTextString(m) } +func (*InstallReleaseResponse) ProtoMessage() {} +func (*InstallReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } + +func (m *InstallReleaseResponse) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +func (m *InstallReleaseResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +type DeleteReleaseRequest struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` +} + +func (m *DeleteReleaseRequest) Reset() { *m = DeleteReleaseRequest{} } +func (m *DeleteReleaseRequest) String() string { return proto.CompactTextString(m) } +func (*DeleteReleaseRequest) ProtoMessage() {} +func (*DeleteReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } + +func (m *DeleteReleaseRequest) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +type DeleteReleaseResponse struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` + Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` +} + +func (m *DeleteReleaseResponse) Reset() { *m = DeleteReleaseResponse{} } +func (m *DeleteReleaseResponse) String() string { return proto.CompactTextString(m) } +func (*DeleteReleaseResponse) ProtoMessage() {} +func (*DeleteReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } + +func (m *DeleteReleaseResponse) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +func (m *DeleteReleaseResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +type UpgradeReleaseRequest struct { + Current *hapi_release5.Release `protobuf:"bytes,1,opt,name=current" json:"current,omitempty"` + Target *hapi_release5.Release `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"` + Timeout int64 `protobuf:"varint,3,opt,name=Timeout" json:"Timeout,omitempty"` + Wait bool `protobuf:"varint,4,opt,name=Wait" json:"Wait,omitempty"` + Recreate bool `protobuf:"varint,5,opt,name=Recreate" json:"Recreate,omitempty"` + Force bool `protobuf:"varint,6,opt,name=Force" json:"Force,omitempty"` +} + +func (m *UpgradeReleaseRequest) Reset() { *m = UpgradeReleaseRequest{} } +func (m *UpgradeReleaseRequest) String() string { return proto.CompactTextString(m) } +func (*UpgradeReleaseRequest) ProtoMessage() {} +func (*UpgradeReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{7} } + +func (m *UpgradeReleaseRequest) GetCurrent() *hapi_release5.Release { + if m != nil { + return m.Current + } + return nil +} + +func (m *UpgradeReleaseRequest) GetTarget() *hapi_release5.Release { + if m != nil { + return m.Target + } + return nil +} + +func (m *UpgradeReleaseRequest) GetTimeout() int64 { + if m != nil { + return m.Timeout + } + return 0 +} + +func (m *UpgradeReleaseRequest) GetWait() bool { + if m != nil { + return m.Wait + } + return false +} + +func (m *UpgradeReleaseRequest) GetRecreate() bool { + if m != nil { + return m.Recreate + } + return false +} + +func (m *UpgradeReleaseRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + +type UpgradeReleaseResponse struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` + Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` +} + +func (m *UpgradeReleaseResponse) Reset() { *m = UpgradeReleaseResponse{} } +func (m *UpgradeReleaseResponse) String() string { return proto.CompactTextString(m) } +func (*UpgradeReleaseResponse) ProtoMessage() {} +func (*UpgradeReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{8} } + +func (m *UpgradeReleaseResponse) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +func (m *UpgradeReleaseResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +type RollbackReleaseRequest struct { + Current *hapi_release5.Release `protobuf:"bytes,1,opt,name=current" json:"current,omitempty"` + Target *hapi_release5.Release `protobuf:"bytes,2,opt,name=target" json:"target,omitempty"` + Timeout int64 `protobuf:"varint,3,opt,name=Timeout" json:"Timeout,omitempty"` + Wait bool `protobuf:"varint,4,opt,name=Wait" json:"Wait,omitempty"` + Recreate bool `protobuf:"varint,5,opt,name=Recreate" json:"Recreate,omitempty"` + Force bool `protobuf:"varint,6,opt,name=Force" json:"Force,omitempty"` +} + +func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} } +func (m *RollbackReleaseRequest) String() string { return proto.CompactTextString(m) } +func (*RollbackReleaseRequest) ProtoMessage() {} +func (*RollbackReleaseRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{9} } + +func (m *RollbackReleaseRequest) GetCurrent() *hapi_release5.Release { + if m != nil { + return m.Current + } + return nil +} + +func (m *RollbackReleaseRequest) GetTarget() *hapi_release5.Release { + if m != nil { + return m.Target + } + return nil +} + +func (m *RollbackReleaseRequest) GetTimeout() int64 { + if m != nil { + return m.Timeout + } + return 0 +} + +func (m *RollbackReleaseRequest) GetWait() bool { + if m != nil { + return m.Wait + } + return false +} + +func (m *RollbackReleaseRequest) GetRecreate() bool { + if m != nil { + return m.Recreate + } + return false +} + +func (m *RollbackReleaseRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + +type RollbackReleaseResponse struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` + Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` +} + +func (m *RollbackReleaseResponse) Reset() { *m = RollbackReleaseResponse{} } +func (m *RollbackReleaseResponse) String() string { return proto.CompactTextString(m) } +func (*RollbackReleaseResponse) ProtoMessage() {} +func (*RollbackReleaseResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{10} } + +func (m *RollbackReleaseResponse) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +func (m *RollbackReleaseResponse) GetResult() *Result { + if m != nil { + return m.Result + } + return nil +} + +type ReleaseStatusRequest struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` +} + +func (m *ReleaseStatusRequest) Reset() { *m = ReleaseStatusRequest{} } +func (m *ReleaseStatusRequest) String() string { return proto.CompactTextString(m) } +func (*ReleaseStatusRequest) ProtoMessage() {} +func (*ReleaseStatusRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{11} } + +func (m *ReleaseStatusRequest) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +type ReleaseStatusResponse struct { + Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` + Info *hapi_release3.Info `protobuf:"bytes,2,opt,name=info" json:"info,omitempty"` +} + +func (m *ReleaseStatusResponse) Reset() { *m = ReleaseStatusResponse{} } +func (m *ReleaseStatusResponse) String() string { return proto.CompactTextString(m) } +func (*ReleaseStatusResponse) ProtoMessage() {} +func (*ReleaseStatusResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{12} } + +func (m *ReleaseStatusResponse) GetRelease() *hapi_release5.Release { + if m != nil { + return m.Release + } + return nil +} + +func (m *ReleaseStatusResponse) GetInfo() *hapi_release3.Info { + if m != nil { + return m.Info + } + return nil +} + +func init() { + proto.RegisterType((*Result)(nil), "hapi.services.rudder.Result") + proto.RegisterType((*VersionReleaseRequest)(nil), "hapi.services.rudder.VersionReleaseRequest") + proto.RegisterType((*VersionReleaseResponse)(nil), "hapi.services.rudder.VersionReleaseResponse") + proto.RegisterType((*InstallReleaseRequest)(nil), "hapi.services.rudder.InstallReleaseRequest") + proto.RegisterType((*InstallReleaseResponse)(nil), "hapi.services.rudder.InstallReleaseResponse") + proto.RegisterType((*DeleteReleaseRequest)(nil), "hapi.services.rudder.DeleteReleaseRequest") + proto.RegisterType((*DeleteReleaseResponse)(nil), "hapi.services.rudder.DeleteReleaseResponse") + proto.RegisterType((*UpgradeReleaseRequest)(nil), "hapi.services.rudder.UpgradeReleaseRequest") + proto.RegisterType((*UpgradeReleaseResponse)(nil), "hapi.services.rudder.UpgradeReleaseResponse") + proto.RegisterType((*RollbackReleaseRequest)(nil), "hapi.services.rudder.RollbackReleaseRequest") + proto.RegisterType((*RollbackReleaseResponse)(nil), "hapi.services.rudder.RollbackReleaseResponse") + proto.RegisterType((*ReleaseStatusRequest)(nil), "hapi.services.rudder.ReleaseStatusRequest") + proto.RegisterType((*ReleaseStatusResponse)(nil), "hapi.services.rudder.ReleaseStatusResponse") + proto.RegisterEnum("hapi.services.rudder.Result_Status", Result_Status_name, Result_Status_value) +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// Client API for ReleaseModuleService service + +type ReleaseModuleServiceClient interface { + Version(ctx context.Context, in *VersionReleaseRequest, opts ...grpc.CallOption) (*VersionReleaseResponse, error) + // InstallRelease requests installation of a chart as a new release. + InstallRelease(ctx context.Context, in *InstallReleaseRequest, opts ...grpc.CallOption) (*InstallReleaseResponse, error) + // DeleteRelease requests deletion of a named release. + DeleteRelease(ctx context.Context, in *DeleteReleaseRequest, opts ...grpc.CallOption) (*DeleteReleaseResponse, error) + // RollbackRelease rolls back a release to a previous version. + RollbackRelease(ctx context.Context, in *RollbackReleaseRequest, opts ...grpc.CallOption) (*RollbackReleaseResponse, error) + // UpgradeRelease updates release content. + UpgradeRelease(ctx context.Context, in *UpgradeReleaseRequest, opts ...grpc.CallOption) (*UpgradeReleaseResponse, error) + // ReleaseStatus retrieves release status. + ReleaseStatus(ctx context.Context, in *ReleaseStatusRequest, opts ...grpc.CallOption) (*ReleaseStatusResponse, error) +} + +type releaseModuleServiceClient struct { + cc *grpc.ClientConn +} + +func NewReleaseModuleServiceClient(cc *grpc.ClientConn) ReleaseModuleServiceClient { + return &releaseModuleServiceClient{cc} +} + +func (c *releaseModuleServiceClient) Version(ctx context.Context, in *VersionReleaseRequest, opts ...grpc.CallOption) (*VersionReleaseResponse, error) { + out := new(VersionReleaseResponse) + err := grpc.Invoke(ctx, "/hapi.services.rudder.ReleaseModuleService/Version", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *releaseModuleServiceClient) InstallRelease(ctx context.Context, in *InstallReleaseRequest, opts ...grpc.CallOption) (*InstallReleaseResponse, error) { + out := new(InstallReleaseResponse) + err := grpc.Invoke(ctx, "/hapi.services.rudder.ReleaseModuleService/InstallRelease", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *releaseModuleServiceClient) DeleteRelease(ctx context.Context, in *DeleteReleaseRequest, opts ...grpc.CallOption) (*DeleteReleaseResponse, error) { + out := new(DeleteReleaseResponse) + err := grpc.Invoke(ctx, "/hapi.services.rudder.ReleaseModuleService/DeleteRelease", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *releaseModuleServiceClient) RollbackRelease(ctx context.Context, in *RollbackReleaseRequest, opts ...grpc.CallOption) (*RollbackReleaseResponse, error) { + out := new(RollbackReleaseResponse) + err := grpc.Invoke(ctx, "/hapi.services.rudder.ReleaseModuleService/RollbackRelease", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *releaseModuleServiceClient) UpgradeRelease(ctx context.Context, in *UpgradeReleaseRequest, opts ...grpc.CallOption) (*UpgradeReleaseResponse, error) { + out := new(UpgradeReleaseResponse) + err := grpc.Invoke(ctx, "/hapi.services.rudder.ReleaseModuleService/UpgradeRelease", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *releaseModuleServiceClient) ReleaseStatus(ctx context.Context, in *ReleaseStatusRequest, opts ...grpc.CallOption) (*ReleaseStatusResponse, error) { + out := new(ReleaseStatusResponse) + err := grpc.Invoke(ctx, "/hapi.services.rudder.ReleaseModuleService/ReleaseStatus", in, out, c.cc, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// Server API for ReleaseModuleService service + +type ReleaseModuleServiceServer interface { + Version(context.Context, *VersionReleaseRequest) (*VersionReleaseResponse, error) + // InstallRelease requests installation of a chart as a new release. + InstallRelease(context.Context, *InstallReleaseRequest) (*InstallReleaseResponse, error) + // DeleteRelease requests deletion of a named release. + DeleteRelease(context.Context, *DeleteReleaseRequest) (*DeleteReleaseResponse, error) + // RollbackRelease rolls back a release to a previous version. + RollbackRelease(context.Context, *RollbackReleaseRequest) (*RollbackReleaseResponse, error) + // UpgradeRelease updates release content. + UpgradeRelease(context.Context, *UpgradeReleaseRequest) (*UpgradeReleaseResponse, error) + // ReleaseStatus retrieves release status. + ReleaseStatus(context.Context, *ReleaseStatusRequest) (*ReleaseStatusResponse, error) +} + +func RegisterReleaseModuleServiceServer(s *grpc.Server, srv ReleaseModuleServiceServer) { + s.RegisterService(&_ReleaseModuleService_serviceDesc, srv) +} + +func _ReleaseModuleService_Version_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(VersionReleaseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReleaseModuleServiceServer).Version(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hapi.services.rudder.ReleaseModuleService/Version", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReleaseModuleServiceServer).Version(ctx, req.(*VersionReleaseRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReleaseModuleService_InstallRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InstallReleaseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReleaseModuleServiceServer).InstallRelease(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hapi.services.rudder.ReleaseModuleService/InstallRelease", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReleaseModuleServiceServer).InstallRelease(ctx, req.(*InstallReleaseRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReleaseModuleService_DeleteRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteReleaseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReleaseModuleServiceServer).DeleteRelease(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hapi.services.rudder.ReleaseModuleService/DeleteRelease", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReleaseModuleServiceServer).DeleteRelease(ctx, req.(*DeleteReleaseRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReleaseModuleService_RollbackRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RollbackReleaseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReleaseModuleServiceServer).RollbackRelease(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hapi.services.rudder.ReleaseModuleService/RollbackRelease", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReleaseModuleServiceServer).RollbackRelease(ctx, req.(*RollbackReleaseRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReleaseModuleService_UpgradeRelease_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpgradeReleaseRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReleaseModuleServiceServer).UpgradeRelease(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hapi.services.rudder.ReleaseModuleService/UpgradeRelease", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReleaseModuleServiceServer).UpgradeRelease(ctx, req.(*UpgradeReleaseRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _ReleaseModuleService_ReleaseStatus_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ReleaseStatusRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ReleaseModuleServiceServer).ReleaseStatus(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/hapi.services.rudder.ReleaseModuleService/ReleaseStatus", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ReleaseModuleServiceServer).ReleaseStatus(ctx, req.(*ReleaseStatusRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _ReleaseModuleService_serviceDesc = grpc.ServiceDesc{ + ServiceName: "hapi.services.rudder.ReleaseModuleService", + HandlerType: (*ReleaseModuleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Version", + Handler: _ReleaseModuleService_Version_Handler, + }, + { + MethodName: "InstallRelease", + Handler: _ReleaseModuleService_InstallRelease_Handler, + }, + { + MethodName: "DeleteRelease", + Handler: _ReleaseModuleService_DeleteRelease_Handler, + }, + { + MethodName: "RollbackRelease", + Handler: _ReleaseModuleService_RollbackRelease_Handler, + }, + { + MethodName: "UpgradeRelease", + Handler: _ReleaseModuleService_UpgradeRelease_Handler, + }, + { + MethodName: "ReleaseStatus", + Handler: _ReleaseModuleService_ReleaseStatus_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "hapi/rudder/rudder.proto", +} + +func init() { proto.RegisterFile("hapi/rudder/rudder.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 597 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x56, 0x5f, 0x8f, 0xd2, 0x4e, + 0x14, 0xa5, 0xb0, 0x14, 0xb8, 0x64, 0x7f, 0x3f, 0x32, 0xa1, 0xd0, 0x34, 0x3e, 0x90, 0x3e, 0x18, + 0xe2, 0xba, 0x25, 0x41, 0x1f, 0x7d, 0x51, 0x96, 0xfd, 0x13, 0x23, 0x9b, 0x0c, 0xe2, 0x26, 0xbe, + 0x75, 0xe1, 0x82, 0xd5, 0xd2, 0xd6, 0xe9, 0x74, 0x1f, 0xd5, 0x4f, 0xe3, 0x57, 0xd2, 0x8f, 0x63, + 0xda, 0x69, 0x89, 0xad, 0xd3, 0x88, 0x6b, 0xc2, 0x83, 0x4f, 0x9d, 0xe9, 0x3d, 0xdc, 0x39, 0xe7, + 0xf4, 0xce, 0x09, 0xa0, 0xbf, 0xb3, 0x03, 0x67, 0xc4, 0xa2, 0xd5, 0x0a, 0x59, 0xfa, 0xb0, 0x02, + 0xe6, 0x73, 0x9f, 0x74, 0xe3, 0x8a, 0x15, 0x22, 0xbb, 0x73, 0x96, 0x18, 0x5a, 0xa2, 0x66, 0xf4, + 0x05, 0x1e, 0x5d, 0xb4, 0x43, 0x1c, 0x39, 0xde, 0xda, 0x17, 0x70, 0xc3, 0xc8, 0x15, 0xd2, 0xa7, + 0xa8, 0x99, 0x2e, 0xa8, 0x14, 0xc3, 0xc8, 0xe5, 0x84, 0xc0, 0x51, 0xfc, 0x1b, 0x5d, 0x19, 0x28, + 0xc3, 0x16, 0x4d, 0xd6, 0xa4, 0x03, 0x35, 0xd7, 0xdf, 0xe8, 0xd5, 0x41, 0x6d, 0xd8, 0xa2, 0xf1, + 0xd2, 0x7c, 0x06, 0xea, 0x9c, 0xdb, 0x3c, 0x0a, 0x49, 0x1b, 0x1a, 0x8b, 0xd9, 0xcb, 0xd9, 0xf5, + 0xcd, 0xac, 0x53, 0x89, 0x37, 0xf3, 0xc5, 0x64, 0x32, 0x9d, 0xcf, 0x3b, 0x0a, 0x39, 0x86, 0xd6, + 0x62, 0x36, 0xb9, 0x7c, 0x3e, 0xbb, 0x98, 0x9e, 0x75, 0xaa, 0xa4, 0x05, 0xf5, 0x29, 0xa5, 0xd7, + 0xb4, 0x53, 0x33, 0xfb, 0xa0, 0xbd, 0x41, 0x16, 0x3a, 0xbe, 0x47, 0x05, 0x0b, 0x8a, 0x1f, 0x23, + 0x0c, 0xb9, 0x79, 0x0e, 0xbd, 0x62, 0x21, 0x0c, 0x7c, 0x2f, 0xc4, 0x98, 0x96, 0x67, 0x6f, 0x31, + 0xa3, 0x15, 0xaf, 0x89, 0x0e, 0x8d, 0x3b, 0x81, 0xd6, 0xab, 0xc9, 0xeb, 0x6c, 0x6b, 0x5e, 0x82, + 0x76, 0xe5, 0x85, 0xdc, 0x76, 0xdd, 0xfc, 0x01, 0x64, 0x04, 0x8d, 0x54, 0x78, 0xd2, 0xa9, 0x3d, + 0xd6, 0xac, 0xc4, 0xc4, 0xcc, 0x8d, 0x0c, 0x9e, 0xa1, 0xcc, 0xcf, 0xd0, 0x2b, 0x76, 0x4a, 0x19, + 0xfd, 0x69, 0x2b, 0xf2, 0x14, 0x54, 0x96, 0x78, 0x9c, 0xb0, 0x6d, 0x8f, 0x1f, 0x58, 0xb2, 0xef, + 0x67, 0x89, 0xef, 0x40, 0x53, 0xac, 0x79, 0x01, 0xdd, 0x33, 0x74, 0x91, 0xe3, 0xdf, 0x2a, 0xf9, + 0x04, 0x5a, 0xa1, 0xd1, 0x61, 0x85, 0x7c, 0x53, 0x40, 0x5b, 0x04, 0x1b, 0x66, 0xaf, 0x24, 0x52, + 0x96, 0x11, 0x63, 0xe8, 0xf1, 0xdf, 0x10, 0x48, 0x51, 0xe4, 0x14, 0x54, 0x6e, 0xb3, 0x0d, 0x66, + 0x04, 0x4a, 0xf0, 0x29, 0x28, 0x9e, 0x93, 0xd7, 0xce, 0x16, 0xfd, 0x88, 0xeb, 0xb5, 0x81, 0x32, + 0xac, 0xd1, 0x6c, 0x1b, 0x4f, 0xd5, 0x8d, 0xed, 0x70, 0xfd, 0x68, 0xa0, 0x0c, 0x9b, 0x34, 0x59, + 0x13, 0x03, 0x9a, 0x14, 0x97, 0x0c, 0x6d, 0x8e, 0x7a, 0x3d, 0x79, 0xbf, 0xdb, 0x93, 0x2e, 0xd4, + 0xcf, 0x7d, 0xb6, 0x44, 0x5d, 0x4d, 0x0a, 0x62, 0x13, 0xcf, 0x48, 0x51, 0xd8, 0x61, 0xad, 0xfd, + 0xae, 0x40, 0x8f, 0xfa, 0xae, 0x7b, 0x6b, 0x2f, 0x3f, 0xfc, 0x63, 0xde, 0x7e, 0x51, 0xa0, 0xff, + 0x8b, 0xb4, 0x83, 0xdf, 0xc0, 0xb4, 0x93, 0x88, 0xbc, 0x7b, 0xdf, 0xc0, 0x00, 0xb4, 0x42, 0xa3, + 0xfb, 0x0a, 0x79, 0x98, 0x86, 0xb4, 0x90, 0x41, 0xf2, 0xe8, 0x2b, 0x6f, 0xed, 0x8b, 0xe0, 0x1e, + 0x7f, 0xad, 0xef, 0xb8, 0xbf, 0xf2, 0x57, 0x91, 0x8b, 0x73, 0x21, 0x95, 0xac, 0xa1, 0x91, 0x06, + 0x2d, 0x39, 0x91, 0x9b, 0x20, 0x0d, 0x68, 0xe3, 0xf1, 0x7e, 0x60, 0xa1, 0xcb, 0xac, 0x90, 0x2d, + 0xfc, 0x97, 0x8f, 0xcf, 0xb2, 0xe3, 0xa4, 0x71, 0x5d, 0x76, 0x9c, 0x3c, 0x91, 0xcd, 0x0a, 0x79, + 0x0f, 0xc7, 0xb9, 0x8c, 0x23, 0x8f, 0xe4, 0x0d, 0x64, 0x89, 0x6a, 0x9c, 0xec, 0x85, 0xdd, 0x9d, + 0x15, 0xc0, 0xff, 0x85, 0xc1, 0x24, 0x25, 0x74, 0xe5, 0x57, 0xd3, 0x38, 0xdd, 0x13, 0xfd, 0xb3, + 0x99, 0xf9, 0x9c, 0x29, 0x33, 0x53, 0x1a, 0xb3, 0x65, 0x66, 0xca, 0xa3, 0x4b, 0x98, 0x99, 0x1b, + 0xd7, 0x32, 0x33, 0x65, 0x97, 0xa3, 0xcc, 0x4c, 0xe9, 0xfc, 0x9b, 0x95, 0x17, 0xcd, 0xb7, 0xaa, + 0x40, 0xdc, 0xaa, 0xc9, 0x1f, 0x92, 0x27, 0x3f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xb6, 0xa5, 0x37, + 0x75, 0xf7, 0x08, 0x00, 0x00, +} diff --git a/pkg/proto/hapi/services/tiller.pb.go b/pkg/proto/hapi/services/tiller.pb.go index d3f8676fc..d629320c5 100644 --- a/pkg/proto/hapi/services/tiller.pb.go +++ b/pkg/proto/hapi/services/tiller.pb.go @@ -40,6 +40,7 @@ import hapi_chart3 "k8s.io/helm/pkg/proto/hapi/chart" import hapi_chart "k8s.io/helm/pkg/proto/hapi/chart" import hapi_release5 "k8s.io/helm/pkg/proto/hapi/release" import hapi_release4 "k8s.io/helm/pkg/proto/hapi/release" +import hapi_release1 "k8s.io/helm/pkg/proto/hapi/release" import hapi_release3 "k8s.io/helm/pkg/proto/hapi/release" import hapi_version "k8s.io/helm/pkg/proto/hapi/version" @@ -374,6 +375,8 @@ type UpdateReleaseRequest struct { // ReuseValues will cause Tiller to reuse the values from the last release. // This is ignored if reset_values is set. ReuseValues bool `protobuf:"varint,10,opt,name=reuse_values,json=reuseValues" json:"reuse_values,omitempty"` + // Force resource update through delete/recreate if needed. + Force bool `protobuf:"varint,11,opt,name=force" json:"force,omitempty"` } func (m *UpdateReleaseRequest) Reset() { *m = UpdateReleaseRequest{} } @@ -451,6 +454,13 @@ func (m *UpdateReleaseRequest) GetReuseValues() bool { return false } +func (m *UpdateReleaseRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + // UpdateReleaseResponse is the response to an update request. type UpdateReleaseResponse struct { Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` @@ -484,6 +494,8 @@ type RollbackReleaseRequest struct { // wait, if true, will wait until all Pods, PVCs, and Services are in a ready state // before marking the release as successful. It will wait for as long as timeout Wait bool `protobuf:"varint,7,opt,name=wait" json:"wait,omitempty"` + // Force resource update through delete/recreate if needed. + Force bool `protobuf:"varint,8,opt,name=force" json:"force,omitempty"` } func (m *RollbackReleaseRequest) Reset() { *m = RollbackReleaseRequest{} } @@ -540,6 +552,13 @@ func (m *RollbackReleaseRequest) GetWait() bool { return false } +func (m *RollbackReleaseRequest) GetForce() bool { + if m != nil { + return m.Force + } + return false +} + // RollbackReleaseResponse is the response to an update request. type RollbackReleaseResponse struct { Release *hapi_release5.Release `protobuf:"bytes,1,opt,name=release" json:"release,omitempty"` @@ -848,7 +867,8 @@ func (m *TestReleaseRequest) GetCleanup() bool { // TestReleaseResponse represents a message from executing a test type TestReleaseResponse struct { - Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"` + Msg string `protobuf:"bytes,1,opt,name=msg" json:"msg,omitempty"` + Status hapi_release1.TestRun_Status `protobuf:"varint,2,opt,name=status,enum=hapi.release.TestRun_Status" json:"status,omitempty"` } func (m *TestReleaseResponse) Reset() { *m = TestReleaseResponse{} } @@ -863,6 +883,13 @@ func (m *TestReleaseResponse) GetMsg() string { return "" } +func (m *TestReleaseResponse) GetStatus() hapi_release1.TestRun_Status { + if m != nil { + return m.Status + } + return hapi_release1.TestRun_UNKNOWN +} + func init() { proto.RegisterType((*ListReleasesRequest)(nil), "hapi.services.tiller.ListReleasesRequest") proto.RegisterType((*ListSort)(nil), "hapi.services.tiller.ListSort") @@ -1342,79 +1369,82 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{ func init() { proto.RegisterFile("hapi/services/tiller.proto", fileDescriptor0) } var fileDescriptor0 = []byte{ - // 1170 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdb, 0x6e, 0xe3, 0x44, - 0x18, 0xae, 0xe3, 0x1c, 0xff, 0x1e, 0x48, 0xa7, 0x27, 0xd7, 0x02, 0x54, 0x8c, 0xa0, 0xd9, 0x85, - 0x4d, 0x21, 0x5c, 0x21, 0x21, 0xa4, 0x6e, 0x37, 0x6a, 0x0b, 0xa5, 0x2b, 0x39, 0xdb, 0x45, 0x42, - 0x88, 0xc8, 0x4d, 0x26, 0xad, 0x59, 0xc7, 0x13, 0x3c, 0xe3, 0xb2, 0xbd, 0xe5, 0x8e, 0x47, 0xe1, - 0x2d, 0x78, 0x01, 0x78, 0x01, 0x5e, 0x06, 0x79, 0x0e, 0x6e, 0xc6, 0xb5, 0x5b, 0x6f, 0x6e, 0x62, - 0xcf, 0xfc, 0xe7, 0xef, 0xff, 0xfd, 0xcd, 0x04, 0xec, 0x6b, 0x6f, 0xe6, 0x1f, 0x50, 0x1c, 0xdd, - 0xf8, 0x23, 0x4c, 0x0f, 0x98, 0x1f, 0x04, 0x38, 0xea, 0xce, 0x22, 0xc2, 0x08, 0xda, 0x4c, 0x64, - 0x5d, 0x25, 0xeb, 0x0a, 0x99, 0xbd, 0xcd, 0x2d, 0x46, 0xd7, 0x5e, 0xc4, 0xc4, 0xaf, 0xd0, 0xb6, - 0x77, 0xe6, 0xf7, 0x49, 0x38, 0xf1, 0xaf, 0xa4, 0x40, 0x84, 0x88, 0x70, 0x80, 0x3d, 0x8a, 0xd5, - 0x53, 0x33, 0x52, 0x32, 0x3f, 0x9c, 0x10, 0x29, 0xd8, 0xd5, 0x04, 0x94, 0x79, 0x2c, 0xa6, 0x9a, - 0xbf, 0x1b, 0x1c, 0x51, 0x9f, 0x84, 0xea, 0x29, 0x64, 0xce, 0xdf, 0x15, 0xd8, 0x38, 0xf3, 0x29, - 0x73, 0x85, 0x21, 0x75, 0xf1, 0x6f, 0x31, 0xa6, 0x0c, 0x6d, 0x42, 0x2d, 0xf0, 0xa7, 0x3e, 0xb3, - 0x8c, 0x3d, 0xa3, 0x63, 0xba, 0x62, 0x81, 0xb6, 0xa1, 0x4e, 0x26, 0x13, 0x8a, 0x99, 0x55, 0xd9, - 0x33, 0x3a, 0x2d, 0x57, 0xae, 0xd0, 0xb7, 0xd0, 0xa0, 0x24, 0x62, 0xc3, 0xcb, 0x5b, 0xcb, 0xdc, - 0x33, 0x3a, 0x6b, 0xbd, 0x4f, 0xba, 0x79, 0x50, 0x74, 0x93, 0x48, 0x03, 0x12, 0xb1, 0x6e, 0xf2, - 0xf3, 0xfc, 0xd6, 0xad, 0x53, 0xfe, 0x4c, 0xfc, 0x4e, 0xfc, 0x80, 0xe1, 0xc8, 0xaa, 0x0a, 0xbf, - 0x62, 0x85, 0x8e, 0x01, 0xb8, 0x5f, 0x12, 0x8d, 0x71, 0x64, 0xd5, 0xb8, 0xeb, 0x4e, 0x09, 0xd7, - 0x2f, 0x13, 0x7d, 0xb7, 0x45, 0xd5, 0x2b, 0xfa, 0x06, 0x56, 0x04, 0x24, 0xc3, 0x11, 0x19, 0x63, - 0x6a, 0xd5, 0xf7, 0xcc, 0xce, 0x5a, 0x6f, 0x57, 0xb8, 0x52, 0x08, 0x0f, 0x04, 0x68, 0x47, 0x64, - 0x8c, 0xdd, 0x65, 0xa1, 0x9e, 0xbc, 0x53, 0xf4, 0x3e, 0xb4, 0x42, 0x6f, 0x8a, 0xe9, 0xcc, 0x1b, - 0x61, 0xab, 0xc1, 0x33, 0xbc, 0xdb, 0x70, 0x7e, 0x81, 0xa6, 0x0a, 0xee, 0xf4, 0xa0, 0x2e, 0x4a, - 0x43, 0xcb, 0xd0, 0xb8, 0x38, 0xff, 0xfe, 0xfc, 0xe5, 0x8f, 0xe7, 0xed, 0x25, 0xd4, 0x84, 0xea, - 0xf9, 0xe1, 0x0f, 0xfd, 0xb6, 0x81, 0xd6, 0x61, 0xf5, 0xec, 0x70, 0xf0, 0x6a, 0xe8, 0xf6, 0xcf, - 0xfa, 0x87, 0x83, 0xfe, 0x8b, 0x76, 0xc5, 0xf9, 0x10, 0x5a, 0x69, 0xce, 0xa8, 0x01, 0xe6, 0xe1, - 0xe0, 0x48, 0x98, 0xbc, 0xe8, 0x0f, 0x8e, 0xda, 0x86, 0xf3, 0xa7, 0x01, 0x9b, 0x7a, 0x8b, 0xe8, - 0x8c, 0x84, 0x14, 0x27, 0x3d, 0x1a, 0x91, 0x38, 0x4c, 0x7b, 0xc4, 0x17, 0x08, 0x41, 0x35, 0xc4, - 0x6f, 0x55, 0x87, 0xf8, 0x7b, 0xa2, 0xc9, 0x08, 0xf3, 0x02, 0xde, 0x1d, 0xd3, 0x15, 0x0b, 0xf4, - 0x25, 0x34, 0x65, 0xe9, 0xd4, 0xaa, 0xee, 0x99, 0x9d, 0xe5, 0xde, 0x96, 0x0e, 0x88, 0x8c, 0xe8, - 0xa6, 0x6a, 0xce, 0x31, 0xec, 0x1c, 0x63, 0x95, 0x89, 0xc0, 0x4b, 0x4d, 0x4c, 0x12, 0xd7, 0x9b, - 0x62, 0x9e, 0x4c, 0x12, 0xd7, 0x9b, 0x62, 0x64, 0x41, 0x43, 0x8e, 0x1b, 0x4f, 0xa7, 0xe6, 0xaa, - 0xa5, 0xc3, 0xc0, 0xba, 0xef, 0x48, 0xd6, 0x95, 0xe7, 0xe9, 0x53, 0xa8, 0x26, 0xc3, 0xce, 0xdd, - 0x2c, 0xf7, 0x90, 0x9e, 0xe7, 0x69, 0x38, 0x21, 0x2e, 0x97, 0xeb, 0xad, 0x32, 0xb3, 0xad, 0x3a, - 0x99, 0x8f, 0x7a, 0x44, 0x42, 0x86, 0x43, 0xb6, 0x58, 0xfe, 0x67, 0xb0, 0x9b, 0xe3, 0x49, 0x16, - 0x70, 0x00, 0x0d, 0x99, 0x1a, 0xf7, 0x56, 0x88, 0xab, 0xd2, 0x72, 0xfe, 0xa9, 0xc0, 0xe6, 0xc5, - 0x6c, 0xec, 0x31, 0xac, 0x44, 0x0f, 0x24, 0xb5, 0x0f, 0x35, 0x4e, 0x1a, 0x12, 0x8b, 0x75, 0xe1, - 0x5b, 0x30, 0xcb, 0x51, 0xf2, 0xeb, 0x0a, 0x39, 0x7a, 0x0a, 0xf5, 0x1b, 0x2f, 0x88, 0x31, 0xe5, - 0x40, 0xa4, 0xa8, 0x49, 0x4d, 0xce, 0x38, 0xae, 0xd4, 0x40, 0x3b, 0xd0, 0x18, 0x47, 0xb7, 0xc3, - 0x28, 0x0e, 0xf9, 0x27, 0xd8, 0x74, 0xeb, 0xe3, 0xe8, 0xd6, 0x8d, 0x43, 0xf4, 0x31, 0xac, 0x8e, - 0x7d, 0xea, 0x5d, 0x06, 0x78, 0x78, 0x4d, 0xc8, 0x1b, 0xca, 0xbf, 0xc2, 0xa6, 0xbb, 0x22, 0x37, - 0x4f, 0x92, 0x3d, 0x64, 0x27, 0x93, 0x34, 0x8a, 0xb0, 0xc7, 0xb0, 0x55, 0xe7, 0xf2, 0x74, 0x9d, - 0x60, 0xc8, 0xfc, 0x29, 0x26, 0x31, 0xe3, 0x9f, 0x8e, 0xe9, 0xaa, 0x25, 0xfa, 0x08, 0x56, 0x22, - 0x4c, 0x31, 0x1b, 0xca, 0x2c, 0x9b, 0xdc, 0x72, 0x99, 0xef, 0xbd, 0x16, 0x69, 0x21, 0xa8, 0xfe, - 0xee, 0xf9, 0xcc, 0x6a, 0x71, 0x11, 0x7f, 0x17, 0x66, 0x31, 0xc5, 0xca, 0x0c, 0x94, 0x59, 0x4c, - 0xb1, 0x30, 0x73, 0x4e, 0x60, 0x2b, 0x03, 0xe7, 0xa2, 0x9d, 0xf9, 0xd7, 0x80, 0x6d, 0x97, 0x04, - 0xc1, 0xa5, 0x37, 0x7a, 0x53, 0xa2, 0x37, 0x73, 0x30, 0x56, 0x1e, 0x86, 0xd1, 0xcc, 0x81, 0x71, - 0x6e, 0xdc, 0xaa, 0xda, 0xb8, 0x69, 0x00, 0xd7, 0x8a, 0x01, 0xae, 0xeb, 0x00, 0x2b, 0xf4, 0x1a, - 0x77, 0xe8, 0x39, 0xdf, 0xc1, 0xce, 0xbd, 0x7a, 0x16, 0x05, 0xe7, 0xaf, 0x0a, 0x6c, 0x9d, 0x86, - 0x94, 0x79, 0x41, 0x90, 0xc1, 0x26, 0x9d, 0x51, 0xa3, 0xf4, 0x8c, 0x56, 0xde, 0x65, 0x46, 0x4d, - 0x0d, 0x5c, 0xd5, 0x89, 0xea, 0x5c, 0x27, 0x4a, 0xcd, 0xad, 0xc6, 0x16, 0xf5, 0x0c, 0x5b, 0xa0, - 0x0f, 0x00, 0xc4, 0xa0, 0x71, 0xe7, 0x02, 0xc4, 0x16, 0xdf, 0x39, 0x97, 0xe4, 0xa0, 0x70, 0x6f, - 0xe6, 0xe3, 0x3e, 0x37, 0xb5, 0xce, 0x29, 0x6c, 0x67, 0xa1, 0x5a, 0x14, 0xf6, 0x3f, 0x0c, 0xd8, - 0xb9, 0x08, 0xfd, 0x5c, 0xe0, 0xf3, 0x86, 0xf2, 0x1e, 0x14, 0x95, 0x1c, 0x28, 0x36, 0xa1, 0x36, - 0x8b, 0xa3, 0x2b, 0x2c, 0xa1, 0x15, 0x8b, 0xf9, 0x1a, 0xab, 0x5a, 0x8d, 0xce, 0x10, 0xac, 0xfb, - 0x39, 0x2c, 0x58, 0x51, 0x92, 0x75, 0xca, 0xee, 0x2d, 0xc1, 0xe4, 0xce, 0x06, 0xac, 0x1f, 0x63, - 0xf6, 0x5a, 0x7c, 0x00, 0xb2, 0x3c, 0xa7, 0x0f, 0x68, 0x7e, 0xf3, 0x2e, 0x9e, 0xdc, 0xd2, 0xe3, - 0xa9, 0xab, 0x8e, 0xd2, 0x57, 0x5a, 0xce, 0xd7, 0xdc, 0xf7, 0x89, 0x4f, 0x19, 0x89, 0x6e, 0x1f, - 0x82, 0xae, 0x0d, 0xe6, 0xd4, 0x7b, 0x2b, 0xc9, 0x3f, 0x79, 0x75, 0x8e, 0x79, 0x06, 0xa9, 0xa9, - 0xcc, 0x60, 0xfe, 0x28, 0x35, 0xca, 0x1d, 0xa5, 0x3f, 0x03, 0x7a, 0x85, 0xd3, 0x53, 0xfd, 0x91, - 0x53, 0x48, 0x35, 0xa1, 0xa2, 0x0f, 0x9a, 0x05, 0x8d, 0x51, 0x80, 0xbd, 0x30, 0x9e, 0xc9, 0xb6, - 0xa9, 0xa5, 0xb3, 0x0f, 0x1b, 0x9a, 0x77, 0x99, 0x67, 0x52, 0x0f, 0xbd, 0x92, 0xde, 0x93, 0xd7, - 0xde, 0x7f, 0x4d, 0x58, 0x53, 0xc7, 0xb0, 0xb8, 0x52, 0x21, 0x1f, 0x56, 0xe6, 0xef, 0x1b, 0xe8, - 0x49, 0xf1, 0x8d, 0x2b, 0x73, 0x6d, 0xb4, 0x9f, 0x96, 0x51, 0x15, 0xb9, 0x38, 0x4b, 0x5f, 0x18, - 0x88, 0x42, 0x3b, 0x7b, 0x0d, 0x40, 0xcf, 0xf2, 0x7d, 0x14, 0xdc, 0x3b, 0xec, 0x6e, 0x59, 0x75, - 0x15, 0x16, 0xdd, 0xf0, 0xee, 0xeb, 0x67, 0x37, 0x7a, 0xd4, 0x8d, 0x7e, 0x5d, 0xb0, 0x0f, 0x4a, - 0xeb, 0xa7, 0x71, 0x7f, 0x85, 0x55, 0xed, 0x54, 0x42, 0x05, 0x68, 0xe5, 0xdd, 0x04, 0xec, 0xcf, - 0x4a, 0xe9, 0xa6, 0xb1, 0xa6, 0xb0, 0xa6, 0xd3, 0x0d, 0x2a, 0x70, 0x90, 0xcb, 0xdf, 0xf6, 0xe7, - 0xe5, 0x94, 0xd3, 0x70, 0x14, 0xda, 0x59, 0x36, 0x28, 0xea, 0x63, 0x01, 0x73, 0x15, 0xf5, 0xb1, - 0x88, 0x64, 0x9c, 0x25, 0xe4, 0x01, 0xdc, 0x91, 0x01, 0xda, 0x2f, 0x6c, 0x88, 0xce, 0x21, 0x76, - 0xe7, 0x71, 0xc5, 0x34, 0xc4, 0x0c, 0xde, 0xcb, 0x9c, 0x96, 0xa8, 0x00, 0x9a, 0xfc, 0x4b, 0x82, - 0xfd, 0xac, 0xa4, 0x76, 0xa6, 0x28, 0xc9, 0x2f, 0x0f, 0x14, 0xa5, 0x93, 0xd7, 0x03, 0x45, 0x65, - 0xa8, 0xca, 0x59, 0x42, 0x3e, 0xac, 0xb9, 0x71, 0x28, 0x43, 0x27, 0x2c, 0x81, 0x0a, 0xac, 0xef, - 0xf3, 0x93, 0xfd, 0xa4, 0x84, 0xe6, 0xdd, 0xf7, 0xfd, 0x1c, 0x7e, 0x6a, 0x2a, 0xd5, 0xcb, 0x3a, - 0xff, 0xc7, 0xf9, 0xd5, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x30, 0x80, 0xed, 0x18, 0x42, 0x0f, - 0x00, 0x00, + // 1217 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xdd, 0x6e, 0xe3, 0xc4, + 0x17, 0xaf, 0xf3, 0x9d, 0x93, 0x36, 0xff, 0x74, 0x9a, 0xb6, 0xae, 0xff, 0x0b, 0x2a, 0x46, 0xb0, + 0xd9, 0x85, 0x4d, 0x21, 0x70, 0x83, 0x84, 0x90, 0xba, 0xdd, 0xa8, 0x2d, 0x94, 0xae, 0xe4, 0x6c, + 0x17, 0x09, 0x01, 0x91, 0x9b, 0x4c, 0x5a, 0xb3, 0x8e, 0x27, 0x78, 0xc6, 0x65, 0x7b, 0xcb, 0x1d, + 0x8f, 0xc2, 0x5b, 0xf0, 0x1e, 0x5c, 0xc2, 0x83, 0x20, 0xcf, 0x87, 0xeb, 0x49, 0xed, 0xd6, 0xf4, + 0x26, 0x9e, 0x99, 0xf3, 0xfd, 0x3b, 0x67, 0xce, 0x9c, 0x80, 0x75, 0xe9, 0x2e, 0xbc, 0x3d, 0x8a, + 0xc3, 0x2b, 0x6f, 0x82, 0xe9, 0x1e, 0xf3, 0x7c, 0x1f, 0x87, 0xfd, 0x45, 0x48, 0x18, 0x41, 0xdd, + 0x98, 0xd6, 0x57, 0xb4, 0xbe, 0xa0, 0x59, 0x5b, 0x5c, 0x62, 0x72, 0xe9, 0x86, 0x4c, 0xfc, 0x0a, + 0x6e, 0x6b, 0x3b, 0x7d, 0x4e, 0x82, 0x99, 0x77, 0x21, 0x09, 0xc2, 0x44, 0x88, 0x7d, 0xec, 0x52, + 0xac, 0xbe, 0x9a, 0x90, 0xa2, 0x79, 0xc1, 0x8c, 0x48, 0xc2, 0xff, 0x35, 0x02, 0xc3, 0x94, 0x8d, + 0xc3, 0x28, 0x90, 0xc4, 0x1d, 0x8d, 0x48, 0x99, 0xcb, 0x22, 0xaa, 0x19, 0xbb, 0xc2, 0x21, 0xf5, + 0x48, 0xa0, 0xbe, 0x82, 0x66, 0xff, 0x59, 0x82, 0x8d, 0x13, 0x8f, 0x32, 0x47, 0x08, 0x52, 0x07, + 0xff, 0x12, 0x61, 0xca, 0x50, 0x17, 0xaa, 0xbe, 0x37, 0xf7, 0x98, 0x69, 0xec, 0x1a, 0xbd, 0xb2, + 0x23, 0x36, 0x68, 0x0b, 0x6a, 0x64, 0x36, 0xa3, 0x98, 0x99, 0xa5, 0x5d, 0xa3, 0xd7, 0x74, 0xe4, + 0x0e, 0x7d, 0x05, 0x75, 0x4a, 0x42, 0x36, 0x3e, 0xbf, 0x36, 0xcb, 0xbb, 0x46, 0xaf, 0x3d, 0xf8, + 0xa0, 0x9f, 0x85, 0x53, 0x3f, 0xb6, 0x34, 0x22, 0x21, 0xeb, 0xc7, 0x3f, 0xcf, 0xaf, 0x9d, 0x1a, + 0xe5, 0xdf, 0x58, 0xef, 0xcc, 0xf3, 0x19, 0x0e, 0xcd, 0x8a, 0xd0, 0x2b, 0x76, 0xe8, 0x10, 0x80, + 0xeb, 0x25, 0xe1, 0x14, 0x87, 0x66, 0x95, 0xab, 0xee, 0x15, 0x50, 0xfd, 0x32, 0xe6, 0x77, 0x9a, + 0x54, 0x2d, 0xd1, 0x97, 0xb0, 0x2a, 0x20, 0x19, 0x4f, 0xc8, 0x14, 0x53, 0xb3, 0xb6, 0x5b, 0xee, + 0xb5, 0x07, 0x3b, 0x42, 0x95, 0x82, 0x7f, 0x24, 0x40, 0x3b, 0x20, 0x53, 0xec, 0xb4, 0x04, 0x7b, + 0xbc, 0xa6, 0xe8, 0x11, 0x34, 0x03, 0x77, 0x8e, 0xe9, 0xc2, 0x9d, 0x60, 0xb3, 0xce, 0x3d, 0xbc, + 0x39, 0xb0, 0x7f, 0x82, 0x86, 0x32, 0x6e, 0x0f, 0xa0, 0x26, 0x42, 0x43, 0x2d, 0xa8, 0x9f, 0x9d, + 0x7e, 0x73, 0xfa, 0xf2, 0xbb, 0xd3, 0xce, 0x0a, 0x6a, 0x40, 0xe5, 0x74, 0xff, 0xdb, 0x61, 0xc7, + 0x40, 0xeb, 0xb0, 0x76, 0xb2, 0x3f, 0x7a, 0x35, 0x76, 0x86, 0x27, 0xc3, 0xfd, 0xd1, 0xf0, 0x45, + 0xa7, 0x64, 0xbf, 0x0b, 0xcd, 0xc4, 0x67, 0x54, 0x87, 0xf2, 0xfe, 0xe8, 0x40, 0x88, 0xbc, 0x18, + 0x8e, 0x0e, 0x3a, 0x86, 0xfd, 0xbb, 0x01, 0x5d, 0x3d, 0x45, 0x74, 0x41, 0x02, 0x8a, 0xe3, 0x1c, + 0x4d, 0x48, 0x14, 0x24, 0x39, 0xe2, 0x1b, 0x84, 0xa0, 0x12, 0xe0, 0xb7, 0x2a, 0x43, 0x7c, 0x1d, + 0x73, 0x32, 0xc2, 0x5c, 0x9f, 0x67, 0xa7, 0xec, 0x88, 0x0d, 0xfa, 0x14, 0x1a, 0x32, 0x74, 0x6a, + 0x56, 0x76, 0xcb, 0xbd, 0xd6, 0x60, 0x53, 0x07, 0x44, 0x5a, 0x74, 0x12, 0x36, 0xfb, 0x10, 0xb6, + 0x0f, 0xb1, 0xf2, 0x44, 0xe0, 0xa5, 0x2a, 0x26, 0xb6, 0xeb, 0xce, 0x31, 0x77, 0x26, 0xb6, 0xeb, + 0xce, 0x31, 0x32, 0xa1, 0x2e, 0xcb, 0x8d, 0xbb, 0x53, 0x75, 0xd4, 0xd6, 0x66, 0x60, 0xde, 0x56, + 0x24, 0xe3, 0xca, 0xd2, 0xf4, 0x21, 0x54, 0xe2, 0x9b, 0xc0, 0xd5, 0xb4, 0x06, 0x48, 0xf7, 0xf3, + 0x38, 0x98, 0x11, 0x87, 0xd3, 0xf5, 0x54, 0x95, 0x97, 0x53, 0x75, 0x94, 0xb6, 0x7a, 0x40, 0x02, + 0x86, 0x03, 0xf6, 0x30, 0xff, 0x4f, 0x60, 0x27, 0x43, 0x93, 0x0c, 0x60, 0x0f, 0xea, 0xd2, 0x35, + 0xae, 0x2d, 0x17, 0x57, 0xc5, 0x65, 0xff, 0x5d, 0x82, 0xee, 0xd9, 0x62, 0xea, 0x32, 0xac, 0x48, + 0x77, 0x38, 0xf5, 0x18, 0xaa, 0xbc, 0xa3, 0x48, 0x2c, 0xd6, 0x85, 0x6e, 0xd1, 0x76, 0x0e, 0xe2, + 0x5f, 0x47, 0xd0, 0xd1, 0x53, 0xa8, 0x5d, 0xb9, 0x7e, 0x84, 0x29, 0x07, 0x22, 0x41, 0x4d, 0x72, + 0xf2, 0x76, 0xe4, 0x48, 0x0e, 0xb4, 0x0d, 0xf5, 0x69, 0x78, 0x1d, 0xf7, 0x13, 0x7e, 0x05, 0x1b, + 0x4e, 0x6d, 0x1a, 0x5e, 0x3b, 0x51, 0x80, 0xde, 0x87, 0xb5, 0xa9, 0x47, 0xdd, 0x73, 0x1f, 0x8f, + 0x2f, 0x09, 0x79, 0x43, 0xf9, 0x2d, 0x6c, 0x38, 0xab, 0xf2, 0xf0, 0x28, 0x3e, 0x43, 0x56, 0x5c, + 0x49, 0x93, 0x10, 0xbb, 0x0c, 0x9b, 0x35, 0x4e, 0x4f, 0xf6, 0x31, 0x86, 0xcc, 0x9b, 0x63, 0x12, + 0x31, 0x7e, 0x75, 0xca, 0x8e, 0xda, 0xa2, 0xf7, 0x60, 0x35, 0xc4, 0x14, 0xb3, 0xb1, 0xf4, 0xb2, + 0xc1, 0x25, 0x5b, 0xfc, 0xec, 0xb5, 0x70, 0x0b, 0x41, 0xe5, 0x57, 0xd7, 0x63, 0x66, 0x93, 0x93, + 0xf8, 0x5a, 0x88, 0x45, 0x14, 0x2b, 0x31, 0x50, 0x62, 0x11, 0xc5, 0x52, 0xac, 0x0b, 0xd5, 0x19, + 0x09, 0x27, 0xd8, 0x6c, 0x71, 0x9a, 0xd8, 0xd8, 0x47, 0xb0, 0xb9, 0x04, 0xf2, 0x43, 0xf3, 0xf5, + 0x8f, 0x01, 0x5b, 0x0e, 0xf1, 0xfd, 0x73, 0x77, 0xf2, 0xa6, 0x40, 0xc6, 0x52, 0xe0, 0x96, 0xee, + 0x06, 0xb7, 0x9c, 0x01, 0x6e, 0xaa, 0x08, 0x2b, 0x5a, 0x11, 0x6a, 0xb0, 0x57, 0xf3, 0x61, 0xaf, + 0xe9, 0xb0, 0x2b, 0x4c, 0xeb, 0x29, 0x4c, 0x13, 0xc0, 0x1a, 0x69, 0xc0, 0xbe, 0x86, 0xed, 0x5b, + 0x51, 0x3e, 0x14, 0xb2, 0x3f, 0x4a, 0xb0, 0x79, 0x1c, 0x50, 0xe6, 0xfa, 0xfe, 0x12, 0x62, 0x49, + 0x3d, 0x1b, 0x85, 0xeb, 0xb9, 0xf4, 0x5f, 0xea, 0xb9, 0xac, 0x41, 0xae, 0xf2, 0x53, 0x49, 0xe5, + 0xa7, 0x50, 0x8d, 0x6b, 0x9d, 0xa5, 0xb6, 0xd4, 0x59, 0xd0, 0x3b, 0x00, 0xa2, 0x28, 0xb9, 0x72, + 0x01, 0x6d, 0x93, 0x9f, 0x9c, 0xca, 0x46, 0xa2, 0xb2, 0xd1, 0xc8, 0xce, 0x46, 0xaa, 0xc2, 0xed, + 0x63, 0xd8, 0x5a, 0x86, 0xea, 0xa1, 0xb0, 0xff, 0x66, 0xc0, 0xf6, 0x59, 0xe0, 0x65, 0x02, 0x9f, + 0x55, 0xaa, 0xb7, 0xa0, 0x28, 0x65, 0x40, 0xd1, 0x85, 0xea, 0x22, 0x0a, 0x2f, 0xb0, 0x84, 0x56, + 0x6c, 0xd2, 0x31, 0x56, 0xb4, 0x18, 0xed, 0x31, 0x98, 0xb7, 0x7d, 0x78, 0x60, 0x44, 0xb1, 0xd7, + 0xc9, 0x4b, 0xd0, 0x14, 0x5d, 0xdf, 0xde, 0x80, 0xf5, 0x43, 0xcc, 0x5e, 0x8b, 0x6b, 0x21, 0xc3, + 0xb3, 0x87, 0x80, 0xd2, 0x87, 0x37, 0xf6, 0xe4, 0x91, 0x6e, 0x4f, 0x8d, 0x45, 0x8a, 0x5f, 0x71, + 0xd9, 0x5f, 0x70, 0xdd, 0x47, 0x1e, 0x65, 0x24, 0xbc, 0xbe, 0x0b, 0xba, 0x0e, 0x94, 0xe7, 0xee, + 0x5b, 0xf9, 0x50, 0xc4, 0x4b, 0xfb, 0x90, 0x7b, 0x90, 0x88, 0x4a, 0x0f, 0xd2, 0xcf, 0xae, 0x51, + 0xec, 0xd9, 0xfd, 0x01, 0xd0, 0x2b, 0x9c, 0x4c, 0x00, 0xf7, 0xbc, 0x58, 0x2a, 0x09, 0x25, 0xbd, + 0xd0, 0x4c, 0xa8, 0x4f, 0x7c, 0xec, 0x06, 0xd1, 0x42, 0xa6, 0x4d, 0x6d, 0xed, 0x1f, 0x61, 0x43, + 0xd3, 0x2e, 0xfd, 0x8c, 0xe3, 0xa1, 0x17, 0x52, 0x7b, 0xbc, 0x44, 0x9f, 0x43, 0x4d, 0x8c, 0x45, + 0x5c, 0x77, 0x7b, 0xf0, 0x48, 0xf7, 0x9b, 0x2b, 0x89, 0x02, 0x39, 0x47, 0x39, 0x92, 0x77, 0xf0, + 0x57, 0x03, 0xda, 0xea, 0xa1, 0x17, 0x43, 0x1b, 0xf2, 0x60, 0x35, 0x3d, 0xd1, 0xa0, 0x27, 0xf9, + 0x33, 0xdd, 0xd2, 0x60, 0x6a, 0x3d, 0x2d, 0xc2, 0x2a, 0x22, 0xb0, 0x57, 0x3e, 0x31, 0x10, 0x85, + 0xce, 0xf2, 0xa0, 0x81, 0x9e, 0x65, 0xeb, 0xc8, 0x99, 0x6c, 0xac, 0x7e, 0x51, 0x76, 0x65, 0x16, + 0x5d, 0xf1, 0x9a, 0xd1, 0xa7, 0x03, 0x74, 0xaf, 0x1a, 0x7d, 0x20, 0xb1, 0xf6, 0x0a, 0xf3, 0x27, + 0x76, 0x7f, 0x86, 0x35, 0xed, 0x85, 0x43, 0x39, 0x68, 0x65, 0xcd, 0x1a, 0xd6, 0x47, 0x85, 0x78, + 0x13, 0x5b, 0x73, 0x68, 0xeb, 0x4d, 0x0a, 0xe5, 0x28, 0xc8, 0xec, 0xfa, 0xd6, 0xc7, 0xc5, 0x98, + 0x13, 0x73, 0x14, 0x3a, 0xcb, 0x3d, 0x24, 0x2f, 0x8f, 0x39, 0xfd, 0x2e, 0x2f, 0x8f, 0x79, 0xad, + 0xc9, 0x5e, 0x41, 0x2e, 0xc0, 0x4d, 0x0b, 0x41, 0x8f, 0x73, 0x13, 0xa2, 0x77, 0x1e, 0xab, 0x77, + 0x3f, 0x63, 0x62, 0x62, 0x01, 0xff, 0x5b, 0x7a, 0x63, 0x51, 0x0e, 0x34, 0xd9, 0x03, 0x87, 0xf5, + 0xac, 0x20, 0xf7, 0x52, 0x50, 0xb2, 0x2b, 0xdd, 0x11, 0x94, 0xde, 0xf2, 0xee, 0x08, 0x6a, 0xa9, + 0xc1, 0xd9, 0x2b, 0xc8, 0x83, 0xb6, 0x13, 0x05, 0xd2, 0x74, 0xdc, 0x16, 0x50, 0x8e, 0xf4, 0xed, + 0xae, 0x66, 0x3d, 0x29, 0xc0, 0x79, 0x73, 0xbf, 0x9f, 0xc3, 0xf7, 0x0d, 0xc5, 0x7a, 0x5e, 0xe3, + 0xff, 0x69, 0x3f, 0xfb, 0x37, 0x00, 0x00, 0xff, 0xff, 0xf3, 0x7c, 0x9c, 0x49, 0xc1, 0x0f, 0x00, + 0x00, } diff --git a/pkg/releasetesting/environment.go b/pkg/releasetesting/environment.go index a56721333..51c1aa95a 100644 --- a/pkg/releasetesting/environment.go +++ b/pkg/releasetesting/environment.go @@ -83,31 +83,31 @@ func (env *Environment) streamResult(r *release.TestRun) error { func (env *Environment) streamRunning(name string) error { msg := "RUNNING: " + name - return env.streamMessage(msg) + return env.streamMessage(msg, release.TestRun_RUNNING) } func (env *Environment) streamError(info string) error { msg := "ERROR: " + info - return env.streamMessage(msg) + return env.streamMessage(msg, release.TestRun_FAILURE) } func (env *Environment) streamFailed(name string) error { msg := fmt.Sprintf("FAILED: %s, run `kubectl logs %s --namespace %s` for more info", name, name, env.Namespace) - return env.streamMessage(msg) + return env.streamMessage(msg, release.TestRun_FAILURE) } func (env *Environment) streamSuccess(name string) error { msg := fmt.Sprintf("PASSED: %s", name) - return env.streamMessage(msg) + return env.streamMessage(msg, release.TestRun_SUCCESS) } func (env *Environment) streamUnknown(name, info string) error { msg := fmt.Sprintf("UNKNOWN: %s: %s", name, info) - return env.streamMessage(msg) + return env.streamMessage(msg, release.TestRun_UNKNOWN) } -func (env *Environment) streamMessage(msg string) error { - resp := &services.TestReleaseResponse{Msg: msg} +func (env *Environment) streamMessage(msg string, status release.TestRun_Status) error { + resp := &services.TestReleaseResponse{Msg: msg, Status: status} return env.Stream.Send(resp) } diff --git a/pkg/releasetesting/environment_test.go b/pkg/releasetesting/environment_test.go index deb8617f6..29ca93d09 100644 --- a/pkg/releasetesting/environment_test.go +++ b/pkg/releasetesting/environment_test.go @@ -24,6 +24,7 @@ import ( "testing" "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" tillerEnv "k8s.io/helm/pkg/tiller/environment" ) @@ -88,6 +89,29 @@ func TestDeleteTestPodsFailingDelete(t *testing.T) { } } +func TestStreamMessage(t *testing.T) { + mockTestEnv := newMockTestingEnvironment() + + expectedMessage := "testing streamMessage" + expectedStatus := release.TestRun_SUCCESS + err := mockTestEnv.streamMessage(expectedMessage, expectedStatus) + if err != nil { + t.Errorf("Expected no errors, got 1: %s", err) + } + + stream := mockTestEnv.Stream.(*mockStream) + if len(stream.messages) != 1 { + t.Errorf("Expected 1 message, got: %v", len(stream.messages)) + } + + if stream.messages[0].Msg != expectedMessage { + t.Errorf("Expected message: %s, got: %s", expectedMessage, stream.messages[0]) + } + if stream.messages[0].Status != expectedStatus { + t.Errorf("Expected status: %v, got: %v", expectedStatus, stream.messages[0].Status) + } +} + type MockTestingEnvironment struct { *Environment } @@ -110,7 +134,10 @@ func (mte MockTestingEnvironment) streamError(info string) error { retur func (mte MockTestingEnvironment) streamFailed(name string) error { return nil } func (mte MockTestingEnvironment) streamSuccess(name string) error { return nil } func (mte MockTestingEnvironment) streamUnknown(name, info string) error { return nil } -func (mte MockTestingEnvironment) streamMessage(msg string) error { return nil } +func (mte MockTestingEnvironment) streamMessage(msg string, status release.TestRun_Status) error { + mte.Stream.Send(&services.TestReleaseResponse{Msg: msg, Status: status}) + return nil +} type getFailingKubeClient struct { tillerEnv.PrintingKubeClient @@ -123,7 +150,7 @@ func newGetFailingKubeClient() *getFailingKubeClient { } func (p *getFailingKubeClient) Get(ns string, r io.Reader) (string, error) { - return "", errors.New("In the end, they did not find Nemo.") + return "", errors.New("in the end, they did not find Nemo") } type deleteFailingKubeClient struct { diff --git a/pkg/releasetesting/test_suite.go b/pkg/releasetesting/test_suite.go index f4362dc5b..e5e8db51e 100644 --- a/pkg/releasetesting/test_suite.go +++ b/pkg/releasetesting/test_suite.go @@ -65,7 +65,8 @@ func (ts *TestSuite) Run(env *Environment) error { ts.StartedAt = timeconv.Now() if len(ts.TestManifests) == 0 { - env.streamMessage("No Tests Found") + // TODO: make this better, adding test run status on test suite is weird + env.streamMessage("No Tests Found", release.TestRun_UNKNOWN) } for _, testManifest := range ts.TestManifests { @@ -78,6 +79,7 @@ func (ts *TestSuite) Run(env *Environment) error { if err := env.streamRunning(test.result.Name); err != nil { return err } + test.result.Status = release.TestRun_RUNNING resourceCreated := true if err := env.createTestPod(test); err != nil { @@ -93,7 +95,7 @@ func (ts *TestSuite) Run(env *Environment) error { status, err = env.getTestPodStatus(test) if err != nil { resourceCleanExit = false - if streamErr := env.streamUnknown(test.result.Name, test.result.Info); streamErr != nil { + if streamErr := env.streamError(test.result.Info); streamErr != nil { return streamErr } } diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index aad1641d7..a0449cc55 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -19,6 +19,7 @@ package releaseutil import ( "fmt" "regexp" + "strings" ) // SimpleHead defines what the structure of the head of a manifest file @@ -34,16 +35,25 @@ type SimpleHead struct { var sep = regexp.MustCompile("(?:^|\\s*\n)---\\s*") // SplitManifests takes a string of manifest and returns a map contains individual manifests -func SplitManifests(bigfile string) map[string]string { +func SplitManifests(bigFile string) map[string]string { // Basically, we're quickly splitting a stream of YAML documents into an // array of YAML docs. In the current implementation, the file name is just // a place holder, and doesn't have any further meaning. tpl := "manifest-%d" res := map[string]string{} // Making sure that any extra whitespace in YAML stream doesn't interfere in splitting documents correctly. - docs := sep.Split(bigfile, -1) - for i, d := range docs { - res[fmt.Sprintf(tpl, i)] = d + bigFileTmp := strings.TrimSpace(bigFile) + docs := sep.Split(bigFileTmp, -1) + var count int + for _, d := range docs { + + if d == "" { + continue + } + + d = strings.TrimSpace(d) + res[fmt.Sprintf(tpl, count)] = d + count = count + 1 } return res } diff --git a/pkg/releaseutil/manifest_test.go b/pkg/releaseutil/manifest_test.go new file mode 100644 index 000000000..7906279ad --- /dev/null +++ b/pkg/releaseutil/manifest_test.go @@ -0,0 +1,61 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package releaseutil // import "k8s.io/helm/pkg/releaseutil" + +import ( + "reflect" + "testing" +) + +const manifestFile = ` + +--- +apiVersion: v1 +kind: Pod +metadata: + name: finding-nemo, + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: nemo-test + image: fake-image + cmd: fake-command +` + +const expectedManifest = `apiVersion: v1 +kind: Pod +metadata: + name: finding-nemo, + annotations: + "helm.sh/hook": test-success +spec: + containers: + - name: nemo-test + image: fake-image + cmd: fake-command` + +func TestSplitManifest(t *testing.T) { + manifests := SplitManifests(manifestFile) + if len(manifests) != 1 { + t.Errorf("Expected 1 manifest, got %v", len(manifests)) + } + expected := map[string]string{"manifest-0": expectedManifest} + if !reflect.DeepEqual(manifests, expected) { + t.Errorf("Expected %v, got %v", expected, manifests) + } +} diff --git a/pkg/repo/chartrepo.go b/pkg/repo/chartrepo.go index d7a4267f8..9c833654a 100644 --- a/pkg/repo/chartrepo.go +++ b/pkg/repo/chartrepo.go @@ -50,13 +50,13 @@ type ChartRepository struct { } // NewChartRepository constructs ChartRepository -func NewChartRepository(cfg *Entry, getters []getter.Prop) (*ChartRepository, error) { +func NewChartRepository(cfg *Entry, getters getter.Providers) (*ChartRepository, error) { u, err := url.Parse(cfg.URL) if err != nil { return nil, fmt.Errorf("invalid chart URL format: %s", cfg.URL) } - getterConstructor, err := getter.ConstructorByScheme(getters, u.Scheme) + getterConstructor, err := getters.ByScheme(u.Scheme) if err != nil { return nil, fmt.Errorf("Could not find protocol handler for: %s", u.Scheme) } @@ -177,3 +177,50 @@ func (r *ChartRepository) generateIndex() error { r.IndexFile.SortEntries() return nil } + +// FindChartInRepoURL finds chart in chart repository pointed by repoURL +// without adding repo to repostiories +func FindChartInRepoURL(repoURL, chartName, chartVersion, certFile, keyFile, caFile string, getters getter.Providers) (string, error) { + + // Download and write the index file to a temporary location + tempIndexFile, err := ioutil.TempFile("", "tmp-repo-file") + if err != nil { + return "", fmt.Errorf("cannot write index file for repository requested") + } + defer os.Remove(tempIndexFile.Name()) + + c := Entry{ + URL: repoURL, + CertFile: certFile, + KeyFile: keyFile, + CAFile: caFile, + } + r, err := NewChartRepository(&c, getters) + if err != nil { + return "", err + } + if err := r.DownloadIndexFile(tempIndexFile.Name()); err != nil { + return "", fmt.Errorf("Looks like %q is not a valid chart repository or cannot be reached: %s", repoURL, err) + } + + // Read the index file for the repository to get chart information and return chart URL + repoIndex, err := LoadIndexFile(tempIndexFile.Name()) + if err != nil { + return "", err + } + + errMsg := fmt.Sprintf("chart %q", chartName) + if chartVersion != "" { + errMsg = fmt.Sprintf("%s version %q", errMsg, chartVersion) + } + cv, err := repoIndex.Get(chartName, chartVersion) + if err != nil { + return "", fmt.Errorf("%s not found in %s repository", errMsg, repoURL) + } + + if len(cv.URLs) == 0 { + return "", fmt.Errorf("%s has no downloadable URLs", errMsg) + } + + return cv.URLs[0], nil +} diff --git a/pkg/repo/chartrepo_test.go b/pkg/repo/chartrepo_test.go index bab5019d8..f6d6df74e 100644 --- a/pkg/repo/chartrepo_test.go +++ b/pkg/repo/chartrepo_test.go @@ -17,13 +17,17 @@ limitations under the License. package repo import ( + "io/ioutil" + "net/http" + "net/http/httptest" "os" "path/filepath" "reflect" + "strings" "testing" "time" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/proto/hapi/chart" ) @@ -37,7 +41,7 @@ func TestLoadChartRepository(t *testing.T) { r, err := NewChartRepository(&Entry{ Name: testRepository, URL: testURL, - }, defaultgetters.Get(environment.EnvSettings{})) + }, getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) } @@ -69,7 +73,7 @@ func TestIndex(t *testing.T) { r, err := NewChartRepository(&Entry{ Name: testRepository, URL: testURL, - }, defaultgetters.Get(environment.EnvSettings{})) + }, getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("Problem creating chart repository from %s: %v", testRepository, err) } @@ -185,3 +189,82 @@ func verifyIndex(t *testing.T, actual *IndexFile) { } } } + +// startLocalServerForTests Start the local helm server +func startLocalServerForTests(handler http.Handler) (*httptest.Server, error) { + if handler == nil { + fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") + if err != nil { + return nil, err + } + handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(fileBytes) + }) + } + + return httptest.NewServer(handler), nil +} + +func TestFindChartInRepoURL(t *testing.T) { + srv, err := startLocalServerForTests(nil) + if err != nil { + t.Fatal(err) + } + defer srv.Close() + + chartURL, err := FindChartInRepoURL(srv.URL, "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) + if err != nil { + t.Errorf("%s", err) + } + if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.2.0.tgz" { + t.Errorf("%s is not the valid URL", chartURL) + } + + chartURL, err = FindChartInRepoURL(srv.URL, "nginx", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) + if err != nil { + t.Errorf("%s", err) + } + if chartURL != "https://kubernetes-charts.storage.googleapis.com/nginx-0.1.0.tgz" { + t.Errorf("%s is not the valid URL", chartURL) + } +} + +func TestErrorFindChartInRepoURL(t *testing.T) { + _, err := FindChartInRepoURL("http://someserver/something", "nginx", "", "", "", "", getter.All(environment.EnvSettings{})) + if err == nil { + t.Errorf("Expected error for bad chart URL, but did not get any errors") + } + if err != nil && !strings.Contains(err.Error(), `Looks like "http://someserver/something" is not a valid chart repository or cannot be reached: Get http://someserver/something/index.yaml`) { + t.Errorf("Expected error for bad chart URL, but got a different error (%v)", err) + } + + srv, err := startLocalServerForTests(nil) + if err != nil { + t.Fatal(err) + } + defer srv.Close() + + _, err = FindChartInRepoURL(srv.URL, "nginx1", "", "", "", "", getter.All(environment.EnvSettings{})) + if err == nil { + t.Errorf("Expected error for chart not found, but did not get any errors") + } + if err != nil && err.Error() != `chart "nginx1" not found in `+srv.URL+` repository` { + t.Errorf("Expected error for chart not found, but got a different error (%v)", err) + } + + _, err = FindChartInRepoURL(srv.URL, "nginx1", "0.1.0", "", "", "", getter.All(environment.EnvSettings{})) + if err == nil { + t.Errorf("Expected error for chart not found, but did not get any errors") + } + if err != nil && err.Error() != `chart "nginx1" version "0.1.0" not found in `+srv.URL+` repository` { + t.Errorf("Expected error for chart not found, but got a different error (%v)", err) + } + + _, err = FindChartInRepoURL(srv.URL, "chartWithNoURL", "", "", "", "", getter.All(environment.EnvSettings{})) + if err == nil { + t.Errorf("Expected error for no chart URLs available, but did not get any errors") + } + if err != nil && err.Error() != `chart "chartWithNoURL" has no downloadable URLs` { + t.Errorf("Expected error for chart not found, but got a different error (%v)", err) + } +} diff --git a/pkg/repo/index_test.go b/pkg/repo/index_test.go index c82e0e727..17a1cc209 100644 --- a/pkg/repo/index_test.go +++ b/pkg/repo/index_test.go @@ -18,13 +18,11 @@ package repo import ( "io/ioutil" - "net/http" - "net/http/httptest" "os" "path/filepath" "testing" - "k8s.io/helm/pkg/getter/defaultgetters" + "k8s.io/helm/pkg/getter" "k8s.io/helm/pkg/helm/environment" "k8s.io/helm/pkg/proto/hapi/chart" ) @@ -131,14 +129,10 @@ func TestMerge(t *testing.T) { } func TestDownloadIndexFile(t *testing.T) { - fileBytes, err := ioutil.ReadFile("testdata/local-index.yaml") + srv, err := startLocalServerForTests(nil) if err != nil { t.Fatal(err) } - - srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Write(fileBytes) - })) defer srv.Close() dirName, err := ioutil.TempDir("", "tmp") @@ -152,7 +146,7 @@ func TestDownloadIndexFile(t *testing.T) { Name: testRepo, URL: srv.URL, Cache: indexFilePath, - }, defaultgetters.Get(environment.EnvSettings{})) + }, getter.All(environment.EnvSettings{})) if err != nil { t.Errorf("Problem creating chart repository from %s: %v", testRepo, err) } @@ -181,8 +175,8 @@ func TestDownloadIndexFile(t *testing.T) { func verifyLocalIndex(t *testing.T, i *IndexFile) { numEntries := len(i.Entries) - if numEntries != 2 { - t.Errorf("Expected 2 entries in index file but got %d", numEntries) + if numEntries != 3 { + t.Errorf("Expected 3 entries in index file but got %d", numEntries) } alpine, ok := i.Entries["alpine"] diff --git a/pkg/repo/local_test.go b/pkg/repo/local_test.go index 3e2ecbbcf..1e5359dee 100644 --- a/pkg/repo/local_test.go +++ b/pkg/repo/local_test.go @@ -19,7 +19,6 @@ package repo import ( "io/ioutil" "net/http" - "net/http/httptest" "strings" "testing" ) @@ -43,7 +42,10 @@ func TestRepositoryServer(t *testing.T) { } s := &RepositoryServer{RepoPath: "testdata/server"} - srv := httptest.NewServer(s) + srv, err := startLocalServerForTests(s) + if err != nil { + t.Fatal(err) + } defer srv.Close() for _, tt := range tests { diff --git a/pkg/repo/repo.go b/pkg/repo/repo.go index cbf54c572..5e1b5c6cd 100644 --- a/pkg/repo/repo.go +++ b/pkg/repo/repo.go @@ -23,6 +23,7 @@ import ( "os" "time" + "github.com/facebookgo/atomicfile" "github.com/ghodss/yaml" ) @@ -134,9 +135,20 @@ func (r *RepoFile) Remove(name string) bool { // WriteFile writes a repositories file to the given path. func (r *RepoFile) WriteFile(path string, perm os.FileMode) error { + f, err := atomicfile.New(path, perm) + if err != nil { + return err + } + data, err := yaml.Marshal(r) if err != nil { return err } - return ioutil.WriteFile(path, data, perm) + + _, err = f.File.Write(data) + if err != nil { + return err + } + + return f.Close() } diff --git a/pkg/repo/repo_test.go b/pkg/repo/repo_test.go index 6aee41faf..d4500c9e2 100644 --- a/pkg/repo/repo_test.go +++ b/pkg/repo/repo_test.go @@ -201,10 +201,18 @@ func TestWriteFile(t *testing.T) { t.Errorf("failed to create test-file (%v)", err) } defer os.Remove(repoFile.Name()) - if err := sampleRepository.WriteFile(repoFile.Name(), 744); err != nil { + + fileMode := os.FileMode(0744) + if err := sampleRepository.WriteFile(repoFile.Name(), fileMode); err != nil { t.Errorf("failed to write file (%v)", err) } + info, _ := os.Stat(repoFile.Name()) + mode := info.Mode() + if mode != fileMode { + t.Errorf("incorrect file mode: %s (expected %s)", mode, fileMode) + } + repos, err := LoadRepositoriesFile(repoFile.Name()) if err != nil { t.Errorf("failed to load file (%v)", err) diff --git a/pkg/repo/testdata/local-index-unordered.yaml b/pkg/repo/testdata/local-index-unordered.yaml index ec529f110..7482baaae 100644 --- a/pkg/repo/testdata/local-index-unordered.yaml +++ b/pkg/repo/testdata/local-index-unordered.yaml @@ -37,3 +37,12 @@ entries: - small - sumtin digest: "sha256:1234567890abcdef" + chartWithNoURL: + - name: chartWithNoURL + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - small + - sumtin + digest: "sha256:1234567890abcdef" diff --git a/pkg/repo/testdata/local-index.yaml b/pkg/repo/testdata/local-index.yaml index f64c54c1b..e680d2a3e 100644 --- a/pkg/repo/testdata/local-index.yaml +++ b/pkg/repo/testdata/local-index.yaml @@ -37,3 +37,12 @@ entries: - small - sumtin digest: "sha256:1234567890abcdef" + chartWithNoURL: + - name: chartWithNoURL + description: string + version: 1.0.0 + home: https://github.com/something + keywords: + - small + - sumtin + digest: "sha256:1234567890abcdef" diff --git a/pkg/resolver/resolver_test.go b/pkg/resolver/resolver_test.go index ef5a3bee0..b87022b93 100644 --- a/pkg/resolver/resolver_test.go +++ b/pkg/resolver/resolver_test.go @@ -146,7 +146,7 @@ func TestResolve(t *testing.T) { } func TestHashReq(t *testing.T) { - expect := "sha256:1feffe2016ca113f64159d91c1f77d6a83bcd23510b171d9264741bf9d63f741" + expect := "sha256:45b06fcc4496c705bf3d634f8a2ff84e6a6f0bdcaf010614b8886572d1e52b99" req := &chartutil.Requirements{ Dependencies: []*chartutil.Dependency{ {Name: "alpine", Version: "0.1.0", Repository: "http://localhost:8879/charts"}, diff --git a/pkg/rudder/client.go b/pkg/rudder/client.go new file mode 100644 index 000000000..219bb010a --- /dev/null +++ b/pkg/rudder/client.go @@ -0,0 +1,91 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package rudder // import "k8s.io/helm/pkg/rudder" + +import ( + "fmt" + + "golang.org/x/net/context" + "google.golang.org/grpc" + + rudderAPI "k8s.io/helm/pkg/proto/hapi/rudder" +) + +// GrpcPort specifies port on which rudder will spawn a server +const ( + GrpcPort = 10001 +) + +var grpcAddr = fmt.Sprintf("127.0.0.1:%d", GrpcPort) + +// InstallRelease calls Rudder InstallRelease method which should create provided release +func InstallRelease(rel *rudderAPI.InstallReleaseRequest) (*rudderAPI.InstallReleaseResponse, error) { + //TODO(mkwiek): parametrize this + conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure()) + if err != nil { + return nil, err + } + + defer conn.Close() + client := rudderAPI.NewReleaseModuleServiceClient(conn) + return client.InstallRelease(context.Background(), rel) +} + +// UpgradeRelease calls Rudder UpgradeRelease method which should perform update +func UpgradeRelease(req *rudderAPI.UpgradeReleaseRequest) (*rudderAPI.UpgradeReleaseResponse, error) { + conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer conn.Close() + client := rudderAPI.NewReleaseModuleServiceClient(conn) + return client.UpgradeRelease(context.Background(), req) +} + +// RollbackRelease calls Rudder RollbackRelease method which should perform update +func RollbackRelease(req *rudderAPI.RollbackReleaseRequest) (*rudderAPI.RollbackReleaseResponse, error) { + conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer conn.Close() + client := rudderAPI.NewReleaseModuleServiceClient(conn) + return client.RollbackRelease(context.Background(), req) +} + +// ReleaseStatus calls Rudder ReleaseStatus method which should perform update +func ReleaseStatus(req *rudderAPI.ReleaseStatusRequest) (*rudderAPI.ReleaseStatusResponse, error) { + conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer conn.Close() + client := rudderAPI.NewReleaseModuleServiceClient(conn) + return client.ReleaseStatus(context.Background(), req) +} + +// DeleteRelease calls Rudder DeleteRelease method which should uninstall provided release +func DeleteRelease(rel *rudderAPI.DeleteReleaseRequest) (*rudderAPI.DeleteReleaseResponse, error) { + conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure()) + if err != nil { + return nil, err + } + + defer conn.Close() + client := rudderAPI.NewReleaseModuleServiceClient(conn) + return client.DeleteRelease(context.Background(), rel) +} diff --git a/pkg/storage/driver/cfgmaps.go b/pkg/storage/driver/cfgmaps.go index d7d48a032..1c30951f3 100644 --- a/pkg/storage/driver/cfgmaps.go +++ b/pkg/storage/driver/cfgmaps.go @@ -22,7 +22,6 @@ import ( "encoding/base64" "fmt" "io/ioutil" - "log" "strconv" "strings" "time" @@ -51,12 +50,16 @@ var magicGzip = []byte{0x1f, 0x8b, 0x08} // ConfigMapsInterface. type ConfigMaps struct { impl internalversion.ConfigMapInterface + Log func(string, ...interface{}) } // NewConfigMaps initializes a new ConfigMaps wrapping an implmenetation of // the kubernetes ConfigMapsInterface. func NewConfigMaps(impl internalversion.ConfigMapInterface) *ConfigMaps { - return &ConfigMaps{impl: impl} + return &ConfigMaps{ + impl: impl, + Log: func(_ string, _ ...interface{}) {}, + } } // Name returns the name of the driver. @@ -74,13 +77,13 @@ func (cfgmaps *ConfigMaps) Get(key string) (*rspb.Release, error) { return nil, ErrReleaseNotFound(key) } - logerrf(err, "get: failed to get %q", key) + cfgmaps.Log("get: failed to get %q: %s", key, err) return nil, err } // found the configmap, decode the base64 data string r, err := decodeRelease(obj.Data["release"]) if err != nil { - logerrf(err, "get: failed to decode data %q", key) + cfgmaps.Log("get: failed to decode data %q: %s", key, err) return nil, err } // return the release object @@ -96,7 +99,7 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas list, err := cfgmaps.impl.List(opts) if err != nil { - logerrf(err, "list: failed to list") + cfgmaps.Log("list: failed to list: %s", err) return nil, err } @@ -107,7 +110,7 @@ func (cfgmaps *ConfigMaps) List(filter func(*rspb.Release) bool) ([]*rspb.Releas for _, item := range list.Items { rls, err := decodeRelease(item.Data["release"]) if err != nil { - logerrf(err, "list: failed to decode release: %v", item) + cfgmaps.Log("list: failed to decode release: %v: %s", item, err) continue } if filter(rls) { @@ -132,7 +135,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err list, err := cfgmaps.impl.List(opts) if err != nil { - logerrf(err, "query: failed to query with labels") + cfgmaps.Log("query: failed to query with labels: %s", err) return nil, err } @@ -144,7 +147,7 @@ func (cfgmaps *ConfigMaps) Query(labels map[string]string) ([]*rspb.Release, err for _, item := range list.Items { rls, err := decodeRelease(item.Data["release"]) if err != nil { - logerrf(err, "query: failed to decode release: %s", err) + cfgmaps.Log("query: failed to decode release: %s", err) continue } results = append(results, rls) @@ -164,7 +167,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { // create a new configmap to hold the release obj, err := newConfigMapsObject(key, rls, lbs) if err != nil { - logerrf(err, "create: failed to encode release %q", rls.Name) + cfgmaps.Log("create: failed to encode release %q: %s", rls.Name, err) return err } // push the configmap object out into the kubiverse @@ -173,7 +176,7 @@ func (cfgmaps *ConfigMaps) Create(key string, rls *rspb.Release) error { return ErrReleaseExists(rls.Name) } - logerrf(err, "create: failed to create") + cfgmaps.Log("create: failed to create: %s", err) return err } return nil @@ -191,13 +194,13 @@ func (cfgmaps *ConfigMaps) Update(key string, rls *rspb.Release) error { // create a new configmap object to hold the release obj, err := newConfigMapsObject(key, rls, lbs) if err != nil { - logerrf(err, "update: failed to encode release %q", rls.Name) + cfgmaps.Log("update: failed to encode release %q: %s", rls.Name, err) return err } // push the configmap object out into the kubiverse _, err = cfgmaps.impl.Update(obj) if err != nil { - logerrf(err, "update: failed to update") + cfgmaps.Log("update: failed to update: %s", err) return err } return nil @@ -211,7 +214,7 @@ func (cfgmaps *ConfigMaps) Delete(key string) (rls *rspb.Release, err error) { return nil, ErrReleaseExists(rls.Name) } - logerrf(err, "delete: failed to get release %q", key) + cfgmaps.Log("delete: failed to get release %q: %s", key, err) return nil, err } // delete the release @@ -316,8 +319,3 @@ func decodeRelease(data string) (*rspb.Release, error) { } return &rls, nil } - -// logerrf wraps an error with a formatted string (used for debugging) -func logerrf(err error, format string, args ...interface{}) { - log.Printf("configmaps: %s: %s\n", fmt.Sprintf(format, args...), err) -} diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index a10f377da..b5aa16ccb 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -18,7 +18,7 @@ package storage // import "k8s.io/helm/pkg/storage" import ( "fmt" - "log" + "sync" rspb "k8s.io/helm/pkg/proto/hapi/release" relutil "k8s.io/helm/pkg/releaseutil" @@ -28,13 +28,20 @@ import ( // Storage represents a storage engine for a Release. type Storage struct { driver.Driver + + // releaseLocks are for locking releases to make sure that only one operation at a time is executed on each release + releaseLocks map[string]*sync.Mutex + // releaseLocksLock is a mutex for accessing releaseLocks + releaseLocksLock *sync.Mutex + + Log func(string, ...interface{}) } // Get retrieves the release from storage. An error is returned // if the storage driver failed to fetch the release, or the // release identified by the key, version pair does not exist. func (s *Storage) Get(name string, version int32) (*rspb.Release, error) { - log.Printf("Getting release %q (v%d) from storage\n", name, version) + s.Log("getting release %q", makeKey(name, version)) return s.Driver.Get(makeKey(name, version)) } @@ -42,7 +49,7 @@ func (s *Storage) Get(name string, version int32) (*rspb.Release, error) { // error is returned if the storage driver failed to store the // release, or a release with identical an key already exists. func (s *Storage) Create(rls *rspb.Release) error { - log.Printf("Create release %q (v%d) in storage\n", rls.Name, rls.Version) + s.Log("creating release %q", makeKey(rls.Name, rls.Version)) return s.Driver.Create(makeKey(rls.Name, rls.Version), rls) } @@ -50,7 +57,7 @@ func (s *Storage) Create(rls *rspb.Release) error { // storage backend fails to update the release or if the release // does not exist. func (s *Storage) Update(rls *rspb.Release) error { - log.Printf("Updating %q (v%d) in storage\n", rls.Name, rls.Version) + s.Log("updating release %q", makeKey(rls.Name, rls.Version)) return s.Driver.Update(makeKey(rls.Name, rls.Version), rls) } @@ -58,21 +65,21 @@ func (s *Storage) Update(rls *rspb.Release) error { // the storage backend fails to delete the release or if the release // does not exist. func (s *Storage) Delete(name string, version int32) (*rspb.Release, error) { - log.Printf("Deleting release %q (v%d) from storage\n", name, version) + s.Log("deleting release %q", makeKey(name, version)) return s.Driver.Delete(makeKey(name, version)) } // ListReleases returns all releases from storage. An error is returned if the // storage backend fails to retrieve the releases. func (s *Storage) ListReleases() ([]*rspb.Release, error) { - log.Println("Listing all releases in storage") + s.Log("listing all releases in storage") return s.Driver.List(func(_ *rspb.Release) bool { return true }) } // ListDeleted returns all releases with Status == DELETED. An error is returned // if the storage backend fails to retrieve the releases. func (s *Storage) ListDeleted() ([]*rspb.Release, error) { - log.Println("List deleted releases in storage") + s.Log("listing deleted releases in storage") return s.Driver.List(func(rls *rspb.Release) bool { return relutil.StatusFilter(rspb.Status_DELETED).Check(rls) }) @@ -81,7 +88,7 @@ func (s *Storage) ListDeleted() ([]*rspb.Release, error) { // ListDeployed returns all releases with Status == DEPLOYED. An error is returned // if the storage backend fails to retrieve the releases. func (s *Storage) ListDeployed() ([]*rspb.Release, error) { - log.Println("Listing all deployed releases in storage") + s.Log("listing all deployed releases in storage") return s.Driver.List(func(rls *rspb.Release) bool { return relutil.StatusFilter(rspb.Status_DEPLOYED).Check(rls) }) @@ -91,7 +98,7 @@ func (s *Storage) ListDeployed() ([]*rspb.Release, error) { // (filter0 && filter1 && ... && filterN), i.e. a Release is included in the results // if and only if all filters return true. func (s *Storage) ListFilterAll(fns ...relutil.FilterFunc) ([]*rspb.Release, error) { - log.Println("Listing all releases with filter") + s.Log("listing all releases with filter") return s.Driver.List(func(rls *rspb.Release) bool { return relutil.All(fns...).Check(rls) }) @@ -101,7 +108,7 @@ func (s *Storage) ListFilterAll(fns ...relutil.FilterFunc) ([]*rspb.Release, err // (filter0 || filter1 || ... || filterN), i.e. a Release is included in the results // if at least one of the filters returns true. func (s *Storage) ListFilterAny(fns ...relutil.FilterFunc) ([]*rspb.Release, error) { - log.Println("Listing any releases with filter") + s.Log("listing any releases with filter") return s.Driver.List(func(rls *rspb.Release) bool { return relutil.Any(fns...).Check(rls) }) @@ -110,7 +117,7 @@ func (s *Storage) ListFilterAny(fns ...relutil.FilterFunc) ([]*rspb.Release, err // Deployed returns the deployed release with the provided release name, or // returns ErrReleaseNotFound if not found. func (s *Storage) Deployed(name string) (*rspb.Release, error) { - log.Printf("Getting deployed release from '%s' history\n", name) + s.Log("getting deployed release from %q history", name) ls, err := s.Driver.Query(map[string]string{ "NAME": name, @@ -121,7 +128,7 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) { case err != nil: return nil, err case len(ls) == 0: - return nil, fmt.Errorf("'%s' has no deployed releases", name) + return nil, fmt.Errorf("%q has no deployed releases", name) default: return ls[0], nil } @@ -130,17 +137,14 @@ func (s *Storage) Deployed(name string) (*rspb.Release, error) { // History returns the revision history for the release with the provided name, or // returns ErrReleaseNotFound if no such release name exists. func (s *Storage) History(name string) ([]*rspb.Release, error) { - log.Printf("Getting release history for '%s'\n", name) + s.Log("getting release history for %q", name) - l, err := s.Driver.Query(map[string]string{"NAME": name, "OWNER": "TILLER"}) - if err != nil { - return nil, err - } - return l, nil + return s.Driver.Query(map[string]string{"NAME": name, "OWNER": "TILLER"}) } // Last fetches the last revision of the named release. func (s *Storage) Last(name string) (*rspb.Release, error) { + s.Log("getting last revision of %q", name) h, err := s.History(name) if err != nil { return nil, err @@ -153,6 +157,53 @@ func (s *Storage) Last(name string) (*rspb.Release, error) { return h[0], nil } +// LockRelease gains a mutually exclusive access to a release via a mutex. +func (s *Storage) LockRelease(name string) error { + s.Log("locking release %s", name) + s.releaseLocksLock.Lock() + defer s.releaseLocksLock.Unlock() + + var lock *sync.Mutex + lock, exists := s.releaseLocks[name] + + if !exists { + releases, err := s.ListReleases() + if err != nil { + return err + } + + found := false + for _, release := range releases { + if release.Name == name { + found = true + } + } + if !found { + return fmt.Errorf("Unable to lock release %q: release not found", name) + } + + lock = &sync.Mutex{} + s.releaseLocks[name] = lock + } + lock.Lock() + return nil +} + +// UnlockRelease releases a mutually exclusive access to a release. +// If release doesn't exist or wasn't previously locked - the unlock will pass +func (s *Storage) UnlockRelease(name string) { + s.Log("unlocking release %s", name) + s.releaseLocksLock.Lock() + defer s.releaseLocksLock.Unlock() + + var lock *sync.Mutex + lock, exists := s.releaseLocks[name] + if !exists { + return + } + lock.Unlock() +} + // makeKey concatenates a release name and version into // a string with format ```#v```. // This key is used to uniquely identify storage objects. @@ -167,5 +218,10 @@ func Init(d driver.Driver) *Storage { if d == nil { d = driver.NewMemory() } - return &Storage{Driver: d} + return &Storage{ + Driver: d, + releaseLocks: make(map[string]*sync.Mutex), + releaseLocksLock: &sync.Mutex{}, + Log: func(_ string, _ ...interface{}) {}, + } } diff --git a/pkg/storage/storage_test.go b/pkg/storage/storage_test.go index 141a019fa..d2dc8cdb2 100644 --- a/pkg/storage/storage_test.go +++ b/pkg/storage/storage_test.go @@ -272,3 +272,31 @@ func assertErrNil(eh func(args ...interface{}), err error, message string) { eh(fmt.Sprintf("%s: %q", message, err)) } } + +func TestReleaseLocksNotExist(t *testing.T) { + s := Init(driver.NewMemory()) + + err := s.LockRelease("no-such-release") + + if err == nil { + t.Errorf("Exptected error when trying to lock non-existing release, got nil") + } +} + +func TestReleaseLocks(t *testing.T) { + s := Init(driver.NewMemory()) + + releaseName := "angry-beaver" + rls := ReleaseTestData{ + Name: releaseName, + Version: 1, + }.ToRelease() + + s.Create(rls) + + err := s.LockRelease(releaseName) + if err != nil { + t.Errorf("Exptected nil err when locking existing release") + } + s.UnlockRelease(releaseName) +} diff --git a/pkg/tiller/environment/environment.go b/pkg/tiller/environment/environment.go index 26516474b..de0dbe33d 100644 --- a/pkg/tiller/environment/environment.go +++ b/pkg/tiller/environment/environment.go @@ -24,7 +24,6 @@ package environment import ( "io" - "os" "time" "k8s.io/helm/pkg/chartutil" @@ -44,14 +43,6 @@ const TillerNamespaceEnvVar = "TILLER_NAMESPACE" // DefaultTillerNamespace is the default namespace for tiller. const DefaultTillerNamespace = "kube-system" -// GetTillerNamespace returns the right tiller namespace. -func GetTillerNamespace() string { - if ns := os.Getenv(TillerNamespaceEnvVar); ns != "" { - return ns - } - return DefaultTillerNamespace -} - // GoTplEngine is the name of the Go template engine, as registered in the EngineYard. const GoTplEngine = "gotpl" @@ -147,7 +138,7 @@ type KubeClient interface { // // reader must contain a YAML stream (one or more YAML documents separated // by "\n---\n"). - Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error + Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error Build(namespace string, reader io.Reader) (kube.Result, error) BuildUnstructured(namespace string, reader io.Reader) (kube.Result, error) @@ -190,7 +181,7 @@ func (p *PrintingKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int } // Update implements KubeClient Update. -func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { +func (p *PrintingKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { _, err := io.Copy(p.Out, modifiedReader) return err } @@ -235,6 +226,5 @@ func New() *Environment { return &Environment{ EngineYard: ey, Releases: storage.Init(driver.NewMemory()), - KubeClient: kube.New(nil), } } diff --git a/pkg/tiller/environment/environment_test.go b/pkg/tiller/environment/environment_test.go index 716836438..916ff602f 100644 --- a/pkg/tiller/environment/environment_test.go +++ b/pkg/tiller/environment/environment_test.go @@ -48,7 +48,7 @@ func (k *mockKubeClient) Get(ns string, r io.Reader) (string, error) { func (k *mockKubeClient) Delete(ns string, r io.Reader) error { return nil } -func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { +func (k *mockKubeClient) Update(ns string, currentReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { return nil } func (k *mockKubeClient) WatchUntilReady(ns string, r io.Reader, timeout int64, shouldWait bool) error { diff --git a/pkg/tiller/hooks.go b/pkg/tiller/hooks.go index b26243479..996253384 100644 --- a/pkg/tiller/hooks.go +++ b/pkg/tiller/hooks.go @@ -51,96 +51,149 @@ type manifest struct { head *util.SimpleHead } -// sortManifests takes a map of filename/YAML contents and sorts them into hook types. +type result struct { + hooks []*release.Hook + generic []manifest +} + +type manifestFile struct { + entries map[string]string + path string + apis chartutil.VersionSet +} + +// sortManifests takes a map of filename/YAML contents, splits the file +// by manifest entries, and sorts the entries into hook types. // // The resulting hooks struct will be populated with all of the generated hooks. // Any file that does not declare one of the hook types will be placed in the // 'generic' bucket. // -// To determine hook type, this looks for a YAML structure like this: -// -// kind: SomeKind -// apiVersion: v1 -// metadata: -// annotations: -// helm.sh/hook: pre-install -// -// Where HOOK_NAME is one of the known hooks. -// -// If a file declares more than one hook, it will be copied into all of the applicable -// hook buckets. (Note: label keys are not unique within the labels section). -// // Files that do not parse into the expected format are simply placed into a map and // returned. func sortManifests(files map[string]string, apis chartutil.VersionSet, sort SortOrder) ([]*release.Hook, []manifest, error) { - hs := []*release.Hook{} - generic := []manifest{} + result := &result{} + + for filePath, c := range files { - for n, c := range files { // Skip partials. We could return these as a separate map, but there doesn't // seem to be any need for that at this time. - if strings.HasPrefix(path.Base(n), "_") { + if strings.HasPrefix(path.Base(filePath), "_") { continue } - // Skip empty files, and log this. + // Skip empty files and log this. if len(strings.TrimSpace(c)) == 0 { - log.Printf("info: manifest %q is empty. Skipping.", n) + log.Printf("info: manifest %q is empty. Skipping.", filePath) continue } - var sh util.SimpleHead - err := yaml.Unmarshal([]byte(c), &sh) + manifestFile := &manifestFile{ + entries: util.SplitManifests(c), + path: filePath, + apis: apis, + } + + if err := manifestFile.sort(result); err != nil { + return result.hooks, result.generic, err + } + } + + return result.hooks, sortByKind(result.generic, sort), nil +} + +// sort takes a manifestFile object which may contain multiple resource definition +// entries and sorts each entry by hook types, and saves the resulting hooks and +// generic manifests (or non-hooks) to the result struct. +// +// To determine hook type, it looks for a YAML structure like this: +// +// kind: SomeKind +// apiVersion: v1 +// metadata: +// annotations: +// helm.sh/hook: pre-install +// +func (file *manifestFile) sort(result *result) error { + for _, m := range file.entries { + var entry util.SimpleHead + err := yaml.Unmarshal([]byte(m), &entry) if err != nil { - e := fmt.Errorf("YAML parse error on %s: %s", n, err) - return hs, generic, e + e := fmt.Errorf("YAML parse error on %s: %s", file.path, err) + return e } - if sh.Version != "" && !apis.Has(sh.Version) { - return hs, generic, fmt.Errorf("apiVersion %q in %s is not available", sh.Version, n) + if entry.Version != "" && !file.apis.Has(entry.Version) { + return fmt.Errorf("apiVersion %q in %s is not available", entry.Version, file.path) } - if sh.Metadata == nil || sh.Metadata.Annotations == nil || len(sh.Metadata.Annotations) == 0 { - generic = append(generic, manifest{name: n, content: c, head: &sh}) + if !hasAnyAnnotation(entry) { + result.generic = append(result.generic, manifest{ + name: file.path, + content: m, + head: &entry, + }) continue } - hookTypes, ok := sh.Metadata.Annotations[hooks.HookAnno] + hookTypes, ok := entry.Metadata.Annotations[hooks.HookAnno] if !ok { - generic = append(generic, manifest{name: n, content: c, head: &sh}) + result.generic = append(result.generic, manifest{ + name: file.path, + content: m, + head: &entry, + }) continue } - hws, _ := sh.Metadata.Annotations[hooks.HookWeightAnno] - hw, err := strconv.Atoi(hws) - if err != nil { - hw = 0 - } + hw := calculateHookWeight(entry) h := &release.Hook{ - Name: sh.Metadata.Name, - Kind: sh.Kind, - Path: n, - Manifest: c, + Name: entry.Metadata.Name, + Kind: entry.Kind, + Path: file.path, + Manifest: m, Events: []release.Hook_Event{}, - Weight: int32(hw), + Weight: hw, } - isHook := false + isKnownHook := false for _, hookType := range strings.Split(hookTypes, ",") { hookType = strings.ToLower(strings.TrimSpace(hookType)) e, ok := events[hookType] if ok { - isHook = true + isKnownHook = true h.Events = append(h.Events, e) } } - if !isHook { + if !isKnownHook { log.Printf("info: skipping unknown hook: %q", hookTypes) continue } - hs = append(hs, h) + + result.hooks = append(result.hooks, h) + } + + return nil +} + +func hasAnyAnnotation(entry util.SimpleHead) bool { + if entry.Metadata == nil || + entry.Metadata.Annotations == nil || + len(entry.Metadata.Annotations) == 0 { + return false } - return hs, sortByKind(generic, sort), nil + + return true +} + +func calculateHookWeight(entry util.SimpleHead) int32 { + hws, _ := entry.Metadata.Annotations[hooks.HookWeightAnno] + hw, err := strconv.Atoi(hws) + if err != nil { + hw = 0 + } + + return int32(hw) } diff --git a/pkg/tiller/hooks_test.go b/pkg/tiller/hooks_test.go index 823e7469c..eabdc7eac 100644 --- a/pkg/tiller/hooks_test.go +++ b/pkg/tiller/hooks_test.go @@ -17,6 +17,7 @@ limitations under the License. package tiller import ( + "reflect" "testing" "github.com/ghodss/yaml" @@ -29,17 +30,17 @@ import ( func TestSortManifests(t *testing.T) { data := []struct { - name string + name []string path string - kind string - hooks []release.Hook_Event + kind []string + hooks map[string][]release.Hook_Event manifest string }{ { - name: "first", + name: []string{"first"}, path: "one", - kind: "Job", - hooks: []release.Hook_Event{release.Hook_PRE_INSTALL}, + kind: []string{"Job"}, + hooks: map[string][]release.Hook_Event{"first": {release.Hook_PRE_INSTALL}}, manifest: `apiVersion: v1 kind: Job metadata: @@ -51,10 +52,10 @@ metadata: `, }, { - name: "second", + name: []string{"second"}, path: "two", - kind: "ReplicaSet", - hooks: []release.Hook_Event{release.Hook_POST_INSTALL}, + kind: []string{"ReplicaSet"}, + hooks: map[string][]release.Hook_Event{"second": {release.Hook_POST_INSTALL}}, manifest: `kind: ReplicaSet apiVersion: v1beta1 metadata: @@ -63,10 +64,10 @@ metadata: "helm.sh/hook": post-install `, }, { - name: "third", + name: []string{"third"}, path: "three", - kind: "ReplicaSet", - hooks: []release.Hook_Event{}, + kind: []string{"ReplicaSet"}, + hooks: map[string][]release.Hook_Event{"third": nil}, manifest: `kind: ReplicaSet apiVersion: v1beta1 metadata: @@ -75,22 +76,21 @@ metadata: "helm.sh/hook": no-such-hook `, }, { - name: "fourth", + name: []string{"fourth"}, path: "four", - kind: "Pod", - hooks: []release.Hook_Event{}, + kind: []string{"Pod"}, + hooks: map[string][]release.Hook_Event{"fourth": nil}, manifest: `kind: Pod apiVersion: v1 metadata: name: fourth annotations: - nothing: here -`, + nothing: here`, }, { - name: "fifth", + name: []string{"fifth"}, path: "five", - kind: "ReplicaSet", - hooks: []release.Hook_Event{release.Hook_POST_DELETE, release.Hook_POST_INSTALL}, + kind: []string{"ReplicaSet"}, + hooks: map[string][]release.Hook_Event{"fifth": {release.Hook_POST_DELETE, release.Hook_POST_INSTALL}}, manifest: `kind: ReplicaSet apiVersion: v1beta1 metadata: @@ -100,19 +100,39 @@ metadata: `, }, { // Regression test: files with an underscore in the base name should be skipped. - name: "sixth", + name: []string{"sixth"}, path: "six/_six", - kind: "ReplicaSet", - hooks: []release.Hook_Event{}, + kind: []string{"ReplicaSet"}, + hooks: map[string][]release.Hook_Event{"sixth": nil}, manifest: `invalid manifest`, // This will fail if partial is not skipped. }, { // Regression test: files with no content should be skipped. - name: "seventh", + name: []string{"seventh"}, path: "seven", - kind: "ReplicaSet", - hooks: []release.Hook_Event{}, + kind: []string{"ReplicaSet"}, + hooks: map[string][]release.Hook_Event{"seventh": nil}, manifest: "", }, + { + name: []string{"eighth", "example-test"}, + path: "eight", + kind: []string{"ConfigMap", "Pod"}, + hooks: map[string][]release.Hook_Event{"eighth": nil, "example-test": {release.Hook_RELEASE_TEST_SUCCESS}}, + manifest: `kind: ConfigMap +apiVersion: v1 +metadata: + name: eighth +data: + name: value +--- +apiVersion: v1 +kind: Pod +metadata: + name: example-test + annotations: + "helm.sh/hook": test-success +`, + }, } manifests := make(map[string]string, len(data)) @@ -126,12 +146,12 @@ metadata: } // This test will fail if 'six' or 'seven' was added. - if len(generic) != 1 { - t.Errorf("Expected 1 generic manifest, got %d", len(generic)) + if len(generic) != 2 { + t.Errorf("Expected 2 generic manifests, got %d", len(generic)) } - if len(hs) != 3 { - t.Errorf("Expected 3 hooks, got %d", len(hs)) + if len(hs) != 4 { + t.Errorf("Expected 4 hooks, got %d", len(hs)) } for _, out := range hs { @@ -142,17 +162,30 @@ metadata: if out.Path != expect.path { t.Errorf("Expected path %s, got %s", expect.path, out.Path) } - if out.Name != expect.name { - t.Errorf("Expected name %s, got %s", expect.name, out.Name) + nameFound := false + for _, expectedName := range expect.name { + if out.Name == expectedName { + nameFound = true + } } - if out.Kind != expect.kind { - t.Errorf("Expected kind %s, got %s", expect.kind, out.Kind) + if !nameFound { + t.Errorf("Got unexpected name %s", out.Name) } - for i := 0; i < len(out.Events); i++ { - if out.Events[i] != expect.hooks[i] { - t.Errorf("Expected event %d, got %d", expect.hooks[i], out.Events[i]) + kindFound := false + for _, expectedKind := range expect.kind { + if out.Kind == expectedKind { + kindFound = true } } + if !kindFound { + t.Errorf("Got unexpected kind %s", out.Kind) + } + + expectedHooks := expect.hooks[out.Name] + if !reflect.DeepEqual(expectedHooks, out.Events) { + t.Errorf("expected events: %v but got: %v", expectedHooks, out.Events) + } + } } if !found { @@ -161,27 +194,40 @@ metadata: } // Verify the sort order - sorted := make([]manifest, len(data)) - for i, s := range data { - var sh util.SimpleHead - err := yaml.Unmarshal([]byte(s.manifest), &sh) - if err != nil { - // This is expected for manifests that are corrupt or empty. - t.Log(err) - } - sorted[i] = manifest{ - content: s.manifest, - name: s.name, - head: &sh, + sorted := []manifest{} + for _, s := range data { + manifests := util.SplitManifests(s.manifest) + mCount := 0 + for _, m := range manifests { + name := s.name[mCount] + + var sh util.SimpleHead + err := yaml.Unmarshal([]byte(m), &sh) + if err != nil { + // This is expected for manifests that are corrupt or empty. + t.Log(err) + } + + //only keep track of non-hook manifests + if err == nil && s.hooks[name] == nil { + another := manifest{ + content: m, + name: name, + head: &sh, + } + sorted = append(sorted, another) + } + + mCount++ } } + sorted = sortByKind(sorted, InstallOrder) for i, m := range generic { if m.content != sorted[i].content { t.Errorf("Expected %q, got %q", m.content, sorted[i].content) } } - } func TestVersionSet(t *testing.T) { diff --git a/pkg/tiller/release_content.go b/pkg/tiller/release_content.go new file mode 100644 index 000000000..7586eb2d8 --- /dev/null +++ b/pkg/tiller/release_content.go @@ -0,0 +1,37 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/proto/hapi/services" +) + +// GetReleaseContent gets all of the stored information for the given release. +func (s *ReleaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleaseContentRequest) (*services.GetReleaseContentResponse, error) { + if !ValidName.MatchString(req.Name) { + return nil, errMissingRelease + } + + if req.Version <= 0 { + rel, err := s.env.Releases.Last(req.Name) + return &services.GetReleaseContentResponse{Release: rel}, err + } + + rel, err := s.env.Releases.Get(req.Name, req.Version) + return &services.GetReleaseContentResponse{Release: rel}, err +} diff --git a/pkg/tiller/release_content_test.go b/pkg/tiller/release_content_test.go new file mode 100644 index 000000000..9a81f1e3f --- /dev/null +++ b/pkg/tiller/release_content_test.go @@ -0,0 +1,41 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/services" + "testing" +) + +func TestGetReleaseContent(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + + res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name, Version: 1}) + if err != nil { + t.Errorf("Error getting release content: %s", err) + } + + if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name { + t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Release.Chart.Metadata.Name) + } +} diff --git a/pkg/tiller/release_history.go b/pkg/tiller/release_history.go index 63f4275e9..09131fad8 100644 --- a/pkg/tiller/release_history.go +++ b/pkg/tiller/release_history.go @@ -25,6 +25,7 @@ import ( // GetHistory gets the history for a given release. func (s *ReleaseServer) GetHistory(ctx context.Context, req *tpb.GetHistoryRequest) (*tpb.GetHistoryResponse, error) { + s.Log("getting history for release %s", req.Name) h, err := s.env.Releases.History(req.Name) if err != nil { return nil, err diff --git a/pkg/tiller/release_install.go b/pkg/tiller/release_install.go new file mode 100644 index 000000000..79b7b6354 --- /dev/null +++ b/pkg/tiller/release_install.go @@ -0,0 +1,222 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + "strings" + + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/hooks" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + relutil "k8s.io/helm/pkg/releaseutil" + "k8s.io/helm/pkg/timeconv" +) + +// InstallRelease installs a release and stores the release record. +func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { + s.Log("preparing install for %s", req.Name) + rel, err := s.prepareRelease(req) + if err != nil { + s.Log("failed install prepare step: %s", err) + res := &services.InstallReleaseResponse{Release: rel} + + // On dry run, append the manifest contents to a failed release. This is + // a stop-gap until we can revisit an error backchannel post-2.0. + if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") { + err = fmt.Errorf("%s\n%s", err, rel.Manifest) + } + return res, err + } + + s.Log("performing install for %s", req.Name) + res, err := s.performRelease(rel, req) + if err != nil { + s.Log("failed install perform step: %s", err) + } + return res, err +} + +// prepareRelease builds a release for an install operation. +func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) { + if req.Chart == nil { + return nil, errMissingChart + } + + name, err := s.uniqName(req.Name, req.ReuseName) + if err != nil { + return nil, err + } + + caps, err := capabilities(s.clientset.Discovery()) + if err != nil { + return nil, err + } + + revision := 1 + ts := timeconv.Now() + options := chartutil.ReleaseOptions{ + Name: name, + Time: ts, + Namespace: req.Namespace, + Revision: revision, + IsInstall: true, + } + valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) + if err != nil { + return nil, err + } + + hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions) + if err != nil { + // Return a release with partial data so that client can show debugging + // information. + rel := &release.Release{ + Name: name, + Namespace: req.Namespace, + Chart: req.Chart, + Config: req.Values, + Info: &release.Info{ + FirstDeployed: ts, + LastDeployed: ts, + Status: &release.Status{Code: release.Status_UNKNOWN}, + Description: fmt.Sprintf("Install failed: %s", err), + }, + Version: 0, + } + if manifestDoc != nil { + rel.Manifest = manifestDoc.String() + } + return rel, err + } + + // Store a release. + rel := &release.Release{ + Name: name, + Namespace: req.Namespace, + Chart: req.Chart, + Config: req.Values, + Info: &release.Info{ + FirstDeployed: ts, + LastDeployed: ts, + Status: &release.Status{Code: release.Status_UNKNOWN}, + Description: "Initial install underway", // Will be overwritten. + }, + Manifest: manifestDoc.String(), + Hooks: hooks, + Version: int32(revision), + } + if len(notesTxt) > 0 { + rel.Info.Status.Notes = notesTxt + } + + err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes()) + return rel, err +} + +// performRelease runs a release. +func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { + res := &services.InstallReleaseResponse{Release: r} + + if req.DryRun { + s.Log("dry run for %s", r.Name) + res.Release.Info.Description = "Dry run complete" + return res, nil + } + + // pre-install hooks + if !req.DisableHooks { + if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil { + return res, err + } + } else { + s.Log("install hooks disabled for %s", req.Name) + } + + switch h, err := s.env.Releases.History(req.Name); { + // if this is a replace operation, append to the release history + case req.ReuseName && err == nil && len(h) >= 1: + s.Log("name reuse for %s requested, replacing release", req.Name) + // get latest release revision + relutil.Reverse(h, relutil.SortByRevision) + + // old release + old := h[0] + + // update old release status + old.Info.Status.Code = release.Status_SUPERSEDED + s.recordRelease(old, true) + + // update new release with next revision number + // so as to append to the old release's history + r.Version = old.Version + 1 + updateReq := &services.UpdateReleaseRequest{ + Wait: req.Wait, + Recreate: false, + Timeout: req.Timeout, + } + if err := s.ReleaseModule.Update(old, r, updateReq, s.env); err != nil { + msg := fmt.Sprintf("Release replace %q failed: %s", r.Name, err) + s.Log("warning: %s", msg) + old.Info.Status.Code = release.Status_SUPERSEDED + r.Info.Status.Code = release.Status_FAILED + r.Info.Description = msg + s.recordRelease(old, true) + s.recordRelease(r, false) + return res, err + } + + default: + // nothing to replace, create as normal + // regular manifests + if err := s.ReleaseModule.Create(r, req, s.env); err != nil { + msg := fmt.Sprintf("Release %q failed: %s", r.Name, err) + s.Log("warning: %s", msg) + r.Info.Status.Code = release.Status_FAILED + r.Info.Description = msg + s.recordRelease(r, false) + return res, fmt.Errorf("release %s failed: %s", r.Name, err) + } + } + + // post-install hooks + if !req.DisableHooks { + if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil { + msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err) + s.Log("warning: %s", msg) + r.Info.Status.Code = release.Status_FAILED + r.Info.Description = msg + s.recordRelease(r, false) + return res, err + } + } + + r.Info.Status.Code = release.Status_DEPLOYED + r.Info.Description = "Install complete" + // This is a tricky case. The release has been created, but the result + // cannot be recorded. The truest thing to tell the user is that the + // release was created. However, the user will not be able to do anything + // further with this release. + // + // One possible strategy would be to do a timed retry to see if we can get + // this stored in the future. + s.recordRelease(r, false) + + return res, nil +} diff --git a/pkg/tiller/release_install_test.go b/pkg/tiller/release_install_test.go new file mode 100644 index 000000000..481dee01b --- /dev/null +++ b/pkg/tiller/release_install_test.go @@ -0,0 +1,454 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + "strings" + "testing" + + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "k8s.io/helm/pkg/version" +) + +func TestInstallRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + // TODO: Refactor this into a mock. + req := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + }, + }, + } + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + if res.Release.Namespace != "spaced" { + t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) + } + + rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + t.Logf("rel: %v", rel) + + if len(rel.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) + } + if rel.Hooks[0].Manifest != manifestWithHook { + t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) + } + + if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { + t.Errorf("Expected event 0 is post install") + } + if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { + t.Errorf("Expected event 0 is pre-delete") + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if len(rel.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if rel.Info.Description != "Install complete" { + t.Errorf("unexpected description: %s", rel.Info.Description) + } +} + +func TestInstallRelease_WithNotes(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + // TODO: Refactor this into a mock. + req := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + {Name: "templates/NOTES.txt", Data: []byte(notesText)}, + }, + }, + } + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + if res.Release.Namespace != "spaced" { + t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) + } + + rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + t.Logf("rel: %v", rel) + + if len(rel.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) + } + if rel.Hooks[0].Manifest != manifestWithHook { + t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) + } + + if rel.Info.Status.Notes != notesText { + t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) + } + + if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { + t.Errorf("Expected event 0 is post install") + } + if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { + t.Errorf("Expected event 0 is pre-delete") + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if len(rel.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if rel.Info.Description != "Install complete" { + t.Errorf("unexpected description: %s", rel.Info.Description) + } +} + +func TestInstallRelease_WithNotesRendered(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + // TODO: Refactor this into a mock. + req := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + {Name: "templates/NOTES.txt", Data: []byte(notesText + " {{.Release.Name}}")}, + }, + }, + } + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + if res.Release.Namespace != "spaced" { + t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) + } + + rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + t.Logf("rel: %v", rel) + + if len(rel.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) + } + if rel.Hooks[0].Manifest != manifestWithHook { + t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) + } + + expectedNotes := fmt.Sprintf("%s %s", notesText, res.Release.Name) + if rel.Info.Status.Notes != expectedNotes { + t.Fatalf("Expected '%s', got '%s'", expectedNotes, rel.Info.Status.Notes) + } + + if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { + t.Errorf("Expected event 0 is post install") + } + if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { + t.Errorf("Expected event 0 is pre-delete") + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if len(rel.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if rel.Info.Description != "Install complete" { + t.Errorf("unexpected description: %s", rel.Info.Description) + } +} + +func TestInstallRelease_TillerVersion(t *testing.T) { + version.Version = "2.2.0" + c := helm.NewContext() + rs := rsFixture() + + // TODO: Refactor this into a mock. + req := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello", TillerVersion: ">=2.2.0"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + }, + }, + } + _, err := rs.InstallRelease(c, req) + if err != nil { + t.Fatalf("Expected valid range. Got %q", err) + } +} + +func TestInstallRelease_WrongTillerVersion(t *testing.T) { + version.Version = "2.2.0" + c := helm.NewContext() + rs := rsFixture() + + // TODO: Refactor this into a mock. + req := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello", TillerVersion: "<2.0.0"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + }, + }, + } + _, err := rs.InstallRelease(c, req) + if err == nil { + t.Fatalf("Expected to fail because of wrong version") + } + + expect := "Chart incompatible with Tiller" + if !strings.Contains(err.Error(), expect) { + t.Errorf("Expected %q to contain %q", err.Error(), expect) + } +} + +func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + // TODO: Refactor this into a mock. + req := &services.InstallReleaseRequest{ + Namespace: "spaced", + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + {Name: "templates/NOTES.txt", Data: []byte(notesText)}, + }, + Dependencies: []*chart.Chart{ + { + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithHook)}, + {Name: "templates/NOTES.txt", Data: []byte(notesText + " child")}, + }, + }, + }, + }, + } + + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + + rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + t.Logf("rel: %v", rel) + + if rel.Info.Status.Notes != notesText { + t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) + } + + if rel.Info.Description != "Install complete" { + t.Errorf("unexpected description: %s", rel.Info.Description) + } +} + +func TestInstallRelease_DryRun(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + + req := &services.InstallReleaseRequest{ + Chart: chartStub(), + DryRun: true, + } + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Errorf("Failed install: %s", err) + } + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + + if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { + t.Errorf("unexpected output: %s", res.Release.Manifest) + } + + if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world") { + t.Errorf("unexpected output: %s", res.Release.Manifest) + } + + if !strings.Contains(res.Release.Manifest, "hello: Earth") { + t.Errorf("Should contain partial content. %s", res.Release.Manifest) + } + + if strings.Contains(res.Release.Manifest, "hello: {{ template \"_planet\" . }}") { + t.Errorf("Should not contain partial templates itself. %s", res.Release.Manifest) + } + + if strings.Contains(res.Release.Manifest, "empty") { + t.Errorf("Should not contain template data for an empty file. %s", res.Release.Manifest) + } + + if _, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version); err == nil { + t.Errorf("Expected no stored release.") + } + + if l := len(res.Release.Hooks); l != 1 { + t.Fatalf("Expected 1 hook, got %d", l) + } + + if res.Release.Hooks[0].LastRun != nil { + t.Error("Expected hook to not be marked as run.") + } + + if res.Release.Info.Description != "Dry run complete" { + t.Errorf("unexpected description: %s", res.Release.Info.Description) + } +} + +func TestInstallRelease_NoHooks(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rs.env.Releases.Create(releaseStub()) + + req := &services.InstallReleaseRequest{ + Chart: chartStub(), + DisableHooks: true, + } + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Errorf("Failed install: %s", err) + } + + if hl := res.Release.Hooks[0].LastRun; hl != nil { + t.Errorf("Expected that no hooks were run. Got %d", hl) + } +} + +func TestInstallRelease_FailedHooks(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rs.env.Releases.Create(releaseStub()) + rs.env.KubeClient = newHookFailingKubeClient() + + req := &services.InstallReleaseRequest{ + Chart: chartStub(), + } + res, err := rs.InstallRelease(c, req) + if err == nil { + t.Error("Expected failed install") + } + + if hl := res.Release.Info.Status.Code; hl != release.Status_FAILED { + t.Errorf("Expected FAILED release. Got %d", hl) + } +} + +func TestInstallRelease_ReuseName(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rel.Info.Status.Code = release.Status_DELETED + rs.env.Releases.Create(rel) + + req := &services.InstallReleaseRequest{ + Chart: chartStub(), + ReuseName: true, + Name: rel.Name, + } + res, err := rs.InstallRelease(c, req) + if err != nil { + t.Fatalf("Failed install: %s", err) + } + + if res.Release.Name != rel.Name { + t.Errorf("expected %q, got %q", rel.Name, res.Release.Name) + } + + getreq := &services.GetReleaseStatusRequest{Name: rel.Name, Version: 0} + getres, err := rs.GetReleaseStatus(c, getreq) + if err != nil { + t.Errorf("Failed to retrieve release: %s", err) + } + if getres.Info.Status.Code != release.Status_DEPLOYED { + t.Errorf("Release status is %q", getres.Info.Status.Code) + } +} diff --git a/pkg/tiller/release_list.go b/pkg/tiller/release_list.go new file mode 100644 index 000000000..14d5c0d38 --- /dev/null +++ b/pkg/tiller/release_list.go @@ -0,0 +1,141 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + relutil "k8s.io/helm/pkg/releaseutil" + "regexp" +) + +// ListReleases lists the releases found by the server. +func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error { + if len(req.StatusCodes) == 0 { + req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED} + } + + //rels, err := s.env.Releases.ListDeployed() + rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool { + for _, sc := range req.StatusCodes { + if sc == r.Info.Status.Code { + return true + } + } + return false + }) + if err != nil { + return err + } + + if req.Namespace != "" { + rels, err = filterByNamespace(req.Namespace, rels) + if err != nil { + return err + } + } + + if len(req.Filter) != 0 { + rels, err = filterReleases(req.Filter, rels) + if err != nil { + return err + } + } + + total := int64(len(rels)) + + switch req.SortBy { + case services.ListSort_NAME: + relutil.SortByName(rels) + case services.ListSort_LAST_RELEASED: + relutil.SortByDate(rels) + } + + if req.SortOrder == services.ListSort_DESC { + ll := len(rels) + rr := make([]*release.Release, ll) + for i, item := range rels { + rr[ll-i-1] = item + } + rels = rr + } + + l := int64(len(rels)) + if req.Offset != "" { + + i := -1 + for ii, cur := range rels { + if cur.Name == req.Offset { + i = ii + } + } + if i == -1 { + return fmt.Errorf("offset %q not found", req.Offset) + } + + if len(rels) < i { + return fmt.Errorf("no items after %q", req.Offset) + } + + rels = rels[i:] + l = int64(len(rels)) + } + + if req.Limit == 0 { + req.Limit = ListDefaultLimit + } + + next := "" + if l > req.Limit { + next = rels[req.Limit].Name + rels = rels[0:req.Limit] + l = int64(len(rels)) + } + + res := &services.ListReleasesResponse{ + Next: next, + Count: l, + Total: total, + Releases: rels, + } + return stream.Send(res) +} + +func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) { + matches := []*release.Release{} + for _, r := range rels { + if namespace == r.Namespace { + matches = append(matches, r) + } + } + return matches, nil +} + +func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) { + preg, err := regexp.Compile(filter) + if err != nil { + return rels, err + } + matches := []*release.Release{} + for _, r := range rels { + if preg.MatchString(r.Name) { + matches = append(matches, r) + } + } + return matches, nil +} diff --git a/pkg/tiller/release_list_test.go b/pkg/tiller/release_list_test.go new file mode 100644 index 000000000..64877422a --- /dev/null +++ b/pkg/tiller/release_list_test.go @@ -0,0 +1,233 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + "testing" + + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" +) + +func TestListReleases(t *testing.T) { + rs := rsFixture() + num := 7 + for i := 0; i < num; i++ { + rel := releaseStub() + rel.Name = fmt.Sprintf("rel-%d", i) + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + mrs := &mockListServer{} + if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != num { + t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) + } +} + +func TestListReleasesByStatus(t *testing.T) { + rs := rsFixture() + stubs := []*release.Release{ + namedReleaseStub("kamal", release.Status_DEPLOYED), + namedReleaseStub("astrolabe", release.Status_DELETED), + namedReleaseStub("octant", release.Status_FAILED), + namedReleaseStub("sextant", release.Status_UNKNOWN), + } + for _, stub := range stubs { + if err := rs.env.Releases.Create(stub); err != nil { + t.Fatalf("Could not create stub: %s", err) + } + } + + tests := []struct { + statusCodes []release.Status_Code + names []string + }{ + { + names: []string{"kamal"}, + statusCodes: []release.Status_Code{release.Status_DEPLOYED}, + }, + { + names: []string{"astrolabe"}, + statusCodes: []release.Status_Code{release.Status_DELETED}, + }, + { + names: []string{"kamal", "octant"}, + statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, + }, + { + names: []string{"kamal", "astrolabe", "octant", "sextant"}, + statusCodes: []release.Status_Code{ + release.Status_DEPLOYED, + release.Status_DELETED, + release.Status_FAILED, + release.Status_UNKNOWN, + }, + }, + } + + for i, tt := range tests { + mrs := &mockListServer{} + if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { + t.Fatalf("Failed listing %d: %s", i, err) + } + + if len(tt.names) != len(mrs.val.Releases) { + t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) + } + + for _, name := range tt.names { + found := false + for _, rel := range mrs.val.Releases { + if rel.Name == name { + found = true + } + } + if !found { + t.Errorf("%d: Did not find name %q", i, name) + } + } + } +} + +func TestListReleasesSort(t *testing.T) { + rs := rsFixture() + + // Put them in by reverse order so that the mock doesn't "accidentally" + // sort. + num := 7 + for i := num; i > 0; i-- { + rel := releaseStub() + rel.Name = fmt.Sprintf("rel-%d", i) + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + limit := 6 + mrs := &mockListServer{} + req := &services.ListReleasesRequest{ + Offset: "", + Limit: int64(limit), + SortBy: services.ListSort_NAME, + } + if err := rs.ListReleases(req, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != limit { + t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) + } + + for i := 0; i < limit; i++ { + n := fmt.Sprintf("rel-%d", i+1) + if mrs.val.Releases[i].Name != n { + t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) + } + } +} + +func TestListReleasesFilter(t *testing.T) { + rs := rsFixture() + names := []string{ + "axon", + "dendrite", + "neuron", + "neuroglia", + "synapse", + "nucleus", + "organelles", + } + num := 7 + for i := 0; i < num; i++ { + rel := releaseStub() + rel.Name = names[i] + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + mrs := &mockListServer{} + req := &services.ListReleasesRequest{ + Offset: "", + Limit: 64, + Filter: "neuro[a-z]+", + SortBy: services.ListSort_NAME, + } + if err := rs.ListReleases(req, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != 2 { + t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) + } + + if mrs.val.Releases[0].Name != "neuroglia" { + t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) + } + if mrs.val.Releases[1].Name != "neuron" { + t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) + } +} + +func TestReleasesNamespace(t *testing.T) { + rs := rsFixture() + + names := []string{ + "axon", + "dendrite", + "neuron", + "ribosome", + } + + namespaces := []string{ + "default", + "test123", + "test123", + "cerebellum", + } + num := 4 + for i := 0; i < num; i++ { + rel := releaseStub() + rel.Name = names[i] + rel.Namespace = namespaces[i] + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + } + + mrs := &mockListServer{} + req := &services.ListReleasesRequest{ + Offset: "", + Limit: 64, + Namespace: "test123", + } + + if err := rs.ListReleases(req, mrs); err != nil { + t.Fatalf("Failed listing: %s", err) + } + + if len(mrs.val.Releases) != 2 { + t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) + } +} diff --git a/pkg/tiller/release_modules.go b/pkg/tiller/release_modules.go new file mode 100644 index 000000000..2f2a3c6ff --- /dev/null +++ b/pkg/tiller/release_modules.go @@ -0,0 +1,173 @@ +/* +Copyright 2017 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "bytes" + "errors" + "fmt" + "log" + "strings" + + "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" + + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/kube" + "k8s.io/helm/pkg/proto/hapi/release" + rudderAPI "k8s.io/helm/pkg/proto/hapi/rudder" + "k8s.io/helm/pkg/proto/hapi/services" + relutil "k8s.io/helm/pkg/releaseutil" + "k8s.io/helm/pkg/rudder" + "k8s.io/helm/pkg/tiller/environment" +) + +// ReleaseModule is an interface that allows ReleaseServer to run operations on release via either local implementation or Rudder service +type ReleaseModule interface { + Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error + Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error + Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error + Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) + Delete(r *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (string, []error) +} + +// LocalReleaseModule is a local implementation of ReleaseModule +type LocalReleaseModule struct { + clientset internalclientset.Interface +} + +// Create creates a release via kubeclient from provided environment +func (m *LocalReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error { + b := bytes.NewBufferString(r.Manifest) + return env.KubeClient.Create(r.Namespace, b, req.Timeout, req.Wait) +} + +// Update performs an update from current to target release +func (m *LocalReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error { + c := bytes.NewBufferString(current.Manifest) + t := bytes.NewBufferString(target.Manifest) + return env.KubeClient.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait) +} + +// Rollback performs a rollback from current to target release +func (m *LocalReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error { + c := bytes.NewBufferString(current.Manifest) + t := bytes.NewBufferString(target.Manifest) + return env.KubeClient.Update(target.Namespace, c, t, req.Force, req.Recreate, req.Timeout, req.Wait) +} + +// Status returns kubectl-like formatted status of release objects +func (m *LocalReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) { + return env.KubeClient.Get(r.Namespace, bytes.NewBufferString(r.Manifest)) +} + +// Delete deletes the release and returns manifests that were kept in the deletion process +func (m *LocalReleaseModule) Delete(rel *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (kept string, errs []error) { + vs, err := GetVersionSet(m.clientset.Discovery()) + if err != nil { + return rel.Manifest, []error{fmt.Errorf("Could not get apiVersions from Kubernetes: %v", err)} + } + return DeleteRelease(rel, vs, env.KubeClient) +} + +// RemoteReleaseModule is a ReleaseModule which calls Rudder service to operate on a release +type RemoteReleaseModule struct{} + +// Create calls rudder.InstallRelease +func (m *RemoteReleaseModule) Create(r *release.Release, req *services.InstallReleaseRequest, env *environment.Environment) error { + request := &rudderAPI.InstallReleaseRequest{Release: r} + _, err := rudder.InstallRelease(request) + return err +} + +// Update calls rudder.UpgradeRelease +func (m *RemoteReleaseModule) Update(current, target *release.Release, req *services.UpdateReleaseRequest, env *environment.Environment) error { + upgrade := &rudderAPI.UpgradeReleaseRequest{ + Current: current, + Target: target, + Recreate: req.Recreate, + Timeout: req.Timeout, + Wait: req.Wait, + Force: req.Force, + } + _, err := rudder.UpgradeRelease(upgrade) + return err +} + +// Rollback calls rudder.Rollback +func (m *RemoteReleaseModule) Rollback(current, target *release.Release, req *services.RollbackReleaseRequest, env *environment.Environment) error { + rollback := &rudderAPI.RollbackReleaseRequest{ + Current: current, + Target: target, + Recreate: req.Recreate, + Timeout: req.Timeout, + Wait: req.Wait, + } + _, err := rudder.RollbackRelease(rollback) + return err +} + +// Status returns status retrieved from rudder.ReleaseStatus +func (m *RemoteReleaseModule) Status(r *release.Release, req *services.GetReleaseStatusRequest, env *environment.Environment) (string, error) { + statusRequest := &rudderAPI.ReleaseStatusRequest{Release: r} + resp, err := rudder.ReleaseStatus(statusRequest) + return resp.Info.Status.Resources, err +} + +// Delete calls rudder.DeleteRelease +func (m *RemoteReleaseModule) Delete(r *release.Release, req *services.UninstallReleaseRequest, env *environment.Environment) (string, []error) { + deleteRequest := &rudderAPI.DeleteReleaseRequest{Release: r} + resp, err := rudder.DeleteRelease(deleteRequest) + if err != nil { + return resp.Release.Manifest, []error{err} + } + return resp.Release.Manifest, []error{} +} + +// DeleteRelease is a helper that allows Rudder to delete a release without exposing most of Tiller inner functions +func DeleteRelease(rel *release.Release, vs chartutil.VersionSet, kubeClient environment.KubeClient) (kept string, errs []error) { + manifests := relutil.SplitManifests(rel.Manifest) + _, files, err := sortManifests(manifests, vs, UninstallOrder) + if err != nil { + // We could instead just delete everything in no particular order. + // FIXME: One way to delete at this point would be to try a label-based + // deletion. The problem with this is that we could get a false positive + // and delete something that was not legitimately part of this release. + return rel.Manifest, []error{fmt.Errorf("corrupted release record. You must manually delete the resources: %s", err)} + } + + filesToKeep, filesToDelete := filterManifestsToKeep(files) + if len(filesToKeep) > 0 { + kept = summarizeKeptManifests(filesToKeep) + } + + errs = []error{} + for _, file := range filesToDelete { + b := bytes.NewBufferString(strings.TrimSpace(file.content)) + if b.Len() == 0 { + continue + } + if err := kubeClient.Delete(rel.Namespace, b); err != nil { + log.Printf("uninstall: Failed deletion of %q: %s", rel.Name, err) + if err == kube.ErrNoObjectsVisited { + // Rewrite the message from "no objects visited" + err = errors.New("object not found, skipping delete") + } + errs = append(errs, err) + } + } + return kept, errs +} diff --git a/pkg/tiller/release_rollback.go b/pkg/tiller/release_rollback.go new file mode 100644 index 000000000..43e06a6b6 --- /dev/null +++ b/pkg/tiller/release_rollback.go @@ -0,0 +1,152 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/hooks" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "k8s.io/helm/pkg/timeconv" +) + +// RollbackRelease rolls back to a previous version of the given release. +func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) { + err := s.env.Releases.LockRelease(req.Name) + if err != nil { + return nil, err + } + defer s.env.Releases.UnlockRelease(req.Name) + + s.Log("preparing rollback of %s", req.Name) + currentRelease, targetRelease, err := s.prepareRollback(req) + if err != nil { + return nil, err + } + + s.Log("performing rollback of %s", req.Name) + res, err := s.performRollback(currentRelease, targetRelease, req) + if err != nil { + return res, err + } + + if !req.DryRun { + s.Log("creating rolled back release %s", req.Name) + if err := s.env.Releases.Create(targetRelease); err != nil { + return res, err + } + } + + return res, nil +} + +// prepareRollback finds the previous release and prepares a new release object with +// the previous release's configuration +func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) { + switch { + case !ValidName.MatchString(req.Name): + return nil, nil, errMissingRelease + case req.Version < 0: + return nil, nil, errInvalidRevision + } + + crls, err := s.env.Releases.Last(req.Name) + if err != nil { + return nil, nil, err + } + + rbv := req.Version + if req.Version == 0 { + rbv = crls.Version - 1 + } + + s.Log("rolling back %s (current: v%d, target: v%d)", req.Name, crls.Version, rbv) + + prls, err := s.env.Releases.Get(req.Name, rbv) + if err != nil { + return nil, nil, err + } + + // Store a new release object with previous release's configuration + target := &release.Release{ + Name: req.Name, + Namespace: crls.Namespace, + Chart: prls.Chart, + Config: prls.Config, + Info: &release.Info{ + FirstDeployed: crls.Info.FirstDeployed, + LastDeployed: timeconv.Now(), + Status: &release.Status{ + Code: release.Status_UNKNOWN, + Notes: prls.Info.Status.Notes, + }, + // Because we lose the reference to rbv elsewhere, we set the + // message here, and only override it later if we experience failure. + Description: fmt.Sprintf("Rollback to %d", rbv), + }, + Version: crls.Version + 1, + Manifest: prls.Manifest, + Hooks: prls.Hooks, + } + + return crls, target, nil +} + +func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) { + res := &services.RollbackReleaseResponse{Release: targetRelease} + + if req.DryRun { + s.Log("dry run for %s", targetRelease.Name) + return res, nil + } + + // pre-rollback hooks + if !req.DisableHooks { + if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PreRollback, req.Timeout); err != nil { + return res, err + } + } else { + s.Log("rollback hooks disabled for %s", req.Name) + } + + if err := s.ReleaseModule.Rollback(currentRelease, targetRelease, req, s.env); err != nil { + msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err) + s.Log("warning: %s", msg) + currentRelease.Info.Status.Code = release.Status_SUPERSEDED + targetRelease.Info.Status.Code = release.Status_FAILED + targetRelease.Info.Description = msg + s.recordRelease(currentRelease, true) + s.recordRelease(targetRelease, false) + return res, err + } + + // post-rollback hooks + if !req.DisableHooks { + if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PostRollback, req.Timeout); err != nil { + return res, err + } + } + + currentRelease.Info.Status.Code = release.Status_SUPERSEDED + s.recordRelease(currentRelease, true) + + targetRelease.Info.Status.Code = release.Status_DEPLOYED + + return res, nil +} diff --git a/pkg/tiller/release_rollback_test.go b/pkg/tiller/release_rollback_test.go new file mode 100644 index 000000000..11ba9d7e2 --- /dev/null +++ b/pkg/tiller/release_rollback_test.go @@ -0,0 +1,226 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "strings" + "testing" +) + +func TestRollbackRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + upgradedRel := upgradeReleaseVersion(rel) + upgradedRel.Hooks = []*release.Hook{ + { + Name: "test-cm", + Kind: "ConfigMap", + Path: "test-cm", + Manifest: manifestWithRollbackHooks, + Events: []release.Hook_Event{ + release.Hook_PRE_ROLLBACK, + release.Hook_POST_ROLLBACK, + }, + }, + } + + upgradedRel.Manifest = "hello world" + rs.env.Releases.Update(rel) + rs.env.Releases.Create(upgradedRel) + + req := &services.RollbackReleaseRequest{ + Name: rel.Name, + } + res, err := rs.RollbackRelease(c, req) + if err != nil { + t.Fatalf("Failed rollback: %s", err) + } + + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + + if res.Release.Name != rel.Name { + t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) + } + + if res.Release.Namespace != rel.Namespace { + t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) + } + + if res.Release.Version != 3 { + t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) + } + + updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + if len(updated.Hooks) != 2 { + t.Fatalf("Expected 2 hooks, got %d", len(updated.Hooks)) + } + + if updated.Hooks[0].Manifest != manifestWithHook { + t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) + } + + anotherUpgradedRelease := upgradeReleaseVersion(upgradedRel) + rs.env.Releases.Update(upgradedRel) + rs.env.Releases.Create(anotherUpgradedRelease) + + res, err = rs.RollbackRelease(c, req) + if err != nil { + t.Fatalf("Failed rollback: %s", err) + } + + updated, err = rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + if len(updated.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) + } + + if updated.Hooks[0].Manifest != manifestWithRollbackHooks { + t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) + } + + if res.Release.Version != 4 { + t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) + } + + if updated.Hooks[0].Events[0] != release.Hook_PRE_ROLLBACK { + t.Errorf("Expected event 0 to be pre rollback") + } + + if updated.Hooks[0].Events[1] != release.Hook_POST_ROLLBACK { + t.Errorf("Expected event 1 to be post rollback") + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if len(updated.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(updated.Manifest, "hello world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if res.Release.Info.Description != "Rollback to 2" { + t.Errorf("Expected rollback to 2, got %q", res.Release.Info.Description) + } +} + +func TestRollbackWithReleaseVersion(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + upgradedRel := upgradeReleaseVersion(rel) + rs.env.Releases.Update(rel) + rs.env.Releases.Create(upgradedRel) + + req := &services.RollbackReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Version: 1, + } + + _, err := rs.RollbackRelease(c, req) + if err != nil { + t.Fatalf("Failed rollback: %s", err) + } +} + +func TestRollbackReleaseNoHooks(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rel.Hooks = []*release.Hook{ + { + Name: "test-cm", + Kind: "ConfigMap", + Path: "test-cm", + Manifest: manifestWithRollbackHooks, + Events: []release.Hook_Event{ + release.Hook_PRE_ROLLBACK, + release.Hook_POST_ROLLBACK, + }, + }, + } + rs.env.Releases.Create(rel) + upgradedRel := upgradeReleaseVersion(rel) + rs.env.Releases.Update(rel) + rs.env.Releases.Create(upgradedRel) + + req := &services.RollbackReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + } + + res, err := rs.RollbackRelease(c, req) + if err != nil { + t.Fatalf("Failed rollback: %s", err) + } + + if hl := res.Release.Hooks[0].LastRun; hl != nil { + t.Errorf("Expected that no hooks were run. Got %d", hl) + } +} + +func TestRollbackReleaseFailure(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + upgradedRel := upgradeReleaseVersion(rel) + rs.env.Releases.Update(rel) + rs.env.Releases.Create(upgradedRel) + + req := &services.RollbackReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + } + + rs.env.KubeClient = newUpdateFailingKubeClient() + res, err := rs.RollbackRelease(c, req) + if err == nil { + t.Error("Expected failed rollback") + } + + if targetStatus := res.Release.Info.Status.Code; targetStatus != release.Status_FAILED { + t.Errorf("Expected FAILED release. Got %v", targetStatus) + } + + oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) + if err != nil { + t.Errorf("Expected to be able to get previous release") + } + if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { + t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) + } +} diff --git a/pkg/tiller/release_server.go b/pkg/tiller/release_server.go index 5bd0a48de..07ea872df 100644 --- a/pkg/tiller/release_server.go +++ b/pkg/tiller/release_server.go @@ -20,24 +20,19 @@ import ( "bytes" "errors" "fmt" - "log" "path" "regexp" "strings" "github.com/technosophos/moniker" - ctx "golang.org/x/net/context" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/discovery" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset" "k8s.io/helm/pkg/chartutil" - "k8s.io/helm/pkg/hooks" - "k8s.io/helm/pkg/kube" "k8s.io/helm/pkg/proto/hapi/chart" "k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/services" - reltesting "k8s.io/helm/pkg/releasetesting" relutil "k8s.io/helm/pkg/releaseutil" "k8s.io/helm/pkg/tiller/environment" "k8s.io/helm/pkg/timeconv" @@ -82,267 +77,29 @@ var ValidName = regexp.MustCompile("^(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])+ // ReleaseServer implements the server-side gRPC endpoint for the HAPI services. type ReleaseServer struct { + ReleaseModule env *environment.Environment clientset internalclientset.Interface + Log func(string, ...interface{}) } // NewReleaseServer creates a new release server. -func NewReleaseServer(env *environment.Environment, clientset internalclientset.Interface) *ReleaseServer { - return &ReleaseServer{ - env: env, - clientset: clientset, - } -} - -// ListReleases lists the releases found by the server. -func (s *ReleaseServer) ListReleases(req *services.ListReleasesRequest, stream services.ReleaseService_ListReleasesServer) error { - if len(req.StatusCodes) == 0 { - req.StatusCodes = []release.Status_Code{release.Status_DEPLOYED} - } - - //rels, err := s.env.Releases.ListDeployed() - rels, err := s.env.Releases.ListFilterAll(func(r *release.Release) bool { - for _, sc := range req.StatusCodes { - if sc == r.Info.Status.Code { - return true - } - } - return false - }) - if err != nil { - return err - } - - if req.Namespace != "" { - rels, err = filterByNamespace(req.Namespace, rels) - if err != nil { - return err - } - } - - if len(req.Filter) != 0 { - rels, err = filterReleases(req.Filter, rels) - if err != nil { - return err - } - } - - total := int64(len(rels)) - - switch req.SortBy { - case services.ListSort_NAME: - relutil.SortByName(rels) - case services.ListSort_LAST_RELEASED: - relutil.SortByDate(rels) - } - - if req.SortOrder == services.ListSort_DESC { - ll := len(rels) - rr := make([]*release.Release, ll) - for i, item := range rels { - rr[ll-i-1] = item - } - rels = rr - } - - l := int64(len(rels)) - if req.Offset != "" { - - i := -1 - for ii, cur := range rels { - if cur.Name == req.Offset { - i = ii - } - } - if i == -1 { - return fmt.Errorf("offset %q not found", req.Offset) - } - - if len(rels) < i { - return fmt.Errorf("no items after %q", req.Offset) - } - - rels = rels[i:] - l = int64(len(rels)) - } - - if req.Limit == 0 { - req.Limit = ListDefaultLimit - } - - next := "" - if l > req.Limit { - next = rels[req.Limit].Name - rels = rels[0:req.Limit] - l = int64(len(rels)) - } - - res := &services.ListReleasesResponse{ - Next: next, - Count: l, - Total: total, - Releases: rels, - } - return stream.Send(res) -} - -func filterByNamespace(namespace string, rels []*release.Release) ([]*release.Release, error) { - matches := []*release.Release{} - for _, r := range rels { - if namespace == r.Namespace { - matches = append(matches, r) - } - } - return matches, nil -} - -func filterReleases(filter string, rels []*release.Release) ([]*release.Release, error) { - preg, err := regexp.Compile(filter) - if err != nil { - return rels, err - } - matches := []*release.Release{} - for _, r := range rels { - if preg.MatchString(r.Name) { - matches = append(matches, r) - } - } - return matches, nil -} - -// GetVersion sends the server version. -func (s *ReleaseServer) GetVersion(c ctx.Context, req *services.GetVersionRequest) (*services.GetVersionResponse, error) { - v := version.GetVersionProto() - return &services.GetVersionResponse{Version: v}, nil -} - -// GetReleaseStatus gets the status information for a named release. -func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) { - if !ValidName.MatchString(req.Name) { - return nil, errMissingRelease - } - - var rel *release.Release - - if req.Version <= 0 { - var err error - rel, err = s.env.Releases.Last(req.Name) - if err != nil { - return nil, fmt.Errorf("getting deployed release %q: %s", req.Name, err) - } +func NewReleaseServer(env *environment.Environment, clientset internalclientset.Interface, useRemote bool) *ReleaseServer { + var releaseModule ReleaseModule + if useRemote { + releaseModule = &RemoteReleaseModule{} } else { - var err error - if rel, err = s.env.Releases.Get(req.Name, req.Version); err != nil { - return nil, fmt.Errorf("getting release '%s' (v%d): %s", req.Name, req.Version, err) - } - } - - if rel.Info == nil { - return nil, errors.New("release info is missing") - } - if rel.Chart == nil { - return nil, errors.New("release chart is missing") - } - - sc := rel.Info.Status.Code - statusResp := &services.GetReleaseStatusResponse{ - Name: rel.Name, - Namespace: rel.Namespace, - Info: rel.Info, - } - - // Ok, we got the status of the release as we had jotted down, now we need to match the - // manifest we stashed away with reality from the cluster. - kubeCli := s.env.KubeClient - resp, err := kubeCli.Get(rel.Namespace, bytes.NewBufferString(rel.Manifest)) - if sc == release.Status_DELETED || sc == release.Status_FAILED { - // Skip errors if this is already deleted or failed. - return statusResp, nil - } else if err != nil { - log.Printf("warning: Get for %s failed: %v", rel.Name, err) - return nil, err - } - rel.Info.Status.Resources = resp - return statusResp, nil -} - -// GetReleaseContent gets all of the stored information for the given release. -func (s *ReleaseServer) GetReleaseContent(c ctx.Context, req *services.GetReleaseContentRequest) (*services.GetReleaseContentResponse, error) { - if !ValidName.MatchString(req.Name) { - return nil, errMissingRelease - } - - if req.Version <= 0 { - rel, err := s.env.Releases.Deployed(req.Name) - return &services.GetReleaseContentResponse{Release: rel}, err - } - - rel, err := s.env.Releases.Get(req.Name, req.Version) - return &services.GetReleaseContentResponse{Release: rel}, err -} - -// UpdateRelease takes an existing release and new information, and upgrades the release. -func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { - currentRelease, updatedRelease, err := s.prepareUpdate(req) - if err != nil { - return nil, err - } - - res, err := s.performUpdate(currentRelease, updatedRelease, req) - if err != nil { - return res, err - } - - if !req.DryRun { - if err := s.env.Releases.Create(updatedRelease); err != nil { - return res, err - } - } - - return res, nil -} - -func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { - res := &services.UpdateReleaseResponse{Release: updatedRelease} - - if req.DryRun { - log.Printf("Dry run for %s", updatedRelease.Name) - res.Release.Info.Description = "Dry run complete" - return res, nil - } - - // pre-upgrade hooks - if !req.DisableHooks { - if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil { - return res, err + releaseModule = &LocalReleaseModule{ + clientset: clientset, } } - if err := s.performKubeUpdate(originalRelease, updatedRelease, req.Recreate, req.Timeout, req.Wait); err != nil { - msg := fmt.Sprintf("Upgrade %q failed: %s", updatedRelease.Name, err) - log.Printf("warning: %s", msg) - originalRelease.Info.Status.Code = release.Status_SUPERSEDED - updatedRelease.Info.Status.Code = release.Status_FAILED - updatedRelease.Info.Description = msg - s.recordRelease(originalRelease, true) - s.recordRelease(updatedRelease, false) - return res, err - } - - // post-upgrade hooks - if !req.DisableHooks { - if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil { - return res, err - } + return &ReleaseServer{ + env: env, + clientset: clientset, + ReleaseModule: releaseModule, + Log: func(_ string, _ ...interface{}) {}, } - - originalRelease.Info.Status.Code = release.Status_SUPERSEDED - s.recordRelease(originalRelease, true) - - updatedRelease.Info.Status.Code = release.Status_DEPLOYED - updatedRelease.Info.Description = "Upgrade complete" - - return res, nil } // reuseValues copies values from the current release to a new release if the @@ -356,19 +113,19 @@ func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.R func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current *release.Release) error { if req.ResetValues { // If ResetValues is set, we comletely ignore current.Config. - log.Print("Reset values to the chart's original version.") + s.Log("resetting values to the chart's original version") return nil } // If the ReuseValues flag is set, we always copy the old values over the new config's values. if req.ReuseValues { - log.Print("Reusing the old release's values") + s.Log("reusing the old release's values") // We have to regenerate the old coalesced values: oldVals, err := chartutil.CoalesceValues(current.Chart, current.Config) if err != nil { err := fmt.Errorf("failed to rebuild old values: %s", err) - log.Print(err) + s.Log("%s", err) return err } nv, err := oldVals.YAML() @@ -385,205 +142,12 @@ func (s *ReleaseServer) reuseValues(req *services.UpdateReleaseRequest, current current.Config != nil && current.Config.Raw != "" && current.Config.Raw != "{}\n" { - log.Printf("Copying values from %s (v%d) to new release.", current.Name, current.Version) + s.Log("copying values from %s (v%d) to new release.", current.Name, current.Version) req.Values = current.Config } return nil } -// prepareUpdate builds an updated release for an update operation. -func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) { - if !ValidName.MatchString(req.Name) { - return nil, nil, errMissingRelease - } - - if req.Chart == nil { - return nil, nil, errMissingChart - } - - // finds the non-deleted release with the given name - currentRelease, err := s.env.Releases.Last(req.Name) - if err != nil { - return nil, nil, err - } - - // If new values were not supplied in the upgrade, re-use the existing values. - if err := s.reuseValues(req, currentRelease); err != nil { - return nil, nil, err - } - - // Increment revision count. This is passed to templates, and also stored on - // the release object. - revision := currentRelease.Version + 1 - - ts := timeconv.Now() - options := chartutil.ReleaseOptions{ - Name: req.Name, - Time: ts, - Namespace: currentRelease.Namespace, - IsUpgrade: true, - Revision: int(revision), - } - - caps, err := capabilities(s.clientset.Discovery()) - if err != nil { - return nil, nil, err - } - valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) - if err != nil { - return nil, nil, err - } - - hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions) - if err != nil { - return nil, nil, err - } - - // Store an updated release. - updatedRelease := &release.Release{ - Name: req.Name, - Namespace: currentRelease.Namespace, - Chart: req.Chart, - Config: req.Values, - Info: &release.Info{ - FirstDeployed: currentRelease.Info.FirstDeployed, - LastDeployed: ts, - Status: &release.Status{Code: release.Status_UNKNOWN}, - Description: "Preparing upgrade", // This should be overwritten later. - }, - Version: revision, - Manifest: manifestDoc.String(), - Hooks: hooks, - } - - if len(notesTxt) > 0 { - updatedRelease.Info.Status.Notes = notesTxt - } - err = validateManifest(s.env.KubeClient, currentRelease.Namespace, manifestDoc.Bytes()) - return currentRelease, updatedRelease, err -} - -// RollbackRelease rolls back to a previous version of the given release. -func (s *ReleaseServer) RollbackRelease(c ctx.Context, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) { - currentRelease, targetRelease, err := s.prepareRollback(req) - if err != nil { - return nil, err - } - - res, err := s.performRollback(currentRelease, targetRelease, req) - if err != nil { - return res, err - } - - if !req.DryRun { - if err := s.env.Releases.Create(targetRelease); err != nil { - return res, err - } - } - - return res, nil -} - -func (s *ReleaseServer) performRollback(currentRelease, targetRelease *release.Release, req *services.RollbackReleaseRequest) (*services.RollbackReleaseResponse, error) { - res := &services.RollbackReleaseResponse{Release: targetRelease} - - if req.DryRun { - log.Printf("Dry run for %s", targetRelease.Name) - return res, nil - } - - // pre-rollback hooks - if !req.DisableHooks { - if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PreRollback, req.Timeout); err != nil { - return res, err - } - } - - if err := s.performKubeUpdate(currentRelease, targetRelease, req.Recreate, req.Timeout, req.Wait); err != nil { - msg := fmt.Sprintf("Rollback %q failed: %s", targetRelease.Name, err) - log.Printf("warning: %s", msg) - currentRelease.Info.Status.Code = release.Status_SUPERSEDED - targetRelease.Info.Status.Code = release.Status_FAILED - targetRelease.Info.Description = msg - s.recordRelease(currentRelease, true) - s.recordRelease(targetRelease, false) - return res, err - } - - // post-rollback hooks - if !req.DisableHooks { - if err := s.execHook(targetRelease.Hooks, targetRelease.Name, targetRelease.Namespace, hooks.PostRollback, req.Timeout); err != nil { - return res, err - } - } - - currentRelease.Info.Status.Code = release.Status_SUPERSEDED - s.recordRelease(currentRelease, true) - - targetRelease.Info.Status.Code = release.Status_DEPLOYED - - return res, nil -} - -func (s *ReleaseServer) performKubeUpdate(currentRelease, targetRelease *release.Release, recreate bool, timeout int64, shouldWait bool) error { - kubeCli := s.env.KubeClient - current := bytes.NewBufferString(currentRelease.Manifest) - target := bytes.NewBufferString(targetRelease.Manifest) - return kubeCli.Update(targetRelease.Namespace, current, target, recreate, timeout, shouldWait) -} - -// prepareRollback finds the previous release and prepares a new release object with -// the previous release's configuration -func (s *ReleaseServer) prepareRollback(req *services.RollbackReleaseRequest) (*release.Release, *release.Release, error) { - switch { - case !ValidName.MatchString(req.Name): - return nil, nil, errMissingRelease - case req.Version < 0: - return nil, nil, errInvalidRevision - } - - crls, err := s.env.Releases.Last(req.Name) - if err != nil { - return nil, nil, err - } - - rbv := req.Version - if req.Version == 0 { - rbv = crls.Version - 1 - } - - log.Printf("rolling back %s (current: v%d, target: v%d)", req.Name, crls.Version, rbv) - - prls, err := s.env.Releases.Get(req.Name, rbv) - if err != nil { - return nil, nil, err - } - - // Store a new release object with previous release's configuration - target := &release.Release{ - Name: req.Name, - Namespace: crls.Namespace, - Chart: prls.Chart, - Config: prls.Config, - Info: &release.Info{ - FirstDeployed: crls.Info.FirstDeployed, - LastDeployed: timeconv.Now(), - Status: &release.Status{ - Code: release.Status_UNKNOWN, - Notes: prls.Info.Status.Notes, - }, - // Because we lose the reference to rbv elsewhere, we set the - // message here, and only override it later if we experience failure. - Description: fmt.Sprintf("Rollback to %d", rbv), - }, - Version: crls.Version + 1, - Manifest: prls.Manifest, - Hooks: prls.Hooks, - } - - return crls, target, nil -} - func (s *ReleaseServer) uniqName(start string, reuse bool) (string, error) { // If a name is supplied, we check to see if that name is taken. If not, it @@ -604,13 +168,13 @@ func (s *ReleaseServer) uniqName(start string, reuse bool) (string, error) { if st := rel.Info.Status.Code; reuse && (st == release.Status_DELETED || st == release.Status_FAILED) { // Allowe re-use of names if the previous release is marked deleted. - log.Printf("reusing name %q", start) + s.Log("name %s exists but is not in use, reusing name", start) return start, nil } else if reuse { return "", errors.New("cannot re-use a name that is still in use") } - return "", fmt.Errorf("a release named %q already exists.\nPlease run: helm ls --all %q; helm del --help", start, start) + return "", fmt.Errorf("a release named %s already exists.\nRun: helm ls --all %s; to check the status of the release\nOr run: helm del --purge %s; to delete it", start, start, start) } maxTries := 5 @@ -623,9 +187,9 @@ func (s *ReleaseServer) uniqName(start string, reuse bool) (string, error) { if _, err := s.env.Releases.Get(name, 1); strings.Contains(err.Error(), "not found") { return name, nil } - log.Printf("info: Name %q is taken. Searching again.", name) + s.Log("info: generated name %s is taken. Searching again.", name) } - log.Printf("warning: No available release names found after %d tries", maxTries) + s.Log("warning: No available release names found after %d tries", maxTries) return "ERROR", errors.New("no available release name found") } @@ -635,41 +199,19 @@ func (s *ReleaseServer) engine(ch *chart.Chart) environment.Engine { if r, ok := s.env.EngineYard.Get(ch.Metadata.Engine); ok { renderer = r } else { - log.Printf("warning: %s requested non-existent template engine %s", ch.Metadata.Name, ch.Metadata.Engine) + s.Log("warning: %s requested non-existent template engine %s", ch.Metadata.Name, ch.Metadata.Engine) } } return renderer } -// InstallRelease installs a release and stores the release record. -func (s *ReleaseServer) InstallRelease(c ctx.Context, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { - rel, err := s.prepareRelease(req) - if err != nil { - log.Printf("Failed install prepare step: %s", err) - res := &services.InstallReleaseResponse{Release: rel} - - // On dry run, append the manifest contents to a failed release. This is - // a stop-gap until we can revisit an error backchannel post-2.0. - if req.DryRun && strings.HasPrefix(err.Error(), "YAML parse error") { - err = fmt.Errorf("%s\n%s", err, rel.Manifest) - } - return res, err - } - - res, err := s.performRelease(rel, req) - if err != nil { - log.Printf("Failed install perform step: %s", err) - } - return res, err -} - // capabilities builds a Capabilities from discovery information. func capabilities(disc discovery.DiscoveryInterface) (*chartutil.Capabilities, error) { sv, err := disc.ServerVersion() if err != nil { return nil, err } - vs, err := getVersionSet(disc) + vs, err := GetVersionSet(disc) if err != nil { return nil, fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err) } @@ -680,84 +222,8 @@ func capabilities(disc discovery.DiscoveryInterface) (*chartutil.Capabilities, e }, nil } -// prepareRelease builds a release for an install operation. -func (s *ReleaseServer) prepareRelease(req *services.InstallReleaseRequest) (*release.Release, error) { - if req.Chart == nil { - return nil, errMissingChart - } - - name, err := s.uniqName(req.Name, req.ReuseName) - if err != nil { - return nil, err - } - - caps, err := capabilities(s.clientset.Discovery()) - if err != nil { - return nil, err - } - - revision := 1 - ts := timeconv.Now() - options := chartutil.ReleaseOptions{ - Name: name, - Time: ts, - Namespace: req.Namespace, - Revision: revision, - IsInstall: true, - } - valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) - if err != nil { - return nil, err - } - - hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions) - if err != nil { - // Return a release with partial data so that client can show debugging - // information. - rel := &release.Release{ - Name: name, - Namespace: req.Namespace, - Chart: req.Chart, - Config: req.Values, - Info: &release.Info{ - FirstDeployed: ts, - LastDeployed: ts, - Status: &release.Status{Code: release.Status_UNKNOWN}, - Description: fmt.Sprintf("Install failed: %s", err), - }, - Version: 0, - } - if manifestDoc != nil { - rel.Manifest = manifestDoc.String() - } - return rel, err - } - - // Store a release. - rel := &release.Release{ - Name: name, - Namespace: req.Namespace, - Chart: req.Chart, - Config: req.Values, - Info: &release.Info{ - FirstDeployed: ts, - LastDeployed: ts, - Status: &release.Status{Code: release.Status_UNKNOWN}, - Description: "Initial install underway", // Will be overwritten. - }, - Manifest: manifestDoc.String(), - Hooks: hooks, - Version: int32(revision), - } - if len(notesTxt) > 0 { - rel.Info.Status.Notes = notesTxt - } - - err = validateManifest(s.env.KubeClient, req.Namespace, manifestDoc.Bytes()) - return rel, err -} - -func getVersionSet(client discovery.ServerGroupsInterface) (chartutil.VersionSet, error) { +// GetVersionSet retrieves a set of available k8s API versions +func GetVersionSet(client discovery.ServerGroupsInterface) (chartutil.VersionSet, error) { groups, err := client.ServerGroups() if err != nil { return chartutil.DefaultVersionSet, err @@ -783,6 +249,7 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values return nil, nil, "", fmt.Errorf("Chart incompatible with Tiller %s", sver) } + s.Log("rendering %s chart using values", ch.GetMetadata().Name) renderer := s.engine(ch) files, err := renderer.Render(ch, values) if err != nil { @@ -840,106 +307,21 @@ func (s *ReleaseServer) renderResources(ch *chart.Chart, values chartutil.Values func (s *ReleaseServer) recordRelease(r *release.Release, reuse bool) { if reuse { if err := s.env.Releases.Update(r); err != nil { - log.Printf("warning: Failed to update release %q: %s", r.Name, err) + s.Log("warning: Failed to update release %s: %s", r.Name, err) } } else if err := s.env.Releases.Create(r); err != nil { - log.Printf("warning: Failed to record release %q: %s", r.Name, err) + s.Log("warning: Failed to record release %s: %s", r.Name, err) } } -// performRelease runs a release. -func (s *ReleaseServer) performRelease(r *release.Release, req *services.InstallReleaseRequest) (*services.InstallReleaseResponse, error) { - res := &services.InstallReleaseResponse{Release: r} - - if req.DryRun { - log.Printf("Dry run for %s", r.Name) - res.Release.Info.Description = "Dry run complete" - return res, nil - } - - // pre-install hooks - if !req.DisableHooks { - if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PreInstall, req.Timeout); err != nil { - return res, err - } - } - - switch h, err := s.env.Releases.History(req.Name); { - // if this is a replace operation, append to the release history - case req.ReuseName && err == nil && len(h) >= 1: - // get latest release revision - relutil.Reverse(h, relutil.SortByRevision) - - // old release - old := h[0] - - // update old release status - old.Info.Status.Code = release.Status_SUPERSEDED - s.recordRelease(old, true) - - // update new release with next revision number - // so as to append to the old release's history - r.Version = old.Version + 1 - - if err := s.performKubeUpdate(old, r, false, req.Timeout, req.Wait); err != nil { - msg := fmt.Sprintf("Release replace %q failed: %s", r.Name, err) - log.Printf("warning: %s", msg) - old.Info.Status.Code = release.Status_SUPERSEDED - r.Info.Status.Code = release.Status_FAILED - r.Info.Description = msg - s.recordRelease(old, true) - s.recordRelease(r, false) - return res, err - } - - default: - // nothing to replace, create as normal - // regular manifests - b := bytes.NewBufferString(r.Manifest) - if err := s.env.KubeClient.Create(r.Namespace, b, req.Timeout, req.Wait); err != nil { - msg := fmt.Sprintf("Release %q failed: %s", r.Name, err) - log.Printf("warning: %s", msg) - r.Info.Status.Code = release.Status_FAILED - r.Info.Description = msg - s.recordRelease(r, false) - return res, fmt.Errorf("release %s failed: %s", r.Name, err) - } - } - - // post-install hooks - if !req.DisableHooks { - if err := s.execHook(r.Hooks, r.Name, r.Namespace, hooks.PostInstall, req.Timeout); err != nil { - msg := fmt.Sprintf("Release %q failed post-install: %s", r.Name, err) - log.Printf("warning: %s", msg) - r.Info.Status.Code = release.Status_FAILED - r.Info.Description = msg - s.recordRelease(r, false) - return res, err - } - } - - r.Info.Status.Code = release.Status_DEPLOYED - r.Info.Description = "Install complete" - // This is a tricky case. The release has been created, but the result - // cannot be recorded. The truest thing to tell the user is that the - // release was created. However, the user will not be able to do anything - // further with this release. - // - // One possible strategy would be to do a timed retry to see if we can get - // this stored in the future. - s.recordRelease(r, false) - - return res, nil -} - func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook string, timeout int64) error { kubeCli := s.env.KubeClient code, ok := events[hook] if !ok { - return fmt.Errorf("unknown hook %q", hook) + return fmt.Errorf("unknown hook %s", hook) } - log.Printf("Executing %s hooks for %s", hook, name) + s.Log("executing %d %s hooks for %s", len(hs), hook, name) executingHooks := []*release.Hook{} for _, h := range hs { for _, e := range h.Events { @@ -955,196 +337,25 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin b := bytes.NewBufferString(h.Manifest) if err := kubeCli.Create(namespace, b, timeout, false); err != nil { - log.Printf("warning: Release %q %s %s failed: %s", name, hook, h.Path, err) + s.Log("warning: Release %s %s %s failed: %s", name, hook, h.Path, err) return err } // No way to rewind a bytes.Buffer()? b.Reset() b.WriteString(h.Manifest) if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil { - log.Printf("warning: Release %q %s %s could not complete: %s", name, hook, h.Path, err) + s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err) return err } h.LastRun = timeconv.Now() } - log.Printf("Hooks complete for %s %s", hook, name) - return nil -} - -func (s *ReleaseServer) purgeReleases(rels ...*release.Release) error { - for _, rel := range rels { - if _, err := s.env.Releases.Delete(rel.Name, rel.Version); err != nil { - return err - } - } + s.Log("hooks complete for %s %s", hook, name) return nil } -// UninstallRelease deletes all of the resources associated with this release, and marks the release DELETED. -func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) { - if !ValidName.MatchString(req.Name) { - log.Printf("uninstall: Release not found: %s", req.Name) - return nil, errMissingRelease - } - - if len(req.Name) > releaseNameMaxLen { - return nil, fmt.Errorf("release name %q exceeds max length of %d", req.Name, releaseNameMaxLen) - } - - rels, err := s.env.Releases.History(req.Name) - if err != nil { - log.Printf("uninstall: Release not loaded: %s", req.Name) - return nil, err - } - if len(rels) < 1 { - return nil, errMissingRelease - } - - relutil.SortByRevision(rels) - rel := rels[len(rels)-1] - - // TODO: Are there any cases where we want to force a delete even if it's - // already marked deleted? - if rel.Info.Status.Code == release.Status_DELETED { - if req.Purge { - if err := s.purgeReleases(rels...); err != nil { - log.Printf("uninstall: Failed to purge the release: %s", err) - return nil, err - } - return &services.UninstallReleaseResponse{Release: rel}, nil - } - return nil, fmt.Errorf("the release named %q is already deleted", req.Name) - } - - log.Printf("uninstall: Deleting %s", req.Name) - rel.Info.Status.Code = release.Status_DELETING - rel.Info.Deleted = timeconv.Now() - rel.Info.Description = "Deletion in progress (or silently failed)" - res := &services.UninstallReleaseResponse{Release: rel} - - if !req.DisableHooks { - if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PreDelete, req.Timeout); err != nil { - return res, err - } - } - - vs, err := getVersionSet(s.clientset.Discovery()) - if err != nil { - return nil, fmt.Errorf("Could not get apiVersions from Kubernetes: %s", err) - } - - // From here on out, the release is currently considered to be in Status_DELETING - // state. - if err := s.env.Releases.Update(rel); err != nil { - log.Printf("uninstall: Failed to store updated release: %s", err) - } - - manifests := relutil.SplitManifests(rel.Manifest) - _, files, err := sortManifests(manifests, vs, UninstallOrder) - if err != nil { - // We could instead just delete everything in no particular order. - // FIXME: One way to delete at this point would be to try a label-based - // deletion. The problem with this is that we could get a false positive - // and delete something that was not legitimately part of this release. - return nil, fmt.Errorf("corrupted release record. You must manually delete the resources: %s", err) - } - - filesToKeep, filesToDelete := filterManifestsToKeep(files) - if len(filesToKeep) > 0 { - res.Info = summarizeKeptManifests(filesToKeep) - } - - // Collect the errors, and return them later. - es := []string{} - for _, file := range filesToDelete { - b := bytes.NewBufferString(strings.TrimSpace(file.content)) - if b.Len() == 0 { - continue - } - if err := s.env.KubeClient.Delete(rel.Namespace, b); err != nil { - log.Printf("uninstall: Failed deletion of %q: %s", req.Name, err) - if err == kube.ErrNoObjectsVisited { - // Rewrite the message from "no objects visited" - err = errors.New("object not found, skipping delete") - } - es = append(es, err.Error()) - } - } - - if !req.DisableHooks { - if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PostDelete, req.Timeout); err != nil { - es = append(es, err.Error()) - } - } - - rel.Info.Status.Code = release.Status_DELETED - rel.Info.Description = "Deletion complete" - - if req.Purge { - err := s.purgeReleases(rels...) - if err != nil { - log.Printf("uninstall: Failed to purge the release: %s", err) - } - return res, err - } - - if err := s.env.Releases.Update(rel); err != nil { - log.Printf("uninstall: Failed to store updated release: %s", err) - } - - if len(es) > 0 { - return res, fmt.Errorf("deletion completed with %d error(s): %s", len(es), strings.Join(es, "; ")) - } - return res, nil -} - func validateManifest(c environment.KubeClient, ns string, manifest []byte) error { r := bytes.NewReader(manifest) _, err := c.BuildUnstructured(ns, r) return err } - -// RunReleaseTest runs pre-defined tests stored as hooks on a given release -func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream services.ReleaseService_RunReleaseTestServer) error { - - if !ValidName.MatchString(req.Name) { - return errMissingRelease - } - - // finds the non-deleted release with the given name - rel, err := s.env.Releases.Last(req.Name) - if err != nil { - return err - } - - testEnv := &reltesting.Environment{ - Namespace: rel.Namespace, - KubeClient: s.env.KubeClient, - Timeout: req.Timeout, - Stream: stream, - } - - tSuite, err := reltesting.NewTestSuite(rel) - if err != nil { - log.Printf("Error creating test suite for %s", rel.Name) - return err - } - - if err := tSuite.Run(testEnv); err != nil { - log.Printf("Error running test suite for %s", rel.Name) - return err - } - - rel.Info.Status.LastTestSuiteRun = &release.TestSuite{ - StartedAt: tSuite.StartedAt, - CompletedAt: tSuite.CompletedAt, - Results: tSuite.Results, - } - - if req.Cleanup { - testEnv.DeleteTestPods(tSuite.TestManifests) - } - - return s.env.Releases.Update(rel) -} diff --git a/pkg/tiller/release_server_test.go b/pkg/tiller/release_server_test.go index 636874b20..8425a58f5 100644 --- a/pkg/tiller/release_server_test.go +++ b/pkg/tiller/release_server_test.go @@ -18,11 +18,9 @@ package tiller import ( "errors" - "fmt" "io" "os" "regexp" - "strings" "testing" "github.com/golang/protobuf/ptypes/timestamp" @@ -49,8 +47,7 @@ metadata: annotations: "helm.sh/hook": post-install,pre-delete data: - name: value -` + name: value` var manifestWithTestHook = ` apiVersion: v1 @@ -83,8 +80,7 @@ metadata: annotations: "helm.sh/hook": post-upgrade,pre-upgrade data: - name: value -` + name: value` var manifestWithRollbackHooks = `apiVersion: v1 kind: ConfigMap @@ -97,9 +93,14 @@ data: ` func rsFixture() *ReleaseServer { + clientset := fake.NewSimpleClientset() return &ReleaseServer{ + ReleaseModule: &LocalReleaseModule{ + clientset: clientset, + }, env: MockEnvironment(), - clientset: fake.NewSimpleClientset(), + clientset: clientset, + Log: func(_ string, _ ...interface{}) {}, } } @@ -205,7 +206,7 @@ func TestValidName(t *testing.T) { func TestGetVersionSet(t *testing.T) { rs := rsFixture() - vs, err := getVersionSet(rs.clientset.Discovery()) + vs, err := GetVersionSet(rs.clientset.Discovery()) if err != nil { t.Error(err) } @@ -261,1039 +262,23 @@ func TestUniqName(t *testing.T) { } } -func TestInstallRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - if res.Release.Namespace != "spaced" { - t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) - } - - rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - t.Logf("rel: %v", rel) - - if len(rel.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) - } - if rel.Hooks[0].Manifest != manifestWithHook { - t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) - } - - if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { - t.Errorf("Expected event 0 is post install") - } - if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { - t.Errorf("Expected event 0 is pre-delete") - } - - if len(res.Release.Manifest) == 0 { - t.Errorf("No manifest returned: %v", res.Release) - } - - if len(rel.Manifest) == 0 { - t.Errorf("Expected manifest in %v", res) - } - - if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { - t.Errorf("unexpected output: %s", rel.Manifest) - } - - if rel.Info.Description != "Install complete" { - t.Errorf("unexpected description: %s", rel.Info.Description) - } -} - -func TestInstallRelease_WithNotes(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText)}, - }, - }, - } - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - if res.Release.Namespace != "spaced" { - t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) - } - - rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - t.Logf("rel: %v", rel) - - if len(rel.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) - } - if rel.Hooks[0].Manifest != manifestWithHook { - t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) - } - - if rel.Info.Status.Notes != notesText { - t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) - } - - if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { - t.Errorf("Expected event 0 is post install") - } - if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { - t.Errorf("Expected event 0 is pre-delete") - } - - if len(res.Release.Manifest) == 0 { - t.Errorf("No manifest returned: %v", res.Release) - } - - if len(rel.Manifest) == 0 { - t.Errorf("Expected manifest in %v", res) - } - - if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { - t.Errorf("unexpected output: %s", rel.Manifest) - } - - if rel.Info.Description != "Install complete" { - t.Errorf("unexpected description: %s", rel.Info.Description) - } -} - -func TestInstallRelease_WithNotesRendered(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText + " {{.Release.Name}}")}, - }, - }, - } - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - if res.Release.Namespace != "spaced" { - t.Errorf("Expected release namespace 'spaced', got '%s'.", res.Release.Namespace) - } - - rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - t.Logf("rel: %v", rel) - - if len(rel.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(rel.Hooks)) - } - if rel.Hooks[0].Manifest != manifestWithHook { - t.Errorf("Unexpected manifest: %v", rel.Hooks[0].Manifest) - } - - expectedNotes := fmt.Sprintf("%s %s", notesText, res.Release.Name) - if rel.Info.Status.Notes != expectedNotes { - t.Fatalf("Expected '%s', got '%s'", expectedNotes, rel.Info.Status.Notes) - } - - if rel.Hooks[0].Events[0] != release.Hook_POST_INSTALL { - t.Errorf("Expected event 0 is post install") - } - if rel.Hooks[0].Events[1] != release.Hook_PRE_DELETE { - t.Errorf("Expected event 0 is pre-delete") - } - - if len(res.Release.Manifest) == 0 { - t.Errorf("No manifest returned: %v", res.Release) - } - - if len(rel.Manifest) == 0 { - t.Errorf("Expected manifest in %v", res) - } - - if !strings.Contains(rel.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { - t.Errorf("unexpected output: %s", rel.Manifest) - } - - if rel.Info.Description != "Install complete" { - t.Errorf("unexpected description: %s", rel.Info.Description) - } -} - -func TestInstallRelease_TillerVersion(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello", TillerVersion: ">=2.2.0"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } - _, err := rs.InstallRelease(c, req) - if err != nil { - t.Fatalf("Expected valid range. Got %q", err) - } -} - -func TestInstallRelease_WrongTillerVersion(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello", TillerVersion: "<2.0.0"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - }, - }, - } - _, err := rs.InstallRelease(c, req) - if err == nil { - t.Fatalf("Expected to fail because of wrong version") - } - - expect := "Chart incompatible with Tiller" - if !strings.Contains(err.Error(), expect) { - t.Errorf("Expected %q to contain %q", err.Error(), expect) - } -} - -func TestInstallRelease_WithChartAndDependencyNotes(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - // TODO: Refactor this into a mock. - req := &services.InstallReleaseRequest{ - Namespace: "spaced", - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText)}, - }, - Dependencies: []*chart.Chart{ - { - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithHook)}, - {Name: "templates/NOTES.txt", Data: []byte(notesText + " child")}, - }, - }, - }, - }, - } - - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - - rel, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - t.Logf("rel: %v", rel) - - if rel.Info.Status.Notes != notesText { - t.Fatalf("Expected '%s', got '%s'", notesText, rel.Info.Status.Notes) - } - - if rel.Info.Description != "Install complete" { - t.Errorf("unexpected description: %s", rel.Info.Description) - } -} - -func TestInstallRelease_DryRun(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - DryRun: true, - } - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Errorf("Failed install: %s", err) - } - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - - if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { - t.Errorf("unexpected output: %s", res.Release.Manifest) - } - - if !strings.Contains(res.Release.Manifest, "---\n# Source: hello/templates/goodbye\ngoodbye: world") { - t.Errorf("unexpected output: %s", res.Release.Manifest) - } - - if !strings.Contains(res.Release.Manifest, "hello: Earth") { - t.Errorf("Should contain partial content. %s", res.Release.Manifest) - } - - if strings.Contains(res.Release.Manifest, "hello: {{ template \"_planet\" . }}") { - t.Errorf("Should not contain partial templates itself. %s", res.Release.Manifest) - } - - if strings.Contains(res.Release.Manifest, "empty") { - t.Errorf("Should not contain template data for an empty file. %s", res.Release.Manifest) - } - - if _, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version); err == nil { - t.Errorf("Expected no stored release.") - } - - if l := len(res.Release.Hooks); l != 1 { - t.Fatalf("Expected 1 hook, got %d", l) - } - - if res.Release.Hooks[0].LastRun != nil { - t.Error("Expected hook to not be marked as run.") - } - - if res.Release.Info.Description != "Dry run complete" { - t.Errorf("unexpected description: %s", res.Release.Info.Description) - } -} - -func TestInstallRelease_NoHooks(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rs.env.Releases.Create(releaseStub()) - - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - DisableHooks: true, - } - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Errorf("Failed install: %s", err) - } - - if hl := res.Release.Hooks[0].LastRun; hl != nil { - t.Errorf("Expected that no hooks were run. Got %d", hl) - } -} - -func TestInstallRelease_FailedHooks(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rs.env.Releases.Create(releaseStub()) - rs.env.KubeClient = newHookFailingKubeClient() - - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - } - res, err := rs.InstallRelease(c, req) - if err == nil { - t.Error("Expected failed install") - } - - if hl := res.Release.Info.Status.Code; hl != release.Status_FAILED { - t.Errorf("Expected FAILED release. Got %d", hl) - } -} - -func TestInstallRelease_ReuseName(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rel.Info.Status.Code = release.Status_DELETED - rs.env.Releases.Create(rel) - - req := &services.InstallReleaseRequest{ - Chart: chartStub(), - ReuseName: true, - Name: rel.Name, - } - res, err := rs.InstallRelease(c, req) - if err != nil { - t.Fatalf("Failed install: %s", err) - } - - if res.Release.Name != rel.Name { - t.Errorf("expected %q, got %q", rel.Name, res.Release.Name) - } - - getreq := &services.GetReleaseStatusRequest{Name: rel.Name, Version: 0} - getres, err := rs.GetReleaseStatus(c, getreq) - if err != nil { - t.Errorf("Failed to retrieve release: %s", err) - } - if getres.Info.Status.Code != release.Status_DEPLOYED { - t.Errorf("Release status is %q", getres.Info.Status.Code) - } -} - -func TestUpdateRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, +func releaseWithKeepStub(rlsName string) *release.Release { + ch := &chart.Chart{ + Metadata: &chart.Metadata{ + Name: "bunnychart", }, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - - if res.Release.Name != rel.Name { - t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) - } - - if res.Release.Namespace != rel.Namespace { - t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) - } - - updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - if len(updated.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) - } - if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { - t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) - } - - if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { - t.Errorf("Expected event 0 to be post upgrade") - } - - if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { - t.Errorf("Expected event 0 to be pre upgrade") - } - - if len(res.Release.Manifest) == 0 { - t.Errorf("No manifest returned: %v", res.Release) - } - - if res.Release.Config == nil { - t.Errorf("Got release without config: %#v", res.Release) - } else if res.Release.Config.Raw != rel.Config.Raw { - t.Errorf("Expected release values %q, got %q", rel.Config.Raw, res.Release.Config.Raw) - } - - if len(updated.Manifest) == 0 { - t.Errorf("Expected manifest in %v", res) - } - - if !strings.Contains(updated.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { - t.Errorf("unexpected output: %s", rel.Manifest) - } - - if res.Release.Version != 2 { - t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) - } - - edesc := "Upgrade complete" - if got := res.Release.Info.Description; got != edesc { - t.Errorf("Expected description %q, got %q", edesc, got) - } -} -func TestUpdateRelease_ResetValues(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, + Templates: []*chart.Template{ + {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, }, - ResetValues: true, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) } - // This should have been unset. Config: &chart.Config{Raw: `name: value`}, - if res.Release.Config != nil && res.Release.Config.Raw != "" { - t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) - } -} - -func TestUpdateRelease_ReuseValues(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - // Since reuseValues is set, this should get ignored. - Values: &chart.Config{Raw: "foo: bar\n"}, - }, - Values: &chart.Config{Raw: "name2: val2"}, - ReuseValues: true, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - // This should have been overwritten with the old value. - expect := "name: value\n" - if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { - t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) - } - // This should have the newly-passed overrides. - expect = "name2: val2" - if res.Release.Config != nil && res.Release.Config.Raw != expect { - t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) - } -} - -func TestUpdateRelease_ResetReuseValues(t *testing.T) { - // This verifies that when both reset and reuse are set, reset wins. - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - }, - ResetValues: true, - ReuseValues: true, - } - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - // This should have been unset. Config: &chart.Config{Raw: `name: value`}, - if res.Release.Config != nil && res.Release.Config.Raw != "" { - t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) - } -} - -func TestUpdateReleaseFailure(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - rs.env.KubeClient = newUpdateFailingKubeClient() - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/something", Data: []byte("hello: world")}, - }, - }, - } - - res, err := rs.UpdateRelease(c, req) - if err == nil { - t.Error("Expected failed update") - } - - if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_FAILED { - t.Errorf("Expected FAILED release. Got %d", updatedStatus) - } - - edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client" - if got := res.Release.Info.Description; got != edesc { - t.Errorf("Expected description %q, got %q", edesc, got) - } - - oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) - if err != nil { - t.Errorf("Expected to be able to get previous release") - } - if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { - t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) - } -} - -func TestRollbackReleaseFailure(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - upgradedRel := upgradeReleaseVersion(rel) - rs.env.Releases.Update(rel) - rs.env.Releases.Create(upgradedRel) - - req := &services.RollbackReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - } - - rs.env.KubeClient = newUpdateFailingKubeClient() - res, err := rs.RollbackRelease(c, req) - if err == nil { - t.Error("Expected failed rollback") - } - - if targetStatus := res.Release.Info.Status.Code; targetStatus != release.Status_FAILED { - t.Errorf("Expected FAILED release. Got %v", targetStatus) - } - - oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) - if err != nil { - t.Errorf("Expected to be able to get previous release") - } - if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { - t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) - } -} - -func TestUpdateReleaseNoHooks(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Chart: &chart.Chart{ - Metadata: &chart.Metadata{Name: "hello"}, - Templates: []*chart.Template{ - {Name: "templates/hello", Data: []byte("hello: world")}, - {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, - }, - }, - } - - res, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } - - if hl := res.Release.Hooks[0].LastRun; hl != nil { - t.Errorf("Expected that no hooks were run. Got %d", hl) - } - -} - -func TestUpdateReleaseNoChanges(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - - req := &services.UpdateReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Chart: rel.GetChart(), - } - - _, err := rs.UpdateRelease(c, req) - if err != nil { - t.Fatalf("Failed updated: %s", err) - } -} - -func TestRollbackReleaseNoHooks(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rel.Hooks = []*release.Hook{ - { - Name: "test-cm", - Kind: "ConfigMap", - Path: "test-cm", - Manifest: manifestWithRollbackHooks, - Events: []release.Hook_Event{ - release.Hook_PRE_ROLLBACK, - release.Hook_POST_ROLLBACK, - }, - }, - } - rs.env.Releases.Create(rel) - upgradedRel := upgradeReleaseVersion(rel) - rs.env.Releases.Update(rel) - rs.env.Releases.Create(upgradedRel) - - req := &services.RollbackReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - } - - res, err := rs.RollbackRelease(c, req) - if err != nil { - t.Fatalf("Failed rollback: %s", err) - } - - if hl := res.Release.Hooks[0].LastRun; hl != nil { - t.Errorf("Expected that no hooks were run. Got %d", hl) - } -} - -func TestRollbackWithReleaseVersion(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - upgradedRel := upgradeReleaseVersion(rel) - rs.env.Releases.Update(rel) - rs.env.Releases.Create(upgradedRel) - - req := &services.RollbackReleaseRequest{ - Name: rel.Name, - DisableHooks: true, - Version: 1, - } - - _, err := rs.RollbackRelease(c, req) - if err != nil { - t.Fatalf("Failed rollback: %s", err) - } -} - -func TestRollbackRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - upgradedRel := upgradeReleaseVersion(rel) - upgradedRel.Hooks = []*release.Hook{ - { - Name: "test-cm", - Kind: "ConfigMap", - Path: "test-cm", - Manifest: manifestWithRollbackHooks, - Events: []release.Hook_Event{ - release.Hook_PRE_ROLLBACK, - release.Hook_POST_ROLLBACK, - }, - }, - } - - upgradedRel.Manifest = "hello world" - rs.env.Releases.Update(rel) - rs.env.Releases.Create(upgradedRel) - - req := &services.RollbackReleaseRequest{ - Name: rel.Name, - } - res, err := rs.RollbackRelease(c, req) - if err != nil { - t.Fatalf("Failed rollback: %s", err) - } - - if res.Release.Name == "" { - t.Errorf("Expected release name.") - } - - if res.Release.Name != rel.Name { - t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) - } - - if res.Release.Namespace != rel.Namespace { - t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) - } - - if res.Release.Version != 3 { - t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) - } - - updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - if len(updated.Hooks) != 2 { - t.Fatalf("Expected 2 hooks, got %d", len(updated.Hooks)) - } - - if updated.Hooks[0].Manifest != manifestWithHook { - t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) - } - - anotherUpgradedRelease := upgradeReleaseVersion(upgradedRel) - rs.env.Releases.Update(upgradedRel) - rs.env.Releases.Create(anotherUpgradedRelease) - - res, err = rs.RollbackRelease(c, req) - if err != nil { - t.Fatalf("Failed rollback: %s", err) - } - - updated, err = rs.env.Releases.Get(res.Release.Name, res.Release.Version) - if err != nil { - t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) - } - - if len(updated.Hooks) != 1 { - t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) - } - - if updated.Hooks[0].Manifest != manifestWithRollbackHooks { - t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) - } - - if res.Release.Version != 4 { - t.Errorf("Expected release version to be %v, got %v", 3, res.Release.Version) - } - - if updated.Hooks[0].Events[0] != release.Hook_PRE_ROLLBACK { - t.Errorf("Expected event 0 to be pre rollback") - } - - if updated.Hooks[0].Events[1] != release.Hook_POST_ROLLBACK { - t.Errorf("Expected event 1 to be post rollback") - } - - if len(res.Release.Manifest) == 0 { - t.Errorf("No manifest returned: %v", res.Release) - } - - if len(updated.Manifest) == 0 { - t.Errorf("Expected manifest in %v", res) - } - - if !strings.Contains(updated.Manifest, "hello world") { - t.Errorf("unexpected output: %s", rel.Manifest) - } - - if res.Release.Info.Description != "Rollback to 2" { - t.Errorf("Expected rollback to 2, got %q", res.Release.Info.Description) - } - -} - -func TestUninstallRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rs.env.Releases.Create(releaseStub()) - - req := &services.UninstallReleaseRequest{ - Name: "angry-panda", - } - - res, err := rs.UninstallRelease(c, req) - if err != nil { - t.Fatalf("Failed uninstall: %s", err) - } - - if res.Release.Name != "angry-panda" { - t.Errorf("Expected angry-panda, got %q", res.Release.Name) - } - - if res.Release.Info.Status.Code != release.Status_DELETED { - t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) - } - - if res.Release.Hooks[0].LastRun.Seconds == 0 { - t.Error("Expected LastRun to be greater than zero.") - } - - if res.Release.Info.Deleted.Seconds <= 0 { - t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) - } - - if res.Release.Info.Description != "Deletion complete" { - t.Errorf("Expected Deletion complete, got %q", res.Release.Info.Description) - } -} - -func TestUninstallPurgeRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rs.env.Releases.Create(rel) - upgradedRel := upgradeReleaseVersion(rel) - rs.env.Releases.Update(rel) - rs.env.Releases.Create(upgradedRel) - - req := &services.UninstallReleaseRequest{ - Name: "angry-panda", - Purge: true, - } - - res, err := rs.UninstallRelease(c, req) - if err != nil { - t.Fatalf("Failed uninstall: %s", err) - } - - if res.Release.Name != "angry-panda" { - t.Errorf("Expected angry-panda, got %q", res.Release.Name) - } - - if res.Release.Info.Status.Code != release.Status_DELETED { - t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) - } - - if res.Release.Info.Deleted.Seconds <= 0 { - t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) - } - rels, err := rs.GetHistory(helm.NewContext(), &services.GetHistoryRequest{Name: "angry-panda"}) - if err != nil { - t.Fatal(err) - } - if len(rels.Releases) != 0 { - t.Errorf("Expected no releases in storage, got %d", len(rels.Releases)) - } -} - -func TestUninstallPurgeDeleteRelease(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rs.env.Releases.Create(releaseStub()) - - req := &services.UninstallReleaseRequest{ - Name: "angry-panda", - } - - _, err := rs.UninstallRelease(c, req) - if err != nil { - t.Fatalf("Failed uninstall: %s", err) - } - - req2 := &services.UninstallReleaseRequest{ - Name: "angry-panda", - Purge: true, - } - - _, err2 := rs.UninstallRelease(c, req2) - if err2 != nil && err2.Error() != "'angry-panda' has no deployed releases" { - t.Errorf("Failed uninstall: %s", err2) - } -} - -func TestUninstallReleaseWithKeepPolicy(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - name := "angry-bunny" - rs.env.Releases.Create(releaseWithKeepStub(name)) - - req := &services.UninstallReleaseRequest{ - Name: name, - } - - res, err := rs.UninstallRelease(c, req) - if err != nil { - t.Fatalf("Failed uninstall: %s", err) - } - - if res.Release.Name != name { - t.Errorf("Expected angry-bunny, got %q", res.Release.Name) - } - - if res.Release.Info.Status.Code != release.Status_DELETED { - t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) - } - - if res.Info == "" { - t.Errorf("Expected response info to not be empty") - } else { - if !strings.Contains(res.Info, "[ConfigMap] test-cm-keep") { - t.Errorf("unexpected output: %s", res.Info) - } - } -} - -func releaseWithKeepStub(rlsName string) *release.Release { - ch := &chart.Chart{ - Metadata: &chart.Metadata{ - Name: "bunnychart", - }, - Templates: []*chart.Template{ - {Name: "templates/configmap", Data: []byte(manifestWithKeep)}, - }, - } - - date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} - return &release.Release{ - Name: rlsName, - Info: &release.Info{ - FirstDeployed: &date, - LastDeployed: &date, - Status: &release.Status{Code: release.Status_DEPLOYED}, + date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} + return &release.Release{ + Name: rlsName, + Info: &release.Info{ + FirstDeployed: &date, + LastDeployed: &date, + Status: &release.Status{Code: release.Status_DEPLOYED}, }, Chart: ch, Config: &chart.Config{Raw: `name: value`}, @@ -1302,305 +287,6 @@ func releaseWithKeepStub(rlsName string) *release.Release { } } -func TestUninstallReleaseNoHooks(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rs.env.Releases.Create(releaseStub()) - - req := &services.UninstallReleaseRequest{ - Name: "angry-panda", - DisableHooks: true, - } - - res, err := rs.UninstallRelease(c, req) - if err != nil { - t.Errorf("Failed uninstall: %s", err) - } - - // The default value for a protobuf timestamp is nil. - if res.Release.Hooks[0].LastRun != nil { - t.Errorf("Expected LastRun to be zero, got %d.", res.Release.Hooks[0].LastRun.Seconds) - } -} - -func TestGetReleaseContent(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - - res, err := rs.GetReleaseContent(c, &services.GetReleaseContentRequest{Name: rel.Name, Version: 1}) - if err != nil { - t.Errorf("Error getting release content: %s", err) - } - - if res.Release.Chart.Metadata.Name != rel.Chart.Metadata.Name { - t.Errorf("Expected %q, got %q", rel.Chart.Metadata.Name, res.Release.Chart.Metadata.Name) - } -} - -func TestGetReleaseStatus(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - - res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) - if err != nil { - t.Errorf("Error getting release content: %s", err) - } - - if res.Name != rel.Name { - t.Errorf("Expected name %q, got %q", rel.Name, res.Name) - } - if res.Info.Status.Code != release.Status_DEPLOYED { - t.Errorf("Expected %d, got %d", release.Status_DEPLOYED, res.Info.Status.Code) - } -} - -func TestGetReleaseStatusDeleted(t *testing.T) { - c := helm.NewContext() - rs := rsFixture() - rel := releaseStub() - rel.Info.Status.Code = release.Status_DELETED - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - - res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) - if err != nil { - t.Fatalf("Error getting release content: %s", err) - } - - if res.Info.Status.Code != release.Status_DELETED { - t.Errorf("Expected %d, got %d", release.Status_DELETED, res.Info.Status.Code) - } -} - -func TestListReleases(t *testing.T) { - rs := rsFixture() - num := 7 - for i := 0; i < num; i++ { - rel := releaseStub() - rel.Name = fmt.Sprintf("rel-%d", i) - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - mrs := &mockListServer{} - if err := rs.ListReleases(&services.ListReleasesRequest{Offset: "", Limit: 64}, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != num { - t.Errorf("Expected %d releases, got %d", num, len(mrs.val.Releases)) - } -} - -func TestListReleasesByStatus(t *testing.T) { - rs := rsFixture() - stubs := []*release.Release{ - namedReleaseStub("kamal", release.Status_DEPLOYED), - namedReleaseStub("astrolabe", release.Status_DELETED), - namedReleaseStub("octant", release.Status_FAILED), - namedReleaseStub("sextant", release.Status_UNKNOWN), - } - for _, stub := range stubs { - if err := rs.env.Releases.Create(stub); err != nil { - t.Fatalf("Could not create stub: %s", err) - } - } - - tests := []struct { - statusCodes []release.Status_Code - names []string - }{ - { - names: []string{"kamal"}, - statusCodes: []release.Status_Code{release.Status_DEPLOYED}, - }, - { - names: []string{"astrolabe"}, - statusCodes: []release.Status_Code{release.Status_DELETED}, - }, - { - names: []string{"kamal", "octant"}, - statusCodes: []release.Status_Code{release.Status_DEPLOYED, release.Status_FAILED}, - }, - { - names: []string{"kamal", "astrolabe", "octant", "sextant"}, - statusCodes: []release.Status_Code{ - release.Status_DEPLOYED, - release.Status_DELETED, - release.Status_FAILED, - release.Status_UNKNOWN, - }, - }, - } - - for i, tt := range tests { - mrs := &mockListServer{} - if err := rs.ListReleases(&services.ListReleasesRequest{StatusCodes: tt.statusCodes, Offset: "", Limit: 64}, mrs); err != nil { - t.Fatalf("Failed listing %d: %s", i, err) - } - - if len(tt.names) != len(mrs.val.Releases) { - t.Fatalf("Expected %d releases, got %d", len(tt.names), len(mrs.val.Releases)) - } - - for _, name := range tt.names { - found := false - for _, rel := range mrs.val.Releases { - if rel.Name == name { - found = true - } - } - if !found { - t.Errorf("%d: Did not find name %q", i, name) - } - } - } -} - -func TestListReleasesSort(t *testing.T) { - rs := rsFixture() - - // Put them in by reverse order so that the mock doesn't "accidentally" - // sort. - num := 7 - for i := num; i > 0; i-- { - rel := releaseStub() - rel.Name = fmt.Sprintf("rel-%d", i) - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - limit := 6 - mrs := &mockListServer{} - req := &services.ListReleasesRequest{ - Offset: "", - Limit: int64(limit), - SortBy: services.ListSort_NAME, - } - if err := rs.ListReleases(req, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != limit { - t.Errorf("Expected %d releases, got %d", limit, len(mrs.val.Releases)) - } - - for i := 0; i < limit; i++ { - n := fmt.Sprintf("rel-%d", i+1) - if mrs.val.Releases[i].Name != n { - t.Errorf("Expected %q, got %q", n, mrs.val.Releases[i].Name) - } - } -} - -func TestListReleasesFilter(t *testing.T) { - rs := rsFixture() - names := []string{ - "axon", - "dendrite", - "neuron", - "neuroglia", - "synapse", - "nucleus", - "organelles", - } - num := 7 - for i := 0; i < num; i++ { - rel := releaseStub() - rel.Name = names[i] - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - mrs := &mockListServer{} - req := &services.ListReleasesRequest{ - Offset: "", - Limit: 64, - Filter: "neuro[a-z]+", - SortBy: services.ListSort_NAME, - } - if err := rs.ListReleases(req, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != 2 { - t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) - } - - if mrs.val.Releases[0].Name != "neuroglia" { - t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) - } - if mrs.val.Releases[1].Name != "neuron" { - t.Errorf("Unexpected sort order: %v.", mrs.val.Releases) - } -} - -func TestReleasesNamespace(t *testing.T) { - rs := rsFixture() - - names := []string{ - "axon", - "dendrite", - "neuron", - "ribosome", - } - - namespaces := []string{ - "default", - "test123", - "test123", - "cerebellum", - } - num := 4 - for i := 0; i < num; i++ { - rel := releaseStub() - rel.Name = names[i] - rel.Namespace = namespaces[i] - if err := rs.env.Releases.Create(rel); err != nil { - t.Fatalf("Could not store mock release: %s", err) - } - } - - mrs := &mockListServer{} - req := &services.ListReleasesRequest{ - Offset: "", - Limit: 64, - Namespace: "test123", - } - - if err := rs.ListReleases(req, mrs); err != nil { - t.Fatalf("Failed listing: %s", err) - } - - if len(mrs.val.Releases) != 2 { - t.Errorf("Expected 2 releases, got %d", len(mrs.val.Releases)) - } -} - -func TestRunReleaseTest(t *testing.T) { - rs := rsFixture() - rel := namedReleaseStub("nemo", release.Status_DEPLOYED) - rs.env.Releases.Create(rel) - - req := &services.TestReleaseRequest{Name: "nemo", Timeout: 2} - err := rs.RunReleaseTest(req, mockRunReleaseTestServer{}) - if err != nil { - t.Fatalf("failed to run release tests on %s: %s", rel.Name, err) - } -} - func MockEnvironment() *environment.Environment { e := environment.New() e.Releases = storage.Init(driver.NewMemory()) @@ -1619,7 +305,7 @@ type updateFailingKubeClient struct { environment.PrintingKubeClient } -func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, recreate bool, timeout int64, shouldWait bool) error { +func (u *updateFailingKubeClient) Update(namespace string, originalReader, modifiedReader io.Reader, force bool, recreate bool, timeout int64, shouldWait bool) error { return errors.New("Failed update in kube client") } diff --git a/pkg/tiller/release_status.go b/pkg/tiller/release_status.go new file mode 100644 index 000000000..41eba1174 --- /dev/null +++ b/pkg/tiller/release_status.go @@ -0,0 +1,74 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "errors" + "fmt" + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" +) + +// GetReleaseStatus gets the status information for a named release. +func (s *ReleaseServer) GetReleaseStatus(c ctx.Context, req *services.GetReleaseStatusRequest) (*services.GetReleaseStatusResponse, error) { + if !ValidName.MatchString(req.Name) { + return nil, errMissingRelease + } + + var rel *release.Release + + if req.Version <= 0 { + var err error + rel, err = s.env.Releases.Last(req.Name) + if err != nil { + return nil, fmt.Errorf("getting deployed release %q: %s", req.Name, err) + } + } else { + var err error + if rel, err = s.env.Releases.Get(req.Name, req.Version); err != nil { + return nil, fmt.Errorf("getting release '%s' (v%d): %s", req.Name, req.Version, err) + } + } + + if rel.Info == nil { + return nil, errors.New("release info is missing") + } + if rel.Chart == nil { + return nil, errors.New("release chart is missing") + } + + sc := rel.Info.Status.Code + statusResp := &services.GetReleaseStatusResponse{ + Name: rel.Name, + Namespace: rel.Namespace, + Info: rel.Info, + } + + // Ok, we got the status of the release as we had jotted down, now we need to match the + // manifest we stashed away with reality from the cluster. + resp, err := s.ReleaseModule.Status(rel, req, s.env) + if sc == release.Status_DELETED || sc == release.Status_FAILED { + // Skip errors if this is already deleted or failed. + return statusResp, nil + } else if err != nil { + s.Log("warning: Get for %s failed: %v", rel.Name, err) + return nil, err + } + rel.Info.Status.Resources = resp + return statusResp, nil +} diff --git a/pkg/tiller/release_status_test.go b/pkg/tiller/release_status_test.go new file mode 100644 index 000000000..9128b43a1 --- /dev/null +++ b/pkg/tiller/release_status_test.go @@ -0,0 +1,64 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "testing" +) + +func TestGetReleaseStatus(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + + res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) + if err != nil { + t.Errorf("Error getting release content: %s", err) + } + + if res.Name != rel.Name { + t.Errorf("Expected name %q, got %q", rel.Name, res.Name) + } + if res.Info.Status.Code != release.Status_DEPLOYED { + t.Errorf("Expected %d, got %d", release.Status_DEPLOYED, res.Info.Status.Code) + } +} + +func TestGetReleaseStatusDeleted(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rel.Info.Status.Code = release.Status_DELETED + if err := rs.env.Releases.Create(rel); err != nil { + t.Fatalf("Could not store mock release: %s", err) + } + + res, err := rs.GetReleaseStatus(c, &services.GetReleaseStatusRequest{Name: rel.Name, Version: 1}) + if err != nil { + t.Fatalf("Error getting release content: %s", err) + } + + if res.Info.Status.Code != release.Status_DELETED { + t.Errorf("Expected %d, got %d", release.Status_DELETED, res.Info.Status.Code) + } +} diff --git a/pkg/tiller/release_testing.go b/pkg/tiller/release_testing.go new file mode 100644 index 000000000..4f9a38a96 --- /dev/null +++ b/pkg/tiller/release_testing.go @@ -0,0 +1,71 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + reltesting "k8s.io/helm/pkg/releasetesting" +) + +// RunReleaseTest runs pre-defined tests stored as hooks on a given release +func (s *ReleaseServer) RunReleaseTest(req *services.TestReleaseRequest, stream services.ReleaseService_RunReleaseTestServer) error { + + if !ValidName.MatchString(req.Name) { + return errMissingRelease + } + + // finds the non-deleted release with the given name + rel, err := s.env.Releases.Last(req.Name) + if err != nil { + return err + } + + testEnv := &reltesting.Environment{ + Namespace: rel.Namespace, + KubeClient: s.env.KubeClient, + Timeout: req.Timeout, + Stream: stream, + } + s.Log("running tests for release %s", rel.Name) + tSuite, err := reltesting.NewTestSuite(rel) + if err != nil { + s.Log("error creating test suite for %s: %s", rel.Name, err) + return err + } + + if err := tSuite.Run(testEnv); err != nil { + s.Log("error running test suite for %s: %s", rel.Name, err) + return err + } + + rel.Info.Status.LastTestSuiteRun = &release.TestSuite{ + StartedAt: tSuite.StartedAt, + CompletedAt: tSuite.CompletedAt, + Results: tSuite.Results, + } + + if req.Cleanup { + testEnv.DeleteTestPods(tSuite.TestManifests) + } + + if err := s.env.Releases.Update(rel); err != nil { + s.Log("test: Failed to store updated release: %s", err) + } + + return nil +} diff --git a/pkg/tiller/release_testing_test.go b/pkg/tiller/release_testing_test.go new file mode 100644 index 000000000..0b3b7768e --- /dev/null +++ b/pkg/tiller/release_testing_test.go @@ -0,0 +1,35 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "testing" +) + +func TestRunReleaseTest(t *testing.T) { + rs := rsFixture() + rel := namedReleaseStub("nemo", release.Status_DEPLOYED) + rs.env.Releases.Create(rel) + + req := &services.TestReleaseRequest{Name: "nemo", Timeout: 2} + err := rs.RunReleaseTest(req, mockRunReleaseTestServer{}) + if err != nil { + t.Fatalf("failed to run release tests on %s: %s", rel.Name, err) + } +} diff --git a/pkg/tiller/release_uninstall.go b/pkg/tiller/release_uninstall.go new file mode 100644 index 000000000..54971ee6e --- /dev/null +++ b/pkg/tiller/release_uninstall.go @@ -0,0 +1,137 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + "strings" + + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/hooks" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + relutil "k8s.io/helm/pkg/releaseutil" + "k8s.io/helm/pkg/timeconv" +) + +// UninstallRelease deletes all of the resources associated with this release, and marks the release DELETED. +func (s *ReleaseServer) UninstallRelease(c ctx.Context, req *services.UninstallReleaseRequest) (*services.UninstallReleaseResponse, error) { + if !ValidName.MatchString(req.Name) { + s.Log("uninstall: Release not found: %s", req.Name) + return nil, errMissingRelease + } + + if len(req.Name) > releaseNameMaxLen { + return nil, fmt.Errorf("release name %q exceeds max length of %d", req.Name, releaseNameMaxLen) + } + + err := s.env.Releases.LockRelease(req.Name) + if err != nil { + return nil, err + } + defer s.env.Releases.UnlockRelease(req.Name) + + rels, err := s.env.Releases.History(req.Name) + if err != nil { + s.Log("uninstall: Release not loaded: %s", req.Name) + return nil, err + } + if len(rels) < 1 { + return nil, errMissingRelease + } + + relutil.SortByRevision(rels) + rel := rels[len(rels)-1] + + // TODO: Are there any cases where we want to force a delete even if it's + // already marked deleted? + if rel.Info.Status.Code == release.Status_DELETED { + if req.Purge { + if err := s.purgeReleases(rels...); err != nil { + s.Log("uninstall: Failed to purge the release: %s", err) + return nil, err + } + return &services.UninstallReleaseResponse{Release: rel}, nil + } + return nil, fmt.Errorf("the release named %q is already deleted", req.Name) + } + + s.Log("uninstall: Deleting %s", req.Name) + rel.Info.Status.Code = release.Status_DELETING + rel.Info.Deleted = timeconv.Now() + rel.Info.Description = "Deletion in progress (or silently failed)" + res := &services.UninstallReleaseResponse{Release: rel} + + if !req.DisableHooks { + if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PreDelete, req.Timeout); err != nil { + return res, err + } + } else { + s.Log("delete hooks disabled for %s", req.Name) + } + + // From here on out, the release is currently considered to be in Status_DELETING + // state. + if err := s.env.Releases.Update(rel); err != nil { + s.Log("uninstall: Failed to store updated release: %s", err) + } + + kept, errs := s.ReleaseModule.Delete(rel, req, s.env) + res.Info = kept + + es := make([]string, 0, len(errs)) + for _, e := range errs { + s.Log("error: %v", e) + es = append(es, e.Error()) + } + + if !req.DisableHooks { + if err := s.execHook(rel.Hooks, rel.Name, rel.Namespace, hooks.PostDelete, req.Timeout); err != nil { + es = append(es, err.Error()) + } + } + + rel.Info.Status.Code = release.Status_DELETED + rel.Info.Description = "Deletion complete" + + if req.Purge { + s.Log("purge requested for %s", req.Name) + err := s.purgeReleases(rels...) + if err != nil { + s.Log("uninstall: Failed to purge the release: %s", err) + } + return res, err + } + + if err := s.env.Releases.Update(rel); err != nil { + s.Log("uninstall: Failed to store updated release: %s", err) + } + + if len(es) > 0 { + return res, fmt.Errorf("deletion completed with %d error(s): %s", len(es), strings.Join(es, "; ")) + } + return res, nil +} + +func (s *ReleaseServer) purgeReleases(rels ...*release.Release) error { + for _, rel := range rels { + if _, err := s.env.Releases.Delete(rel.Name, rel.Version); err != nil { + return err + } + } + return nil +} diff --git a/pkg/tiller/release_uninstall_test.go b/pkg/tiller/release_uninstall_test.go new file mode 100644 index 000000000..ef3afb444 --- /dev/null +++ b/pkg/tiller/release_uninstall_test.go @@ -0,0 +1,177 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "strings" + "testing" +) + +func TestUninstallRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rs.env.Releases.Create(releaseStub()) + + req := &services.UninstallReleaseRequest{ + Name: "angry-panda", + } + + res, err := rs.UninstallRelease(c, req) + if err != nil { + t.Fatalf("Failed uninstall: %s", err) + } + + if res.Release.Name != "angry-panda" { + t.Errorf("Expected angry-panda, got %q", res.Release.Name) + } + + if res.Release.Info.Status.Code != release.Status_DELETED { + t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) + } + + if res.Release.Hooks[0].LastRun.Seconds == 0 { + t.Error("Expected LastRun to be greater than zero.") + } + + if res.Release.Info.Deleted.Seconds <= 0 { + t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) + } + + if res.Release.Info.Description != "Deletion complete" { + t.Errorf("Expected Deletion complete, got %q", res.Release.Info.Description) + } +} + +func TestUninstallPurgeRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + upgradedRel := upgradeReleaseVersion(rel) + rs.env.Releases.Update(rel) + rs.env.Releases.Create(upgradedRel) + + req := &services.UninstallReleaseRequest{ + Name: "angry-panda", + Purge: true, + } + + res, err := rs.UninstallRelease(c, req) + if err != nil { + t.Fatalf("Failed uninstall: %s", err) + } + + if res.Release.Name != "angry-panda" { + t.Errorf("Expected angry-panda, got %q", res.Release.Name) + } + + if res.Release.Info.Status.Code != release.Status_DELETED { + t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) + } + + if res.Release.Info.Deleted.Seconds <= 0 { + t.Errorf("Expected valid UNIX date, got %d", res.Release.Info.Deleted.Seconds) + } + rels, err := rs.GetHistory(helm.NewContext(), &services.GetHistoryRequest{Name: "angry-panda"}) + if err != nil { + t.Fatal(err) + } + if len(rels.Releases) != 0 { + t.Errorf("Expected no releases in storage, got %d", len(rels.Releases)) + } +} + +func TestUninstallPurgeDeleteRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rs.env.Releases.Create(releaseStub()) + + req := &services.UninstallReleaseRequest{ + Name: "angry-panda", + } + + _, err := rs.UninstallRelease(c, req) + if err != nil { + t.Fatalf("Failed uninstall: %s", err) + } + + req2 := &services.UninstallReleaseRequest{ + Name: "angry-panda", + Purge: true, + } + + _, err2 := rs.UninstallRelease(c, req2) + if err2 != nil && err2.Error() != "'angry-panda' has no deployed releases" { + t.Errorf("Failed uninstall: %s", err2) + } +} + +func TestUninstallReleaseWithKeepPolicy(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + name := "angry-bunny" + rs.env.Releases.Create(releaseWithKeepStub(name)) + + req := &services.UninstallReleaseRequest{ + Name: name, + } + + res, err := rs.UninstallRelease(c, req) + if err != nil { + t.Fatalf("Failed uninstall: %s", err) + } + + if res.Release.Name != name { + t.Errorf("Expected angry-bunny, got %q", res.Release.Name) + } + + if res.Release.Info.Status.Code != release.Status_DELETED { + t.Errorf("Expected status code to be DELETED, got %d", res.Release.Info.Status.Code) + } + + if res.Info == "" { + t.Errorf("Expected response info to not be empty") + } else { + if !strings.Contains(res.Info, "[ConfigMap] test-cm-keep") { + t.Errorf("unexpected output: %s", res.Info) + } + } +} + +func TestUninstallReleaseNoHooks(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rs.env.Releases.Create(releaseStub()) + + req := &services.UninstallReleaseRequest{ + Name: "angry-panda", + DisableHooks: true, + } + + res, err := rs.UninstallRelease(c, req) + if err != nil { + t.Errorf("Failed uninstall: %s", err) + } + + // The default value for a protobuf timestamp is nil. + if res.Release.Hooks[0].LastRun != nil { + t.Errorf("Expected LastRun to be zero, got %d.", res.Release.Hooks[0].LastRun.Seconds) + } +} diff --git a/pkg/tiller/release_update.go b/pkg/tiller/release_update.go new file mode 100644 index 000000000..fb30d1661 --- /dev/null +++ b/pkg/tiller/release_update.go @@ -0,0 +1,174 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "fmt" + + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/chartutil" + "k8s.io/helm/pkg/hooks" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "k8s.io/helm/pkg/timeconv" +) + +// UpdateRelease takes an existing release and new information, and upgrades the release. +func (s *ReleaseServer) UpdateRelease(c ctx.Context, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { + err := s.env.Releases.LockRelease(req.Name) + if err != nil { + return nil, err + } + defer s.env.Releases.UnlockRelease(req.Name) + + s.Log("preparing update for %s", req.Name) + currentRelease, updatedRelease, err := s.prepareUpdate(req) + if err != nil { + return nil, err + } + + s.Log("performing update for %s", req.Name) + res, err := s.performUpdate(currentRelease, updatedRelease, req) + if err != nil { + return res, err + } + + if !req.DryRun { + s.Log("creating updated release for %s", req.Name) + if err := s.env.Releases.Create(updatedRelease); err != nil { + return res, err + } + } + + return res, nil +} + +// prepareUpdate builds an updated release for an update operation. +func (s *ReleaseServer) prepareUpdate(req *services.UpdateReleaseRequest) (*release.Release, *release.Release, error) { + if !ValidName.MatchString(req.Name) { + return nil, nil, errMissingRelease + } + + if req.Chart == nil { + return nil, nil, errMissingChart + } + + // finds the non-deleted release with the given name + currentRelease, err := s.env.Releases.Last(req.Name) + if err != nil { + return nil, nil, err + } + + // If new values were not supplied in the upgrade, re-use the existing values. + if err := s.reuseValues(req, currentRelease); err != nil { + return nil, nil, err + } + + // Increment revision count. This is passed to templates, and also stored on + // the release object. + revision := currentRelease.Version + 1 + + ts := timeconv.Now() + options := chartutil.ReleaseOptions{ + Name: req.Name, + Time: ts, + Namespace: currentRelease.Namespace, + IsUpgrade: true, + Revision: int(revision), + } + + caps, err := capabilities(s.clientset.Discovery()) + if err != nil { + return nil, nil, err + } + valuesToRender, err := chartutil.ToRenderValuesCaps(req.Chart, req.Values, options, caps) + if err != nil { + return nil, nil, err + } + + hooks, manifestDoc, notesTxt, err := s.renderResources(req.Chart, valuesToRender, caps.APIVersions) + if err != nil { + return nil, nil, err + } + + // Store an updated release. + updatedRelease := &release.Release{ + Name: req.Name, + Namespace: currentRelease.Namespace, + Chart: req.Chart, + Config: req.Values, + Info: &release.Info{ + FirstDeployed: currentRelease.Info.FirstDeployed, + LastDeployed: ts, + Status: &release.Status{Code: release.Status_UNKNOWN}, + Description: "Preparing upgrade", // This should be overwritten later. + }, + Version: revision, + Manifest: manifestDoc.String(), + Hooks: hooks, + } + + if len(notesTxt) > 0 { + updatedRelease.Info.Status.Notes = notesTxt + } + err = validateManifest(s.env.KubeClient, currentRelease.Namespace, manifestDoc.Bytes()) + return currentRelease, updatedRelease, err +} + +func (s *ReleaseServer) performUpdate(originalRelease, updatedRelease *release.Release, req *services.UpdateReleaseRequest) (*services.UpdateReleaseResponse, error) { + res := &services.UpdateReleaseResponse{Release: updatedRelease} + + if req.DryRun { + s.Log("dry run for %s", updatedRelease.Name) + res.Release.Info.Description = "Dry run complete" + return res, nil + } + + // pre-upgrade hooks + if !req.DisableHooks { + if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PreUpgrade, req.Timeout); err != nil { + return res, err + } + } else { + s.Log("update hooks disabled for %s", req.Name) + } + if err := s.ReleaseModule.Update(originalRelease, updatedRelease, req, s.env); err != nil { + msg := fmt.Sprintf("Upgrade %q failed: %s", updatedRelease.Name, err) + s.Log("warning: %s", msg) + originalRelease.Info.Status.Code = release.Status_SUPERSEDED + updatedRelease.Info.Status.Code = release.Status_FAILED + updatedRelease.Info.Description = msg + s.recordRelease(originalRelease, true) + s.recordRelease(updatedRelease, false) + return res, err + } + + // post-upgrade hooks + if !req.DisableHooks { + if err := s.execHook(updatedRelease.Hooks, updatedRelease.Name, updatedRelease.Namespace, hooks.PostUpgrade, req.Timeout); err != nil { + return res, err + } + } + + originalRelease.Info.Status.Code = release.Status_SUPERSEDED + s.recordRelease(originalRelease, true) + + updatedRelease.Info.Status.Code = release.Status_DEPLOYED + updatedRelease.Info.Description = "Upgrade complete" + + return res, nil +} diff --git a/pkg/tiller/release_update_test.go b/pkg/tiller/release_update_test.go new file mode 100644 index 000000000..cba07e11e --- /dev/null +++ b/pkg/tiller/release_update_test.go @@ -0,0 +1,286 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + "k8s.io/helm/pkg/helm" + "k8s.io/helm/pkg/proto/hapi/chart" + "k8s.io/helm/pkg/proto/hapi/release" + "k8s.io/helm/pkg/proto/hapi/services" + "strings" + "testing" +) + +func TestUpdateRelease(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + + if res.Release.Name == "" { + t.Errorf("Expected release name.") + } + + if res.Release.Name != rel.Name { + t.Errorf("Updated release name does not match previous release name. Expected %s, got %s", rel.Name, res.Release.Name) + } + + if res.Release.Namespace != rel.Namespace { + t.Errorf("Expected release namespace '%s', got '%s'.", rel.Namespace, res.Release.Namespace) + } + + updated, err := rs.env.Releases.Get(res.Release.Name, res.Release.Version) + if err != nil { + t.Errorf("Expected release for %s (%v).", res.Release.Name, rs.env.Releases) + } + + if len(updated.Hooks) != 1 { + t.Fatalf("Expected 1 hook, got %d", len(updated.Hooks)) + } + if updated.Hooks[0].Manifest != manifestWithUpgradeHooks { + t.Errorf("Unexpected manifest: %v", updated.Hooks[0].Manifest) + } + + if updated.Hooks[0].Events[0] != release.Hook_POST_UPGRADE { + t.Errorf("Expected event 0 to be post upgrade") + } + + if updated.Hooks[0].Events[1] != release.Hook_PRE_UPGRADE { + t.Errorf("Expected event 0 to be pre upgrade") + } + + if len(res.Release.Manifest) == 0 { + t.Errorf("No manifest returned: %v", res.Release) + } + + if res.Release.Config == nil { + t.Errorf("Got release without config: %#v", res.Release) + } else if res.Release.Config.Raw != rel.Config.Raw { + t.Errorf("Expected release values %q, got %q", rel.Config.Raw, res.Release.Config.Raw) + } + + if len(updated.Manifest) == 0 { + t.Errorf("Expected manifest in %v", res) + } + + if !strings.Contains(updated.Manifest, "---\n# Source: hello/templates/hello\nhello: world") { + t.Errorf("unexpected output: %s", rel.Manifest) + } + + if res.Release.Version != 2 { + t.Errorf("Expected release version to be %v, got %v", 2, res.Release.Version) + } + + edesc := "Upgrade complete" + if got := res.Release.Info.Description; got != edesc { + t.Errorf("Expected description %q, got %q", edesc, got) + } +} +func TestUpdateRelease_ResetValues(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + ResetValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been unset. Config: &chart.Config{Raw: `name: value`}, + if res.Release.Config != nil && res.Release.Config.Raw != "" { + t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) + } +} + +func TestUpdateRelease_ReuseValues(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + // Since reuseValues is set, this should get ignored. + Values: &chart.Config{Raw: "foo: bar\n"}, + }, + Values: &chart.Config{Raw: "name2: val2"}, + ReuseValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been overwritten with the old value. + expect := "name: value\n" + if res.Release.Chart.Values != nil && res.Release.Chart.Values.Raw != expect { + t.Errorf("Expected chart values to be %q, got %q", expect, res.Release.Chart.Values.Raw) + } + // This should have the newly-passed overrides. + expect = "name2: val2" + if res.Release.Config != nil && res.Release.Config.Raw != expect { + t.Errorf("Expected request config to be %q, got %q", expect, res.Release.Config.Raw) + } +} + +func TestUpdateRelease_ResetReuseValues(t *testing.T) { + // This verifies that when both reset and reuse are set, reset wins. + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + ResetValues: true, + ReuseValues: true, + } + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + // This should have been unset. Config: &chart.Config{Raw: `name: value`}, + if res.Release.Config != nil && res.Release.Config.Raw != "" { + t.Errorf("Expected chart config to be empty, got %q", res.Release.Config.Raw) + } +} + +func TestUpdateReleaseFailure(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + rs.env.KubeClient = newUpdateFailingKubeClient() + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/something", Data: []byte("hello: world")}, + }, + }, + } + + res, err := rs.UpdateRelease(c, req) + if err == nil { + t.Error("Expected failed update") + } + + if updatedStatus := res.Release.Info.Status.Code; updatedStatus != release.Status_FAILED { + t.Errorf("Expected FAILED release. Got %d", updatedStatus) + } + + edesc := "Upgrade \"angry-panda\" failed: Failed update in kube client" + if got := res.Release.Info.Description; got != edesc { + t.Errorf("Expected description %q, got %q", edesc, got) + } + + oldRelease, err := rs.env.Releases.Get(rel.Name, rel.Version) + if err != nil { + t.Errorf("Expected to be able to get previous release") + } + if oldStatus := oldRelease.Info.Status.Code; oldStatus != release.Status_SUPERSEDED { + t.Errorf("Expected SUPERSEDED status on previous Release version. Got %v", oldStatus) + } +} + +func TestUpdateReleaseNoHooks(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: &chart.Chart{ + Metadata: &chart.Metadata{Name: "hello"}, + Templates: []*chart.Template{ + {Name: "templates/hello", Data: []byte("hello: world")}, + {Name: "templates/hooks", Data: []byte(manifestWithUpgradeHooks)}, + }, + }, + } + + res, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } + + if hl := res.Release.Hooks[0].LastRun; hl != nil { + t.Errorf("Expected that no hooks were run. Got %d", hl) + } + +} + +func TestUpdateReleaseNoChanges(t *testing.T) { + c := helm.NewContext() + rs := rsFixture() + rel := releaseStub() + rs.env.Releases.Create(rel) + + req := &services.UpdateReleaseRequest{ + Name: rel.Name, + DisableHooks: true, + Chart: rel.GetChart(), + } + + _, err := rs.UpdateRelease(c, req) + if err != nil { + t.Fatalf("Failed updated: %s", err) + } +} diff --git a/pkg/tiller/release_version.go b/pkg/tiller/release_version.go new file mode 100644 index 000000000..5cf3ff828 --- /dev/null +++ b/pkg/tiller/release_version.go @@ -0,0 +1,29 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tiller + +import ( + ctx "golang.org/x/net/context" + "k8s.io/helm/pkg/proto/hapi/services" + "k8s.io/helm/pkg/version" +) + +// GetVersion sends the server version. +func (s *ReleaseServer) GetVersion(c ctx.Context, req *services.GetVersionRequest) (*services.GetVersionResponse, error) { + v := version.GetVersionProto() + return &services.GetVersionResponse{Version: v}, nil +} diff --git a/pkg/tiller/server.go b/pkg/tiller/server.go index 75db5e093..8d6b6fa13 100644 --- a/pkg/tiller/server.go +++ b/pkg/tiller/server.go @@ -88,8 +88,8 @@ func versionFromContext(ctx context.Context) string { func checkClientVersion(ctx context.Context) error { clientVersion := versionFromContext(ctx) - if !version.IsCompatible(clientVersion, version.Version) { - return fmt.Errorf("incompatible versions client: %s server: %s", clientVersion, version.Version) + if !version.IsCompatible(clientVersion, version.GetVersion()) { + return fmt.Errorf("incompatible versions client[%s] server[%s]", clientVersion, version.GetVersion()) } return nil } diff --git a/pkg/urlutil/urlutil.go b/pkg/urlutil/urlutil.go index 3a6570470..fb67708ae 100644 --- a/pkg/urlutil/urlutil.go +++ b/pkg/urlutil/urlutil.go @@ -17,10 +17,10 @@ limitations under the License. package urlutil import ( - "net" "net/url" "path" "path/filepath" + "strings" ) // URLJoin joins a base URL to one or more path components. @@ -70,10 +70,18 @@ func ExtractHostname(addr string) (string, error) { if err != nil { return "", err } + return stripPort(u.Host), nil +} - host, _, err := net.SplitHostPort(u.Host) - if err != nil { - return "", err +// Backported from Go 1.8 because Circle is still on 1.7 +func stripPort(hostport string) string { + colon := strings.IndexByte(hostport, ':') + if colon == -1 { + return hostport } - return host, nil + if i := strings.IndexByte(hostport, ']'); i != -1 { + return strings.TrimPrefix(hostport[:i], "[") + } + return hostport[:colon] + } diff --git a/pkg/urlutil/urlutil_test.go b/pkg/urlutil/urlutil_test.go index 5944df1ae..f0c82c0a9 100644 --- a/pkg/urlutil/urlutil_test.go +++ b/pkg/urlutil/urlutil_test.go @@ -62,3 +62,16 @@ func TestEqual(t *testing.T) { } } } + +func TestExtractHostname(t *testing.T) { + tests := map[string]string{ + "http://example.com": "example.com", + "https://example.com/foo": "example.com", + "https://example.com:31337/not/with/a/bang/but/a/whimper": "example.com", + } + for start, expect := range tests { + if got, _ := ExtractHostname(start); got != expect { + t.Errorf("Got %q, expected %q", got, expect) + } + } +} diff --git a/pkg/version/compatible.go b/pkg/version/compatible.go index c8f359971..735610778 100644 --- a/pkg/version/compatible.go +++ b/pkg/version/compatible.go @@ -18,12 +18,16 @@ package version // import "k8s.io/helm/pkg/version" import ( "fmt" + "strings" "github.com/Masterminds/semver" ) // IsCompatible tests if a client and server version are compatible. func IsCompatible(client, server string) bool { + if isUnreleased(client) || isUnreleased(server) { + return true + } cv, err := semver.NewVersion(client) if err != nil { return false @@ -55,3 +59,7 @@ func IsCompatibleRange(constraint, ver string) bool { } return c.Check(sv) } + +func isUnreleased(v string) bool { + return strings.HasSuffix(v, "unreleased") +} diff --git a/pkg/version/version.go b/pkg/version/version.go index ff68c4a91..c0b54fef7 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -26,10 +26,10 @@ var ( // Increment major number for new feature additions and behavioral changes. // Increment minor number for bug fixes and performance enhancements. // Increment patch number for critical fixes to existing releases. - Version = "v2.3.0" + Version = "v2.4" // BuildMetadata is extra build time data - BuildMetadata = "" + BuildMetadata = "unreleased" // GitCommit is the git sha1 GitCommit = "" // GitTreeState is the state of the git tree diff --git a/rootfs/Dockerfile b/rootfs/Dockerfile index e30d981cb..53757cd8d 100644 --- a/rootfs/Dockerfile +++ b/rootfs/Dockerfile @@ -14,9 +14,11 @@ FROM alpine:3.3 +RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* + ENV HOME /tmp -COPY . / +COPY tiller /tiller EXPOSE 44134 diff --git a/rootfs/Dockerfile.experimental b/rootfs/Dockerfile.experimental new file mode 100644 index 000000000..990bcde51 --- /dev/null +++ b/rootfs/Dockerfile.experimental @@ -0,0 +1,26 @@ +# Copyright 2017 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM alpine:3.3 + +RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* + +ENV HOME /tmp + +COPY tiller /tiller + +EXPOSE 44134 + +CMD ["/tiller", "--experimental-release"] + diff --git a/rootfs/Dockerfile.rudder b/rootfs/Dockerfile.rudder new file mode 100644 index 000000000..6bb3a2d92 --- /dev/null +++ b/rootfs/Dockerfile.rudder @@ -0,0 +1,25 @@ +# Copyright 2017 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM alpine:3.3 + +RUN apk update && apk add ca-certificates && rm -rf /var/cache/apk/* + +ENV HOME /tmp + +COPY rudder /rudder + +EXPOSE 10001 + +CMD ["/rudder"] diff --git a/scripts/cluster/skydns.yaml b/scripts/cluster/skydns.yaml deleted file mode 100644 index 720877d5d..000000000 --- a/scripts/cluster/skydns.yaml +++ /dev/null @@ -1,137 +0,0 @@ -apiVersion: v1 -kind: ReplicationController -metadata: - name: kube-dns-v10 - namespace: kube-system - labels: - k8s-app: kube-dns - version: v10 - kubernetes.io/cluster-service: "true" -spec: - replicas: 1 - selector: - k8s-app: kube-dns - version: v10 - template: - metadata: - labels: - k8s-app: kube-dns - version: v10 - kubernetes.io/cluster-service: "true" - spec: - containers: - - name: etcd - image: gcr.io/google_containers/etcd-amd64:2.2.1 - resources: - # keep request = limit to keep this container in guaranteed class - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 100m - memory: 50Mi - command: - - /usr/local/bin/etcd - - -data-dir - - /var/etcd/data - - -listen-client-urls - - http://127.0.0.1:2379,http://127.0.0.1:4001 - - -advertise-client-urls - - http://127.0.0.1:2379,http://127.0.0.1:4001 - - -initial-cluster-token - - skydns-etcd - volumeMounts: - - name: etcd-storage - mountPath: /var/etcd/data - - name: kube2sky - image: gcr.io/google_containers/kube2sky:1.12 - resources: - # keep request = limit to keep this container in guaranteed class - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 100m - memory: 50Mi - args: - # command = "/kube2sky" - - --domain=cluster.local - - name: skydns - image: gcr.io/google_containers/skydns:2015-10-13-8c72f8c - resources: - # keep request = limit to keep this container in guaranteed class - limits: - cpu: 100m - memory: 50Mi - requests: - cpu: 100m - memory: 50Mi - args: - # command = "/skydns" - - -machines=http://127.0.0.1:4001 - - -addr=0.0.0.0:53 - - -ns-rotate=false - - -domain=cluster.local. - ports: - - containerPort: 53 - name: dns - protocol: UDP - - containerPort: 53 - name: dns-tcp - protocol: TCP - livenessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - initialDelaySeconds: 30 - timeoutSeconds: 5 - readinessProbe: - httpGet: - path: /healthz - port: 8080 - scheme: HTTP - initialDelaySeconds: 1 - timeoutSeconds: 5 - - name: healthz - image: gcr.io/google_containers/exechealthz:1.0 - resources: - # keep request = limit to keep this container in guaranteed class - limits: - cpu: 10m - memory: 20Mi - requests: - cpu: 10m - memory: 20Mi - args: - - -cmd=nslookup kubernetes.default.svc.cluster.local 127.0.0.1 >/dev/null - - -port=8080 - ports: - - containerPort: 8080 - protocol: TCP - volumes: - - name: etcd-storage - emptyDir: {} - dnsPolicy: Default # Don't use cluster DNS. ---- -apiVersion: v1 -kind: Service -metadata: - name: kube-dns - namespace: kube-system - labels: - k8s-app: kube-dns - kubernetes.io/cluster-service: "true" - kubernetes.io/name: "KubeDNS" -spec: - selector: - k8s-app: kube-dns - clusterIP: 10.0.0.10 - ports: - - name: dns - port: 53 - protocol: UDP - - name: dns-tcp - port: 53 - protocol: TCP - diff --git a/scripts/completions.bash b/scripts/completions.bash index f928ae502..b2524ea38 100644 --- a/scripts/completions.bash +++ b/scripts/completions.bash @@ -242,6 +242,8 @@ _helm_completion() must_have_one_flag=() must_have_one_noun=() + must_have_one_noun+=("bash") + must_have_one_noun+=("zsh") noun_aliases=() } @@ -422,13 +424,23 @@ _helm_fetch() flags_with_completion=() flags_completion=() + flags+=("--ca-file=") + local_nonpersistent_flags+=("--ca-file=") + flags+=("--cert-file=") + local_nonpersistent_flags+=("--cert-file=") flags+=("--destination=") two_word_flags+=("-d") local_nonpersistent_flags+=("--destination=") + flags+=("--devel") + local_nonpersistent_flags+=("--devel") + flags+=("--key-file=") + local_nonpersistent_flags+=("--key-file=") flags+=("--keyring=") local_nonpersistent_flags+=("--keyring=") flags+=("--prov") local_nonpersistent_flags+=("--prov") + flags+=("--repo=") + local_nonpersistent_flags+=("--repo=") flags+=("--untar") local_nonpersistent_flags+=("--untar") flags+=("--untardir=") @@ -638,6 +650,8 @@ _helm_init() local_nonpersistent_flags+=("--local-repo-url=") flags+=("--net-host") local_nonpersistent_flags+=("--net-host") + flags+=("--service-account=") + local_nonpersistent_flags+=("--service-account=") flags+=("--skip-refresh") local_nonpersistent_flags+=("--skip-refresh") flags+=("--stable-repo-url=") @@ -679,8 +693,16 @@ _helm_inspect_chart() flags_with_completion=() flags_completion=() + flags+=("--ca-file=") + local_nonpersistent_flags+=("--ca-file=") + flags+=("--cert-file=") + local_nonpersistent_flags+=("--cert-file=") + flags+=("--key-file=") + local_nonpersistent_flags+=("--key-file=") flags+=("--keyring=") local_nonpersistent_flags+=("--keyring=") + flags+=("--repo=") + local_nonpersistent_flags+=("--repo=") flags+=("--verify") local_nonpersistent_flags+=("--verify") flags+=("--version=") @@ -707,8 +729,16 @@ _helm_inspect_values() flags_with_completion=() flags_completion=() + flags+=("--ca-file=") + local_nonpersistent_flags+=("--ca-file=") + flags+=("--cert-file=") + local_nonpersistent_flags+=("--cert-file=") + flags+=("--key-file=") + local_nonpersistent_flags+=("--key-file=") flags+=("--keyring=") local_nonpersistent_flags+=("--keyring=") + flags+=("--repo=") + local_nonpersistent_flags+=("--repo=") flags+=("--verify") local_nonpersistent_flags+=("--verify") flags+=("--version=") @@ -737,8 +767,16 @@ _helm_inspect() flags_with_completion=() flags_completion=() + flags+=("--ca-file=") + local_nonpersistent_flags+=("--ca-file=") + flags+=("--cert-file=") + local_nonpersistent_flags+=("--cert-file=") + flags+=("--key-file=") + local_nonpersistent_flags+=("--key-file=") flags+=("--keyring=") local_nonpersistent_flags+=("--keyring=") + flags+=("--repo=") + local_nonpersistent_flags+=("--repo=") flags+=("--verify") local_nonpersistent_flags+=("--verify") flags+=("--version=") @@ -765,8 +803,16 @@ _helm_install() flags_with_completion=() flags_completion=() + flags+=("--ca-file=") + local_nonpersistent_flags+=("--ca-file=") + flags+=("--cert-file=") + local_nonpersistent_flags+=("--cert-file=") + flags+=("--devel") + local_nonpersistent_flags+=("--devel") flags+=("--dry-run") local_nonpersistent_flags+=("--dry-run") + flags+=("--key-file=") + local_nonpersistent_flags+=("--key-file=") flags+=("--keyring=") local_nonpersistent_flags+=("--keyring=") flags+=("--name=") @@ -780,6 +826,8 @@ _helm_install() local_nonpersistent_flags+=("--no-hooks") flags+=("--replace") local_nonpersistent_flags+=("--replace") + flags+=("--repo=") + local_nonpersistent_flags+=("--repo=") flags+=("--set=") local_nonpersistent_flags+=("--set=") flags+=("--timeout=") @@ -1000,6 +1048,28 @@ _helm_plugin_remove() noun_aliases=() } +_helm_plugin_update() +{ + last_command="helm_plugin_update" + commands=() + + flags=() + two_word_flags=() + local_nonpersistent_flags=() + flags_with_completion=() + flags_completion=() + + flags+=("--debug") + flags+=("--home=") + flags+=("--host=") + flags+=("--kube-context=") + flags+=("--tiller-namespace=") + + must_have_one_flag=() + must_have_one_noun=() + noun_aliases=() +} + _helm_plugin() { last_command="helm_plugin" @@ -1007,6 +1077,7 @@ _helm_plugin() commands+=("install") commands+=("list") commands+=("remove") + commands+=("update") flags=() two_word_flags=() @@ -1224,6 +1295,8 @@ _helm_rollback() flags+=("--dry-run") local_nonpersistent_flags+=("--dry-run") + flags+=("--force") + local_nonpersistent_flags+=("--force") flags+=("--no-hooks") local_nonpersistent_flags+=("--no-hooks") flags+=("--recreate-pods") @@ -1392,13 +1465,23 @@ _helm_upgrade() flags_with_completion=() flags_completion=() + flags+=("--ca-file=") + local_nonpersistent_flags+=("--ca-file=") + flags+=("--cert-file=") + local_nonpersistent_flags+=("--cert-file=") + flags+=("--devel") + local_nonpersistent_flags+=("--devel") flags+=("--disable-hooks") local_nonpersistent_flags+=("--disable-hooks") flags+=("--dry-run") local_nonpersistent_flags+=("--dry-run") + flags+=("--force") + local_nonpersistent_flags+=("--force") flags+=("--install") flags+=("-i") local_nonpersistent_flags+=("--install") + flags+=("--key-file=") + local_nonpersistent_flags+=("--key-file=") flags+=("--keyring=") local_nonpersistent_flags+=("--keyring=") flags+=("--namespace=") @@ -1407,6 +1490,8 @@ _helm_upgrade() local_nonpersistent_flags+=("--no-hooks") flags+=("--recreate-pods") local_nonpersistent_flags+=("--recreate-pods") + flags+=("--repo=") + local_nonpersistent_flags+=("--repo=") flags+=("--reset-values") local_nonpersistent_flags+=("--reset-values") flags+=("--reuse-values") diff --git a/scripts/get b/scripts/get index a52a0a9a1..02884d7f9 100755 --- a/scripts/get +++ b/scripts/get @@ -62,31 +62,31 @@ verifySupported() { fi } -# checkLatestVersion checks the latest available version. -checkLatestVersion() { - # Use the GitHub releases webpage for the project to find the latest version for this project. - local latest_url="https://github.com/kubernetes/helm/releases/latest" +# checkDesiredVersion checks if the desired version is available. +checkDesiredVersion() { + # Use the GitHub releases webpage for the project to find the desired version for this project. + local release_url="https://github.com/kubernetes/helm/releases/${DESIRED_VERSION:-latest}" if type "curl" > /dev/null; then - TAG=$(curl -SsL $latest_url | awk '/\/tag\//' | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}') + TAG=$(curl -SsL $release_url | awk '/\/tag\//' | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}') elif type "wget" > /dev/null; then - TAG=$(wget -q -O - $latest_url | awk '/\/tag\//' | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}') + TAG=$(wget -q -O - $release_url | awk '/\/tag\//' | head -n 1 | cut -d '"' -f 2 | awk '{n=split($NF,a,"/");print a[n]}') fi if [ "x$TAG" == "x" ]; then - echo "Cannot determine latest tag." + echo "Cannot determine ${DESIRED_VERSION} tag." exit 1 fi } # checkHelmInstalledVersion checks which version of helm is installed and -# if it needs to be updated. +# if it needs to be changed. checkHelmInstalledVersion() { if [[ -f "${HELM_INSTALL_DIR}/${PROJECT_NAME}" ]]; then local version=$(helm version | grep '^Client' | cut -d'"' -f2) if [[ "$version" == "$TAG" ]]; then - echo "Helm ${version} is up-to-date." + echo "Helm ${version} is already ${DESIRED_VERSION:-latest}" return 0 else - echo "Helm ${TAG} is available. Upgrading from version ${version}." + echo "Helm ${TAG} is available. Changing from version ${version}." return 1 fi else @@ -137,7 +137,12 @@ installFile() { fail_trap() { result=$? if [ "$result" != "0" ]; then - echo "Failed to install $PROJECT_NAME" + if [[ -n "$INPUT_ARGUMENTS" ]]; then + echo "Failed to install $PROJECT_NAME with the arguments provided: $INPUT_ARGUMENTS" + help + else + echo "Failed to install $PROJECT_NAME" + fi echo -e "\tFor support, go to https://github.com/kubernetes/helm." fi exit $result @@ -156,15 +161,44 @@ testVersion() { echo "Run '$PROJECT_NAME init' to configure $PROJECT_NAME." } +# help provides possible cli installation arguments +help () { + echo "Accepted cli arguments are:" + echo -e "\t[--help|-h ] ->> prints this help" + echo -e "\t[--version|-v ] . When not defined it defaults to latest" + echo -e "\te.g. --version v2.4.0 or -v latest" +} + # Execution #Stop execution on any error trap "fail_trap" EXIT set -e + +# Parsing input arguments (if any) +export INPUT_ARGUMENTS="${@}" +set -u +while [[ $# -gt 0 ]]; do + case $1 in + '--version'|-v) + export DESIRED_VERSION="${2}" + shift + ;; + '--help'|-h) + help + exit 0 + ;; + *) exit 1 + ;; + esac + shift +done +set +u + initArch initOS verifySupported -checkLatestVersion +checkDesiredVersion if ! checkHelmInstalledVersion; then downloadFile installFile diff --git a/scripts/local-cluster.sh b/scripts/local-cluster.sh deleted file mode 100755 index 83a57c3ce..000000000 --- a/scripts/local-cluster.sh +++ /dev/null @@ -1,358 +0,0 @@ -#!/usr/bin/env bash - -# Copyright 2016 The Kubernetes Authors All rights reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Bash 'Strict Mode' -# http://redsymbol.net/articles/unofficial-bash-strict-mode -set -euo pipefail -IFS=$'\n\t' - -HELM_ROOT="${BASH_SOURCE[0]%/*}/.." -cd "$HELM_ROOT" - -# Globals ---------------------------------------------------------------------- - -KUBE_VERSION=${KUBE_VERSION:-} -KUBE_PORT=${KUBE_PORT:-8080} -KUBE_CONTEXT=${KUBE_CONTEXT:-docker} -KUBECTL=${KUBECTL:-kubectl} -ENABLE_CLUSTER_DNS=${KUBE_ENABLE_CLUSTER_DNS:-true} -LOG_LEVEL=${LOG_LEVEL:-2} - -# Helper Functions ------------------------------------------------------------- - -# Display error message and exit -error_exit() { - echo "error: ${1:-"unknown error"}" 1>&2 - exit 1 -} - -# Checks if a command exists. Returns 1 or 0 -command_exists() { - hash "${1}" 2>/dev/null -} - -# fetch url using wget or curl and print to stdout -fetch_url() { - local url="$1" - if command_exists wget; then - curl -sSL "$url" - elif command_exists curl; then - wget -qO- "$url" - else - error_exit "Couldn't find curl or wget. Bailing out." - fi -} - -# Program Functions ------------------------------------------------------------ - -# Check host platform and docker host -verify_prereqs() { - echo "Verifying Prerequisites...." - - case "$(uname -s)" in - Darwin) - host_os=darwin - ;; - Linux) - host_os=linux - ;; - *) - error_exit "Unsupported host OS. Must be Linux or Mac OS X." - ;; - esac - - case "$(uname -m)" in - x86_64*) - host_arch=amd64 - ;; - i?86_64*) - host_arch=amd64 - ;; - amd64*) - host_arch=amd64 - ;; - arm*) - host_arch=arm - ;; - i?86*) - host_arch=x86 - ;; - s390x*) - host_arch=s390x - ;; - ppc64le*) - host_arch=ppc64le - ;; - *) - error_exit "Unsupported host arch. Must be x86_64, 386, arm, s390x or ppc64le." - ;; - esac - - - command_exists docker || error_exit "You need docker" - - if ! docker info >/dev/null 2>&1 ; then - error_exit "Can't connect to 'docker' daemon." - fi - - if docker inspect kubelet >/dev/null 2>&1 ; then - error_exit "Kubernetes is already running" - fi - - $KUBECTL version --client >/dev/null || download_kubectl -} - -# Get the latest stable release tag -get_latest_version_number() { - local channel="stable" - if [[ -n "${ALPHA:-}" ]]; then - channel="latest" - fi - local latest_url="https://storage.googleapis.com/kubernetes-release/release/${channel}.txt" - fetch_url "$latest_url" -} - -# Detect ip address od docker host -detect_docker_host_ip() { - if [[ -n "${DOCKER_HOST:-}" ]]; then - awk -F'[/:]' '{print $4}' <<< "$DOCKER_HOST" - else - ifconfig docker0 \ - | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' \ - | grep -Eo '([0-9]*\.){3}[0-9]*' >/dev/null 2>&1 || : - fi -} - -# Set KUBE_MASTER_IP from docker host ip. Defaults to localhost -set_master_ip() { - local docker_ip - - if [[ -z "${KUBE_MASTER_IP:-}" ]]; then - docker_ip=$(detect_docker_host_ip) - if [[ -n "${docker_ip}" ]]; then - KUBE_MASTER_IP="${docker_ip}" - else - KUBE_MASTER_IP=localhost - fi - fi -} - -# Start dockerized kubelet -start_kubernetes() { - echo "Starting kubelet" - - # Enable dns - if [[ "${ENABLE_CLUSTER_DNS}" = true ]]; then - dns_args="--cluster-dns=10.0.0.10 --cluster-domain=cluster.local" - else - # DNS server for real world hostnames. - dns_args="--cluster-dns=8.8.8.8" - fi - - local start_time=$(date +%s) - - docker run \ - --name=kubelet \ - --volume=/:/rootfs:ro \ - --volume=/sys:/sys:ro \ - --volume=/var/lib/docker/:/var/lib/docker:rw \ - --volume=/var/lib/kubelet/:/var/lib/kubelet:rw,rslave \ - --volume=/var/run:/var/run:rw \ - --net=host \ - --pid=host \ - --privileged=true \ - -d \ - gcr.io/google_containers/hyperkube-amd64:${KUBE_VERSION} \ - /hyperkube kubelet \ - --containerized \ - --hostname-override="127.0.0.1" \ - --api-servers=http://localhost:${KUBE_PORT} \ - --config=/etc/kubernetes/manifests \ - --allow-privileged=true \ - ${dns_args} \ - --v=${LOG_LEVEL} >/dev/null - - until $KUBECTL cluster-info &> /dev/null; do - sleep 1 - done - - if [[ $KUBE_VERSION == "1.2"* ]]; then - create_kube_system_namespace - create_kube_dns - fi - - # We expect to have at least 3 running pods - etcd, master and kube-proxy. - local attempt=1 - while (($(KUBECTL get pods --all-namespaces --no-headers 2>/dev/null | grep -c "Running") < 3)); do - echo -n "." - sleep $(( attempt++ )) - done - echo - - echo "Started master components in $(($(date +%s) - start_time)) seconds." -} - -# Open kubernetes master api port. -setup_firewall() { - [[ -n "${DOCKER_MACHINE_NAME:-}" ]] || return - - echo "Adding iptables hackery for docker-machine..." - - local machine_ip - machine_ip=$(docker-machine ip "$DOCKER_MACHINE_NAME") - local iptables_rule="PREROUTING -p tcp -d ${machine_ip} --dport ${KUBE_PORT} -j DNAT --to-destination 127.0.0.1:${KUBE_PORT}" - - if ! docker-machine ssh "${DOCKER_MACHINE_NAME}" "sudo /usr/local/sbin/iptables -t nat -C ${iptables_rule}" &> /dev/null; then - docker-machine ssh "${DOCKER_MACHINE_NAME}" "sudo /usr/local/sbin/iptables -t nat -I ${iptables_rule}" - fi -} - -# Create kube-system namespace in kubernetes -create_kube_system_namespace() { - $KUBECTL create namespace kube-system >/dev/null -} - -# Activate skydns in kubernetes and wait for pods to be ready. -create_kube_dns() { - [[ "${ENABLE_CLUSTER_DNS}" = true ]] || return - - local start_time=$(date +%s) - - echo "Setting up cluster dns..." - - $KUBECTL create -f ./scripts/cluster/skydns.yaml >/dev/null - - echo "Waiting for cluster DNS to become available..." - - local attempt=1 - until $KUBECTL get pods --no-headers --namespace kube-system --selector=k8s-app=kube-dns 2>/dev/null | grep "Running" &>/dev/null; do - echo -n "." - sleep $(( attempt++ )) - done - echo - echo "Started DNS in $(($(date +%s) - start_time)) seconds." -} - -# Generate kubeconfig data for the created cluster. -generate_kubeconfig() { - local cluster_args=( - "--server=http://${KUBE_MASTER_IP}:${KUBE_PORT}" - "--insecure-skip-tls-verify=true" - ) - - $KUBECTL config set-cluster "${KUBE_CONTEXT}" "${cluster_args[@]}" >/dev/null - $KUBECTL config set-context "${KUBE_CONTEXT}" --cluster="${KUBE_CONTEXT}" >/dev/null - $KUBECTL config use-context "${KUBE_CONTEXT}" >/dev/null - - echo "Wrote config for kubeconfig using context: '${KUBE_CONTEXT}'" -} - -# Download kubectl -download_kubectl() { - echo "Downloading kubectl binary..." - - local output="/usr/local/bin/kubectl" - - kubectl_url="https://storage.googleapis.com/kubernetes-release/release/${KUBE_VERSION}/bin/${host_os}/${host_arch}/kubectl" - fetch_url "${kubectl_url}" > "${output}" - chmod a+x "${output}" - - KUBECTL="${output}" -} - -# Clean volumes that are left by kubelet -# -# https://github.com/kubernetes/kubernetes/issues/23197 -# code stolen from https://github.com/huggsboson/docker-compose-kubernetes/blob/SwitchToSharedMount/kube-up.sh -clean_volumes() { - if [[ -n "${DOCKER_MACHINE_NAME:-}" ]]; then - docker-machine ssh "${DOCKER_MACHINE_NAME}" "mount | grep -o 'on /var/lib/kubelet.* type' | cut -c 4- | rev | cut -c 6- | rev | sort -r | xargs --no-run-if-empty sudo umount" - docker-machine ssh "${DOCKER_MACHINE_NAME}" "sudo rm -Rf /var/lib/kubelet" - docker-machine ssh "${DOCKER_MACHINE_NAME}" "sudo mkdir -p /var/lib/kubelet" - docker-machine ssh "${DOCKER_MACHINE_NAME}" "sudo mount --bind /var/lib/kubelet /var/lib/kubelet" - docker-machine ssh "${DOCKER_MACHINE_NAME}" "sudo mount --make-shared /var/lib/kubelet" - else - mount | grep -o 'on /var/lib/kubelet.* type' | cut -c 4- | rev | cut -c 6- | rev | sort -r | xargs --no-run-if-empty sudo umount - sudo rm -Rf /var/lib/kubelet - sudo mkdir -p /var/lib/kubelet - sudo mount --bind /var/lib/kubelet /var/lib/kubelet - sudo mount --make-shared /var/lib/kubelet - fi -} - -# Helper function to properly remove containers -delete_container() { - local container=("$@") - docker stop "${container[@]}" &>/dev/null || : - docker wait "${container[@]}" &>/dev/null || : - docker rm --force --volumes "${container[@]}" &>/dev/null || : -} - -# Delete master components and resources in kubernetes. -kube_down() { - echo "Deleting all resources in kubernetes..." - $KUBECTL delete replicationcontrollers,services,pods,secrets --all >/dev/null 2>&1 || : - $KUBECTL delete replicationcontrollers,services,pods,secrets --all --namespace=kube-system >/dev/null 2>&1 || : - $KUBECTL delete namespace kube-system >/dev/null 2>&1 || : - - echo "Stopping kubelet..." - delete_container kubelet - - echo "Stopping remaining kubernetes containers..." - local kube_containers=($(docker ps -aqf "name=k8s_")) - if [[ "${#kube_containers[@]}" -gt 0 ]]; then - delete_container "${kube_containers[@]}" - fi -} - -# Start a kubernetes cluster in docker. -kube_up() { - verify_prereqs - - set_master_ip - clean_volumes - setup_firewall - - generate_kubeconfig - start_kubernetes - - $KUBECTL cluster-info -} - -KUBE_VERSION=${KUBE_VERSION:-$(get_latest_version_number)} - -# Main ------------------------------------------------------------------------- - -main() { - case "$1" in - up|start) - kube_up - ;; - down|stop) - kube_down - ;; - restart) - kube_down - kube_up - ;; - *) - echo "Usage: $0 {up|down|restart}" - ;; - esac -} - -main "${@:-}" - diff --git a/scripts/update-docs.sh b/scripts/update-docs.sh index 12a4ead4b..e014b537e 100755 --- a/scripts/update-docs.sh +++ b/scripts/update-docs.sh @@ -31,11 +31,13 @@ kube::util::ensure-temp-dir export HELM_NO_PLUGINS=1 -mkdir -p ${KUBE_TEMP}/docs/helm ${KUBE_TEMP}/docs/man/man1 ${KUBE_TEMP}/scripts +# Reset Helm Home because it is used in the generation of docs. +OLD_HELM_HOME=${HELM_HOME:-} +HELM_HOME="$HOME/.helm" +bin/helm init --client-only +mkdir -p ${KUBE_TEMP}/docs/helm bin/helm docs --dir ${KUBE_TEMP}/docs/helm -bin/helm docs --dir ${KUBE_TEMP}/docs/man/man1 --type man -bin/helm docs --dir ${KUBE_TEMP}/scripts --type bash - +HELM_HOME=$OLD_HELM_HOME FILES=$(find ${KUBE_TEMP} -type f) diff --git a/scripts/verify-docs.sh b/scripts/verify-docs.sh index 72af9e743..b0b799eac 100755 --- a/scripts/verify-docs.sh +++ b/scripts/verify-docs.sh @@ -31,10 +31,13 @@ kube::util::ensure-temp-dir export HELM_NO_PLUGINS=1 -mkdir -p ${KUBE_TEMP}/docs/helm ${KUBE_TEMP}/docs/man/man1 ${KUBE_TEMP}/scripts +# Reset Helm Home because it is used in the generation of docs. +OLD_HELM_HOME=${HELM_HOME:-} +HELM_HOME="$HOME/.helm" +bin/helm init --client-only +mkdir -p ${KUBE_TEMP}/docs/helm bin/helm docs --dir ${KUBE_TEMP}/docs/helm -bin/helm docs --dir ${KUBE_TEMP}/docs/man/man1 --type man -bin/helm docs --dir ${KUBE_TEMP}/scripts --type bash +HELM_HOME=$OLD_HELM_HOME FILES=$(find ${KUBE_TEMP} -type f) diff --git a/versioning.mk b/versioning.mk index b336a7999..2333530d9 100644 --- a/versioning.mk +++ b/versioning.mk @@ -1,9 +1,9 @@ -MUTABLE_VERSION ?= canary +MUTABLE_VERSION := canary -GIT_COMMIT ?= $(shell git rev-parse HEAD) -GIT_SHA ?= $(shell git rev-parse --short HEAD) -GIT_TAG ?= $(shell git describe --tags --abbrev=0 2>/dev/null) -GIT_DIRTY ?= $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean") +GIT_COMMIT = $(shell git rev-parse HEAD) +GIT_SHA = $(shell git rev-parse --short HEAD) +GIT_TAG = $(shell git describe --tags --abbrev=0 --exact-match 2>/dev/null) +GIT_DIRTY = $(shell test -n "`git status --porcelain`" && echo "dirty" || echo "clean") ifdef VERSION DOCKER_VERSION = $(VERSION) @@ -11,25 +11,39 @@ ifdef VERSION endif DOCKER_VERSION ?= git-${GIT_SHA} -BINARY_VERSION ?= ${GIT_TAG}-${GIT_SHA} +BINARY_VERSION ?= ${GIT_TAG} -IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME}:${DOCKER_VERSION} -MUTABLE_IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME}:${MUTABLE_VERSION} +# Only set Version if building a tag or VERSION is set +ifneq ($(BINARY_VERSION),) + LDFLAGS += -X k8s.io/helm/pkg/version.Version=${BINARY_VERSION} +endif -LDFLAGS += -X k8s.io/helm/pkg/version.Version=${GIT_TAG} +# Clear the "unreleased" string in BuildMetadata +ifneq ($(GIT_TAG),) + LDFLAGS += -X k8s.io/helm/pkg/version.BuildMetadata= +endif LDFLAGS += -X k8s.io/helm/pkg/version.GitCommit=${GIT_COMMIT} LDFLAGS += -X k8s.io/helm/pkg/version.GitTreeState=${GIT_DIRTY} +IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME}:${DOCKER_VERSION} +IMAGE_RUDDER := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME_RUDDER}:${DOCKER_VERSION} +MUTABLE_IMAGE := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME}:${MUTABLE_VERSION} +MUTABLE_IMAGE_RUDDER := ${DOCKER_REGISTRY}/${IMAGE_PREFIX}/${SHORT_NAME_RUDDER}:${DOCKER_VERSION} + DOCKER_PUSH = docker push ifeq ($(DOCKER_REGISTRY),gcr.io) DOCKER_PUSH = gcloud docker push endif info: - @echo "Build tag: ${DOCKER_VERSION}" - @echo "Registry: ${DOCKER_REGISTRY}" - @echo "Immutable tag: ${IMAGE}" - @echo "Mutable tag: ${MUTABLE_IMAGE}" + @echo "Version: ${VERSION}" + @echo "Git Tag: ${GIT_TAG}" + @echo "Git Commit: ${GIT_COMMIT}" + @echo "Git Tree State: ${GIT_DIRTY}" + @echo "Docker Version: ${DOCKER_VERSION}" + @echo "Registry: ${DOCKER_REGISTRY}" + @echo "Immutable Image: ${IMAGE}" + @echo "Mutable Image: ${MUTABLE_IMAGE}" .PHONY: docker-push docker-push: docker-mutable-push docker-immutable-push