[WIP] PoC implementation for HIP suggesting `warn` template function

Signed-off-by: Philipp Stehle <philipp.stehle@sap.com>
pull/11619/head
Philipp Stehle 3 years ago
parent ced54b1289
commit bb0ed7a1b7

@ -168,6 +168,10 @@ func newTemplateCmd(cfg *action.Configuration, out io.Writer) *cobra.Command {
} }
} }
for _, warn := range rel.Warnings {
warning(warn.Message)
}
return err return err
}, },
} }

@ -101,19 +101,20 @@ type Configuration struct {
// //
// TODO: This function is badly in need of a refactor. // TODO: This function is badly in need of a refactor.
// TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed // TODO: As part of the refactor the duplicate code in cmd/helm/template.go should be removed
// This code has to do with writing files to disk. //
func (cfg *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) { // This code has to do with writing files to disk.
func (cfg *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, []engine.Warning, error) {
hs := []*release.Hook{} hs := []*release.Hook{}
b := bytes.NewBuffer(nil) b := bytes.NewBuffer(nil)
caps, err := cfg.getCapabilities() caps, err := cfg.getCapabilities()
if err != nil { if err != nil {
return hs, b, "", err return hs, b, "", nil, err
} }
if ch.Metadata.KubeVersion != "" { if ch.Metadata.KubeVersion != "" {
if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) { if !chartutil.IsCompatibleRange(ch.Metadata.KubeVersion, caps.KubeVersion.String()) {
return hs, b, "", errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String()) return hs, b, "", nil, errors.Errorf("chart requires kubeVersion: %s which is incompatible with Kubernetes %s", ch.Metadata.KubeVersion, caps.KubeVersion.String())
} }
} }
@ -125,18 +126,20 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
// is mocked. It is not up to the template author to decide when the user wants to // 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 // connect to the cluster. So when the user says to dry run, respect the user's
// wishes and do not connect to the cluster. // wishes and do not connect to the cluster.
var theEngine engine.Engine
if !dryRun && cfg.RESTClientGetter != nil { if !dryRun && cfg.RESTClientGetter != nil {
restConfig, err := cfg.RESTClientGetter.ToRESTConfig() restConfig, err := cfg.RESTClientGetter.ToRESTConfig()
if err != nil { if err != nil {
return hs, b, "", err return hs, b, "", nil, err
} }
files, err2 = engine.RenderWithClient(ch, values, restConfig) theEngine = engine.NewEngineWithClient(restConfig)
} else { } else {
files, err2 = engine.Render(ch, values) theEngine = engine.NewEngine()
} }
files, err2 = theEngine.Render(ch, values)
if err2 != nil { if err2 != nil {
return hs, b, "", err2 return hs, b, "", nil, err2
} }
// NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource, // NOTES.txt gets rendered like all the other files, but because it's not a hook nor a resource,
@ -175,7 +178,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
} }
fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content) fmt.Fprintf(b, "---\n# Source: %s\n%s\n", name, content)
} }
return hs, b, "", err return hs, b, "", nil, err
} }
// Aggregate all valid manifests into one big doc. // Aggregate all valid manifests into one big doc.
@ -188,7 +191,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
} else { } else {
err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name]) err = writeToFile(outputDir, crd.Filename, string(crd.File.Data[:]), fileWritten[crd.Name])
if err != nil { if err != nil {
return hs, b, "", err return hs, b, "", nil, err
} }
fileWritten[crd.Name] = true fileWritten[crd.Name] = true
} }
@ -209,7 +212,7 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
// used by install or upgrade // used by install or upgrade
err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name]) err = writeToFile(newDir, m.Name, m.Content, fileWritten[m.Name])
if err != nil { if err != nil {
return hs, b, "", err return hs, b, "", nil, err
} }
fileWritten[m.Name] = true fileWritten[m.Name] = true
} }
@ -218,11 +221,11 @@ func (cfg *Configuration) renderResources(ch *chart.Chart, values chartutil.Valu
if pr != nil { if pr != nil {
b, err = pr.Run(b) b, err = pr.Run(b)
if err != nil { if err != nil {
return hs, b, notes, errors.Wrap(err, "error while running post render on files") return hs, b, notes, nil, errors.Wrap(err, "error while running post render on files")
} }
} }
return hs, b, notes, nil return hs, b, notes, theEngine.GetWarnings(), nil
} }
// RESTClientGetter gets the rest client // RESTClientGetter gets the rest client

