github.com/askholme/packer@v0.7.2-0.20140924152349-70d9566a6852/packer/config_template.go (about) 1 package packer 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "strconv" 8 "strings" 9 "text/template" 10 "time" 11 12 "github.com/mitchellh/packer/common/uuid" 13 ) 14 15 // InitTime is the UTC time when this package was initialized. It is 16 // used as the timestamp for all configuration templates so that they 17 // match for a single build. 18 var InitTime time.Time 19 20 func init() { 21 InitTime = time.Now().UTC() 22 } 23 24 // ConfigTemplate processes string data as a text/template with some common 25 // elements and functions available. Plugin creators should process as 26 // many fields as possible through this. 27 type ConfigTemplate struct { 28 UserVars map[string]string 29 30 root *template.Template 31 i int 32 } 33 34 // NewConfigTemplate creates a new configuration template processor. 35 func NewConfigTemplate() (*ConfigTemplate, error) { 36 result := &ConfigTemplate{ 37 UserVars: make(map[string]string), 38 } 39 40 result.root = template.New("configTemplateRoot") 41 result.root.Funcs(template.FuncMap{ 42 "env": templateDisableEnv, 43 "pwd": templatePwd, 44 "isotime": templateISOTime, 45 "timestamp": templateTimestamp, 46 "user": result.templateUser, 47 "uuid": templateUuid, 48 "upper": strings.ToUpper, 49 "lower": strings.ToLower, 50 }) 51 52 return result, nil 53 } 54 55 // Process processes a single string, compiling and executing the template. 56 func (t *ConfigTemplate) Process(s string, data interface{}) (string, error) { 57 tpl, err := t.root.New(t.nextTemplateName()).Parse(s) 58 if err != nil { 59 return "", err 60 } 61 62 buf := new(bytes.Buffer) 63 if err := tpl.Execute(buf, data); err != nil { 64 return "", err 65 } 66 67 return buf.String(), nil 68 } 69 70 // Validate the template. 71 func (t *ConfigTemplate) Validate(s string) error { 72 root, err := t.root.Clone() 73 if err != nil { 74 return err 75 } 76 77 _, err = root.New("template").Parse(s) 78 return err 79 } 80 81 // Add additional functions to the template 82 func (t *ConfigTemplate) Funcs(funcs template.FuncMap) { 83 t.root.Funcs(funcs) 84 } 85 86 func (t *ConfigTemplate) nextTemplateName() string { 87 name := fmt.Sprintf("tpl%d", t.i) 88 t.i++ 89 return name 90 } 91 92 // User is the function exposed as "user" within the templates and 93 // looks up user variables. 94 func (t *ConfigTemplate) templateUser(n string) (string, error) { 95 result, ok := t.UserVars[n] 96 if !ok { 97 return "", fmt.Errorf("unknown user var: %s", n) 98 } 99 100 return result, nil 101 } 102 103 func templateDisableEnv(n string) (string, error) { 104 return "", fmt.Errorf( 105 "Environmental variables can only be used as default values for user variables.") 106 } 107 108 func templateDisableUser(n string) (string, error) { 109 return "", fmt.Errorf( 110 "User variable can't be used within a default value for a user variable: %s", n) 111 } 112 113 func templateEnv(n string) string { 114 return os.Getenv(n) 115 } 116 117 func templateISOTime(timeFormat ...string) (string, error) { 118 if len(timeFormat) == 0 { 119 return time.Now().UTC().Format(time.RFC3339), nil 120 } 121 122 if len(timeFormat) > 1 { 123 return "", fmt.Errorf("too many values, 1 needed: %v", timeFormat) 124 } 125 126 return time.Now().UTC().Format(timeFormat[0]), nil 127 } 128 129 func templatePwd() (string, error) { 130 return os.Getwd() 131 } 132 133 func templateTimestamp() string { 134 return strconv.FormatInt(InitTime.Unix(), 10) 135 } 136 137 func templateUuid() string { 138 return uuid.TimeOrderedUUID() 139 }