diff --git a/cmd/helm/helm.go b/cmd/helm/helm.go index 98cb00f43..5d2bdb157 100644 --- a/cmd/helm/helm.go +++ b/cmd/helm/helm.go @@ -43,6 +43,10 @@ import ( // FeatureGateOCI is the feature gate for checking if `helm chart` and `helm registry` commands should work const FeatureGateOCI = gates.Gate("HELM_EXPERIMENTAL_OCI") +// UnsafeTemplateLiveConn handles unsafe-template-live-conn arg as env var +// to simplify the integration with third party tools and plugins. +const UnsafeTemplateLiveConn = gates.Gate("HELM_UNSAFE_TEMPLATE_LIVE_CONN") + var settings = cli.New() func init() { diff --git a/cmd/helm/template.go b/cmd/helm/template.go index 83ad61f22..f61ff941f 100644 --- a/cmd/helm/template.go +++ b/cmd/helm/template.go @@ -45,6 +45,7 @@ faked locally. Additionally, none of the server-side testing of chart validity func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { var validate bool var includeCrds bool + var unsafeTemplateLiveConn bool client := action.NewInstall(cfg) valueOpts := &values.Options{} var extraAPIs []string @@ -59,6 +60,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { return compInstall(args, toComplete, client) }, RunE: func(_ *cobra.Command, args []string) error { + client.UnsafeTemplateLiveConn = unsafeTemplateLiveConn || UnsafeTemplateLiveConn.IsEnabled() client.DryRun = true client.ReleaseName = "RELEASE-NAME" client.Replace = true // Skip the name check @@ -147,6 +149,7 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command { f.BoolVar(&client.IsUpgrade, "is-upgrade", false, "set .Release.IsUpgrade instead of .Release.IsInstall") f.StringArrayVarP(&extraAPIs, "api-versions", "a", []string{}, "Kubernetes api versions used for Capabilities.APIVersions") f.BoolVar(&client.UseReleaseName, "release-name", false, "use release name in the output-dir path.") + f.BoolVar(&unsafeTemplateLiveConn, "unsafe-template-live-conn", false, "enables the connection to the cluster. It is considered unsafe use under your own risk.") bindPostRenderFlag(cmd, &client.PostRenderer) return cmd diff --git a/pkg/action/action.go b/pkg/action/action.go index bb9ef5f71..7dd9ae54e 100644 --- a/pkg/action/action.go +++ b/pkg/action/action.go @@ -97,7 +97,7 @@ type Configuration struct { // renderResources renders the templates in a chart // // TODO: This function is badly in need of a refactor. -func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool) ([]*release.Hook, *bytes.Buffer, string, error) { +func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values, releaseName, outputDir string, subNotes, useReleaseName, includeCrds bool, pr postrender.PostRenderer, dryRun bool, unsafeTemplateLiveConn bool) ([]*release.Hook, *bytes.Buffer, string, error) { hs := []*release.Hook{} b := bytes.NewBuffer(nil) @@ -119,15 +119,34 @@ func (c *Configuration) renderResources(ch *chart.Chart, values chartutil.Values // It will break in interesting and exotic ways because other data (e.g. discovery) // is mocked. It is not up to the template author to decide when the user wants to // connect to the cluster. So when the user says to dry run, respect the user's - // wishes and do not connect to the cluster. - if !dryRun && c.RESTClientGetter != nil { + // wishes and do not connect to the cluster + + // When the user uses `helm template --unsafe-template-live-conn` we understand that wants + // to use a live cluster connection. + + if unsafeTemplateLiveConn && c.RESTClientGetter != nil { rest, err := c.RESTClientGetter.ToRESTConfig() if err != nil { return hs, b, "", err } files, err2 = engine.RenderWithClient(ch, values, rest) - } else { - files, err2 = engine.Render(ch, values) + } + + if err2 != nil { + return hs, b, "", err2 + } + + // When templateLive is false then `helm template` was not invoked. + if len(files) == 0 { + if !dryRun && c.RESTClientGetter != nil { + rest, err := c.RESTClientGetter.ToRESTConfig() + if err != nil { + return hs, b, "", err + } + files, err2 = engine.RenderWithClient(ch, values, rest) + } else { + files, err2 = engine.Render(ch, values) + } } if err2 != nil { diff --git a/pkg/action/install.go b/pkg/action/install.go index 00fb208b0..caaa4e3a8 100644 --- a/pkg/action/install.go +++ b/pkg/action/install.go @@ -100,6 +100,8 @@ type Install struct { // OutputDir/ UseReleaseName bool PostRenderer postrender.PostRenderer + // Handle case when user wants to retrieve date from the cluster during template + UnsafeTemplateLiveConn bool } // ChartPathOptions captures common options used for controlling chart paths @@ -236,7 +238,7 @@ func (i *Install) Run(chrt *chart.Chart, vals map[string]interface{}) (*release. rel := i.createRelease(chrt, vals) var manifestDoc *bytes.Buffer - rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun) + rel.Hooks, manifestDoc, rel.Info.Notes, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun, i.UnsafeTemplateLiveConn) // Even for errors, attach this if available if manifestDoc != nil { rel.Manifest = manifestDoc.String() diff --git a/pkg/action/upgrade.go b/pkg/action/upgrade.go index fc289dbab..69fada638 100644 --- a/pkg/action/upgrade.go +++ b/pkg/action/upgrade.go @@ -217,7 +217,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin return nil, nil, err } - hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun) + hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun, false) if err != nil { return nil, nil, err }