github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/builtin/providers/template/resource.go (about) 1 package template 2 3 import ( 4 "crypto/sha256" 5 "encoding/hex" 6 "fmt" 7 "io/ioutil" 8 9 "github.com/hashicorp/terraform/config" 10 "github.com/hashicorp/terraform/config/lang" 11 "github.com/hashicorp/terraform/config/lang/ast" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/mitchellh/go-homedir" 14 ) 15 16 func resource() *schema.Resource { 17 return &schema.Resource{ 18 Create: Create, 19 Read: Read, 20 Update: Update, 21 Delete: Delete, 22 Exists: Exists, 23 24 Schema: map[string]*schema.Schema{ 25 "filename": &schema.Schema{ 26 Type: schema.TypeString, 27 Required: true, 28 Description: "file to read template from", 29 }, 30 "vars": &schema.Schema{ 31 Type: schema.TypeMap, 32 Optional: true, 33 Default: make(map[string]interface{}), 34 Description: "variables to substitute", 35 }, 36 "rendered": &schema.Schema{ 37 Type: schema.TypeString, 38 Computed: true, 39 Description: "rendered template", 40 }, 41 }, 42 } 43 } 44 45 func Create(d *schema.ResourceData, meta interface{}) error { return eval(d) } 46 func Update(d *schema.ResourceData, meta interface{}) error { return eval(d) } 47 func Read(d *schema.ResourceData, meta interface{}) error { return nil } 48 func Delete(d *schema.ResourceData, meta interface{}) error { 49 d.SetId("") 50 return nil 51 } 52 func Exists(d *schema.ResourceData, meta interface{}) (bool, error) { 53 // Reload every time in case something has changed. 54 // This should be cheap, and cache invalidation is hard. 55 return false, nil 56 } 57 58 var readfile func(string) ([]byte, error) = ioutil.ReadFile // testing hook 59 60 func eval(d *schema.ResourceData) error { 61 filename := d.Get("filename").(string) 62 vars := d.Get("vars").(map[string]interface{}) 63 64 path, err := homedir.Expand(filename) 65 if err != nil { 66 return err 67 } 68 69 buf, err := readfile(path) 70 if err != nil { 71 return err 72 } 73 74 rendered, err := execute(string(buf), vars) 75 if err != nil { 76 return fmt.Errorf("failed to render %v: %v", filename, err) 77 } 78 79 d.Set("rendered", rendered) 80 d.SetId(hash(rendered)) 81 return nil 82 } 83 84 // execute parses and executes a template using vars. 85 func execute(s string, vars map[string]interface{}) (string, error) { 86 root, err := lang.Parse(s) 87 if err != nil { 88 return "", err 89 } 90 91 varmap := make(map[string]ast.Variable) 92 for k, v := range vars { 93 // As far as I can tell, v is always a string. 94 // If it's not, tell the user gracefully. 95 s, ok := v.(string) 96 if !ok { 97 return "", fmt.Errorf("unexpected type for variable %q: %T", k, v) 98 } 99 varmap[k] = ast.Variable{ 100 Value: s, 101 Type: ast.TypeString, 102 } 103 } 104 105 cfg := lang.EvalConfig{ 106 GlobalScope: &ast.BasicScope{ 107 VarMap: varmap, 108 FuncMap: config.Funcs, 109 }, 110 } 111 112 out, typ, err := lang.Eval(root, &cfg) 113 if err != nil { 114 return "", err 115 } 116 if typ != ast.TypeString { 117 return "", fmt.Errorf("unexpected output ast.Type: %v", typ) 118 } 119 120 return out.(string), nil 121 } 122 123 func hash(s string) string { 124 sha := sha256.Sum256([]byte(s)) 125 return hex.EncodeToString(sha[:])[:20] 126 }