Commit 81ac98ad authored by Miguel Martinez's avatar Miguel Martinez

Adding support for associated templates

Fixing unit test
parent 03d27779
......@@ -5,11 +5,11 @@ kind: ConfigMap
metadata:
name: {{template "fullname" .}}
labels:
release: {{.Release.Name}}
release: {{ .Release.Name | quote }}
app: {{template "fullname" .}}
heritage: {{.Release.Service}}
heritage: {{.Release.Service | quote }}
data:
# When the config map is mounted as a volume, these will be created as
# files.
index.html: {{default "Hello" .index | squote}}
index.html: {{ default "Hello" .index | quote }}
test.txt: test
......@@ -9,18 +9,18 @@ metadata:
# 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}}
heritage: {{ .Release.Service | quote }}
# This makes it easy to search for all components of a release using kubectl.
release: {{.Release.Name}}
release: {{ .Release.Name | quote }}
# This makes it easy to audit chart usage.
chart: {{.Chart.Name}}-{{.Chart.Version}}
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
spec:
replicas: {{default 1 .replicaCount}}
replicas: {{ default 1 .replicaCount | quote }}
template:
metadata:
labels:
app: {{template "fullname" .}}
release: {{.Release.Name}}
release: {{.Release.Name | quote }}
spec:
containers:
- name: {{template "fullname" .}}
......
......@@ -5,12 +5,12 @@ kind: Service
metadata:
name: {{template "fullname" .}}
labels:
heritage: {{.Release.Service}}
release: {{.Release.Name}}
chart: {{.Chart.Name}}-{{.Chart.Version}}
heritage: {{ .Release.Service | quote }}
release: {{ .Release.Name | quote }}
chart: "{{.Chart.Name}}-{{.Chart.Version}}"
spec:
ports:
- port: {{default 80 .httpPort}}
- port: {{ default 80 .httpPort | quote }}
targetPort: 80
protocol: TCP
name: http
......
......@@ -35,7 +35,7 @@ import (
func Templates(linter *support.Linter) {
templatesPath := filepath.Join(linter.ChartDir, "templates")
templatesDirExist := linter.RunLinterRule(support.WarningSev, validateTemplatesDir(linter, templatesPath))
templatesDirExist := linter.RunLinterRule(support.WarningSev, validateTemplatesDir(templatesPath))
// Templates directory is optional for now
if !templatesDirExist {
......@@ -80,14 +80,15 @@ func Templates(linter *support.Linter) {
for _, template := range chart.Templates {
fileName, preExecutedTemplate := template.Name, template.Data
yamlFile := linter.RunLinterRule(support.ErrorSev, validateYamlExtension(linter, fileName))
linter.RunLinterRule(support.ErrorSev, validateAllowedExtension(fileName))
if !yamlFile {
return
// We only apply the following lint rules to yaml files
if filepath.Ext(fileName) != ".yaml" {
continue
}
// Check that all the templates have a matching value
linter.RunLinterRule(support.WarningSev, validateNonMissingValues(fileName, chartValues, preExecutedTemplate))
linter.RunLinterRule(support.WarningSev, validateNonMissingValues(fileName, templatesPath, chartValues, preExecutedTemplate))
linter.RunLinterRule(support.WarningSev, validateQuotes(fileName, string(preExecutedTemplate)))
......@@ -100,7 +101,7 @@ func Templates(linter *support.Linter) {
validYaml := linter.RunLinterRule(support.ErrorSev, validateYamlContent(fileName, err))
if !validYaml {
return
continue
}
linter.RunLinterRule(support.ErrorSev, validateNoNamespace(fileName, yamlStruct))
......@@ -108,7 +109,7 @@ func Templates(linter *support.Linter) {
}
// Validation functions
func validateTemplatesDir(linter *support.Linter, templatesPath string) (lintError support.LintError) {
func validateTemplatesDir(templatesPath string) (lintError support.LintError) {
if fi, err := os.Stat(templatesPath); err != nil {
lintError = fmt.Errorf("Templates directory not found")
} else if err == nil && !fi.IsDir() {
......@@ -145,22 +146,42 @@ func validateQuotes(templateName string, templateContent string) (lintError supp
return
}
func validateYamlExtension(linter *support.Linter, fileName string) (lintError support.LintError) {
if filepath.Ext(fileName) != ".yaml" {
lintError = fmt.Errorf("templates: \"%s\" needs to use the .yaml extension", fileName)
func validateAllowedExtension(fileName string) (lintError support.LintError) {
ext := filepath.Ext(fileName)
validExtensions := []string{".yaml", ".tpl"}
for _, b := range validExtensions {
if b == ext {
return
}
}
lintError = fmt.Errorf("templates: \"%s\" needs to use .yaml or .tpl extensions", fileName)
return
}
// validateNonMissingValues checks that all the {{}} functions returns a non empty value (<no value> or "")
// and return an error otherwise.
func validateNonMissingValues(fileName string, chartValues chartutil.Values, templateContent []byte) (lintError support.LintError) {
func validateNonMissingValues(fileName string, templatesPath string, chartValues chartutil.Values, templateContent []byte) (lintError support.LintError) {
// 1 - Load Main and associated templates
// Main template that we will parse dynamically
tmpl := template.New("tpl").Funcs(sprig.TxtFuncMap())
// If the templatesPath includes any *.tpl files we will parse and import them as associated templates
associatedTemplates, err := filepath.Glob(filepath.Join(templatesPath, "*.tpl"))
if len(associatedTemplates) > 0 {
tmpl, err = tmpl.ParseFiles(associatedTemplates...)
if err != nil {
return err
}
}
var buf bytes.Buffer
var emptyValues []string
// 2 - Extract every function and execute them agains the loaded values
// Supported {{ .Chart.Name }}, {{ .Chart.Name | quote }}
r, _ := regexp.Compile(`{{([\w]|\.*|\s|\|)+}}`)
r, _ := regexp.Compile(`{{[\w|\.|\s|\|\"|\']+}}`)
functions := r.FindAllString(string(templateContent), -1)
// Iterate over the {{ FOO }} templates, executing them against the chartValues
......@@ -172,7 +193,12 @@ func validateNonMissingValues(fileName string, chartValues chartutil.Values, tem
return
}
err = newtmpl.Execute(&buf, chartValues)
err = newtmpl.ExecuteTemplate(&buf, "tpl", chartValues)
if err != nil {
return err
}
renderedValue := buf.String()
if renderedValue == "<no value>" || renderedValue == "" {
......@@ -182,7 +208,7 @@ func validateNonMissingValues(fileName string, chartValues chartutil.Values, tem
}
if len(emptyValues) > 0 {
lintError = fmt.Errorf("templates: %s: The following functions are not returning eny value %v", fileName, emptyValues)
lintError = fmt.Errorf("templates: %s: The following functions are not returning any value %v", fileName, emptyValues)
}
return
}
......
......@@ -24,6 +24,23 @@ import (
const templateTestBasedir = "./testdata/albatross"
func TestValidateAllowedExtension(t *testing.T) {
var failTest = []string{"/foo", "/test.yml", "/test.toml", "test.yml"}
for _, test := range failTest {
err := validateAllowedExtension(test)
if err == nil || !strings.Contains(err.Error(), "needs to use .yaml or .tpl extension") {
t.Errorf("validateAllowedExtension('%s') to return \"needs to use .yaml or .tpl extension\", got no error", test)
}
}
var successTest = []string{"/foo.yaml", "foo.yaml", "foo.tpl", "/foo/bar/baz.yaml"}
for _, test := range successTest {
err := validateAllowedExtension(test)
if err != nil {
t.Errorf("validateAllowedExtension('%s') to return no error but got \"%s\"", test, err.Error())
}
}
}
func TestValidateQuotes(t *testing.T) {
// add `| quote` lint error
var failTest = []string{"foo: {{.Release.Service }}", "foo: {{.Release.Service }}", "- {{.Release.Service }}", "foo: {{default 'Never' .restart_policy}}", "- {{.Release.Service }} "}
......
......@@ -19,8 +19,6 @@ package support
import "fmt"
// Severity indicatest the severity of a Message.
type Severity int
const (
// UnknownSev indicates that the severity of the error is unknown, and should not stop processing.
UnknownSev = iota
......@@ -38,7 +36,7 @@ var sev = []string{"UNKNOWN", "INFO", "WARNING", "ERROR"}
// Message is a linting output message
type Message struct {
// Severity is one of the *Sev constants
Severity Severity
Severity int
// Text contains the message text
Text string
}
......@@ -60,9 +58,9 @@ func (m Message) String() string {
}
// Returns true if the validation passed
func (l *Linter) RunLinterRule(severity Severity, lintError LintError) bool {
func (l *Linter) RunLinterRule(severity int, lintError LintError) bool {
// severity is out of bound
if severity < 0 || int(severity) >= len(sev) {
if severity < 0 || severity >= len(sev) {
return false
}
......
......@@ -26,7 +26,7 @@ var lintError LintError = fmt.Errorf("Foobar")
func TestRunLinterRule(t *testing.T) {
var tests = []struct {
Severity Severity
Severity int
LintError error
ExpectedMessages int
ExpectedReturn bool
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment