Skip to content

Commit

Permalink
providers: add template provider
Browse files Browse the repository at this point in the history
  • Loading branch information
josharian committed May 1, 2015
1 parent 15c75c5 commit 745d83a
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 0 deletions.
12 changes: 12 additions & 0 deletions builtin/bins/provider-template/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
"github.com/hashicorp/terraform/builtin/providers/template"
"github.com/hashicorp/terraform/plugin"
)

func main() {
plugin.Serve(&plugin.ServeOpts{
ProviderFunc: template.Provider,
})
}
14 changes: 14 additions & 0 deletions builtin/providers/template/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package template

import (
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/terraform"
)

func Provider() terraform.ResourceProvider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"template_file": resource(),
},
}
}
13 changes: 13 additions & 0 deletions builtin/providers/template/provider_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package template

import (
"testing"

"github.com/hashicorp/terraform/helper/schema"
)

func TestProvider(t *testing.T) {
if err := Provider().(*schema.Provider).InternalValidate(); err != nil {
t.Fatalf("err: %s", err)
}
}
119 changes: 119 additions & 0 deletions builtin/providers/template/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package template

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io/ioutil"

"github.com/hashicorp/terraform/config"
"github.com/hashicorp/terraform/config/lang"
"github.com/hashicorp/terraform/config/lang/ast"
"github.com/hashicorp/terraform/helper/schema"
)

func resource() *schema.Resource {
return &schema.Resource{
Create: Create,
Read: Read,
Update: Update,
Delete: Delete,
Exists: Exists,

Schema: map[string]*schema.Schema{
"filename": &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "file to read template from",
},
"vars": &schema.Schema{
Type: schema.TypeMap,
Optional: true,
Default: make(map[string]interface{}),
Description: "variables to substitute",
},
"rendered": &schema.Schema{
Type: schema.TypeString,
Computed: true,
Default: nil,
Description: "rendered template",
},
},
}
}

func Create(d *schema.ResourceData, meta interface{}) error { return eval(d) }
func Update(d *schema.ResourceData, meta interface{}) error { return eval(d) }
func Read(d *schema.ResourceData, meta interface{}) error { return nil }
func Delete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}
func Exists(d *schema.ResourceData, meta interface{}) (bool, error) {
// Reload every time in case something has changed.
// This should be cheap, and cache invalidation is hard.
return false, nil
}

func eval(d *schema.ResourceData) error {
filename := d.Get("filename").(string)
vars := d.Get("vars").(map[string]interface{})

buf, err := ioutil.ReadFile(filename)
if err != nil {
return err
}

rendered, err := execute(string(buf), vars)
if err != nil {
return fmt.Errorf("failed to render %v: %v", filename, err)
}

d.Set("rendered", rendered)
d.SetId(hash(rendered))
return nil
}

// execute parses and executes a template using vars.
func execute(s string, vars map[string]interface{}) (string, error) {
root, err := lang.Parse(s)
if err != nil {
return "", err
}

varmap := make(map[string]ast.Variable)
for k, v := range vars {
// As far as I can tell, v is always a string.
// If it's not, tell the user gracefully.
s, ok := v.(string)
if !ok {
return "", fmt.Errorf("unexpected type for variable %q: %T", k, v)
}
varmap[k] = ast.Variable{
Value: s,
Type: ast.TypeString,
}
}

cfg := lang.EvalConfig{
GlobalScope: &ast.BasicScope{
VarMap: varmap,
FuncMap: config.Funcs,
},
}

out, typ, err := lang.Eval(root, &cfg)
if err != nil {
return "", err
}
if typ != ast.TypeString {
return "", fmt.Errorf("unexpected output ast.Type: %v", typ)
}

return out.(string), nil
}

func hash(s string) string {
sha := sha256.Sum256([]byte(s))
return hex.EncodeToString(sha[:])[:20]
}

0 comments on commit 745d83a

Please sign in to comment.