mirror of
https://github.com/mudler/luet.git
synced 2025-09-21 11:08:08 +00:00
Update vendor/
This commit is contained in:
382
vendor/helm.sh/helm/v3/pkg/engine/engine.go
vendored
Normal file
382
vendor/helm.sh/helm/v3/pkg/engine/engine.go
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
Copyright The Helm 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.
|
||||
*/
|
||||
|
||||
package engine
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/client-go/rest"
|
||||
|
||||
"helm.sh/helm/v3/pkg/chart"
|
||||
"helm.sh/helm/v3/pkg/chartutil"
|
||||
)
|
||||
|
||||
// Engine is an implementation of the Helm rendering implementation for templates.
|
||||
type Engine struct {
|
||||
// If strict is enabled, template rendering will fail if a template references
|
||||
// a value that was not passed in.
|
||||
Strict bool
|
||||
// In LintMode, some 'required' template values may be missing, so don't fail
|
||||
LintMode bool
|
||||
// the rest config to connect to te kubernetes api
|
||||
config *rest.Config
|
||||
}
|
||||
|
||||
// Render takes a chart, optional values, and value overrides, and attempts to render the Go templates.
|
||||
//
|
||||
// Render can be called repeatedly on the same engine.
|
||||
//
|
||||
// This will look in the chart's 'templates' data (e.g. the 'templates/' directory)
|
||||
// and attempt to render the templates there using the values passed in.
|
||||
//
|
||||
// Values are scoped to their templates. A dependency template will not have
|
||||
// access to the values set for its parent. If chart "foo" includes chart "bar",
|
||||
// "bar" will not have access to the values for "foo".
|
||||
//
|
||||
// Values should be prepared with something like `chartutils.ReadValues`.
|
||||
//
|
||||
// Values are passed through the templates according to scope. If the top layer
|
||||
// chart includes the chart foo, which includes the chart bar, the values map
|
||||
// will be examined for a table called "foo". If "foo" is found in vals,
|
||||
// that section of the values will be passed into the "foo" chart. And if that
|
||||
// section contains a value named "bar", that value will be passed on to the
|
||||
// bar chart during render time.
|
||||
func (e Engine) Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
|
||||
tmap := allTemplates(chrt, values)
|
||||
return e.render(tmap)
|
||||
}
|
||||
|
||||
// Render takes a chart, optional values, and value overrides, and attempts to
|
||||
// render the Go templates using the default options.
|
||||
func Render(chrt *chart.Chart, values chartutil.Values) (map[string]string, error) {
|
||||
return new(Engine).Render(chrt, values)
|
||||
}
|
||||
|
||||
// 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
|
||||
// functions that interact with the client
|
||||
func RenderWithClient(chrt *chart.Chart, values chartutil.Values, config *rest.Config) (map[string]string, error) {
|
||||
return Engine{
|
||||
config: config,
|
||||
}.Render(chrt, values)
|
||||
}
|
||||
|
||||
// renderable is an object that can be rendered.
|
||||
type renderable struct {
|
||||
// tpl is the current template.
|
||||
tpl string
|
||||
// vals are the values to be supplied to the template.
|
||||
vals chartutil.Values
|
||||
// namespace prefix to the templates of the current chart
|
||||
basePath string
|
||||
}
|
||||
|
||||
const warnStartDelim = "HELM_ERR_START"
|
||||
const warnEndDelim = "HELM_ERR_END"
|
||||
const recursionMaxNums = 1000
|
||||
|
||||
var warnRegex = regexp.MustCompile(warnStartDelim + `(.*)` + warnEndDelim)
|
||||
|
||||
func warnWrap(warn string) string {
|
||||
return warnStartDelim + warn + warnEndDelim
|
||||
}
|
||||
|
||||
// initFunMap creates the Engine's FuncMap and adds context-specific functions.
|
||||
func (e Engine) initFunMap(t *template.Template, referenceTpls map[string]renderable) {
|
||||
funcMap := funcMap()
|
||||
includedNames := make(map[string]int)
|
||||
|
||||
// Add the 'include' function here so we can close over t.
|
||||
funcMap["include"] = func(name string, data interface{}) (string, error) {
|
||||
var buf strings.Builder
|
||||
if v, ok := includedNames[name]; ok {
|
||||
if v > recursionMaxNums {
|
||||
return "", errors.Wrapf(fmt.Errorf("unable to execute template"), "rendering template has a nested reference name: %s", name)
|
||||
}
|
||||
includedNames[name]++
|
||||
} else {
|
||||
includedNames[name] = 1
|
||||
}
|
||||
err := t.ExecuteTemplate(&buf, name, data)
|
||||
includedNames[name]--
|
||||
return buf.String(), err
|
||||
}
|
||||
|
||||
// Add the 'tpl' function here
|
||||
funcMap["tpl"] = func(tpl string, vals chartutil.Values) (string, error) {
|
||||
basePath, err := vals.PathValue("Template.BasePath")
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot retrieve Template.Basepath from values inside tpl function: %s", tpl)
|
||||
}
|
||||
|
||||
templateName, err := vals.PathValue("Template.Name")
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "cannot retrieve Template.Name from values inside tpl function: %s", tpl)
|
||||
}
|
||||
|
||||
templates := map[string]renderable{
|
||||
templateName.(string): {
|
||||
tpl: tpl,
|
||||
vals: vals,
|
||||
basePath: basePath.(string),
|
||||
},
|
||||
}
|
||||
|
||||
result, err := e.renderWithReferences(templates, referenceTpls)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(err, "error during tpl function execution for %q", tpl)
|
||||
}
|
||||
return result[templateName.(string)], nil
|
||||
}
|
||||
|
||||
// Add the `required` function here so we can use lintMode
|
||||
funcMap["required"] = func(warn string, val interface{}) (interface{}, error) {
|
||||
if val == nil {
|
||||
if e.LintMode {
|
||||
// Don't fail on missing required values when linting
|
||||
log.Printf("[INFO] Missing required value: %s", warn)
|
||||
return "", nil
|
||||
}
|
||||
return val, errors.Errorf(warnWrap(warn))
|
||||
} else if _, ok := val.(string); ok {
|
||||
if val == "" {
|
||||
if e.LintMode {
|
||||
// Don't fail on missing required values when linting
|
||||
log.Printf("[INFO] Missing required value: %s", warn)
|
||||
return "", nil
|
||||
}
|
||||
return val, errors.Errorf(warnWrap(warn))
|
||||
}
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// If we are not linting and have a cluster connection, provide a Kubernetes-backed
|
||||
// implementation.
|
||||
if !e.LintMode && e.config != nil {
|
||||
funcMap["lookup"] = NewLookupFunction(e.config)
|
||||
}
|
||||
|
||||
t.Funcs(funcMap)
|
||||
}
|
||||
|
||||
// render takes a map of templates/values and renders them.
|
||||
func (e Engine) render(tpls map[string]renderable) (map[string]string, error) {
|
||||
return e.renderWithReferences(tpls, tpls)
|
||||
}
|
||||
|
||||
// renderWithReferences takes a map of templates/values to render, and a map of
|
||||
// templates which can be referenced within them.
|
||||
func (e Engine) renderWithReferences(tpls, referenceTpls map[string]renderable) (rendered map[string]string, err error) {
|
||||
// Basically, what we do here is start with an empty parent template and then
|
||||
// build up a list of templates -- one for each file. Once all of the templates
|
||||
// have been parsed, we loop through again and execute every template.
|
||||
//
|
||||
// The idea with this process is to make it possible for more complex templates
|
||||
// to share common blocks, but to make the entire thing feel like a file-based
|
||||
// template engine.
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = errors.Errorf("rendering template failed: %v", r)
|
||||
}
|
||||
}()
|
||||
t := template.New("gotpl")
|
||||
if e.Strict {
|
||||
t.Option("missingkey=error")
|
||||
} else {
|
||||
// Not that zero will attempt to add default values for types it knows,
|
||||
// but will still emit <no value> for others. We mitigate that later.
|
||||
t.Option("missingkey=zero")
|
||||
}
|
||||
|
||||
e.initFunMap(t, referenceTpls)
|
||||
|
||||
// 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)
|
||||
referenceKeys := sortTemplates(referenceTpls)
|
||||
|
||||
for _, filename := range keys {
|
||||
r := tpls[filename]
|
||||
if _, err := t.New(filename).Parse(r.tpl); err != nil {
|
||||
return map[string]string{}, cleanupParseError(filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Adding the reference templates to the template context
|
||||
// so they can be referenced in the tpl function
|
||||
for _, filename := range referenceKeys {
|
||||
if t.Lookup(filename) == nil {
|
||||
r := referenceTpls[filename]
|
||||
if _, err := t.New(filename).Parse(r.tpl); err != nil {
|
||||
return map[string]string{}, cleanupParseError(filename, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rendered = make(map[string]string, len(keys))
|
||||
for _, filename := range keys {
|
||||
// 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(filename), "_") {
|
||||
continue
|
||||
}
|
||||
// At render time, add information about the template that is being rendered.
|
||||
vals := tpls[filename].vals
|
||||
vals["Template"] = chartutil.Values{"Name": filename, "BasePath": tpls[filename].basePath}
|
||||
var buf strings.Builder
|
||||
if err := t.ExecuteTemplate(&buf, filename, vals); err != nil {
|
||||
return map[string]string{}, cleanupExecError(filename, err)
|
||||
}
|
||||
|
||||
// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
|
||||
// is set. Since missing=error will never get here, we do not need to handle
|
||||
// the Strict case.
|
||||
rendered[filename] = strings.ReplaceAll(buf.String(), "<no value>", "")
|
||||
}
|
||||
|
||||
return rendered, nil
|
||||
}
|
||||
|
||||
func cleanupParseError(filename string, err error) error {
|
||||
tokens := strings.Split(err.Error(), ": ")
|
||||
if len(tokens) == 1 {
|
||||
// This might happen if a non-templating error occurs
|
||||
return fmt.Errorf("parse error in (%s): %s", filename, err)
|
||||
}
|
||||
// The first token is "template"
|
||||
// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
|
||||
location := tokens[1]
|
||||
// The remaining tokens make up a stacktrace-like chain, ending with the relevant error
|
||||
errMsg := tokens[len(tokens)-1]
|
||||
return fmt.Errorf("parse error at (%s): %s", string(location), errMsg)
|
||||
}
|
||||
|
||||
func cleanupExecError(filename string, err error) error {
|
||||
if _, isExecError := err.(template.ExecError); !isExecError {
|
||||
return err
|
||||
}
|
||||
|
||||
tokens := strings.SplitN(err.Error(), ": ", 3)
|
||||
if len(tokens) != 3 {
|
||||
// This might happen if a non-templating error occurs
|
||||
return fmt.Errorf("execution error in (%s): %s", filename, err)
|
||||
}
|
||||
|
||||
// The first token is "template"
|
||||
// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
|
||||
location := tokens[1]
|
||||
|
||||
parts := warnRegex.FindStringSubmatch(tokens[2])
|
||||
if len(parts) >= 2 {
|
||||
return fmt.Errorf("execution error at (%s): %s", string(location), parts[1])
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
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.
|
||||
func allTemplates(c *chart.Chart, vals chartutil.Values) map[string]renderable {
|
||||
templates := make(map[string]renderable)
|
||||
recAllTpls(c, templates, vals)
|
||||
return templates
|
||||
}
|
||||
|
||||
// recAllTpls recurses through the templates in a chart.
|
||||
//
|
||||
// As it recurses, it also sets the values to be appropriate for the template
|
||||
// scope.
|
||||
func recAllTpls(c *chart.Chart, templates map[string]renderable, vals chartutil.Values) {
|
||||
next := map[string]interface{}{
|
||||
"Chart": c.Metadata,
|
||||
"Files": newFiles(c.Files),
|
||||
"Release": vals["Release"],
|
||||
"Capabilities": vals["Capabilities"],
|
||||
"Values": make(chartutil.Values),
|
||||
}
|
||||
|
||||
// If there is a {{.Values.ThisChart}} in the parent metadata,
|
||||
// copy that into the {{.Values}} for this template.
|
||||
if c.IsRoot() {
|
||||
next["Values"] = vals["Values"]
|
||||
} else if vs, err := vals.Table("Values." + c.Name()); err == nil {
|
||||
next["Values"] = vs
|
||||
}
|
||||
|
||||
for _, child := range c.Dependencies() {
|
||||
recAllTpls(child, templates, next)
|
||||
}
|
||||
|
||||
newParentID := c.ChartFullPath()
|
||||
for _, t := range c.Templates {
|
||||
if !isTemplateValid(c, t.Name) {
|
||||
continue
|
||||
}
|
||||
templates[path.Join(newParentID, t.Name)] = renderable{
|
||||
tpl: string(t.Data),
|
||||
vals: next,
|
||||
basePath: path.Join(newParentID, "templates"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// isTemplateValid returns true if the template is valid for the chart type
|
||||
func isTemplateValid(ch *chart.Chart, templateName string) bool {
|
||||
if isLibraryChart(ch) {
|
||||
return strings.HasPrefix(filepath.Base(templateName), "_")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// isLibraryChart returns true if the chart is a library chart
|
||||
func isLibraryChart(c *chart.Chart) bool {
|
||||
return strings.EqualFold(c.Metadata.Type, "library")
|
||||
}
|
Reference in New Issue
Block a user