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