feat(template): introduce --validate

This feature flag allows `helm template` to be used against a live cluster. Some charts need CRDs to be applied to the cluster before calling `helm install`. This allows users to validate their templates will render with those resources set.

Signed-off-by: Matthew Fisher <matt.fisher@microsoft.com>
pull/6049/head
Matthew Fisher 5 years ago
parent 880f4456b3
commit 7de91248ce
No known key found for this signature in database
GPG Key ID: 92AA783CBAAE8E3B

@ -172,6 +172,7 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newReleaseTestCmd(actionConfig, out), newReleaseTestCmd(actionConfig, out),
newRollbackCmd(actionConfig, out), newRollbackCmd(actionConfig, out),
newStatusCmd(actionConfig, out), newStatusCmd(actionConfig, out),
newTemplateCmd(actionConfig, out),
newUninstallCmd(actionConfig, out), newUninstallCmd(actionConfig, out),
newUpgradeCmd(actionConfig, out), newUpgradeCmd(actionConfig, out),
@ -179,7 +180,6 @@ func newRootCmd(actionConfig *action.Configuration, out io.Writer, args []string
newHomeCmd(out), newHomeCmd(out),
newInitCmd(out), newInitCmd(out),
newPluginCmd(out), newPluginCmd(out),
newTemplateCmd(out),
newVersionCmd(out), newVersionCmd(out),
// Hidden documentation generator command: 'helm docs' // Hidden documentation generator command: 'helm docs'

@ -19,18 +19,12 @@ package main
import ( import (
"fmt" "fmt"
"io" "io"
"io/ioutil"
"strings" "strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
"helm.sh/helm/cmd/helm/require" "helm.sh/helm/cmd/helm/require"
"helm.sh/helm/pkg/action" "helm.sh/helm/pkg/action"
"helm.sh/helm/pkg/chartutil"
kubefake "helm.sh/helm/pkg/kube/fake"
"helm.sh/helm/pkg/storage"
"helm.sh/helm/pkg/storage/driver"
) )
const templateDesc = ` const templateDesc = `
@ -42,18 +36,9 @@ of the server-side testing of chart validity (e.g. whether an API is supported)
is done. is done.
` `
func newTemplateCmd(out io.Writer) *cobra.Command { func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
customConfig := &action.Configuration{ var validate bool
// Add mock objects in here so it doesn't use Kube API server client := action.NewInstall(cfg)
Releases: storage.Init(driver.NewMemory()),
KubeClient: &kubefake.PrintingKubeClient{Out: ioutil.Discard},
Capabilities: chartutil.DefaultCapabilities,
Log: func(format string, v ...interface{}) {
fmt.Fprintf(out, format, v...)
},
}
client := action.NewInstall(customConfig)
cmd := &cobra.Command{ cmd := &cobra.Command{
Use: "template [NAME] [CHART]", Use: "template [NAME] [CHART]",
@ -64,6 +49,7 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
client.DryRun = true client.DryRun = true
client.ReleaseName = "RELEASE-NAME" client.ReleaseName = "RELEASE-NAME"
client.Replace = true // Skip the name check client.Replace = true // Skip the name check
client.ClientOnly = !validate
rel, err := runInstall(args, client, out) rel, err := runInstall(args, client, out)
if err != nil { if err != nil {
return err return err
@ -73,12 +59,10 @@ func newTemplateCmd(out io.Writer) *cobra.Command {
}, },
} }
addInstallFlags(cmd.Flags(), client) f := cmd.Flags()
addTemplateFlags(cmd.Flags(), client) addInstallFlags(f, client)
f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
f.BoolVar(&validate, "validate", false, "establish a connection to Kubernetes for schema validation")
return cmd return cmd
} }
func addTemplateFlags(f *pflag.FlagSet, client *action.Install) {
f.StringVar(&client.OutputDir, "output-dir", "", "writes the executed templates to files in output-dir instead of stdout")
}

@ -41,9 +41,12 @@ import (
"helm.sh/helm/pkg/engine" "helm.sh/helm/pkg/engine"
"helm.sh/helm/pkg/getter" "helm.sh/helm/pkg/getter"
"helm.sh/helm/pkg/hooks" "helm.sh/helm/pkg/hooks"
kubefake "helm.sh/helm/pkg/kube/fake"
"helm.sh/helm/pkg/release" "helm.sh/helm/pkg/release"
"helm.sh/helm/pkg/releaseutil" "helm.sh/helm/pkg/releaseutil"
"helm.sh/helm/pkg/repo" "helm.sh/helm/pkg/repo"
"helm.sh/helm/pkg/storage"
"helm.sh/helm/pkg/storage/driver"
"helm.sh/helm/pkg/strvals" "helm.sh/helm/pkg/strvals"
"helm.sh/helm/pkg/version" "helm.sh/helm/pkg/version"
) )
@ -70,6 +73,7 @@ type Install struct {
ChartPathOptions ChartPathOptions
ValueOptions ValueOptions
ClientOnly bool
DryRun bool DryRun bool
DisableHooks bool DisableHooks bool
Replace bool Replace bool
@ -119,6 +123,14 @@ func (i *Install) Run(chrt *chart.Chart) (*release.Release, error) {
return nil, err return nil, err
} }
if i.ClientOnly {
// Add mock objects in here so it doesn't use Kube API server
// NOTE(bacongobbler): used for `helm template`
i.cfg.Capabilities = chartutil.DefaultCapabilities
i.cfg.KubeClient = &kubefake.PrintingKubeClient{Out: ioutil.Discard}
i.cfg.Releases = storage.Init(driver.NewMemory())
}
// Make sure if Atomic is set, that wait is set as well. This makes it so // Make sure if Atomic is set, that wait is set as well. This makes it so
// the user doesn't have to specify both // the user doesn't have to specify both
i.Wait = i.Wait || i.Atomic i.Wait = i.Wait || i.Atomic

@ -29,9 +29,10 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"helm.sh/helm/internal/test" "helm.sh/helm/internal/test"
"helm.sh/helm/pkg/chartutil"
kubefake "helm.sh/helm/pkg/kube/fake"
"helm.sh/helm/pkg/release" "helm.sh/helm/pkg/release"
"helm.sh/helm/pkg/storage/driver" "helm.sh/helm/pkg/storage/driver"
kubefake "helm.sh/helm/pkg/kube/fake"
) )
type nameTemplateTestCase struct { type nameTemplateTestCase struct {
@ -74,6 +75,16 @@ func TestInstallRelease(t *testing.T) {
is.Equal(rel.Info.Description, "Install complete") is.Equal(rel.Info.Description, "Install complete")
} }
func TestInstallReleaseClientOnly(t *testing.T) {
is := assert.New(t)
instAction := installAction(t)
instAction.ClientOnly = true
instAction.Run(buildChart()) // disregard output
is.Equal(instAction.cfg.Capabilities, chartutil.DefaultCapabilities)
is.Equal(instAction.cfg.KubeClient, &kubefake.PrintingKubeClient{Out: ioutil.Discard})
}
func TestInstallRelease_NoName(t *testing.T) { func TestInstallRelease_NoName(t *testing.T) {
instAction := installAction(t) instAction := installAction(t)
instAction.ReleaseName = "" instAction.ReleaseName = ""

Loading…
Cancel
Save