1package config
2
3import (
4	"errors"
5	"fmt"
6	"os"
7	"regexp"
8	"strings"
9	"time"
10)
11
12const (
13	// DefaultTemplateCommandTimeout is the amount of time to wait for a command
14	// to return.
15	DefaultTemplateCommandTimeout = 30 * time.Second
16)
17
18var (
19	// ErrTemplateStringEmpty is the error returned with the template contents
20	// are empty.
21	ErrTemplateStringEmpty = errors.New("template: cannot be empty")
22
23	// configTemplateRe is the pattern to split the config template syntax.
24	configTemplateRe = regexp.MustCompile("([a-zA-Z]:)?([^:]+)")
25)
26
27// TemplateConfig is a representation of a template on disk, as well as the
28// associated commands and reload instructions.
29type TemplateConfig struct {
30	// Backup determines if this template should retain a backup. The default
31	// value is false.
32	Backup *bool `mapstructure:"backup"`
33
34	// Command is the arbitrary command to execute after a template has
35	// successfully rendered. This is DEPRECATED. Use Exec instead.
36	Command *string `mapstructure:"command"`
37
38	// CommandTimeout is the amount of time to wait for the command to finish
39	// before force-killing it. This is DEPRECATED. Use Exec instead.
40	CommandTimeout *time.Duration `mapstructure:"command_timeout"`
41
42	// Contents are the raw template contents to evaluate. Either this or Source
43	// must be specified, but not both.
44	Contents *string `mapstructure:"contents"`
45
46	// CreateDestDirs tells Consul Template to create the parent directories of
47	// the destination path if they do not exist. The default value is true.
48	CreateDestDirs *bool `mapstructure:"create_dest_dirs"`
49
50	// Destination is the location on disk where the template should be rendered.
51	// This is required unless running in debug/dry mode.
52	Destination *string `mapstructure:"destination"`
53
54	// ErrMissingKey is used to control how the template behaves when attempting
55	// to index a struct or map key that does not exist.
56	ErrMissingKey *bool `mapstructure:"error_on_missing_key"`
57
58	// Exec is the configuration for the command to run when the template renders
59	// successfully.
60	Exec *ExecConfig `mapstructure:"exec"`
61
62	// Perms are the file system permissions to use when creating the file on
63	// disk. This is useful for when files contain sensitive information, such as
64	// secrets from Vault.
65	Perms *os.FileMode `mapstructure:"perms"`
66
67	// Source is the path on disk to the template contents to evaluate. Either
68	// this or Contents should be specified, but not both.
69	Source *string `mapstructure:"source"`
70
71	// Wait configures per-template quiescence timers.
72	Wait *WaitConfig `mapstructure:"wait"`
73
74	// LeftDelim and RightDelim are optional configurations to control what
75	// delimiter is utilized when parsing the template.
76	LeftDelim  *string `mapstructure:"left_delimiter"`
77	RightDelim *string `mapstructure:"right_delimiter"`
78
79	// FunctionBlacklist is a list of functions that this template is not
80	// permitted to run.
81	FunctionBlacklist []string `mapstructure:"function_blacklist"`
82
83	// SandboxPath adds a prefix to any path provided to the `file` function
84	// and causes an error if a relative path tries to traverse outside that
85	// prefix.
86	SandboxPath *string `mapstructure:"sandbox_path"`
87}
88
89// DefaultTemplateConfig returns a configuration that is populated with the
90// default values.
91func DefaultTemplateConfig() *TemplateConfig {
92	return &TemplateConfig{
93		Exec: DefaultExecConfig(),
94		Wait: DefaultWaitConfig(),
95	}
96}
97
98// Copy returns a deep copy of this configuration.
99func (c *TemplateConfig) Copy() *TemplateConfig {
100	if c == nil {
101		return nil
102	}
103
104	var o TemplateConfig
105
106	o.Backup = c.Backup
107
108	o.Command = c.Command
109
110	o.CommandTimeout = c.CommandTimeout
111
112	o.Contents = c.Contents
113
114	o.CreateDestDirs = c.CreateDestDirs
115
116	o.Destination = c.Destination
117
118	o.ErrMissingKey = c.ErrMissingKey
119
120	if c.Exec != nil {
121		o.Exec = c.Exec.Copy()
122	}
123
124	o.Perms = c.Perms
125
126	o.Source = c.Source
127
128	if c.Wait != nil {
129		o.Wait = c.Wait.Copy()
130	}
131
132	o.LeftDelim = c.LeftDelim
133	o.RightDelim = c.RightDelim
134
135	for _, fun := range c.FunctionBlacklist {
136		o.FunctionBlacklist = append(o.FunctionBlacklist, fun)
137	}
138	o.SandboxPath = c.SandboxPath
139
140	return &o
141}
142
143// Merge combines all values in this configuration with the values in the other
144// configuration, with values in the other configuration taking precedence.
145// Maps and slices are merged, most other values are overwritten. Complex
146// structs define their own merge functionality.
147func (c *TemplateConfig) Merge(o *TemplateConfig) *TemplateConfig {
148	if c == nil {
149		if o == nil {
150			return nil
151		}
152		return o.Copy()
153	}
154
155	if o == nil {
156		return c.Copy()
157	}
158
159	r := c.Copy()
160
161	if o.Backup != nil {
162		r.Backup = o.Backup
163	}
164
165	if o.Command != nil {
166		r.Command = o.Command
167	}
168
169	if o.CommandTimeout != nil {
170		r.CommandTimeout = o.CommandTimeout
171	}
172
173	if o.Contents != nil {
174		r.Contents = o.Contents
175	}
176
177	if o.CreateDestDirs != nil {
178		r.CreateDestDirs = o.CreateDestDirs
179	}
180
181	if o.Destination != nil {
182		r.Destination = o.Destination
183	}
184
185	if o.ErrMissingKey != nil {
186		r.ErrMissingKey = o.ErrMissingKey
187	}
188
189	if o.Exec != nil {
190		r.Exec = r.Exec.Merge(o.Exec)
191	}
192
193	if o.Perms != nil {
194		r.Perms = o.Perms
195	}
196
197	if o.Source != nil {
198		r.Source = o.Source
199	}
200
201	if o.Wait != nil {
202		r.Wait = r.Wait.Merge(o.Wait)
203	}
204
205	if o.LeftDelim != nil {
206		r.LeftDelim = o.LeftDelim
207	}
208
209	if o.RightDelim != nil {
210		r.RightDelim = o.RightDelim
211	}
212
213	for _, fun := range o.FunctionBlacklist {
214		r.FunctionBlacklist = append(r.FunctionBlacklist, fun)
215	}
216	if o.SandboxPath != nil {
217		r.SandboxPath = o.SandboxPath
218	}
219
220	return r
221}
222
223// Finalize ensures the configuration has no nil pointers and sets default
224// values.
225func (c *TemplateConfig) Finalize() {
226	if c.Backup == nil {
227		c.Backup = Bool(false)
228	}
229
230	if c.Command == nil {
231		c.Command = String("")
232	}
233
234	if c.CommandTimeout == nil {
235		c.CommandTimeout = TimeDuration(DefaultTemplateCommandTimeout)
236	}
237
238	if c.Contents == nil {
239		c.Contents = String("")
240	}
241
242	if c.CreateDestDirs == nil {
243		c.CreateDestDirs = Bool(true)
244	}
245
246	if c.Destination == nil {
247		c.Destination = String("")
248	}
249
250	if c.ErrMissingKey == nil {
251		c.ErrMissingKey = Bool(false)
252	}
253
254	if c.Exec == nil {
255		c.Exec = DefaultExecConfig()
256	}
257
258	// Backwards compat for specifying command directly
259	if c.Exec.Command == nil && c.Command != nil {
260		c.Exec.Command = c.Command
261	}
262	if c.Exec.Timeout == nil && c.CommandTimeout != nil {
263		c.Exec.Timeout = c.CommandTimeout
264	}
265	c.Exec.Finalize()
266
267	if c.Perms == nil {
268		c.Perms = FileMode(0)
269	}
270
271	if c.Source == nil {
272		c.Source = String("")
273	}
274
275	if c.Wait == nil {
276		c.Wait = DefaultWaitConfig()
277	}
278	c.Wait.Finalize()
279
280	if c.LeftDelim == nil {
281		c.LeftDelim = String("")
282	}
283
284	if c.RightDelim == nil {
285		c.RightDelim = String("")
286	}
287
288	if c.SandboxPath == nil {
289		c.SandboxPath = String("")
290	}
291}
292
293// GoString defines the printable version of this struct.
294func (c *TemplateConfig) GoString() string {
295	if c == nil {
296		return "(*TemplateConfig)(nil)"
297	}
298
299	return fmt.Sprintf("&TemplateConfig{"+
300		"Backup:%s, "+
301		"Command:%s, "+
302		"CommandTimeout:%s, "+
303		"Contents:%s, "+
304		"CreateDestDirs:%s, "+
305		"Destination:%s, "+
306		"ErrMissingKey:%s, "+
307		"Exec:%#v, "+
308		"Perms:%s, "+
309		"Source:%s, "+
310		"Wait:%#v, "+
311		"LeftDelim:%s, "+
312		"RightDelim:%s"+
313		"FunctionBlacklist:%s"+
314		"SandboxPath:%s"+
315		"}",
316		BoolGoString(c.Backup),
317		StringGoString(c.Command),
318		TimeDurationGoString(c.CommandTimeout),
319		StringGoString(c.Contents),
320		BoolGoString(c.CreateDestDirs),
321		StringGoString(c.Destination),
322		BoolGoString(c.ErrMissingKey),
323		c.Exec,
324		FileModeGoString(c.Perms),
325		StringGoString(c.Source),
326		c.Wait,
327		StringGoString(c.LeftDelim),
328		StringGoString(c.RightDelim),
329		c.FunctionBlacklist,
330		StringGoString(c.SandboxPath),
331	)
332}
333
334// Display is the human-friendly form of this configuration. It tries to
335// describe this template in as much detail as possible in a single line, so
336// log consumers can uniquely identify it.
337func (c *TemplateConfig) Display() string {
338	if c == nil {
339		return ""
340	}
341
342	source := c.Source
343	if StringPresent(c.Contents) {
344		source = String("(dynamic)")
345	}
346
347	return fmt.Sprintf("%q => %q",
348		StringVal(source),
349		StringVal(c.Destination),
350	)
351}
352
353// TemplateConfigs is a collection of TemplateConfigs
354type TemplateConfigs []*TemplateConfig
355
356// DefaultTemplateConfigs returns a configuration that is populated with the
357// default values.
358func DefaultTemplateConfigs() *TemplateConfigs {
359	return &TemplateConfigs{}
360}
361
362// Copy returns a deep copy of this configuration.
363func (c *TemplateConfigs) Copy() *TemplateConfigs {
364	o := make(TemplateConfigs, len(*c))
365	for i, t := range *c {
366		o[i] = t.Copy()
367	}
368	return &o
369}
370
371// Merge combines all values in this configuration with the values in the other
372// configuration, with values in the other configuration taking precedence.
373// Maps and slices are merged, most other values are overwritten. Complex
374// structs define their own merge functionality.
375func (c *TemplateConfigs) Merge(o *TemplateConfigs) *TemplateConfigs {
376	if c == nil {
377		if o == nil {
378			return nil
379		}
380		return o.Copy()
381	}
382
383	if o == nil {
384		return c.Copy()
385	}
386
387	r := c.Copy()
388
389	*r = append(*r, *o...)
390
391	return r
392}
393
394// Finalize ensures the configuration has no nil pointers and sets default
395// values.
396func (c *TemplateConfigs) Finalize() {
397	if c == nil {
398		*c = *DefaultTemplateConfigs()
399	}
400
401	for _, t := range *c {
402		t.Finalize()
403	}
404}
405
406// GoString defines the printable version of this struct.
407func (c *TemplateConfigs) GoString() string {
408	if c == nil {
409		return "(*TemplateConfigs)(nil)"
410	}
411
412	s := make([]string, len(*c))
413	for i, t := range *c {
414		s[i] = t.GoString()
415	}
416
417	return "{" + strings.Join(s, ", ") + "}"
418}
419
420// ParseTemplateConfig parses a string in the form source:destination:command
421// into a TemplateConfig.
422func ParseTemplateConfig(s string) (*TemplateConfig, error) {
423	if len(strings.TrimSpace(s)) < 1 {
424		return nil, ErrTemplateStringEmpty
425	}
426
427	var source, destination, command string
428	parts := configTemplateRe.FindAllString(s, -1)
429
430	switch len(parts) {
431	case 1:
432		source = parts[0]
433	case 2:
434		source, destination = parts[0], parts[1]
435	case 3:
436		source, destination, command = parts[0], parts[1], parts[2]
437	default:
438		source, destination = parts[0], parts[1]
439		command = strings.Join(parts[2:], ":")
440	}
441
442	var sourcePtr, destinationPtr, commandPtr *string
443	if source != "" {
444		sourcePtr = String(source)
445	}
446	if destination != "" {
447		destinationPtr = String(destination)
448	}
449	if command != "" {
450		commandPtr = String(command)
451	}
452
453	return &TemplateConfig{
454		Source:      sourcePtr,
455		Destination: destinationPtr,
456		Command:     commandPtr,
457	}, nil
458}
459