github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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 "strings" 10 11 "github.com/hashicorp/hil" 12 "github.com/hashicorp/hil/ast" 13 "github.com/hashicorp/terraform/config" 14 "github.com/hashicorp/terraform/helper/pathorcontents" 15 "github.com/hashicorp/terraform/helper/schema" 16 ) 17 18 func dataSourceFile() *schema.Resource { 19 return &schema.Resource{ 20 Read: dataSourceFileRead, 21 22 Schema: map[string]*schema.Schema{ 23 "template": &schema.Schema{ 24 Type: schema.TypeString, 25 Optional: true, 26 Description: "Contents of the template", 27 ConflictsWith: []string{"filename"}, 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 ValidateFunc: validateVarsAttribute, 57 }, 58 "rendered": &schema.Schema{ 59 Type: schema.TypeString, 60 Computed: true, 61 Description: "rendered template", 62 }, 63 }, 64 } 65 } 66 67 func dataSourceFileRead(d *schema.ResourceData, meta interface{}) error { 68 rendered, err := renderFile(d) 69 if err != nil { 70 return err 71 } 72 d.Set("rendered", rendered) 73 d.SetId(hash(rendered)) 74 return nil 75 } 76 77 type templateRenderError error 78 79 func renderFile(d *schema.ResourceData) (string, error) { 80 template := d.Get("template").(string) 81 filename := d.Get("filename").(string) 82 vars := d.Get("vars").(map[string]interface{}) 83 84 contents := template 85 if template == "" && filename != "" { 86 data, _, err := pathorcontents.Read(filename) 87 if err != nil { 88 return "", err 89 } 90 91 contents = data 92 } 93 94 rendered, err := execute(contents, vars) 95 if err != nil { 96 return "", templateRenderError( 97 fmt.Errorf("failed to render %v: %v", filename, err), 98 ) 99 } 100 101 return rendered, nil 102 } 103 104 // execute parses and executes a template using vars. 105 func execute(s string, vars map[string]interface{}) (string, error) { 106 root, err := hil.Parse(s) 107 if err != nil { 108 return "", err 109 } 110 111 varmap := make(map[string]ast.Variable) 112 for k, v := range vars { 113 // As far as I can tell, v is always a string. 114 // If it's not, tell the user gracefully. 115 s, ok := v.(string) 116 if !ok { 117 return "", fmt.Errorf("unexpected type for variable %q: %T", k, v) 118 } 119 varmap[k] = ast.Variable{ 120 Value: s, 121 Type: ast.TypeString, 122 } 123 } 124 125 cfg := hil.EvalConfig{ 126 GlobalScope: &ast.BasicScope{ 127 VarMap: varmap, 128 FuncMap: config.Funcs(), 129 }, 130 } 131 132 result, err := hil.Eval(root, &cfg) 133 if err != nil { 134 return "", err 135 } 136 if result.Type != hil.TypeString { 137 return "", fmt.Errorf("unexpected output hil.Type: %v", result.Type) 138 } 139 140 return result.Value.(string), nil 141 } 142 143 func hash(s string) string { 144 sha := sha256.Sum256([]byte(s)) 145 return hex.EncodeToString(sha[:]) 146 } 147 148 func validateVarsAttribute(v interface{}, key string) (ws []string, es []error) { 149 // vars can only be primitives right now 150 var badVars []string 151 for k, v := range v.(map[string]interface{}) { 152 switch v.(type) { 153 case []interface{}: 154 badVars = append(badVars, fmt.Sprintf("%s (list)", k)) 155 case map[string]interface{}: 156 badVars = append(badVars, fmt.Sprintf("%s (map)", k)) 157 } 158 } 159 if len(badVars) > 0 { 160 es = append(es, fmt.Errorf( 161 "%s: cannot contain non-primitives; bad keys: %s", 162 key, strings.Join(badVars, ", "))) 163 } 164 return 165 }