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  }