@ -256,7 +256,7 @@ func (i *Install) RunWithContext(ctx context.Context, chrt *chart.Chart, vals ma
rel := i.createRelease(chrt, vals) rel := i.createRelease(chrt, vals)
var manifestDoc *bytes.Buffer 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, rel.Warnings, err = i.cfg.renderResources(chrt, valuesToRender, i.ReleaseName, i.OutputDir, i.SubNotes, i.UseReleaseName, i.IncludeCRDs, i.PostRenderer, i.DryRun)
// Even for errors, attach this if available // Even for errors, attach this if available
if manifestDoc != nil { if manifestDoc != nil {
rel.Manifest = manifestDoc.String() rel.Manifest = manifestDoc.String()

@ -231,7 +231,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
return nil, nil, err return nil, nil, err
} }
hooks, manifestDoc, notesTxt, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun) hooks, manifestDoc, notesTxt, warnings, err := u.cfg.renderResources(chart, valuesToRender, "", "", u.SubNotes, false, false, u.PostRenderer, u.DryRun)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -250,6 +250,7 @@ func (u *Upgrade) prepareUpgrade(name string, chart *chart.Chart, vals map[strin
}, },
Version: revision, Version: revision,
Manifest: manifestDoc.String(), Manifest: manifestDoc.String(),
Warnings: warnings,
Hooks: hooks, Hooks: hooks,
} }

@ -42,6 +42,21 @@ type Engine struct {
LintMode bool LintMode bool
// the rest config to connect to the kubernetes api // the rest config to connect to the kubernetes api
config *rest.Config config *rest.Config
warnings *Warnings
}
type Warning struct {
Message string
}
type Warnings []Warning
func (w *Warnings) append(warning Warning) {
if w == nil {
w = &Warnings{}
}
*w = append(*w, warning)
} }
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates. // Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
@ -71,16 +86,26 @@ func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]s
// Render takes a chart, optional values, and value overrides, and attempts to // Render takes a chart, optional values, and value overrides, and attempts to
// render the Go templates using the default options. // render the Go templates using the default options.
func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) { func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
return new(Engine).Render(chrt, values) return NewEngine().Render(chrt, values)
} }
// RenderWithClient takes a chart, optional values, and value overrides, and attempts to // RenderWithClient takes a chart, optional values, and value overrides, and attempts to
// render the Go templates using the default options. This engine is client aware and so can have template // render the Go templates using the default options. This engine is client aware and so can have template
// functions that interact with the client // functions that interact with the client
func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) { func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) {
return NewEngineWithClient(config).Render(chrt, values)
}
func NewEngineWithClient(config *rest.Config) Engine {
engine := NewEngine()
engine.config = config
return engine
}
func NewEngine() Engine {
return Engine{ return Engine{
config: config, warnings: &Warnings{},
}.Render(chrt, values) }
} }
// renderable is an object that can be rendered. // renderable is an object that can be rendered.
@ -103,6 +128,13 @@ func warnWrap(warn string) string {
return warnStartDelim + warn + warnEndDelim return warnStartDelim + warn + warnEndDelim
} }
func (e Engine) GetWarnings() []Warning {
if e.warnings == nil {
panic("Use NewEngine or NewEngineWithClient to create Engine")
}
return *e.warnings
}
// initFunMap creates the Engine's FuncMap and adds context-specific functions. // initFunMap creates the Engine's FuncMap and adds context-specific functions.
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) { func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
funcMap := funcMap() funcMap := funcMap()
@ -189,6 +221,17 @@ func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]render
funcMap["lookup"] = NewLookupFunction(e.config) funcMap["lookup"] = NewLookupFunction(e.config)
} }
funcMap["warn"] = func(msg string) string {
if e.warnings == nil { // Fallback if Engine was not created with constructor
fmt.Println("Warning:", msg)
} else {
e.warnings.append(Warning{
Message: msg,
})
}
return ""
}
t.Funcs(funcMap) t.Funcs(funcMap)
} }

@ -15,7 +15,10 @@ limitations under the License.
package release package release
import "helm.sh/helm/v3/pkg/chart" import (
"helm.sh/helm/v3/pkg/chart"
"helm.sh/helm/v3/pkg/engine"
)
// Release describes a deployment of a chart, together with the chart // Release describes a deployment of a chart, together with the chart
// and the variables used to deploy that chart. // and the variables used to deploy that chart.
@ -37,6 +40,8 @@ type Release struct {
Version int `json:"version,omitempty"` Version int `json:"version,omitempty"`
// Namespace is the kubernetes namespace of the release. // Namespace is the kubernetes namespace of the release.
Namespace string `json:"namespace,omitempty"` Namespace string `json:"namespace,omitempty"`
Warnings []engine.Warning
// Labels of the release. // Labels of the release.
// Disabled encoding into Json cause labels are stored in storage driver metadata field. // Disabled encoding into Json cause labels are stored in storage driver metadata field.
Labels map[string]string `json:"-"` Labels map[string]string `json:"-"`

Loading…
Cancel
Save