forked from hashicorp/packer
/
config_template.go
139 lines (112 loc) · 3.12 KB
/
config_template.go
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 packer
import (
"bytes"
"fmt"
"os"
"strconv"
"strings"
"text/template"
"time"
"github.com/mitchellh/packer/common/uuid"
)
// InitTime is the UTC time when this package was initialized. It is
// used as the timestamp for all configuration templates so that they
// match for a single build.
var InitTime time.Time
func init() {
InitTime = time.Now().UTC()
}
// ConfigTemplate processes string data as a text/template with some common
// elements and functions available. Plugin creators should process as
// many fields as possible through this.
type ConfigTemplate struct {
UserVars map[string]string
root *template.Template
i int
}
// NewConfigTemplate creates a new configuration template processor.
func NewConfigTemplate() (*ConfigTemplate, error) {
result := &ConfigTemplate{
UserVars: make(map[string]string),
}
result.root = template.New("configTemplateRoot")
result.root.Funcs(template.FuncMap{
"env": templateDisableEnv,
"pwd": templatePwd,
"isotime": templateISOTime,
"timestamp": templateTimestamp,
"user": result.templateUser,
"uuid": templateUuid,
"upper": strings.ToUpper,
"lower": strings.ToLower,
})
return result, nil
}
// Process processes a single string, compiling and executing the template.
func (t *ConfigTemplate) Process(s string, data interface{}) (string, error) {
tpl, err := t.root.New(t.nextTemplateName()).Parse(s)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
if err := tpl.Execute(buf, data); err != nil {
return "", err
}
return buf.String(), nil
}
// Validate the template.
func (t *ConfigTemplate) Validate(s string) error {
root, err := t.root.Clone()
if err != nil {
return err
}
_, err = root.New("template").Parse(s)
return err
}
// Add additional functions to the template
func (t *ConfigTemplate) Funcs(funcs template.FuncMap) {
t.root.Funcs(funcs)
}
func (t *ConfigTemplate) nextTemplateName() string {
name := fmt.Sprintf("tpl%d", t.i)
t.i++
return name
}
// User is the function exposed as "user" within the templates and
// looks up user variables.
func (t *ConfigTemplate) templateUser(n string) (string, error) {
result, ok := t.UserVars[n]
if !ok {
return "", fmt.Errorf("unknown user var: %s", n)
}
return result, nil
}
func templateDisableEnv(n string) (string, error) {
return "", fmt.Errorf(
"Environmental variables can only be used as default values for user variables.")
}
func templateDisableUser(n string) (string, error) {
return "", fmt.Errorf(
"User variable can't be used within a default value for a user variable: %s", n)
}
func templateEnv(n string) string {
return os.Getenv(n)
}
func templateISOTime(timeFormat ...string) (string, error) {
if len(timeFormat) == 0 {
return time.Now().UTC().Format(time.RFC3339), nil
}
if len(timeFormat) > 1 {
return "", fmt.Errorf("too many values, 1 needed: %v", timeFormat)
}
return time.Now().UTC().Format(timeFormat[0]), nil
}
func templatePwd() (string, error) {
return os.Getwd()
}
func templateTimestamp() string {
return strconv.FormatInt(InitTime.Unix(), 10)
}
func templateUuid() string {
return uuid.TimeOrderedUUID()
}