diff --git a/.gitignore b/.gitignore index 7b993acb..1caeb871 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,6 @@ website/vendor # Test exclusions !command/test-fixtures/**/*.tfstate !command/test-fixtures/**/.terraform/ + +# Archives generated by tests +internal/provider/*.zip diff --git a/internal/provider/archive-content.zip b/internal/provider/archive-content.zip deleted file mode 100644 index a1c53685..00000000 Binary files a/internal/provider/archive-content.zip and /dev/null differ diff --git a/internal/provider/archive-dir.zip b/internal/provider/archive-dir.zip deleted file mode 100644 index b88214af..00000000 Binary files a/internal/provider/archive-dir.zip and /dev/null differ diff --git a/internal/provider/archive-file.zip b/internal/provider/archive-file.zip deleted file mode 100644 index b2a7ea85..00000000 Binary files a/internal/provider/archive-file.zip and /dev/null differ diff --git a/internal/provider/archiver.go b/internal/provider/archiver.go index 7491f98b..feb43b2b 100644 --- a/internal/provider/archiver.go +++ b/internal/provider/archiver.go @@ -7,8 +7,8 @@ import ( type Archiver interface { ArchiveContent(content []byte, infilename string) error - ArchiveFile(infilename string) error - ArchiveDir(indirname string, excludes []string) error + ArchiveFile(prepath string, infilename string) error + ArchiveDir(prepath string, indirname string, excludes []string) error ArchiveMultiple(content map[string][]byte) error } diff --git a/internal/provider/data_source_archive_file.go b/internal/provider/data_source_archive_file.go index fb95166b..11353c04 100644 --- a/internal/provider/data_source_archive_file.go +++ b/internal/provider/data_source_archive_file.go @@ -86,6 +86,12 @@ func dataSourceFile() *schema.Resource { Type: schema.TypeString, }, }, + "prepended_path": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"source_content", "source_content_filename", "source_file"}, + }, "output_path": { Type: schema.TypeString, Required: true, @@ -171,20 +177,25 @@ func archive(d *schema.ResourceData) error { return fmt.Errorf("archive type not supported: %s", archiveType) } + var prepath string + if prepended_path, ok := d.GetOk("prepended_path"); ok { + prepath = prepended_path.(string) + } + if dir, ok := d.GetOk("source_dir"); ok { if excludes, ok := d.GetOk("excludes"); ok { excludeList := expandStringList(excludes.(*schema.Set).List()) - if err := archiver.ArchiveDir(dir.(string), excludeList); err != nil { + if err := archiver.ArchiveDir(prepath, dir.(string), excludeList); err != nil { return fmt.Errorf("error archiving directory: %s", err) } } else { - if err := archiver.ArchiveDir(dir.(string), []string{""}); err != nil { + if err := archiver.ArchiveDir(prepath, dir.(string), []string{""}); err != nil { return fmt.Errorf("error archiving directory: %s", err) } } } else if file, ok := d.GetOk("source_file"); ok { - if err := archiver.ArchiveFile(file.(string)); err != nil { + if err := archiver.ArchiveFile(prepath, file.(string)); err != nil { return fmt.Errorf("error archiving file: %s", err) } } else if filename, ok := d.GetOk("source_content_filename"); ok { diff --git a/internal/provider/data_source_archive_file_test.go b/internal/provider/data_source_archive_file_test.go index 9f23eb1b..10bf261b 100644 --- a/internal/provider/data_source_archive_file_test.go +++ b/internal/provider/data_source_archive_file_test.go @@ -73,6 +73,13 @@ func TestAccArchiveFile_Basic(t *testing.T) { r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), ), }, + { + Config: testAccArchiveFilePrependPath(f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileExists(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, }, }) } @@ -144,6 +151,17 @@ data "archive_file" "foo" { `, filepath.ToSlash(outputPath)) } +func testAccArchiveFilePrependPath(outputPath string) string { + return fmt.Sprintf(` +data "archive_file" "foo" { + type = "zip" + source_dir = "test-fixtures/test-dir" + prepended_path = "pre-dir" + output_path = "%s" +} +`, filepath.ToSlash(outputPath)) +} + func testTempDir(t *testing.T) string { tmp, err := ioutil.TempDir("", "tf") if err != nil { diff --git a/internal/provider/zip_archiver.go b/internal/provider/zip_archiver.go index aa5826ea..c2462bf7 100644 --- a/internal/provider/zip_archiver.go +++ b/internal/provider/zip_archiver.go @@ -5,12 +5,14 @@ import ( "fmt" "io/ioutil" "os" + "path" "path/filepath" "sort" "time" ) type ZipArchiver struct { + prepath string filepath string filewriter *os.File writer *zip.Writer @@ -37,7 +39,7 @@ func (a *ZipArchiver) ArchiveContent(content []byte, infilename string) error { return err } -func (a *ZipArchiver) ArchiveFile(infilename string) error { +func (a *ZipArchiver) ArchiveFile(prepath string, infilename string) error { fi, err := assertValidFile(infilename) if err != nil { return err @@ -84,7 +86,7 @@ func checkMatch(fileName string, excludes []string) (value bool) { return false } -func (a *ZipArchiver) ArchiveDir(indirname string, excludes []string) error { +func (a *ZipArchiver) ArchiveDir(prepath string, indirname string, excludes []string) error { _, err := assertValidDir(indirname) if err != nil { return err @@ -100,13 +102,13 @@ func (a *ZipArchiver) ArchiveDir(indirname string, excludes []string) error { } defer a.close() - return filepath.Walk(indirname, func(path string, info os.FileInfo, err error) error { + return filepath.Walk(indirname, func(file_path string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("error encountered during file walk: %s", err) } - relname, err := filepath.Rel(indirname, path) + relname, err := filepath.Rel(indirname, file_path) if err != nil { return fmt.Errorf("error relativizing file for archival: %s", err) } @@ -132,7 +134,7 @@ func (a *ZipArchiver) ArchiveDir(indirname string, excludes []string) error { if err != nil { return fmt.Errorf("error creating file header: %s", err) } - fh.Name = filepath.ToSlash(relname) + fh.Name = path.Join(prepath, filepath.ToSlash(relname)) fh.Method = zip.Deflate // fh.Modified alone isn't enough when using a zero value fh.SetModTime(time.Time{}) @@ -141,7 +143,7 @@ func (a *ZipArchiver) ArchiveDir(indirname string, excludes []string) error { if err != nil { return fmt.Errorf("error creating file inside archive: %s", err) } - content, err := ioutil.ReadFile(path) + content, err := ioutil.ReadFile(file_path) if err != nil { return fmt.Errorf("error reading file for archival: %s", err) } diff --git a/internal/provider/zip_archiver_test.go b/internal/provider/zip_archiver_test.go index 5429096c..4166c046 100644 --- a/internal/provider/zip_archiver_test.go +++ b/internal/provider/zip_archiver_test.go @@ -5,6 +5,7 @@ import ( "bytes" "io/ioutil" "os" + "path" "path/filepath" "testing" "time" @@ -25,7 +26,7 @@ func TestZipArchiver_Content(t *testing.T) { func TestZipArchiver_File(t *testing.T) { zipfilepath := "archive-file.zip" archiver := NewZipArchiver(zipfilepath) - if err := archiver.ArchiveFile("./test-fixtures/test-file.txt"); err != nil { + if err := archiver.ArchiveFile("", "./test-fixtures/test-file.txt"); err != nil { t.Fatalf("unexpected error: %s", err) } @@ -41,7 +42,7 @@ func TestZipArchiver_FileModified(t *testing.T) { var zip = func() { archiver := NewZipArchiver(zipFilePath) - if err := archiver.ArchiveFile(toZipPath); err != nil { + if err := archiver.ArchiveFile("", toZipPath); err != nil { t.Fatalf("unexpected error: %s", err) } } @@ -74,7 +75,7 @@ func TestZipArchiver_FileModified(t *testing.T) { func TestZipArchiver_Dir(t *testing.T) { zipfilepath := "archive-dir.zip" archiver := NewZipArchiver(zipfilepath) - if err := archiver.ArchiveDir("./test-fixtures/test-dir", []string{""}); err != nil { + if err := archiver.ArchiveDir("", "./test-fixtures/test-dir", []string{""}); err != nil { t.Fatalf("unexpected error: %s", err) } @@ -88,7 +89,7 @@ func TestZipArchiver_Dir(t *testing.T) { func TestZipArchiver_Dir_Exclude(t *testing.T) { zipfilepath := "archive-dir.zip" archiver := NewZipArchiver(zipfilepath) - if err := archiver.ArchiveDir("./test-fixtures/test-dir", []string{"file2.txt"}); err != nil { + if err := archiver.ArchiveDir("", "./test-fixtures/test-dir", []string{"file2.txt"}); err != nil { t.Fatalf("unexpected error: %s", err) } @@ -101,7 +102,7 @@ func TestZipArchiver_Dir_Exclude(t *testing.T) { func TestZipArchiver_Dir_Exclude_With_Directory(t *testing.T) { zipfilepath := "archive-dir.zip" archiver := NewZipArchiver(zipfilepath) - if err := archiver.ArchiveDir("./test-fixtures/", []string{"test-dir", "test-dir2/file2.txt"}); err != nil { + if err := archiver.ArchiveDir("", "./test-fixtures/", []string{"test-dir", "test-dir2/file2.txt"}); err != nil { t.Fatalf("unexpected error: %s", err) } @@ -129,6 +130,21 @@ func TestZipArchiver_Multiple(t *testing.T) { } +func TestZipArchiver_Prepend_Path(t *testing.T) { + prepended_path := "prepath" + zipfilepath := "archive-pre-path.zip" + archiver := NewZipArchiver(zipfilepath) + if err := archiver.ArchiveDir(prepended_path, "./test-fixtures/", []string{"test-dir", "test-dir2/file2.txt"}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureContents(t, zipfilepath, map[string][]byte{ + path.Join(prepended_path, "test-dir2/file1.txt"): []byte("This is file 1"), + path.Join(prepended_path, "test-dir2/file3.txt"): []byte("This is file 3"), + path.Join(prepended_path, "test-file.txt"): []byte("This is test content"), + }) +} + func ensureContents(t *testing.T, zipfilepath string, wants map[string][]byte) { r, err := zip.OpenReader(zipfilepath) if err != nil { diff --git a/website/docs/d/archive_file.html.markdown b/website/docs/d/archive_file.html.markdown index 48c9c503..5540d21b 100644 --- a/website/docs/d/archive_file.html.markdown +++ b/website/docs/d/archive_file.html.markdown @@ -70,6 +70,8 @@ NOTE: One of `source`, `source_content_filename` (with `source_content`), `sourc * `excludes` - (Optional) Specify files to ignore when reading the `source_dir`. +* `prepended_path` - (Optional) Specifies the path prefix to prepend to `source_file` or `source_dir` in the archive path. + The `source` block supports the following: * `content` - (Required) Add this content to the archive with `filename` as the filename.