forked from hashicorp/terraform
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes hashicorp#215.
- Loading branch information
Showing
4 changed files
with
158 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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, | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(), | ||
}, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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] | ||
} |