After filtering resource sets, check whether any resource sets "survived". Otherwise it can be assumed that the user specified invalid exclude/include combinations and should be warned about that. Fixes #35
		
			
				
	
	
		
			173 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package templater
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"io/ioutil"
 | |
| 	"os"
 | |
| 	"path"
 | |
| 	"strings"
 | |
| 	"text/template"
 | |
| 
 | |
| 	"github.com/Masterminds/sprig"
 | |
| 	"github.com/polydawn/meep"
 | |
| 	"github.com/tazjin/kontemplate/context"
 | |
| 	"github.com/tazjin/kontemplate/util"
 | |
| )
 | |
| 
 | |
| const failOnMissingKeys string = "missingkey=error"
 | |
| 
 | |
| // Error that is caused by non-existent template files being specified
 | |
| type TemplateNotFoundError struct {
 | |
| 	meep.AllTraits
 | |
| 	Name string
 | |
| }
 | |
| 
 | |
| // Error that is caused during templating, e.g. required value being absent or invalid template format
 | |
| type TemplatingError struct {
 | |
| 	meep.TraitAutodescribing
 | |
| 	meep.TraitCausable
 | |
| }
 | |
| 
 | |
| func LoadAndPrepareTemplates(include *[]string, exclude *[]string, c *context.Context) (output []string, err error) {
 | |
| 	limitedResourceSets := applyLimits(&c.ResourceSets, include, exclude)
 | |
| 
 | |
| 	if len(*limitedResourceSets) == 0 {
 | |
| 		fmt.Fprintln(os.Stderr, "No valid resource sets included!")
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	for _, rs := range *limitedResourceSets {
 | |
| 		err = processResourceSet(c, &rs, &output)
 | |
| 
 | |
| 		if err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func processResourceSet(c *context.Context, rs *context.ResourceSet, output *[]string) error {
 | |
| 	fmt.Fprintf(os.Stderr, "Loading resources for %s\n", rs.Name)
 | |
| 
 | |
| 	rp := path.Join(c.BaseDir, rs.Name)
 | |
| 	files, err := ioutil.ReadDir(rp)
 | |
| 
 | |
| 	err = processFiles(c, rs, rp, files, output)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return meep.New(
 | |
| 			&TemplateNotFoundError{Name: rs.Name},
 | |
| 			meep.Cause(err),
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func processFiles(c *context.Context, rs *context.ResourceSet, rp string, files []os.FileInfo, output *[]string) error {
 | |
| 	for _, file := range files {
 | |
| 		if !file.IsDir() && isResourceFile(file) {
 | |
| 			p := path.Join(rp, file.Name())
 | |
| 			o, err := templateFile(c, rs, p)
 | |
| 
 | |
| 			if err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 
 | |
| 			*output = append(*output, o)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func templateFile(c *context.Context, rs *context.ResourceSet, filename string) (string, error) {
 | |
| 	tpl, err := template.New(path.Base(filename)).Funcs(templateFuncs()).Option(failOnMissingKeys).ParseFiles(filename)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return "", meep.New(
 | |
| 			&TemplateNotFoundError{Name: filename},
 | |
| 			meep.Cause(err),
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	var b bytes.Buffer
 | |
| 
 | |
| 	rs.Values = *util.Merge(&c.Global, &rs.Values)
 | |
| 
 | |
| 	err = tpl.Execute(&b, rs.Values)
 | |
| 
 | |
| 	if err != nil {
 | |
| 		return "", meep.New(
 | |
| 			&TemplatingError{},
 | |
| 			meep.Cause(err),
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	return b.String(), nil
 | |
| }
 | |
| 
 | |
| // Applies the limits of explicitly included or excluded resources and returns the updated resource set.
 | |
| // Exclude takes priority over include
 | |
| func applyLimits(rs *[]context.ResourceSet, include *[]string, exclude *[]string) *[]context.ResourceSet {
 | |
| 	if len(*include) == 0 && len(*exclude) == 0 {
 | |
| 		return rs
 | |
| 	}
 | |
| 
 | |
| 	// Exclude excluded resource sets
 | |
| 	excluded := make([]context.ResourceSet, 0)
 | |
| 	for _, r := range *rs {
 | |
| 		if !matchesResourceSet(exclude, &r) {
 | |
| 			excluded = append(excluded, r)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	// Include included resource sets
 | |
| 	if len(*include) == 0 {
 | |
| 		return &excluded
 | |
| 	}
 | |
| 	included := make([]context.ResourceSet, 0)
 | |
| 	for _, r := range excluded {
 | |
| 		if matchesResourceSet(include, &r) {
 | |
| 			included = append(included, r)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return &included
 | |
| }
 | |
| 
 | |
| // Check whether an include/exclude string slice matches a resource set
 | |
| func matchesResourceSet(s *[]string, rs *context.ResourceSet) bool {
 | |
| 	for _, r := range *s {
 | |
| 		if r == rs.Name || r == rs.Parent {
 | |
| 			return true
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return false
 | |
| }
 | |
| 
 | |
| func templateFuncs() template.FuncMap {
 | |
| 	m := sprig.TxtFuncMap()
 | |
| 	m["json"] = func(data interface{}) string {
 | |
| 		b, _ := json.Marshal(data)
 | |
| 		return string(b)
 | |
| 	}
 | |
| 	m["passLookup"] = GetFromPass
 | |
| 
 | |
| 	return m
 | |
| }
 | |
| 
 | |
| // Checks whether a file is a resource file (i.e. is YAML or JSON) and not a default values file.
 | |
| func isResourceFile(f os.FileInfo) bool {
 | |
| 	if f.Name() == "default.json" || f.Name() == "default.yaml" {
 | |
| 		return false
 | |
| 	}
 | |
| 
 | |
| 	return strings.HasSuffix(f.Name(), "yaml") ||
 | |
| 		strings.HasSuffix(f.Name(), "yml") ||
 | |
| 		strings.HasSuffix(f.Name(), "json")
 | |
| }
 |