diff --git a/template/datasource_template_file_list.go b/template/datasource_template_file_list.go new file mode 100644 index 00000000..e7cce06c --- /dev/null +++ b/template/datasource_template_file_list.go @@ -0,0 +1,94 @@ +package template + +import ( + "crypto/sha1" + "encoding/hex" + "os" + "path/filepath" + + "github.com/hashicorp/terraform/helper/pathorcontents" + "github.com/hashicorp/terraform/helper/schema" +) + +func dataSourceFileList() *schema.Resource { + return &schema.Resource{ + Read: dataSourceFileListRead, + + Schema: map[string]*schema.Schema{ + "source_dir": { + Type: schema.TypeString, + Description: "Path to the directory where the files reside", + Required: true, + ForceNew: true, + }, + "files": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "content": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceFileListRead(d *schema.ResourceData, meta interface{}) error { + sourceDir := d.Get("source_dir").(string) + + files := make([]map[string]interface{}, 0) + + err := filepath.Walk(sourceDir, func(p string, f os.FileInfo, err error) error { + if err != nil { + return err + } + + if f.IsDir() { + return nil + } + + fileContent, _, inputErr := pathorcontents.Read(p) + if inputErr != nil { + return inputErr + } + + checksum := sha1.Sum([]byte(f.Name() + fileContent)) + fileHash := hex.EncodeToString(checksum[:]) + + file := make(map[string]interface{}) + file["id"] = fileHash + file["name"] = f.Name() + file["content"] = fileContent + + files = append(files, file) + + return nil + }) + if err != nil { + return err + } + + if err := d.Set("files", files); err != nil { + return err + } + + hash, err := generateDirHash(sourceDir) + if err != nil { + return err + } + d.SetId(hash) + + return nil +} diff --git a/template/datasource_template_file_list_test.go b/template/datasource_template_file_list_test.go new file mode 100644 index 00000000..6248391e --- /dev/null +++ b/template/datasource_template_file_list_test.go @@ -0,0 +1,116 @@ +package template + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "testing" + + r "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +type testTemplateFilelist struct { + name string + content string +} + +func testTemplateFilelistWriteFiles(testCase string, files map[string]testTemplateFilelist) (in string, err error) { + in, err = ioutil.TempDir(os.TempDir(), testCase) + if err != nil { + return + } + + for name, file := range files { + path := filepath.Join(in, name) + + err = os.MkdirAll(filepath.Dir(path), 0777) + if err != nil { + return + } + + err = ioutil.WriteFile(path, []byte(file.content), 0777) + if err != nil { + return + } + } + return +} + +func TestTemplateFileListOutput(t *testing.T) { + var cases = []struct { + testCase string + files map[string]testTemplateFilelist + }{ + { + testCase: "terraform_template_file_list_1", + files: map[string]testTemplateFilelist{ + "foo.txt": {"foo.txt", "bar"}, + }, + }, + { + testCase: "terraform_template_file_list_2", + files: map[string]testTemplateFilelist{ + "foo.txt": {"foo.txt", "bar"}, + "nested/monkey.txt": {"monkey.txt", "ooh-ooh-ooh-eee-eee"}, + "cheese.txt": {"cheese.txt", "cheddar"}, + }, + }, + } + + for _, tt := range cases { + // Write the desired templates in a temporary directory. + in, err := testTemplateFilelistWriteFiles(tt.testCase, tt.files) + if err != nil { + t.Skipf("could not write templates to temporary directory: %s", err) + continue + } + defer os.RemoveAll(in) + + // Run test case. + r.UnitTest(t, r.TestCase{ + Providers: testProviders, + Steps: []r.TestStep{ + { + Config: testTemplateFileListConfig(in), + Check: func(s *terraform.State) error { + got := s.RootModule().Outputs["files"].Value.([]interface{}) + + if len(got) != len(tt.files) { + return fmt.Errorf("\ntest case:\n%s\ngot:\n%d files\nwant:\n%d files\n", tt.testCase, len(got), len(tt.files)) + } + + output := make(map[string]string) + for _, v := range got { + f := v.(map[string]interface{}) + output[f["name"].(string)] = f["content"].(string) + } + + for _, file := range tt.files { + content, ok := output[file.name] + if !ok { + return fmt.Errorf("\ntest case:\n%s\nfile missing:\n%s\n", tt.testCase, file.name) + } + + if content != file.content { + return fmt.Errorf("\ntest case:\n%s\ncontent mis-match for file:\n%s\ngot:\n%s\nwant:\n%s\n", tt.testCase, file.name, content, file.content) + } + } + return nil + }, + }, + }, + }) + } +} + +func testTemplateFileListConfig(sourceDir string) string { + return fmt.Sprintf(` + data "template_file_list" "list" { + source_dir = "%s" + } + output "files" { + value = "${data.template_file_list.list.files}" + }`, sourceDir) +} diff --git a/template/provider.go b/template/provider.go index fb340754..be30b308 100644 --- a/template/provider.go +++ b/template/provider.go @@ -9,6 +9,7 @@ func Provider() terraform.ResourceProvider { return &schema.Provider{ DataSourcesMap: map[string]*schema.Resource{ "template_file": dataSourceFile(), + "template_file_list": dataSourceFileList(), "template_cloudinit_config": dataSourceCloudinitConfig(), }, ResourcesMap: map[string]*schema.Resource{ @@ -16,6 +17,10 @@ func Provider() terraform.ResourceProvider { "template_file", dataSourceFile(), ), + "template_file_list": schema.DataSourceResourceShim( + "template_file_list", + dataSourceFileList(), + ), "template_cloudinit_config": schema.DataSourceResourceShim( "template_cloudinit_config", dataSourceCloudinitConfig(),