-
Notifications
You must be signed in to change notification settings - Fork 106
Expand file tree
/
Copy pathrender.go
More file actions
139 lines (117 loc) · 3.43 KB
/
render.go
File metadata and controls
139 lines (117 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package bindata
import (
"bytes"
"io"
"os"
"path/filepath"
"strings"
"text/template"
//sprig "github.com/go-task/slim-sprig/v3"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
)
// RenderData -
type RenderData struct {
Funcs template.FuncMap
Data map[string]any
}
// MakeRenderData -
func MakeRenderData() RenderData {
return RenderData{
Funcs: template.FuncMap{},
Data: map[string]any{},
}
}
// RenderDir will render all manifests in a directory, descending in to subdirectories
// It will perform template substitutions based on the data supplied by the RenderData
func RenderDir(manifestDir string, d *RenderData) ([]*unstructured.Unstructured, error) {
out := []*unstructured.Unstructured{}
if err := filepath.Walk(manifestDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
// Skip non-manifest files
if !strings.HasSuffix(path, ".yml") && !strings.HasSuffix(path, ".yaml") && !strings.HasSuffix(path, ".json") {
return nil
}
objs, err := RenderTemplate(path, d)
if err != nil {
return err
}
out = append(out, objs...)
return nil
}); err != nil {
return nil, errors.Wrap(err, "error rendering manifests")
}
return out, nil
}
// hasEnvVar checks if an EnvVar with a specific name exists in a slice.
func hasEnvVar(envVars []corev1.EnvVar, name string) bool {
for _, env := range envVars {
if env.Name == name {
return true
}
}
return false
}
// isEnvVarTrue checks if an EnvVar with a specific name exists
// and if its value is "true" (case-insensitive).
func isEnvVarTrue(envVars []corev1.EnvVar, name string) bool {
for _, env := range envVars {
if env.Name == name {
// Found the right env var, now check its value.
// Using ToLower for a case-insensitive comparison ("true", "True", etc.)
return strings.ToLower(env.Value) == "true"
}
}
// Return false if the env var was not found at all.
return false
}
// RenderTemplate reads, renders, and attempts to parse a yaml or
// json file representing one or more k8s api objects
func RenderTemplate(path string, d *RenderData) ([]*unstructured.Unstructured, error) {
// Create a FuncMap to register our function.
funcMap := template.FuncMap{
"hasEnvVar": hasEnvVar,
"isEnvVarTrue": isEnvVarTrue,
}
tmpl := template.New(path).Option("missingkey=error").Funcs(funcMap)
if d.Funcs != nil {
tmpl.Funcs(d.Funcs)
}
// Add universal functions
//tmpl.Funcs(sprig.TxtFuncMap())
source, err := os.ReadFile(path)
if err != nil {
return nil, errors.Wrapf(err, "failed to read manifest %s", path)
}
if _, err := tmpl.Parse(string(source)); err != nil {
return nil, errors.Wrapf(err, "failed to parse manifest %s as template", path)
}
rendered := bytes.Buffer{}
if err := tmpl.Execute(&rendered, d.Data); err != nil {
return nil, errors.Wrapf(err, "failed to render manifest %s", path)
}
out := []*unstructured.Unstructured{}
// special case - if the entire file is whitespace, skip
if len(strings.TrimSpace(rendered.String())) == 0 {
return out, nil
}
decoder := yaml.NewYAMLOrJSONDecoder(&rendered, 4096)
for {
u := unstructured.Unstructured{}
if err := decoder.Decode(&u); err != nil {
if errors.Is(err, io.EOF) {
break
}
return nil, errors.Wrapf(err, "failed to unmarshal manifest %s", path)
}
out = append(out, &u)
}
return out, nil
}