feat(helm): add --no-hook to helm install

This includes a substantial bit of unit test improvements. Also, in
order to allow us to tests command line args (which translate to
helm.Option objects), I had to add a new interface to pkg/helm.
pull/955/head
Matt Butcher 8 years ago
parent ed5be30acc
commit a42b43a9fa

@ -175,6 +175,9 @@ message InstallReleaseRequest {
// namespace, otherwise the server will return an error. If it is not // namespace, otherwise the server will return an error. If it is not
// supplied, the server will autogenerate one. // supplied, the server will autogenerate one.
string name = 4; string name = 4;
// DisableHooks causes the server to skip running any hooks for the install.
bool disable_hooks = 5;
} }
// InstallReleaseResponse is the response from a release installation. // InstallReleaseResponse is the response from a release installation.
@ -186,6 +189,8 @@ message InstallReleaseResponse {
message UninstallReleaseRequest { message UninstallReleaseRequest {
// Name is the name of the release to delete. // Name is the name of the release to delete.
string name = 1; string name = 1;
// DisableHooks causes the server to skip running any hooks for the uninstall.
bool disable_hooks = 2;
} }
// UninstallReleaseResponse represents a successful response to an uninstall request. // UninstallReleaseResponse represents a successful response to an uninstall request.

@ -126,7 +126,7 @@ func tpl(t string, vals map[string]interface{}, out io.Writer) error {
return tt.Execute(out, vals) return tt.Execute(out, vals)
} }
func ensureHelmClient(h helm.Interface) helm.Interface { func ensureHelmClient(h helm.OptionalInterface) helm.OptionalInterface {
if h != nil { if h != nil {
return h return h
} }

@ -34,10 +34,10 @@ Hooks are formatted in YAML and separated by the YAML '---\n' separator.
type getHooksCmd struct { type getHooksCmd struct {
release string release string
out io.Writer out io.Writer
client helm.Interface client helm.OptionalInterface
} }
func newGetHooksCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetHooksCmd(client helm.OptionalInterface, out io.Writer) *cobra.Command {
ghc := &getHooksCmd{ ghc := &getHooksCmd{
out: out, out: out,
client: client, client: client,

@ -34,10 +34,10 @@ type getValuesCmd struct {
release string release string
allValues bool allValues bool
out io.Writer out io.Writer
client helm.Interface client helm.OptionalInterface
} }
func newGetValuesCmd(client helm.Interface, out io.Writer) *cobra.Command { func newGetValuesCmd(client helm.OptionalInterface, out io.Writer) *cobra.Command {
get := &getValuesCmd{ get := &getValuesCmd{
out: out, out: out,
client: client, client: client,

@ -90,6 +90,7 @@ func newRootCmd(out io.Writer) *cobra.Command {
newGetCmd(nil, out), newGetCmd(nil, out),
newListCmd(nil, out), newListCmd(nil, out),
newStatusCmd(nil, out), newStatusCmd(nil, out),
newInstallCmd(nil, out),
) )
return cmd return cmd
} }

@ -92,7 +92,9 @@ func (c *fakeReleaseClient) ListReleases(opts ...helm.ReleaseListOption) (*rls.L
} }
func (c *fakeReleaseClient) InstallRelease(chStr string, opts ...helm.InstallOption) (*rls.InstallReleaseResponse, error) { func (c *fakeReleaseClient) InstallRelease(chStr string, opts ...helm.InstallOption) (*rls.InstallReleaseResponse, error) {
return nil, nil return &rls.InstallReleaseResponse{
Release: c.rels[0],
}, nil
} }
func (c *fakeReleaseClient) DeleteRelease(rlsName string, opts ...helm.DeleteOption) (*rls.UninstallReleaseResponse, error) { func (c *fakeReleaseClient) DeleteRelease(rlsName string, opts ...helm.DeleteOption) (*rls.UninstallReleaseResponse, error) {
@ -116,6 +118,10 @@ func (c *fakeReleaseClient) ReleaseContent(rlsName string, opts ...helm.ContentO
return resp, c.err return resp, c.err
} }
func (c *fakeReleaseClient) Option(opt ...helm.Option) helm.OptionalInterface {
return c
}
// releaseCmd is a command that works with a fakeReleaseClient // releaseCmd is a command that works with a fakeReleaseClient
type releaseCmd func(c *fakeReleaseClient, out io.Writer) *cobra.Command type releaseCmd func(c *fakeReleaseClient, out io.Writer) *cobra.Command
@ -127,9 +133,11 @@ func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) {
rels: []*release.Release{tt.resp}, rels: []*release.Release{tt.resp},
} }
cmd := rcmd(c, &buf) cmd := rcmd(c, &buf)
cmd.ParseFlags(tt.flags)
println("parsed flags")
err := cmd.RunE(cmd, tt.args) err := cmd.RunE(cmd, tt.args)
if (err != nil) != tt.err { if (err != nil) != tt.err {
t.Errorf("%q. expected error: %v, got %v", tt.name, tt.err, err) t.Errorf("%q. expected error: %v, got '%v'", tt.name, tt.err, err)
} }
re := regexp.MustCompile(tt.expected) re := regexp.MustCompile(tt.expected)
if !re.Match(buf.Bytes()) { if !re.Match(buf.Bytes()) {
@ -141,8 +149,9 @@ func runReleaseCases(t *testing.T, tests []releaseCase, rcmd releaseCmd) {
// releaseCase describes a test case that works with releases. // releaseCase describes a test case that works with releases.
type releaseCase struct { type releaseCase struct {
name string name string
args []string args []string
flags []string
// expected is the string to be matched. This supports regular expressions. // expected is the string to be matched. This supports regular expressions.
expected string expected string
err bool err bool

@ -18,6 +18,7 @@ package main
import ( import (
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
@ -46,70 +47,102 @@ var (
installValues string installValues string
// installRelName is the user-supplied release name. // installRelName is the user-supplied release name.
installRelName string installRelName string
// installNoHooks is the flag for controlling hook execution.
installNoHooks bool
) )
var installCmd = &cobra.Command{ type installCmd struct {
Use: "install [CHART]", name string
Short: "install a chart archive", valuesFile string
Long: installDesc, chartPath string
RunE: runInstall, dryRun bool
PersistentPreRunE: setupConnection, disableHooks bool
}
func init() {
f := installCmd.Flags()
f.StringVarP(&installValues, "values", "f", "", "path to a values YAML file")
f.StringVarP(&installRelName, "name", "n", "", "the release name. If unspecified, it will autogenerate one for you.")
f.BoolVar(&installDryRun, "dry-run", false, "simulate an install")
RootCommand.AddCommand(installCmd) out io.Writer
client helm.OptionalInterface
} }
func runInstall(cmd *cobra.Command, args []string) error { func newInstallCmd(c helm.OptionalInterface, out io.Writer) *cobra.Command {
if err := checkArgsLength(1, len(args), "chart name"); err != nil { inst := &installCmd{
return err out: out,
client: c,
} }
chartpath, err := locateChartPath(args[0])
if err != nil { cmd := &cobra.Command{
return err Use: "install [CHART]",
Short: "install a chart archive",
Long: installDesc,
PersistentPreRunE: setupConnection,
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("%v", args)
if err := checkArgsLength(1, len(args), "chart name"); err != nil {
return err
}
cp, err := locateChartPath(args[0])
if err != nil {
return err
}
inst.chartPath = cp
inst.client = ensureHelmClient(inst.client)
return inst.run()
},
} }
f := cmd.Flags()
f.StringVarP(&inst.valuesFile, "values", "f", "", "path to a values YAML file")
f.StringVarP(&inst.name, "name", "n", "", "the release name. If unspecified, it will autogenerate one for you.")
f.BoolVar(&inst.dryRun, "dry-run", false, "simulate an install")
f.BoolVar(&inst.disableHooks, "no-hooks", false, "prevent hooks from running during install")
return cmd
}
func (i *installCmd) run() error {
if flagDebug { if flagDebug {
fmt.Printf("Chart path: %s\n", chartpath) fmt.Printf("Chart path: %s\n", i.chartPath)
} }
rawVals, err := vals() rawVals, err := i.vals()
if err != nil { if err != nil {
return err return err
} }
res, err := helm.InstallRelease(rawVals, installRelName, chartpath, installDryRun) if i.dryRun {
i.client.Option(helm.DryRun())
}
if i.disableHooks {
i.client.Option(helm.DisableHooks())
}
res, err := i.client.InstallRelease(i.chartPath, helm.ValueOverrides(rawVals), helm.ReleaseName(i.name))
if err != nil { if err != nil {
return prettyError(err) return prettyError(err)
} }
printRelease(res.GetRelease()) i.printRelease(res.GetRelease())
return nil return nil
} }
func vals() ([]byte, error) { func (i *installCmd) vals() ([]byte, error) {
if installValues == "" { if i.valuesFile == "" {
return []byte{}, nil return []byte{}, nil
} }
return ioutil.ReadFile(installValues) return ioutil.ReadFile(i.valuesFile)
} }
func printRelease(rel *release.Release) { func (i *installCmd) printRelease(rel *release.Release) {
if rel == nil { if rel == nil {
return return
} }
// TODO: Switch to text/template like everything else.
if flagDebug { if flagDebug {
fmt.Printf("NAME: %s\n", rel.Name) fmt.Fprintf(i.out, "NAME: %s\n", rel.Name)
fmt.Printf("INFO: %s %s\n", timeconv.String(rel.Info.LastDeployed), rel.Info.Status) fmt.Fprintf(i.out, "INFO: %s %s\n", timeconv.String(rel.Info.LastDeployed), rel.Info.Status)
fmt.Printf("CHART: %s %s\n", rel.Chart.Metadata.Name, rel.Chart.Metadata.Version) fmt.Fprintf(i.out, "CHART: %s %s\n", rel.Chart.Metadata.Name, rel.Chart.Metadata.Version)
fmt.Printf("MANIFEST: %s\n", rel.Manifest) fmt.Fprintf(i.out, "MANIFEST: %s\n", rel.Manifest)
} else { } else {
fmt.Println(rel.Name) fmt.Fprintln(i.out, rel.Name)
} }
} }

@ -0,0 +1,56 @@
/*
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 (
"io"
"strings"
"testing"
"github.com/spf13/cobra"
)
func TestInstall(t *testing.T) {
tests := []releaseCase{
// Install, base case
{
name: "basic install",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name aeneas", " "),
expected: "aeneas",
resp: releaseMock("aeneas"),
},
// Install, no hooks
{
name: "install without hooks",
args: []string{"testdata/testcharts/alpine"},
flags: strings.Split("--name aeneas --no-hooks", " "),
expected: "juno",
resp: releaseMock("juno"),
},
// Install, no charts
{
name: "install with no chart specified",
args: []string{},
err: true,
},
}
runReleaseCases(t, tests, func(c *fakeReleaseClient, out io.Writer) *cobra.Command {
return newInstallCmd(c, out)
})
}

@ -0,0 +1,6 @@
name: alpine
description: Deploy a basic Alpine Linux pod
version: 0.1.0
home: https://k8s.io/helm
sources:
- https://github.com/kubernetes/helm

@ -0,0 +1,13 @@
#Alpine: A simple Helm chart
Run a single pod of Alpine Linux.
This example was generated using the command `helm create alpine`.
The `templates/` directory contains a very simple pod resource with a
couple of parameters.
The `values.yaml` file contains the default values for the
`alpine-pod.yaml` template.
You can install this example using `helm install docs/examples/alpine`.

@ -0,0 +1,26 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{.Release.Name}}-{{.Values.Name}}"
labels:
# The "heritage" label is used to track which tool deployed a given chart.
# It is useful for admins who want to see what releases a particular tool
# is responsible for.
heritage: {{.Release.Service | quote }}
# The "release" convention makes it easy to tie a release to all of the
# Kubernetes resources that were created as part of that release.
release: {{.Release.Name | quote }}
# This makes it easy to audit chart usage.
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
annotations:
"helm.sh/created": {{.Release.Time.Seconds | quote }}
spec:
# This shows how to use a simple value. This will look for a passed-in value
# called restartPolicy. If it is not found, it will use the default value.
# {{default "Never" .restartPolicy}} is a slightly optimized version of the
# more conventional syntax: {{.restartPolicy | default "Never"}}
restartPolicy: {{default "Never" .Values.restartPolicy}}
containers:
- name: waiter
image: "alpine:3.3"
command: ["/bin/sleep","9000"]

@ -0,0 +1,2 @@
# The pod name
Name: my-alpine

@ -292,8 +292,10 @@ func (s *releaseServer) performRelease(r *release.Release, req *services.Install
} }
// pre-install hooks // pre-install hooks
if err := s.execHook(r.Hooks, r.Name, preInstall); err != nil { if !req.DisableHooks {
return res, err if err := s.execHook(r.Hooks, r.Name, preInstall); err != nil {
return res, err
}
} }
// regular manifests // regular manifests
@ -309,8 +311,10 @@ func (s *releaseServer) performRelease(r *release.Release, req *services.Install
} }
// post-install hooks // post-install hooks
if err := s.execHook(r.Hooks, r.Name, postInstall); err != nil { if !req.DisableHooks {
return res, err if err := s.execHook(r.Hooks, r.Name, postInstall); err != nil {
return res, err
}
} }
// This is a tricky case. The release has been created, but the result // This is a tricky case. The release has been created, but the result
@ -382,8 +386,10 @@ func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
rel.Info.Deleted = timeconv.Now() rel.Info.Deleted = timeconv.Now()
res := &services.UninstallReleaseResponse{Release: rel} res := &services.UninstallReleaseResponse{Release: rel}
if err := s.execHook(rel.Hooks, rel.Name, preDelete); err != nil { if !req.DisableHooks {
return res, err if err := s.execHook(rel.Hooks, rel.Name, preDelete); err != nil {
return res, err
}
} }
b := bytes.NewBuffer([]byte(rel.Manifest)) b := bytes.NewBuffer([]byte(rel.Manifest))
@ -392,8 +398,10 @@ func (s *releaseServer) UninstallRelease(c ctx.Context, req *services.UninstallR
return nil, err return nil, err
} }
if err := s.execHook(rel.Hooks, rel.Name, postDelete); err != nil { if !req.DisableHooks {
return res, err if err := s.execHook(rel.Hooks, rel.Name, postDelete); err != nil {
return res, err
}
} }
if err := s.env.Releases.Update(rel); err != nil { if err := s.env.Releases.Update(rel); err != nil {

@ -31,7 +31,7 @@ import (
"k8s.io/helm/pkg/proto/hapi/release" "k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services" "k8s.io/helm/pkg/proto/hapi/services"
"k8s.io/helm/pkg/storage" "k8s.io/helm/pkg/storage"
"k8s.io/helm/pkg/timeconv" //"k8s.io/helm/pkg/timeconv"
) )
var manifestWithHook = `apiVersion: v1 var manifestWithHook = `apiVersion: v1
@ -50,7 +50,27 @@ func rsFixture() *releaseServer {
} }
} }
func releaseMock() *release.Release { // chartStub creates a fully stubbed out chart.
func chartStub() *chart.Chart {
return &chart.Chart{
// TODO: This should be more complete.
Metadata: &chart.Metadata{
Name: "hello",
},
// This adds basic templates, partials, and hooks.
Templates: []*chart.Template{
{Name: "hello", Data: []byte("hello: world")},
{Name: "goodbye", Data: []byte("goodbye: world")},
{Name: "empty", Data: []byte("")},
{Name: "with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)},
{Name: "partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)},
{Name: "hooks", Data: []byte(manifestWithHook)},
},
}
}
// releaseStub creates a release stub, complete with the chartStub as its chart.
func releaseStub() *release.Release {
date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0} date := timestamp.Timestamp{Seconds: 242085845, Nanos: 0}
return &release.Release{ return &release.Release{
Name: "angry-panda", Name: "angry-panda",
@ -59,15 +79,7 @@ func releaseMock() *release.Release {
LastDeployed: &date, LastDeployed: &date,
Status: &release.Status{Code: release.Status_DEPLOYED}, Status: &release.Status{Code: release.Status_DEPLOYED},
}, },
Chart: &chart.Chart{ Chart: chartStub(),
Metadata: &chart.Metadata{
Name: "foo",
Version: "0.1.0-beta.1",
},
Templates: []*chart.Template{
{Name: "foo.tpl", Data: []byte("Hello")},
},
},
Config: &chart.Config{Raw: `name = "value"`}, Config: &chart.Config{Raw: `name = "value"`},
Hooks: []*release.Hook{ Hooks: []*release.Hook{
{ {
@ -88,14 +100,9 @@ func TestInstallRelease(t *testing.T) {
c := context.Background() c := context.Background()
rs := rsFixture() rs := rsFixture()
// TODO: Refactor this into a mock.
req := &services.InstallReleaseRequest{ req := &services.InstallReleaseRequest{
Chart: &chart.Chart{ Chart: chartStub(),
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "hello", Data: []byte("hello: world")},
{Name: "hooks", Data: []byte(manifestWithHook)},
},
},
} }
res, err := rs.InstallRelease(c, req) res, err := rs.InstallRelease(c, req)
if err != nil { if err != nil {
@ -144,17 +151,7 @@ func TestInstallReleaseDryRun(t *testing.T) {
rs := rsFixture() rs := rsFixture()
req := &services.InstallReleaseRequest{ req := &services.InstallReleaseRequest{
Chart: &chart.Chart{ Chart: chartStub(),
Metadata: &chart.Metadata{Name: "hello"},
Templates: []*chart.Template{
{Name: "hello", Data: []byte("hello: world")},
{Name: "goodbye", Data: []byte("goodbye: world")},
{Name: "empty", Data: []byte("")},
{Name: "with-partials", Data: []byte(`hello: {{ template "_planet" . }}`)},
{Name: "partials/_planet", Data: []byte(`{{define "_planet"}}Earth{{end}}`)},
{Name: "hooks", Data: []byte(manifestWithHook)},
},
},
DryRun: true, DryRun: true,
} }
res, err := rs.InstallRelease(c, req) res, err := rs.InstallRelease(c, req)
@ -198,30 +195,29 @@ func TestInstallReleaseDryRun(t *testing.T) {
} }
} }
func TestInstallReleaseNoHooks(t *testing.T) {
c := context.Background()
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 TestUninstallRelease(t *testing.T) { func TestUninstallRelease(t *testing.T) {
c := context.Background() c := context.Background()
rs := rsFixture() rs := rsFixture()
rs.env.Releases.Create(&release.Release{ rs.env.Releases.Create(releaseStub())
Name: "angry-panda",
Info: &release.Info{
FirstDeployed: timeconv.Now(),
Status: &release.Status{
Code: release.Status_DEPLOYED,
},
},
Hooks: []*release.Hook{
{
Name: "test-cm",
Kind: "ConfigMap",
Path: "test-cm",
Manifest: manifestWithHook,
Events: []release.Hook_Event{
release.Hook_POST_INSTALL,
release.Hook_PRE_DELETE,
},
},
},
})
req := &services.UninstallReleaseRequest{ req := &services.UninstallReleaseRequest{
Name: "angry-panda", Name: "angry-panda",
@ -249,10 +245,31 @@ func TestUninstallRelease(t *testing.T) {
} }
} }
func TestUninstallReleaseNoHooks(t *testing.T) {
c := context.Background()
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) { func TestGetReleaseContent(t *testing.T) {
c := context.Background() c := context.Background()
rs := rsFixture() rs := rsFixture()
rel := releaseMock() rel := releaseStub()
if err := rs.env.Releases.Create(rel); err != nil { if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err) t.Fatalf("Could not store mock release: %s", err)
} }
@ -270,7 +287,7 @@ func TestGetReleaseContent(t *testing.T) {
func TestGetReleaseStatus(t *testing.T) { func TestGetReleaseStatus(t *testing.T) {
c := context.Background() c := context.Background()
rs := rsFixture() rs := rsFixture()
rel := releaseMock() rel := releaseStub()
if err := rs.env.Releases.Create(rel); err != nil { if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err) t.Fatalf("Could not store mock release: %s", err)
} }
@ -289,7 +306,7 @@ func TestListReleases(t *testing.T) {
rs := rsFixture() rs := rsFixture()
num := 7 num := 7
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
rel := releaseMock() rel := releaseStub()
rel.Name = fmt.Sprintf("rel-%d", i) rel.Name = fmt.Sprintf("rel-%d", i)
if err := rs.env.Releases.Create(rel); err != nil { if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err) t.Fatalf("Could not store mock release: %s", err)
@ -313,7 +330,7 @@ func TestListReleasesSort(t *testing.T) {
// sort. // sort.
num := 7 num := 7
for i := num; i > 0; i-- { for i := num; i > 0; i-- {
rel := releaseMock() rel := releaseStub()
rel.Name = fmt.Sprintf("rel-%d", i) rel.Name = fmt.Sprintf("rel-%d", i)
if err := rs.env.Releases.Create(rel); err != nil { if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err) t.Fatalf("Could not store mock release: %s", err)
@ -356,7 +373,7 @@ func TestListReleasesFilter(t *testing.T) {
} }
num := 7 num := 7
for i := 0; i < num; i++ { for i := 0; i < num; i++ {
rel := releaseMock() rel := releaseStub()
rel.Name = names[i] rel.Name = names[i]
if err := rs.env.Releases.Create(rel); err != nil { if err := rs.env.Releases.Create(rel); err != nil {
t.Fatalf("Could not store mock release: %s", err) t.Fatalf("Could not store mock release: %s", err)

@ -44,11 +44,11 @@ type Client struct {
// NewClient creates a new client. // NewClient creates a new client.
func NewClient(opts ...Option) *Client { func NewClient(opts ...Option) *Client {
return new(Client).Init().Option(opts...) return new(Client).Init().Option(opts...).(*Client)
} }
// Option configures the helm client with the provided options // Option configures the helm client with the provided options
func (h *Client) Option(opts ...Option) *Client { func (h *Client) Option(opts ...Option) OptionalInterface {
for _, opt := range opts { for _, opt := range opts {
opt(&h.opts) opt(&h.opts)
} }
@ -58,7 +58,7 @@ func (h *Client) Option(opts ...Option) *Client {
// Init initializes the helm client with default options // Init initializes the helm client with default options
func (h *Client) Init() *Client { func (h *Client) Init() *Client {
return h.Option(Host(DefaultHelmHost)). return h.Option(Host(DefaultHelmHost)).
Option(Home(os.ExpandEnv(DefaultHelmHome))) Option(Home(os.ExpandEnv(DefaultHelmHome))).(*Client)
} }
// ListReleases lists the current releases. // ListReleases lists the current releases.

@ -63,11 +63,14 @@ func UpdateRelease(rlsName string) (*rls.UpdateReleaseResponse, error) {
// InstallRelease runs an install for a release. // InstallRelease runs an install for a release.
// Soon to be deprecated helm InstallRelease API. // Soon to be deprecated helm InstallRelease API.
func InstallRelease(vals []byte, rlsName, chStr string, dryRun bool) (*rls.InstallReleaseResponse, error) { func InstallRelease(vals []byte, rlsName, chStr string, dryRun, skipHooks bool) (*rls.InstallReleaseResponse, error) {
client := NewClient(Host(Config.ServAddr)) client := NewClient(Host(Config.ServAddr))
if dryRun { if dryRun {
client.Option(DryRun()) client.Option(DryRun())
} }
if skipHooks {
client.Option(DisableHooks())
}
return client.InstallRelease(chStr, ValueOverrides(vals), ReleaseName(rlsName)) return client.InstallRelease(chStr, ValueOverrides(vals), ReleaseName(rlsName))
} }

@ -29,3 +29,9 @@ type Interface interface {
UpdateRelease(rlsName string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error) UpdateRelease(rlsName string, opts ...UpdateOption) (*rls.UpdateReleaseResponse, error)
ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error) ReleaseContent(rlsName string, opts ...ContentOption) (*rls.GetReleaseContentResponse, error)
} }
// OptionalInterface is an Interface that also supports Option().
type OptionalInterface interface {
Interface
Option(opts ...Option) OptionalInterface
}

@ -38,6 +38,8 @@ type options struct {
chart string chart string
// if set dry-run helm client calls // if set dry-run helm client calls
dryRun bool dryRun bool
// if set, skip running hooks
disableHooks bool
// release list options are applied directly to the list releases request // release list options are applied directly to the list releases request
listReq rls.ListReleasesRequest listReq rls.ListReleasesRequest
// release install options are applied directly to the install release request // release install options are applied directly to the install release request
@ -51,6 +53,12 @@ func DryRun() Option {
} }
} }
func DisableHooks() Option {
return func(opts *options) {
opts.disableHooks = true
}
}
// Home specifies the location of helm home, (default = "$HOME/.helm"). // Home specifies the location of helm home, (default = "$HOME/.helm").
func Home(home string) Option { func Home(home string) Option {
return func(opts *options) { return func(opts *options) {
@ -163,6 +171,7 @@ func (o *options) rpcInstallRelease(chr *cpb.Chart, rlc rls.ReleaseServiceClient
} }
o.instReq.Chart = chr o.instReq.Chart = chr
o.instReq.DryRun = o.dryRun o.instReq.DryRun = o.dryRun
o.instReq.DisableHooks = o.disableHooks
return rlc.InstallRelease(context.TODO(), &o.instReq) return rlc.InstallRelease(context.TODO(), &o.instReq)
} }

@ -248,6 +248,8 @@ type InstallReleaseRequest struct {
// namespace, otherwise the server will return an error. If it is not // namespace, otherwise the server will return an error. If it is not
// supplied, the server will autogenerate one. // supplied, the server will autogenerate one.
Name string `protobuf:"bytes,4,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,4,opt,name=name" json:"name,omitempty"`
// DisableHooks causes the server to skip running any hooks for the install.
DisableHooks bool `protobuf:"varint,5,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
} }
func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} } func (m *InstallReleaseRequest) Reset() { *m = InstallReleaseRequest{} }
@ -290,6 +292,8 @@ func (m *InstallReleaseResponse) GetRelease() *hapi_release3.Release {
type UninstallReleaseRequest struct { type UninstallReleaseRequest struct {
// Name is the name of the release to delete. // Name is the name of the release to delete.
Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
// DisableHooks causes the server to skip running any hooks for the uninstall.
DisableHooks bool `protobuf:"varint,2,opt,name=disable_hooks,json=disableHooks" json:"disable_hooks,omitempty"`
} }
func (m *UninstallReleaseRequest) Reset() { *m = UninstallReleaseRequest{} } func (m *UninstallReleaseRequest) Reset() { *m = UninstallReleaseRequest{} }
@ -586,48 +590,50 @@ var _ReleaseService_serviceDesc = grpc.ServiceDesc{
} }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 688 bytes of a gzipped FileDescriptorProto // 720 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xdd, 0x4e, 0x13, 0x41, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x9c, 0x55, 0xdd, 0x6e, 0xd3, 0x4a,
0x14, 0x66, 0x69, 0x69, 0xcb, 0x41, 0x48, 0x39, 0x96, 0xb6, 0xee, 0x85, 0x31, 0x9b, 0xa8, 0x88, 0x10, 0xae, 0x9b, 0x34, 0x49, 0xa7, 0x3f, 0x4a, 0xf7, 0xa4, 0x49, 0x8e, 0x2f, 0x8e, 0x8e, 0x8c,
0xb2, 0xd5, 0x7a, 0x6f, 0x52, 0xa0, 0x21, 0x84, 0x5a, 0x92, 0xa9, 0x68, 0xe2, 0x85, 0x64, 0x81, 0x80, 0x52, 0xa8, 0x03, 0xe1, 0x1e, 0x29, 0x6d, 0xa3, 0x52, 0x35, 0xa4, 0xd2, 0x86, 0x82, 0xc4,
0xa9, 0xac, 0x59, 0x76, 0xeb, 0xce, 0x94, 0xc8, 0x23, 0xf8, 0x08, 0xbe, 0x89, 0x0f, 0xe4, 0x83, 0x05, 0x91, 0xdb, 0x6c, 0xa8, 0xc1, 0xf5, 0x06, 0xef, 0xa6, 0xa2, 0x8f, 0xc0, 0x1b, 0x71, 0xc5,
0x38, 0x3f, 0x3b, 0x9b, 0x6e, 0xd9, 0xd5, 0x86, 0x9b, 0xdd, 0x99, 0xfd, 0xbe, 0x39, 0xdf, 0x99, 0xd3, 0xf0, 0x20, 0xec, 0x8f, 0xd7, 0x8a, 0x13, 0x1b, 0xa2, 0xde, 0x38, 0xbb, 0x3b, 0xdf, 0xce,
0xef, 0x9c, 0xd3, 0x82, 0x7d, 0xe5, 0x4d, 0xfc, 0x0e, 0xa3, 0xf1, 0x8d, 0x7f, 0x41, 0x59, 0x87, 0x37, 0xfe, 0x66, 0x3e, 0x07, 0xec, 0x6b, 0x6f, 0xe2, 0xb7, 0x18, 0x89, 0x6e, 0xfd, 0x2b, 0xc2,
0xfb, 0x41, 0x40, 0x63, 0x77, 0x12, 0x47, 0x3c, 0xc2, 0x86, 0xc4, 0x5c, 0x83, 0xb9, 0x1a, 0xb3, 0x5a, 0xdc, 0x0f, 0x02, 0x12, 0xb9, 0x93, 0x88, 0x72, 0x8a, 0x6a, 0x32, 0xe6, 0x9a, 0x98, 0xab,
0x9b, 0xea, 0xc4, 0xc5, 0x95, 0x17, 0x73, 0xfd, 0xd4, 0x6c, 0xbb, 0x35, 0xfb, 0x3d, 0x0a, 0xc7, 0x63, 0x76, 0x5d, 0xdd, 0xb8, 0xba, 0xf6, 0x22, 0xae, 0x9f, 0x1a, 0x6d, 0x37, 0x66, 0xcf, 0x69,
0xfe, 0xd7, 0x04, 0xd0, 0x12, 0x31, 0x0d, 0xa8, 0xc7, 0xa8, 0x79, 0x67, 0x0e, 0x19, 0xcc, 0x0f, 0x38, 0xf6, 0x3f, 0xc5, 0x01, 0x4d, 0x11, 0x91, 0x80, 0x78, 0x8c, 0x98, 0xdf, 0xd4, 0x25, 0x13,
0xc7, 0x91, 0x06, 0x9c, 0x3f, 0x16, 0x3c, 0x1c, 0xf8, 0x8c, 0x13, 0x0d, 0x31, 0x42, 0xbf, 0x4f, 0xf3, 0xc3, 0x31, 0xd5, 0x01, 0xe7, 0x97, 0x05, 0xff, 0xf4, 0x7c, 0xc6, 0xb1, 0x0e, 0x31, 0x4c,
0x29, 0xe3, 0xd8, 0x80, 0x95, 0xc0, 0xbf, 0xf6, 0x79, 0xdb, 0x7a, 0x62, 0x6d, 0x97, 0x88, 0xde, 0xbe, 0x4e, 0x09, 0xe3, 0xa8, 0x06, 0x6b, 0x81, 0x7f, 0xe3, 0xf3, 0xa6, 0xf5, 0xbf, 0xb5, 0x57,
0x60, 0x13, 0x2a, 0xd1, 0x78, 0xcc, 0x28, 0x6f, 0x2f, 0x8b, 0xcf, 0xab, 0x24, 0xd9, 0xe1, 0x3b, 0xc0, 0x7a, 0x83, 0xea, 0x50, 0xa2, 0xe3, 0x31, 0x23, 0xbc, 0xb9, 0x2a, 0x8e, 0xd7, 0x71, 0xbc,
0xa8, 0xb2, 0x28, 0xe6, 0x67, 0xe7, 0xb7, 0xed, 0x92, 0x00, 0x36, 0xba, 0x4f, 0xdd, 0xbc, 0x3b, 0x43, 0xaf, 0xa0, 0xcc, 0x68, 0xc4, 0x87, 0x97, 0x77, 0xcd, 0x82, 0x08, 0x6c, 0xb7, 0x1f, 0xba,
0xb9, 0x52, 0x69, 0x24, 0x88, 0xae, 0x7c, 0xec, 0xdd, 0x92, 0x0a, 0x53, 0x6f, 0x19, 0x77, 0xec, 0x59, 0xef, 0xe4, 0x4a, 0xa6, 0x81, 0x00, 0xba, 0xf2, 0x71, 0x78, 0x87, 0x4b, 0x4c, 0xfd, 0xca,
0x07, 0x9c, 0xc6, 0xed, 0xb2, 0x8e, 0xab, 0x77, 0x78, 0x08, 0xa0, 0xe2, 0x46, 0xf1, 0xa5, 0xc0, 0xbc, 0x63, 0x3f, 0xe0, 0x24, 0x6a, 0x16, 0x75, 0x5e, 0xbd, 0x43, 0x27, 0x00, 0x2a, 0x2f, 0x8d,
0x56, 0x54, 0xe8, 0xed, 0x05, 0x42, 0x9f, 0x48, 0x3e, 0x59, 0x65, 0x66, 0xe9, 0x7c, 0x81, 0x9a, 0x46, 0x22, 0xb6, 0xa6, 0x52, 0xef, 0x2d, 0x91, 0xfa, 0x5c, 0xe2, 0xf1, 0x3a, 0x33, 0x4b, 0xe7,
0x21, 0x38, 0x5d, 0xa8, 0x68, 0x79, 0x5c, 0x83, 0xea, 0xe9, 0xf0, 0x78, 0x78, 0xf2, 0x69, 0x58, 0x23, 0x54, 0x0c, 0xc0, 0x69, 0x43, 0x49, 0xd3, 0xa3, 0x0d, 0x28, 0x5f, 0xf4, 0xcf, 0xfa, 0xe7,
0x5f, 0xc2, 0x1a, 0x94, 0x87, 0xbd, 0xf7, 0xfd, 0xba, 0x85, 0x9b, 0xb0, 0x3e, 0xe8, 0x8d, 0x3e, 0xef, 0xfb, 0xd5, 0x15, 0x54, 0x81, 0x62, 0xbf, 0xf3, 0xa6, 0x5b, 0xb5, 0xd0, 0x0e, 0x6c, 0xf5,
0x9c, 0x91, 0xfe, 0xa0, 0xdf, 0x1b, 0xf5, 0x0f, 0xea, 0xcb, 0xce, 0x63, 0x58, 0x4d, 0xe3, 0x62, 0x3a, 0x83, 0xb7, 0x43, 0xdc, 0xed, 0x75, 0x3b, 0x83, 0xee, 0x71, 0x75, 0xd5, 0xf9, 0x0f, 0xd6,
0x15, 0x4a, 0xbd, 0xd1, 0xbe, 0x3e, 0x72, 0xd0, 0x17, 0x2b, 0xcb, 0xf9, 0x69, 0x41, 0x23, 0x6b, 0x93, 0xbc, 0xa8, 0x0c, 0x85, 0xce, 0xe0, 0x48, 0x5f, 0x39, 0xee, 0x8a, 0x95, 0xe5, 0x7c, 0xb7,
0x23, 0x9b, 0x44, 0x21, 0xa3, 0xd2, 0xc7, 0x8b, 0x68, 0x1a, 0xa6, 0x3e, 0xaa, 0x0d, 0x22, 0x94, 0xa0, 0x96, 0x96, 0x91, 0x4d, 0x68, 0xc8, 0x88, 0xd4, 0xf1, 0x8a, 0x4e, 0xc3, 0x44, 0x47, 0xb5,
0x43, 0xfa, 0xc3, 0xb8, 0xa8, 0xd6, 0x92, 0xc9, 0x23, 0xee, 0x05, 0xca, 0x41, 0xc1, 0x54, 0x1b, 0x41, 0x08, 0x8a, 0x21, 0xf9, 0x66, 0x54, 0x54, 0x6b, 0x89, 0xe4, 0x94, 0x7b, 0x81, 0x52, 0x50,
0x7c, 0x03, 0xb5, 0xa4, 0x6a, 0x4c, 0x78, 0x53, 0xda, 0x5e, 0xeb, 0x6e, 0xe9, 0xfb, 0x9b, 0xfa, 0x20, 0xd5, 0x06, 0xbd, 0x80, 0x4a, 0xdc, 0x35, 0x26, 0xb4, 0x29, 0xec, 0x6d, 0xb4, 0x77, 0xf5,
0x26, 0x8a, 0x24, 0xa5, 0x39, 0xbb, 0xd0, 0x3a, 0xa4, 0x26, 0x93, 0x11, 0xf7, 0xf8, 0x34, 0xad, 0xfb, 0x9b, 0xfe, 0xc6, 0x8c, 0x38, 0x81, 0x39, 0x07, 0xd0, 0x38, 0x21, 0xa6, 0x92, 0x01, 0xf7,
0xaa, 0xd4, 0xf5, 0xae, 0xa9, 0x4a, 0x46, 0xea, 0x8a, 0xb5, 0xf3, 0x11, 0xda, 0x77, 0xe9, 0x49, 0xf8, 0x34, 0xe9, 0xaa, 0xe4, 0xf5, 0x6e, 0x88, 0x2a, 0x46, 0xf2, 0x8a, 0xb5, 0xf3, 0x0e, 0x9a,
0xf6, 0x39, 0x7c, 0x7c, 0x06, 0x65, 0xd9, 0x3f, 0x2a, 0xf7, 0xb5, 0x2e, 0x66, 0xb3, 0x39, 0x12, 0x8b, 0xf0, 0xb8, 0xfa, 0x0c, 0x3c, 0x7a, 0x04, 0x45, 0x39, 0x3f, 0xaa, 0xf6, 0x8d, 0x36, 0x4a,
0x08, 0x51, 0xb8, 0xe3, 0xce, 0xc6, 0xdd, 0x8f, 0x42, 0x4e, 0x43, 0xfe, 0xaf, 0x3c, 0x06, 0xf0, 0x57, 0x73, 0x2a, 0x22, 0x58, 0xc5, 0x1d, 0x77, 0x36, 0xef, 0x11, 0x0d, 0x39, 0x09, 0xf9, 0x9f,
0x28, 0x87, 0x9f, 0x24, 0xd2, 0x81, 0x6a, 0x22, 0xa1, 0xce, 0x14, 0xba, 0x60, 0x58, 0x4e, 0x13, 0xea, 0xe8, 0xc1, 0xbf, 0x19, 0xf8, 0xb8, 0x90, 0x16, 0x94, 0x63, 0x0a, 0x75, 0x27, 0x57, 0x05,
0x1a, 0xa7, 0x93, 0x4b, 0x8f, 0x53, 0x83, 0x68, 0x65, 0xa7, 0x05, 0x5b, 0x73, 0xdf, 0xb5, 0x82, 0x83, 0x72, 0xea, 0x50, 0xbb, 0x98, 0x8c, 0x3c, 0x4e, 0x4c, 0x44, 0x33, 0x3b, 0x0d, 0xd8, 0x9d,
0xf3, 0xcb, 0x82, 0xad, 0xa3, 0x90, 0x09, 0xcf, 0x83, 0xec, 0x11, 0x7c, 0x2e, 0x4a, 0x28, 0xa7, 0x3b, 0xd7, 0x0c, 0xce, 0x4f, 0x0b, 0x76, 0x4f, 0x43, 0x26, 0x34, 0x0f, 0xd2, 0x57, 0xd0, 0x63,
0x2d, 0x51, 0xde, 0xd4, 0xca, 0x7a, 0x24, 0xf7, 0xe5, 0x93, 0x68, 0x1c, 0x77, 0xa0, 0x72, 0xe3, 0xd1, 0x42, 0xe9, 0xb6, 0x98, 0x79, 0x47, 0x33, 0x6b, 0x4b, 0x1e, 0xc9, 0x27, 0xd6, 0x71, 0xb4,
0x05, 0xe2, 0x4c, 0xd6, 0x9b, 0x84, 0xa9, 0x46, 0x95, 0x24, 0x0c, 0x6c, 0x41, 0xf5, 0x32, 0xbe, 0x0f, 0xa5, 0x5b, 0x2f, 0x10, 0x77, 0xd2, 0xda, 0xc4, 0x48, 0x65, 0x55, 0x1c, 0x23, 0x50, 0x03,
0x3d, 0x8b, 0xa7, 0xa1, 0xaa, 0x77, 0x8d, 0x54, 0xc4, 0x96, 0x4c, 0xc3, 0xd4, 0x9a, 0xf2, 0x8c, 0xca, 0xa3, 0xe8, 0x6e, 0x18, 0x4d, 0x43, 0xd5, 0xef, 0x0a, 0x2e, 0x89, 0x2d, 0x9e, 0x86, 0x89,
0x35, 0x47, 0xd0, 0x9c, 0x4f, 0xed, 0xbe, 0xbe, 0x88, 0xe6, 0x38, 0x0d, 0xfd, 0xdc, 0x7b, 0xe6, 0x34, 0xc5, 0x19, 0xc9, 0x1f, 0xc0, 0xd6, 0xc8, 0x67, 0xde, 0x65, 0x40, 0x86, 0xd7, 0x94, 0x7e,
0x15, 0xe5, 0x18, 0xda, 0x77, 0xe9, 0xf7, 0xd4, 0xee, 0xfe, 0x5e, 0x81, 0x0d, 0xd3, 0x67, 0x7a, 0x61, 0xca, 0x09, 0x15, 0xbc, 0x19, 0x1f, 0xbe, 0x96, 0x67, 0xce, 0x29, 0xd4, 0xe7, 0xeb, 0xbf,
0x7a, 0xd1, 0x87, 0x07, 0xb3, 0x63, 0x83, 0x2f, 0x8a, 0x87, 0x7b, 0xee, 0x17, 0xca, 0xde, 0x59, 0xaf, 0x78, 0x18, 0x1a, 0x17, 0xa1, 0x9f, 0x29, 0x46, 0xd6, 0x44, 0x2c, 0x94, 0xb7, 0x9a, 0x51,
0x84, 0x9a, 0x14, 0x77, 0xe9, 0xb5, 0x85, 0x0c, 0xea, 0xf3, 0x7d, 0x8e, 0xbb, 0xf9, 0x31, 0x0a, 0xde, 0x19, 0x34, 0x17, 0x73, 0xde, 0xb3, 0xc0, 0xf6, 0x8f, 0x35, 0xd8, 0x36, 0x13, 0xab, 0xbf,
0xc6, 0xc7, 0x76, 0x17, 0xa5, 0x1b, 0x59, 0xbc, 0x81, 0xcd, 0x3b, 0x4d, 0x8d, 0xff, 0x0d, 0x93, 0x03, 0xc8, 0x87, 0xcd, 0x59, 0x03, 0xa2, 0x27, 0xf9, 0x9f, 0x89, 0xb9, 0x6f, 0x9d, 0xbd, 0xbf,
0x9d, 0x16, 0xbb, 0xb3, 0x30, 0x3f, 0xd5, 0xfd, 0x06, 0xeb, 0x99, 0x36, 0xc7, 0x02, 0xb7, 0xf2, 0x0c, 0x34, 0x1e, 0x93, 0x95, 0xe7, 0x16, 0x62, 0x50, 0x9d, 0x77, 0x0c, 0x3a, 0xc8, 0xce, 0x91,
0x66, 0xc4, 0x7e, 0xb9, 0x10, 0x37, 0xd5, 0xba, 0x86, 0x8d, 0x6c, 0x77, 0x62, 0x41, 0x80, 0xdc, 0x63, 0x44, 0xdb, 0x5d, 0x16, 0x6e, 0x68, 0xd1, 0x2d, 0xec, 0x2c, 0xd8, 0x03, 0xfd, 0x35, 0x4d,
0xf1, 0xb2, 0x5f, 0x2d, 0x46, 0x4e, 0xe5, 0x44, 0x1d, 0xe7, 0x5b, 0xb2, 0xa8, 0x8e, 0x05, 0x9d, 0xda, 0x77, 0x76, 0x6b, 0x69, 0x7c, 0xc2, 0xfb, 0x19, 0xb6, 0x52, 0x86, 0x41, 0x39, 0x6a, 0x65,
0x5e, 0x54, 0xc7, 0xa2, 0x4e, 0x77, 0x96, 0xf6, 0xe0, 0x73, 0xcd, 0xb0, 0xcf, 0x2b, 0xea, 0x9f, 0xb9, 0xcd, 0x7e, 0xba, 0x14, 0x36, 0xe1, 0xba, 0x81, 0xed, 0xf4, 0x08, 0xa3, 0x9c, 0x04, 0x99,
0xf3, 0xed, 0xdf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x39, 0x3c, 0xcd, 0x3c, 0xd3, 0x07, 0x00, 0x00, 0x46, 0xb5, 0x9f, 0x2d, 0x07, 0x4e, 0xe8, 0x44, 0x1f, 0xe7, 0x47, 0x32, 0xaf, 0x8f, 0x39, 0x76,
0xc8, 0xeb, 0x63, 0xde, 0xa4, 0x3b, 0x2b, 0x87, 0xf0, 0xa1, 0x62, 0xd0, 0x97, 0x25, 0xf5, 0x1f,
0xfc, 0xf2, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x38, 0x16, 0x87, 0x5f, 0x1d, 0x08, 0x00, 0x00,
} }

Loading…
Cancel
Save