github.com/keshavdv/terraform@v0.7.0-rc2.0.20160711232630-d69256dcb425/builtin/providers/template/datasource_template_file.go (about) 1 package template 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "fmt" 7 "os" 8 "path/filepath" 9 10 "github.com/hashicorp/hil" 11 "github.com/hashicorp/hil/ast" 12 "github.com/hashicorp/terraform/config" 13 "github.com/hashicorp/terraform/helper/pathorcontents" 14 "github.com/hashicorp/terraform/helper/schema" 15 ) 16 17 func dataSourceFile() *schema.Resource { 18 return &schema.Resource{ 19 Read: dataSourceFileRead, 20 21 Schema: map[string]*schema.Schema{ 22 "template": &schema.Schema{ 23 Type: schema.TypeString, 24 Optional: true, 25 Description: "Contents of the template", 26 ConflictsWith: []string{"filename"}, 27 ValidateFunc: validateTemplateAttribute, 28 }, 29 "filename": &schema.Schema{ 30 Type: schema.TypeString, 31 Optional: true, 32 Description: "file to read template from", 33 // Make a "best effort" attempt to relativize the file path. 34 StateFunc: func(v interface{}) string { 35 if v == nil || v.(string) == "" { 36 return "" 37 } 38 pwd, err := os.Getwd() 39 if err != nil { 40 return v.(string) 41 } 42 rel, err := filepath.Rel(pwd, v.(string)) 43 if err != nil { 44 return v.(string) 45 } 46 return rel 47 }, 48 Deprecated: "Use the 'template' attribute instead.", 49 ConflictsWith: []string{"template"}, 50 }, 51 "vars": &schema.Schema{ 52 Type: schema.TypeMap, 53 Optional: true, 54 Default: make(map[string]interface{}), 55 Description: "variables to substitute", 56 }, 57 "rendered": &schema.Schema{ 58 Type: schema.TypeString, 59 Computed: true, 60 Description: "rendered template", 61 }, 62 }, 63 } 64 } 65 66 func dataSourceFileRead(d *schema.ResourceData, meta interface{}) error { 67 rendered, err := renderFile(d) 68 if err != nil { 69 return err 70 } 71 d.Set("rendered", rendered) 72 d.SetId(hash(rendered)) 73 return nil 74 } 75 76 type templateRenderError error 77 78 func renderFile(d *schema.ResourceData) (string, error) { 79 template := d.Get("template").(string) 80 filename := d.Get("filename").(string) 81 vars := d.Get("vars").(map[string]interface{}) 82 83 if template == "" && filename != "" { 84 template = filename 85 } 86 87 contents, _, err := pathorcontents.Read(template) 88 if err != nil { 89 return "", err 90 } 91 92 rendered, err := execute(contents, vars) 93 if err != nil { 94 return "", templateRenderError( 95 fmt.Errorf("failed to render %v: %v", filename, err), 96 ) 97 } 98 99 return rendered, nil 100 } 101 102 // execute parses and executes a template using vars. 103 func execute(s string, vars map[string]interface{}) (string, error) { 104 root, err := hil.Parse(s) 105 if err != nil { 106 return "", err 107 } 108 109 varmap := make(map[string]ast.Variable) 110 for k, v := range vars { 111 // As far as I can tell, v is always a string. 112 // If it's not, tell the user gracefully. 113 s, ok := v.(string) 114 if !ok { 115 return "", fmt.Errorf("unexpected type for variable %q: %T", k, v) 116 } 117 varmap[k] = ast.Variable{ 118 Value: s, 119 Type: ast.TypeString, 120 } 121 } 122 123 cfg := hil.EvalConfig{ 124 GlobalScope: &ast.BasicScope{ 125 VarMap: varmap, 126 FuncMap: config.Funcs(), 127 }, 128 } 129 130 result, err := hil.Eval(root, &cfg) 131 if err != nil { 132 return "", err 133 } 134 if result.Type != hil.TypeString { 135 return "", fmt.Errorf("unexpected output hil.Type: %v", result.Type) 136 } 137 138 return result.Value.(string), nil 139 } 140 141 func hash(s string) string { 142 sha := sha256.Sum256([]byte(s)) 143 return hex.EncodeToString(sha[:]) 144 } 145 146 func validateTemplateAttribute(v interface{}, key string) (ws []string, es []error) { 147 _, wasPath, err := pathorcontents.Read(v.(string)) 148 if err != nil { 149 es = append(es, err) 150 return 151 } 152 153 if wasPath { 154 ws = append(ws, fmt.Sprintf("%s: looks like you specified a path instead of file contents. Use `file()` to load this path. Specifying a path directly is deprecated and will be removed in a future version.", key)) 155 } 156 157 return 158 }