From 140df95eaacb63e024a4e8f3c051c54ede6af68c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 21 Nov 2023 08:52:47 +0100 Subject: [PATCH 01/33] Add format tar.gz --- internal/provider/archiver.go | 3 +- internal/provider/data_source_archive_file.go | 2 +- .../provider/data_source_archive_file_test.go | 357 +++++++++------- internal/provider/tar_archiver.go | 273 ++++++++++++ internal/provider/tar_archiver_test.go | 402 ++++++++++++++++++ 5 files changed, 891 insertions(+), 146 deletions(-) create mode 100644 internal/provider/tar_archiver.go create mode 100644 internal/provider/tar_archiver_test.go diff --git a/internal/provider/archiver.go b/internal/provider/archiver.go index 5cd4794b..74ea4cad 100644 --- a/internal/provider/archiver.go +++ b/internal/provider/archiver.go @@ -24,7 +24,8 @@ type Archiver interface { type ArchiverBuilder func(outputPath string) Archiver var archiverBuilders = map[string]ArchiverBuilder{ - "zip": NewZipArchiver, + "zip": NewZipArchiver, + "tar.gz": NewTarGzArchiver, } func getArchiver(archiveType string, outputPath string) Archiver { diff --git a/internal/provider/data_source_archive_file.go b/internal/provider/data_source_archive_file.go index 5a21511b..6a03a8ef 100644 --- a/internal/provider/data_source_archive_file.go +++ b/internal/provider/data_source_archive_file.go @@ -80,7 +80,7 @@ func (d *archiveFileDataSource) Schema(ctx context.Context, req datasource.Schem Computed: true, }, "type": schema.StringAttribute{ - Description: "The type of archive to generate. NOTE: `zip` is supported.", + Description: "The type of archive to generate. NOTE: `zip` and `tar.gz` is supported.", Required: true, }, "source_content": schema.StringAttribute{ diff --git a/internal/provider/data_source_archive_file_test.go b/internal/provider/data_source_archive_file_test.go index 8ec32870..d25ce494 100644 --- a/internal/provider/data_source_archive_file_test.go +++ b/internal/provider/data_source_archive_file_test.go @@ -15,105 +15,166 @@ import ( ) func TestAccArchiveFile_Basic(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveFileContentConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_md5", "ea35f0444ea9a3d5641d8760bc2815cc", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha", "019c79c4dc14dbe1edb3e467b2de6a6aad148717", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha256", "3fb55c931a048943b8d7558dde7c2e4bfc8e04be33b1b55691053d1352391fa7", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha256", "P7VckxoEiUO411WN3nwuS/yOBL4zsbVWkQU9E1I5H6c=", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha512", "57e2d073dce214609bd61113b90b0b2b7c75034047224d56e35f363c8f2662e3acd561eebf94826a67453411181eca7e1cbf15db1f2fdd496cf13df46b7848c3", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha512", "V+LQc9ziFGCb1hETuQsLK3x1A0BHIk1W4182PI8mYuOs1WHuv5SCamdFNBEYHsp+HL8V2x8v3Uls8T30a3hIww==", - ), - ), + hashes := map[string]map[string]map[string]string{ + "content.txt": { + "zip": { + "md5": "ea35f0444ea9a3d5641d8760bc2815cc", + "sha": "019c79c4dc14dbe1edb3e467b2de6a6aad148717", + "sha256": "3fb55c931a048943b8d7558dde7c2e4bfc8e04be33b1b55691053d1352391fa7", + "base64sha256": "P7VckxoEiUO411WN3nwuS/yOBL4zsbVWkQU9E1I5H6c=", + "sha512": "57e2d073dce214609bd61113b90b0b2b7c75034047224d56e35f363c8f2662e3acd561eebf94826a67453411181eca7e1cbf15db1f2fdd496cf13df46b7848c3", + "base64sha512": "V+LQc9ziFGCb1hETuQsLK3x1A0BHIk1W4182PI8mYuOs1WHuv5SCamdFNBEYHsp+HL8V2x8v3Uls8T30a3hIww==", }, - { - Config: testAccArchiveFileFileConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_md5", "59fbc9e62af3cbc2f588f97498240dae", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha", "ce4ee1450ab93ac86e11446649e44cea907b6568", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha256", "5131387f97167da47aa741df3ab2c82f182f17c514c222538d34708d04e0756b", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha256", "UTE4f5cWfaR6p0HfOrLILxgvF8UUwiJTjTRwjQTgdWs=", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha512", "eb33eb0f8cd8efe1a5a0b99acbd22ed22dbebb80817f8de6e8fed15c21c52240838d9bb46fb0938846c74f694425551ba60829a6396f91fcfe49d21a1e3bb409", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha512", "6zPrD4zY7+GloLmay9Iu0i2+u4CBf43m6P7RXCHFIkCDjZu0b7CTiEbHT2lEJVUbpggppjlvkfz+SdIaHju0CQ==", - ), - ), + "tar.gz": { + "md5": "a09ee39e708c38ccd9ba44cc39e7cacc", + "sha": "6c84af188d367644731196007301c9dc93914b0e", + "sha256": "ae18ec27af576dd62f29cec7ae0df130e7487c1a3cddefdec9f27d5ed3a4ca95", + "base64sha256": "rhjsJ69XbdYvKc7Hrg3xMOdIfBo83e/eyfJ9XtOkypU=", + "sha512": "ce93a8ba072bad42656a41def0c9ed160f9109c1a7087fb0dcf0b9fce9effc25477f9cdbf9cbc5aa593f3ded0e0db11d2c8cf67dc8d2693ff4069aa01071e68d", + "base64sha512": "zpOougcrrUJlakHe8MntFg+RCcGnCH+w3PC5/Onv/CVHf5zb+cvFqlk/Pe0ODbEdLIz2fcjSaT/0BpqgEHHmjQ==", }, - { - Config: testAccArchiveFileDirConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_md5", "b73f64a383716070aa4a29563b8b14d4", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha", "76d20a402eefd1cfbdc47886abd4e0909616c191", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha256", "c9d07cc2dabc9caf6f43bed51fa613c281e6ca58cae3a8d6fae2094b00b3369a", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha256", "ydB8wtq8nK9vQ77VH6YTwoHmyljK46jW+uIJSwCzNpo=", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha512", "b96ac6b9554a04473a733be36f190422bf7162b4afdb211a0f551713eadf4092459426750646c70383ce6c8b89171b88582a608e5841bfaaafa17004a2a2ca0a", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha512", "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", - ), - ), + }, + "test-file.txt": { + "zip": { + "md5": "59fbc9e62af3cbc2f588f97498240dae", + "sha": "ce4ee1450ab93ac86e11446649e44cea907b6568", + "sha256": "5131387f97167da47aa741df3ab2c82f182f17c514c222538d34708d04e0756b", + "base64sha256": "UTE4f5cWfaR6p0HfOrLILxgvF8UUwiJTjTRwjQTgdWs=", + "sha512": "eb33eb0f8cd8efe1a5a0b99acbd22ed22dbebb80817f8de6e8fed15c21c52240838d9bb46fb0938846c74f694425551ba60829a6396f91fcfe49d21a1e3bb409", + "base64sha512": "6zPrD4zY7+GloLmay9Iu0i2+u4CBf43m6P7RXCHFIkCDjZu0b7CTiEbHT2lEJVUbpggppjlvkfz+SdIaHju0CQ==", }, - { - Config: testAccArchiveFileDirExcludesConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - ), + "tar.gz": { + "md5": "39948f8ddedc8914ac2e42dd18dd3c06", + "sha": "d2f26a69cbb920715f797f81c1477c41d8fc9195", + "sha256": "6f1b1a5d17e42fae154f0bdf9301a0ad43394d7fe8485b64dfdb533a0cf07784", + "base64sha256": "bxsaXRfkL64VTwvfkwGgrUM5TX/oSFtk39tTOgzwd4Q=", + "sha512": "e0dc636c3d11095a5b5e97c598185ee3d8a0ed7d1accb69cc28419aeeaeda22b2e774a260f71892a2e85efae1a3aee36669b61dafaed9ac0886d2ca8c5add6e9", + "base64sha512": "4NxjbD0RCVpbXpfFmBhe49ig7X0azLacwoQZrurtoisud0omD3GJKi6F764aOu42Zpth2vrtmsCIbSyoxa3W6Q==", }, - { - Config: testAccArchiveFileMultiSourceConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - ), + }, + "test-dir1": { + "zip": { + "md5": "b73f64a383716070aa4a29563b8b14d4", + "sha": "76d20a402eefd1cfbdc47886abd4e0909616c191", + "sha256": "c9d07cc2dabc9caf6f43bed51fa613c281e6ca58cae3a8d6fae2094b00b3369a", + "base64sha256": "ydB8wtq8nK9vQ77VH6YTwoHmyljK46jW+uIJSwCzNpo=", + "sha512": "b96ac6b9554a04473a733be36f190422bf7162b4afdb211a0f551713eadf4092459426750646c70383ce6c8b89171b88582a608e5841bfaaafa17004a2a2ca0a", + "base64sha512": "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", + }, + "tar.gz": { + "md5": "56d3b5da9e46596a7037008224a3535d", + "sha": "d1934f7c5cd325b2c1b3128621e3041c931d5706", + "sha256": "0496affbe2b4dcde9cd3e49665b351a1ef7633faa35bb908de617b155f74f123", + "base64sha256": "BJav++K03N6c0+SWZbNRoe92M/qjW7kI3mF7FV908SM=", + "sha512": "5ddacbb55be1dc431cef74ab65ad8059a51990eb4cf108f679b79a59004a586cdd0b1bb0d869fa1fe679fa71a1d85391c19413a2d79050df68703aa3390eabee", + "base64sha512": "XdrLtVvh3EMc73SrZa2AWaUZkOtM8Qj2ebeaWQBKWGzdCxuw2Gn6H+Z5+nGh2FORwZQToteQUN9ocDqjOQ6r7g==", }, }, - }) + } + + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "file_acc_test."+format) + + var fileSize string + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveFileContentConfig(format, f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", hashes["content.txt"][format]["md5"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", hashes["content.txt"][format]["sha"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", hashes["content.txt"][format]["sha256"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", hashes["content.txt"][format]["base64sha256"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", hashes["content.txt"][format]["sha512"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", hashes["content.txt"][format]["base64sha512"], + ), + ), + }, + { + Config: testAccArchiveFileFileConfig(format, f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", hashes["test-file.txt"][format]["md5"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", hashes["test-file.txt"][format]["sha"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", hashes["test-file.txt"][format]["sha256"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", hashes["test-file.txt"][format]["base64sha256"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", hashes["test-file.txt"][format]["sha512"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", hashes["test-file.txt"][format]["base64sha512"], + ), + ), + }, + { + Config: testAccArchiveFileDirConfig(format, f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", hashes["test-dir1"][format]["md5"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", hashes["test-dir1"][format]["sha"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", hashes["test-dir1"][format]["sha256"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", hashes["test-dir1"][format]["base64sha256"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", hashes["test-dir1"][format]["sha512"], + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", hashes["test-dir1"][format]["base64sha512"], + ), + ), + }, + { + Config: testAccArchiveFileDirExcludesConfig(format, f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileMultiSourceConfig(format, f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + }, + }) + }) + } } func TestDataSource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { @@ -132,7 +193,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileContentConfig(f), + Config: testAccArchiveFileContentConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -149,7 +210,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileContentConfig(f), + Config: testAccArchiveFileContentConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -183,7 +244,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_FileConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileFileConfig(f), + Config: testAccArchiveFileFileConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -200,7 +261,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_FileConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileFileConfig(f), + Config: testAccArchiveFileFileConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -234,7 +295,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileDirConfig(f), + Config: testAccArchiveFileDirConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -251,7 +312,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileDirConfig(f), + Config: testAccArchiveFileDirConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -285,7 +346,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirExcludesConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileDirExcludesConfig(f), + Config: testAccArchiveFileDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), testExtractResourceAttr("data.archive_file.foo", "output_sha", &outputSha), @@ -294,7 +355,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirExcludesConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileDirExcludesConfig(f), + Config: testAccArchiveFileDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_sha", &outputSha), @@ -320,7 +381,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileMultiSourceConfig(f), + Config: testAccArchiveFileMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), testExtractResourceAttr("data.archive_file.foo", "output_sha", &outputSha), @@ -329,7 +390,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileMultiSourceConfig(f), + Config: testAccArchiveFileMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_sha", &outputSha), @@ -340,27 +401,35 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { } func TestAccArchiveFile_SourceConfigMissing(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigMissing(), - ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), - }, - }, - }) + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing(format), + ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), + }, + }, + }) + }) + } } func TestAccArchiveFile_SourceConfigConflicting(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigConflicting(), - ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), - }, - }, - }) + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigConflicting(format), + ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), + }, + }, + }) + }) + } } // TestAccArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an @@ -1307,54 +1376,54 @@ func testAccArchiveFileSize(filename string, fileSize *string) r.TestCheckFunc { } } -func testAccArchiveFileContentConfig(outputPath string) string { +func testAccArchiveFileContentConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_content = "This is some content" source_content_filename = "content.txt" output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileFileConfig(outputPath string) string { +func testAccArchiveFileFileConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_file = "test-fixtures/test-dir/test-file.txt" output_path = "%s" output_file_mode = "0666" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileDirConfig(outputPath string) string { +func testAccArchiveFileDirConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_dir = "test-fixtures/test-dir/test-dir1" output_path = "%s" output_file_mode = "0666" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileDirExcludesConfig(outputPath string) string { +func testAccArchiveFileDirExcludesConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_dir = "test-fixtures/test-dir/test-dir1" excludes = ["test-fixtures/test-dir/test-dir1/file2.txt"] output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileMultiSourceConfig(outputPath string) string { +func testAccArchiveFileMultiSourceConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source { filename = "content_1.txt" content = "This is the content for content_1.txt" @@ -1365,22 +1434,22 @@ data "archive_file" "foo" { } output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveSourceConfigMissing() string { - return ` +func testAccArchiveSourceConfigMissing(format string) string { + return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" output_path = "path" } -` +`, format) } -func testAccArchiveSourceConfigConflicting() string { - return ` +func testAccArchiveSourceConfigConflicting(format string) string { + return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source { filename = "content_1.txt" content = "This is the content for content_1.txt" @@ -1388,7 +1457,7 @@ data "archive_file" "foo" { source_dir = "test-fixtures/test-dir" output_path = "path" } -` +`, format) } //nolint:unparam diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go new file mode 100644 index 00000000..ea4c4457 --- /dev/null +++ b/internal/provider/tar_archiver.go @@ -0,0 +1,273 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "archive/tar" + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strconv" + "time" +) + +type TarCompressionType int + +const ( + TarCompressionGz TarCompressionType = iota +) + +type TarArchiver struct { + compression TarCompressionType + filepath string + outputFileMode string // Default value "" means unset + filewriter *os.File + tarWriter *tar.Writer + compressionWriter io.WriteCloser +} + +func NewTarGzArchiver(filepath string) Archiver { + return NewTarArchiver(filepath, TarCompressionGz) +} + +func NewTarArchiver(filepath string, compression TarCompressionType) Archiver { + return &TarArchiver{ + filepath: filepath, + compression: compression, + } +} + +func (a *TarArchiver) ArchiveContent(content []byte, infilename string) error { + if err := a.open(); err != nil { + return err + } + defer a.close() + + return a.addContent(content, &tar.Header{ + Name: infilename, + Size: int64(len(content)), + ModTime: time.Time{}, + }) +} + +func (a *TarArchiver) ArchiveFile(infilename string) error { + fi, err := assertValidFile(infilename) + if err != nil { + return err + } + + if err := a.open(); err != nil { + return err + } + defer a.close() + + if err := a.addFile(infilename, filepath.ToSlash(fi.Name()), time.Time{}); err != nil { + return err + } + + return err +} + +func (a *TarArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { + _, err := assertValidDir(indirname) + if err != nil { + return err + } + + // ensure exclusions are OS compatible paths + for i := range opts.Excludes { + opts.Excludes[i] = filepath.FromSlash(opts.Excludes[i]) + } + + if err := a.open(); err != nil { + return err + } + defer a.close() + + return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts)) +} + +func (a *TarArchiver) createWalkFunc(basePath string, indirname string, opts ArchiveDirOpts) func(path string, info os.FileInfo, err error) error { + return func(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) + if err != nil { + return fmt.Errorf("error relativizing file for archival: %s", err) + } + + archivePath := filepath.Join(basePath, relname) + + isMatch := checkMatch(archivePath, opts.Excludes) + + if info.IsDir() { + if isMatch { + return filepath.SkipDir + } + return nil + } + + if isMatch { + return nil + } + + if err != nil { + return err + } + + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + if !opts.ExcludeSymlinkDirectories { + path, err = filepath.EvalSymlinks(path) + if err != nil { + return err + } + + info, err = os.Stat(path) + if err != nil { + return err + } + + if info.IsDir() { + return filepath.Walk(path, a.createWalkFunc(archivePath, path, opts)) + } + } + } + + return a.addFile(path, archivePath, info.ModTime()) + } +} + +func (a *TarArchiver) ArchiveMultiple(content map[string][]byte) error { + if err := a.open(); err != nil { + return err + } + defer a.close() + + // Ensure files are processed in the same order so hashes don't change + keys := make([]string, len(content)) + i := 0 + for k := range content { + keys[i] = k + i++ + } + sort.Strings(keys) + + for _, filename := range keys { + header := &tar.Header{ + Name: filepath.ToSlash(filename), + Size: int64(len(content[filename])), + ModTime: time.Now().Round(time.Second), + } + + if err := a.addContent(content[filename], header); err != nil { + return err + } + } + return nil +} + +func (a *TarArchiver) SetOutputFileMode(outputFileMode string) { + a.outputFileMode = outputFileMode +} + +func (a *TarArchiver) open() error { + file, err := os.Create(filepath.ToSlash(a.filepath)) + if err != nil { + return err + } + + switch a.compression { + case TarCompressionGz: + a.compressionWriter = gzip.NewWriter(file) + } + + a.tarWriter = tar.NewWriter(a.compressionWriter) + return nil +} + +func (a *TarArchiver) close() { + if a.filewriter != nil { + a.filewriter.Close() + a.filewriter = nil + } + if a.tarWriter != nil { + a.tarWriter.Close() + a.tarWriter = nil + } + if a.compressionWriter != nil { + a.compressionWriter.Close() + a.compressionWriter = nil + } +} + +func (a *TarArchiver) addFile(filePath, fileName string, modTime time.Time) error { + file, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("could not open file '%s', got error '%w'", filePath, err) + } + defer file.Close() + + stat, err := file.Stat() + if err != nil { + return errors.New(fmt.Sprintf("Could not get stat for file '%s', got error '%s'", filePath, err.Error())) + } + + header := &tar.Header{ + Name: fileName, + Size: stat.Size(), + Mode: int64(stat.Mode()), + ModTime: modTime, + } + + if a.outputFileMode != "" { + filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) + if err != nil { + return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode) + } + header.Mode = filemode + } + + err = a.tarWriter.WriteHeader(header) + if err != nil { + return fmt.Errorf("could not write header for file '%s', got error '%w'", filePath, err) + } + + _, err = io.Copy(a.tarWriter, file) + if err != nil { + return fmt.Errorf("error reading file for archival: %s", err) + } + + return nil +} + +func (a *TarArchiver) addContent(content []byte, header *tar.Header) error { + if header == nil { + return errors.New("tar.Header is nil") + } + + if a.outputFileMode != "" { + filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) + if err != nil { + return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode) + } + header.Mode = filemode + } + + if err := a.tarWriter.WriteHeader(header); err != nil { + return errors.New(fmt.Sprintf("Could not write header, got error '%s'", err.Error())) + } + + _, err := a.tarWriter.Write(content) + if err != nil { + return errors.New(fmt.Sprintf("Could not copy data to the tarball, got error '%s'", err.Error())) + } + + return nil +} diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go new file mode 100644 index 00000000..2a7ba238 --- /dev/null +++ b/internal/provider/tar_archiver_test.go @@ -0,0 +1,402 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "io" + "os" + "path/filepath" + "regexp" + "strconv" + "testing" + "time" +) + +func TestTarArchiver_Content(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-content.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveContent([]byte("This is some content"), "content.txt"); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "content.txt": []byte("This is some content"), + }) +} + +func TestTarArchiver_File(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-file.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveFile("./test-fixtures/test-dir/test-file.txt"); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-file.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_FileMode(t *testing.T) { + file, err := os.CreateTemp("", "archive-file-mode-test.tar.gz") + if err != nil { + t.Fatal(err) + } + + var ( + tarFilePath = file.Name() + toTarPath = filepath.FromSlash("./test-fixtures/test-dir/test-file.txt") + ) + + stringArray := [5]string{"0444", "0644", "0666", "0744", "0777"} + for _, element := range stringArray { + archiver := NewTarGzArchiver(tarFilePath) + archiver.SetOutputFileMode(element) + if err := archiver.ArchiveFile(toTarPath); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarFileMode(t, tarFilePath, element) + } +} + +func TestTarArchiver_FileModified(t *testing.T) { + var ( + tarFilePath = filepath.Join(t.TempDir(), "archive-file-modified.tar.gz") + toTarPath = filepath.FromSlash("./test-fixtures/test-dir/test-file.txt") + ) + + var tarFunc = func() { + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveFile(toTarPath); err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + + tarFunc() + + expectedContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // touch file modified, in the future just in case of weird race issues + newTime := time.Now().Add(1 * time.Hour) + if err := os.Chtimes(toTarPath, newTime, newTime); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + tarFunc() + + actualContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !bytes.Equal(expectedContents, actualContents) { + t.Fatalf("tar contents do not match, potentially a modified time issue") + } +} + +func TestTarArchiver_Dir(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir/test-dir1", ArchiveDirOpts{}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file2.txt": []byte("This is file 2"), + "file3.txt": []byte("This is file 3"), + }) +} + +func TestTarArchiver_Dir_Exclude(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-exclude.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir/test-dir1", ArchiveDirOpts{ + Excludes: []string{"file2.txt"}, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file3.txt": []byte("This is file 3"), + }) +} + +func TestTarArchiver_Dir_Exclude_With_Directory(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-exclude-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir", ArchiveDirOpts{ + Excludes: []string{"test-dir1", "test-dir2/file2.txt"}, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir2/file1.txt": []byte("This is file 1"), + "test-dir2/file3.txt": []byte("This is file 3"), + "test-file.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Multiple(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-content.tar.gz") + + content := map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file2.txt": []byte("This is file 2"), + "file3.txt": []byte("This is file 3"), + } + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveMultiple(content); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, content) +} + +func TestTarArchiver_Dir_With_Symlink_File(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-file.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir-with-symlink-file", ArchiveDirOpts{}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-file.txt": []byte("This is test content"), + "test-symlink.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Dir_DoNotExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + ExcludeSymlinkDirectories: true, + }) + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + found := regex.Match([]byte(err.Error())) + + if !found { + t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + } +} + +func TestTarArchiver_Dir_Exclude_DoNotExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + Excludes: []string{ + "test-symlink-dir/file1.txt", + "test-symlink-dir-with-symlink-file/test-symlink.txt", + }, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + Excludes: []string{ + "test-dir/test-dir1/file1.txt", + "test-symlink-dir-with-symlink-file/test-symlink.txt", + }, + ExcludeSymlinkDirectories: true, + }) + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + found := regex.Match([]byte(err.Error())) + + if !found { + t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + } +} + +func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte) { + t.Helper() + + f, err := os.Open(tarfilepath) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + defer f.Close() + + gzf, err := gzip.NewReader(f) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + + tarReader := tar.NewReader(gzf) + + i := 0 + for { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + t.Fatal(err) + } + + if len(wants) < i+1 { + t.Fatalf("unexpect file count in tar. expect %d", len(wants)) + } + + name := header.Name + + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + buf := bytes.Buffer{} + io.Copy(&buf, tarReader) + + wantFile, ok := wants[name] + if !ok { + t.Fatalf("missing file %s in tar", name) + } + + wantContent := string(wantFile) + gotContent := buf.String() + if gotContent != wantContent { + t.Errorf("mismatched content\ngot\n%s\nwant\n%s", gotContent, wantContent) + } + + default: + t.Fatalf("%s : %c %s %s\n", + "Yikes! Unable to figure out type", + header.Typeflag, + "in file", + name, + ) + } + + i++ + } +} + +func ensureTarFileMode(t *testing.T, tarfilepath string, outputFileMode string) { + t.Helper() + + f, err := os.Open(tarfilepath) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + defer f.Close() + + gzf, err := gzip.NewReader(f) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + + tarReader := tar.NewReader(gzf) + + filemode, err := strconv.ParseUint(outputFileMode, 0, 32) + if err != nil { + t.Fatalf("error parsing outputFileMode value: %s", outputFileMode) + } + + var osfilemode = os.FileMode(filemode) + + i := 0 + for { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + t.Fatal(err) + } + + name := header.Name + + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + if header.FileInfo().Mode() != osfilemode { + t.Fatalf("Expected filemode \"%s\" but was \"%s\"", osfilemode, header.FileInfo().Mode()) + } + default: + t.Fatalf("%s : %c %s %s\n", + "Yikes! Unable to figure out type", + header.Typeflag, + "in file", + name, + ) + } + + i++ + } +} From 00ebf197ddab57d64fb6ffda9607ce8317f36312 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sun, 11 Feb 2024 08:20:58 +0100 Subject: [PATCH 02/33] add doc note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- docs/data-sources/file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data-sources/file.md b/docs/data-sources/file.md index 19bde89d..a8c58e56 100644 --- a/docs/data-sources/file.md +++ b/docs/data-sources/file.md @@ -58,7 +58,7 @@ data "archive_file" "lambda_my_function" { ### Required - `output_path` (String) The output of the archive file. -- `type` (String) The type of archive to generate. NOTE: `zip` is supported. +- `type` (String) The type of archive to generate. NOTE: `zip` or `tar.gz` is supported. ### Optional From 9f9155e4a07a392fd15a9c12c5ad09f3b82258d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 10 May 2024 11:48:16 +0200 Subject: [PATCH 03/33] fix: inconsistent output with multiple files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver.go | 2 +- internal/provider/tar_archiver_test.go | 36 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index ea4c4457..faf51e98 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -163,7 +163,7 @@ func (a *TarArchiver) ArchiveMultiple(content map[string][]byte) error { header := &tar.Header{ Name: filepath.ToSlash(filename), Size: int64(len(content[filename])), - ModTime: time.Now().Round(time.Second), + ModTime: time.Time{}, } if err := a.addContent(content[filename], header); err != nil { diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 2a7ba238..71a5af58 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -168,6 +168,42 @@ func TestTarArchiver_Multiple(t *testing.T) { ensureTarContents(t, tarFilePath, content) } +func TestTarArchiver_Multiple_NoChange(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-content.tar.gz") + + content := map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file2.txt": []byte("This is file 2"), + "file3.txt": []byte("This is file 3"), + } + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveMultiple(content); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + time.Sleep(1 * time.Second) + + archiver = NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveMultiple(content); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + actualContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !bytes.Equal(expectedContents, actualContents) { + t.Fatalf("tar contents do not match, potentially a modified time issue") + } +} + func TestTarArchiver_Dir_With_Symlink_File(t *testing.T) { tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-file.tar.gz") From d3a8956cfaa5497fad4b5d5d09238a330dab2333 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 22 Jul 2024 21:51:46 +0200 Subject: [PATCH 04/33] fix lint issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/archiver.go | 12 +++++++----- internal/provider/tar_archiver.go | 8 ++++---- internal/provider/tar_archiver_test.go | 5 ++++- internal/provider/zip_archiver.go | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/internal/provider/archiver.go b/internal/provider/archiver.go index 74ea4cad..8dcf12b2 100644 --- a/internal/provider/archiver.go +++ b/internal/provider/archiver.go @@ -43,16 +43,18 @@ func assertValidFile(infilename string) (os.FileInfo, error) { return fi, err } -func assertValidDir(indirname string) (os.FileInfo, error) { +func assertValidDir(indirname string) error { fi, err := os.Stat(indirname) if err != nil { if os.IsNotExist(err) { - return fi, fmt.Errorf("could not archive missing directory: %s", indirname) + return fmt.Errorf("could not archive missing directory: %s", indirname) } - return fi, err + return err } + if !fi.IsDir() { - return fi, fmt.Errorf("could not archive directory that is a file: %s", indirname) + return fmt.Errorf("could not archive directory that is a file: %s", indirname) } - return fi, nil + + return nil } diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index faf51e98..b9c9010e 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -74,7 +74,7 @@ func (a *TarArchiver) ArchiveFile(infilename string) error { } func (a *TarArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { - _, err := assertValidDir(indirname) + err := assertValidDir(indirname) if err != nil { return err } @@ -216,7 +216,7 @@ func (a *TarArchiver) addFile(filePath, fileName string, modTime time.Time) erro stat, err := file.Stat() if err != nil { - return errors.New(fmt.Sprintf("Could not get stat for file '%s', got error '%s'", filePath, err.Error())) + return fmt.Errorf("could not get stat for file '%s', got error '%w'", filePath, err) } header := &tar.Header{ @@ -261,12 +261,12 @@ func (a *TarArchiver) addContent(content []byte, header *tar.Header) error { } if err := a.tarWriter.WriteHeader(header); err != nil { - return errors.New(fmt.Sprintf("Could not write header, got error '%s'", err.Error())) + return fmt.Errorf("could not write header, got error '%w'", err) } _, err := a.tarWriter.Write(content) if err != nil { - return errors.New(fmt.Sprintf("Could not copy data to the tarball, got error '%s'", err.Error())) + return fmt.Errorf("could not copy data to the tarball, got error '%w'", err) } return nil diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 71a5af58..efbe3ca2 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -354,7 +354,10 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte continue case tar.TypeReg: buf := bytes.Buffer{} - io.Copy(&buf, tarReader) + _, err = io.Copy(&buf, tarReader) + if err != nil { + t.Fatalf("could not read file %s in tar: %s", name, err) + } wantFile, ok := wants[name] if !ok { diff --git a/internal/provider/zip_archiver.go b/internal/provider/zip_archiver.go index d4c00121..a64af3d4 100644 --- a/internal/provider/zip_archiver.go +++ b/internal/provider/zip_archiver.go @@ -97,7 +97,7 @@ func checkMatch(fileName string, excludes []string) (value bool) { } func (a *ZipArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { - _, err := assertValidDir(indirname) + err := assertValidDir(indirname) if err != nil { return err } From 24be5b0b01f3858d6c38bb6ed75a8ebcf2920eaa Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 22 Jul 2024 16:22:53 -0400 Subject: [PATCH 05/33] Regenerate provider documentation --- docs/data-sources/file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data-sources/file.md b/docs/data-sources/file.md index a8c58e56..43e5bd14 100644 --- a/docs/data-sources/file.md +++ b/docs/data-sources/file.md @@ -58,7 +58,7 @@ data "archive_file" "lambda_my_function" { ### Required - `output_path` (String) The output of the archive file. -- `type` (String) The type of archive to generate. NOTE: `zip` or `tar.gz` is supported. +- `type` (String) The type of archive to generate. NOTE: `zip` and `tar.gz` is supported. ### Optional From 0af35094ad1e25992969170c4a696d5ea1809302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 22 Jul 2024 23:30:42 +0200 Subject: [PATCH 06/33] fix: stabilize checksum in data resource for directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- .../provider/data_source_archive_file_test.go | 12 ++--- internal/provider/tar_archiver.go | 51 ++++++++++++++----- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/internal/provider/data_source_archive_file_test.go b/internal/provider/data_source_archive_file_test.go index 1d2220cc..04952a8b 100644 --- a/internal/provider/data_source_archive_file_test.go +++ b/internal/provider/data_source_archive_file_test.go @@ -62,12 +62,12 @@ func TestAccArchiveFile_Basic(t *testing.T) { "base64sha512": "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", }, "tar.gz": { - "md5": "56d3b5da9e46596a7037008224a3535d", - "sha": "d1934f7c5cd325b2c1b3128621e3041c931d5706", - "sha256": "0496affbe2b4dcde9cd3e49665b351a1ef7633faa35bb908de617b155f74f123", - "base64sha256": "BJav++K03N6c0+SWZbNRoe92M/qjW7kI3mF7FV908SM=", - "sha512": "5ddacbb55be1dc431cef74ab65ad8059a51990eb4cf108f679b79a59004a586cdd0b1bb0d869fa1fe679fa71a1d85391c19413a2d79050df68703aa3390eabee", - "base64sha512": "XdrLtVvh3EMc73SrZa2AWaUZkOtM8Qj2ebeaWQBKWGzdCxuw2Gn6H+Z5+nGh2FORwZQToteQUN9ocDqjOQ6r7g==", + "md5": "6678fae1fe2077c767bac136861e3bdc", + "sha": "3af6ef3c57aaa5ab3681cd25f916d6651b806cb6", + "sha256": "1b10e0f355025819486fb688aa04217939ea976cd271089bc0092e2994dbaaba", + "base64sha256": "GxDg81UCWBlIb7aIqgQheTnql2zScQibwAkuKZTbqro=", + "sha512": "adb56fca1e40420d4f994d031a08ca0d1ee51783f3c5d1631b6ed2b460ff2577f9154cb5f1c06edd0b0162899f7cfa7cc3d1f02ec9c9ae76f7ea64a31ba8cb81", + "base64sha512": "rbVvyh5AQg1PmU0DGgjKDR7lF4PzxdFjG27StGD/JXf5FUy18cBu3QsBYomffPp8w9HwLsnJrnb36mSjG6jLgQ==", }, }, } diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index b9c9010e..9b4fba28 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -84,15 +84,28 @@ func (a *TarArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { opts.Excludes[i] = filepath.FromSlash(opts.Excludes[i]) } + // Determine whether an empty archive would be generated. + isArchiveEmpty := true + + err = filepath.Walk(indirname, a.createWalkFunc("", indirname, opts, &isArchiveEmpty, true)) + if err != nil { + return err + } + + // Return an error if an empty archive would be generated. + if isArchiveEmpty { + return fmt.Errorf("archive has not been created as it would be empty") + } + if err := a.open(); err != nil { return err } defer a.close() - return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts)) + return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts, &isArchiveEmpty, false)) } -func (a *TarArchiver) createWalkFunc(basePath string, indirname string, opts ArchiveDirOpts) func(path string, info os.FileInfo, err error) error { +func (a *TarArchiver) createWalkFunc(basePath, indirname string, opts ArchiveDirOpts, isArchiveEmpty *bool, dryRun bool) func(path string, info os.FileInfo, err error) error { return func(path string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("error encountered during file walk: %s", err) @@ -123,24 +136,34 @@ func (a *TarArchiver) createWalkFunc(basePath string, indirname string, opts Arc } if info.Mode()&os.ModeSymlink == os.ModeSymlink { - if !opts.ExcludeSymlinkDirectories { - path, err = filepath.EvalSymlinks(path) - if err != nil { - return err - } + realPath, err := filepath.EvalSymlinks(path) + if err != nil { + return err + } - info, err = os.Stat(path) - if err != nil { - return err - } + realInfo, err := os.Stat(realPath) + if err != nil { + return err + } - if info.IsDir() { - return filepath.Walk(path, a.createWalkFunc(archivePath, path, opts)) + if realInfo.IsDir() { + if !opts.ExcludeSymlinkDirectories { + return filepath.Walk(realPath, a.createWalkFunc(archivePath, realPath, opts, isArchiveEmpty, dryRun)) + } else { + return filepath.SkipDir } } + + info = realInfo + } + + *isArchiveEmpty = false + + if dryRun { + return nil } - return a.addFile(path, archivePath, info.ModTime()) + return a.addFile(path, archivePath, time.Time{}) } } From 2af360b2519f752bb9c4b7a7561e0b1588e7a8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 22 Jul 2024 23:31:57 +0200 Subject: [PATCH 07/33] fix: nil pointer in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver_test.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index efbe3ca2..eabcc3a1 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -255,11 +255,15 @@ func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + if err == nil { + t.Fatalf("expected error, but err is nil.") + } + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) found := regex.Match([]byte(err.Error())) if !found { - t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + t.Fatalf("expected error to match %q, got: %s", regex.String(), err.Error()) } } @@ -307,7 +311,11 @@ func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + if err == nil { + t.Fatalf("expected error, but err is nil.") + } + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) found := regex.Match([]byte(err.Error())) if !found { From 29d709a7538d35cc243cb2ef9cee49ab99ef26aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 23 Jul 2024 07:16:12 +0200 Subject: [PATCH 08/33] fix: tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver_test.go | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index eabcc3a1..70ac5d5e 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -10,7 +10,6 @@ import ( "io" "os" "path/filepath" - "regexp" "strconv" "testing" "time" @@ -255,15 +254,8 @@ func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - if err == nil { - t.Fatalf("expected error, but err is nil.") - } - - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) - found := regex.Match([]byte(err.Error())) - - if !found { - t.Fatalf("expected error to match %q, got: %s", regex.String(), err.Error()) + if err != nil { + t.Errorf("expected no error: %s", err) } } @@ -311,15 +303,8 @@ func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - if err == nil { - t.Fatalf("expected error, but err is nil.") - } - - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) - found := regex.Match([]byte(err.Error())) - - if !found { - t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + if err != nil { + t.Errorf("expected no error: %s", err) } } From 0a6c04fb426730df333876f86ec843fe25828096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 23 Jul 2024 07:26:20 +0200 Subject: [PATCH 09/33] fix: tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver.go | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index 9b4fba28..f9b38f33 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -66,7 +66,14 @@ func (a *TarArchiver) ArchiveFile(infilename string) error { } defer a.close() - if err := a.addFile(infilename, filepath.ToSlash(fi.Name()), time.Time{}); err != nil { + header := &tar.Header{ + Name: filepath.ToSlash(fi.Name()), + Size: fi.Size(), + Mode: int64(fi.Mode()), + ModTime: time.Time{}, + } + + if err := a.addFile(infilename, header); err != nil { return err } @@ -163,7 +170,14 @@ func (a *TarArchiver) createWalkFunc(basePath, indirname string, opts ArchiveDir return nil } - return a.addFile(path, archivePath, time.Time{}) + header := &tar.Header{ + Name: filepath.ToSlash(archivePath), + Size: info.Size(), + Mode: int64(info.Mode()), + ModTime: time.Time{}, + } + + return a.addFile(path, header) } } @@ -230,25 +244,17 @@ func (a *TarArchiver) close() { } } -func (a *TarArchiver) addFile(filePath, fileName string, modTime time.Time) error { +func (a *TarArchiver) addFile(filePath string, header *tar.Header) error { + if header == nil { + return fmt.Errorf("tar.Header is nil") + } + file, err := os.Open(filePath) if err != nil { return fmt.Errorf("could not open file '%s', got error '%w'", filePath, err) } defer file.Close() - stat, err := file.Stat() - if err != nil { - return fmt.Errorf("could not get stat for file '%s', got error '%w'", filePath, err) - } - - header := &tar.Header{ - Name: fileName, - Size: stat.Size(), - Mode: int64(stat.Mode()), - ModTime: modTime, - } - if a.outputFileMode != "" { filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) if err != nil { From 5943e85df8d6b9f9a7b05db539b710c2d475dfd6 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 23 Jul 2024 15:09:20 -0400 Subject: [PATCH 10/33] Close gzip reader --- internal/provider/tar_archiver_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 70ac5d5e..26edb907 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -321,6 +321,7 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte if err != nil { t.Fatalf("could not open tar.gz file: %s", err) } + defer gzf.Close() tarReader := tar.NewReader(gzf) From 2c39a6030df5489ab66172bedf80fc4f927c0f4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 30 Jul 2024 20:53:34 +0200 Subject: [PATCH 11/33] fix windows issues --- internal/provider/tar_archiver.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index f9b38f33..d4c3fde8 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -26,7 +26,7 @@ type TarArchiver struct { compression TarCompressionType filepath string outputFileMode string // Default value "" means unset - filewriter *os.File + fileWriter *os.File tarWriter *tar.Writer compressionWriter io.WriteCloser } @@ -215,14 +215,16 @@ func (a *TarArchiver) SetOutputFileMode(outputFileMode string) { } func (a *TarArchiver) open() error { - file, err := os.Create(filepath.ToSlash(a.filepath)) + var err error + + a.fileWriter, err = os.Create(filepath.ToSlash(a.filepath)) if err != nil { return err } switch a.compression { case TarCompressionGz: - a.compressionWriter = gzip.NewWriter(file) + a.compressionWriter = gzip.NewWriter(a.fileWriter) } a.tarWriter = tar.NewWriter(a.compressionWriter) @@ -230,18 +232,27 @@ func (a *TarArchiver) open() error { } func (a *TarArchiver) close() { - if a.filewriter != nil { - a.filewriter.Close() - a.filewriter = nil - } if a.tarWriter != nil { - a.tarWriter.Close() + err := a.tarWriter.Close() + if err != nil { + fmt.Printf("error closing tarwriter : %s\n\n", err) + } a.tarWriter = nil } if a.compressionWriter != nil { - a.compressionWriter.Close() + err := a.compressionWriter.Close() + if err != nil { + fmt.Printf("error closing compressionWriter : %s\n\n", err) + } a.compressionWriter = nil } + if a.fileWriter != nil { + err := a.fileWriter.Close() + if err != nil { + fmt.Printf("error closing fileWriter: %s\n\n", err) + } + a.fileWriter = nil + } } func (a *TarArchiver) addFile(filePath string, header *tar.Header) error { From 165dd3a85fd0678ff1198a31c1bc446ad63d5e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 21 Nov 2023 08:52:47 +0100 Subject: [PATCH 12/33] Add format tar.gz --- internal/provider/archiver.go | 3 +- internal/provider/data_source_archive_file.go | 2 +- .../provider/data_source_archive_file_test.go | 110 ++--- internal/provider/tar_archiver.go | 273 ++++++++++++ internal/provider/tar_archiver_test.go | 402 ++++++++++++++++++ 5 files changed, 737 insertions(+), 53 deletions(-) create mode 100644 internal/provider/tar_archiver.go create mode 100644 internal/provider/tar_archiver_test.go diff --git a/internal/provider/archiver.go b/internal/provider/archiver.go index 5cd4794b..74ea4cad 100644 --- a/internal/provider/archiver.go +++ b/internal/provider/archiver.go @@ -24,7 +24,8 @@ type Archiver interface { type ArchiverBuilder func(outputPath string) Archiver var archiverBuilders = map[string]ArchiverBuilder{ - "zip": NewZipArchiver, + "zip": NewZipArchiver, + "tar.gz": NewTarGzArchiver, } func getArchiver(archiveType string, outputPath string) Archiver { diff --git a/internal/provider/data_source_archive_file.go b/internal/provider/data_source_archive_file.go index 507d1dd5..c65755bf 100644 --- a/internal/provider/data_source_archive_file.go +++ b/internal/provider/data_source_archive_file.go @@ -80,7 +80,7 @@ func (d *archiveFileDataSource) Schema(ctx context.Context, req datasource.Schem Computed: true, }, "type": schema.StringAttribute{ - Description: "The type of archive to generate. NOTE: `zip` is supported.", + Description: "The type of archive to generate. NOTE: `zip` and `tar.gz` is supported.", Required: true, }, "source_content": schema.StringAttribute{ diff --git a/internal/provider/data_source_archive_file_test.go b/internal/provider/data_source_archive_file_test.go index d9afe27f..6e36426a 100644 --- a/internal/provider/data_source_archive_file_test.go +++ b/internal/provider/data_source_archive_file_test.go @@ -139,7 +139,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileContentConfig(f), + Config: testAccArchiveFileContentConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -156,7 +156,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileContentConfig(f), + Config: testAccArchiveFileContentConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -190,7 +190,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_FileConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileFileConfig(f), + Config: testAccArchiveFileFileConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -207,7 +207,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_FileConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileFileConfig(f), + Config: testAccArchiveFileFileConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -241,7 +241,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileDirConfig(f), + Config: testAccArchiveFileDirConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -258,7 +258,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileDirConfig(f), + Config: testAccArchiveFileDirConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -292,7 +292,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirExcludesConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileDirExcludesConfig(f), + Config: testAccArchiveFileDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), testExtractResourceAttr("data.archive_file.foo", "output_sha", &outputSha), @@ -301,7 +301,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_DirExcludesConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileDirExcludesConfig(f), + Config: testAccArchiveFileDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_sha", &outputSha), @@ -327,7 +327,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileMultiSourceConfig(f), + Config: testAccArchiveFileMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), testExtractResourceAttr("data.archive_file.foo", "output_sha", &outputSha), @@ -336,7 +336,7 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileMultiSourceConfig(f), + Config: testAccArchiveFileMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_sha", &outputSha), @@ -347,27 +347,35 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { } func TestAccArchiveFile_SourceConfigMissing(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigMissing(), - ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), - }, - }, - }) + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing(format), + ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), + }, + }, + }) + }) + } } func TestAccArchiveFile_SourceConfigConflicting(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigConflicting(), - ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), - }, - }, - }) + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigConflicting(format), + ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), + }, + }, + }) + }) + } } // TestAccArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an @@ -1352,48 +1360,48 @@ func testAccArchiveFileSize(filename string, fileSize *string) r.TestCheckFunc { } } -func testAccArchiveFileContentConfig(outputPath string) string { +func testAccArchiveFileContentConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_content = "This is some content" source_content_filename = "content.txt" output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileFileConfig(outputPath string) string { +func testAccArchiveFileFileConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_file = "test-fixtures/test-dir/test-file.txt" output_path = "%s" output_file_mode = "0666" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileDirConfig(outputPath string) string { +func testAccArchiveFileDirConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_dir = "test-fixtures/test-dir/test-dir1" output_path = "%s" output_file_mode = "0666" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileDirExcludesConfig(outputPath string) string { +func testAccArchiveFileDirExcludesConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_dir = "test-fixtures/test-dir/test-dir1" excludes = ["test-fixtures/test-dir/test-dir1/file2.txt"] output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } func testAccArchiveFileDirExcludesGlobConfig(outputPath string) string { @@ -1407,10 +1415,10 @@ data "archive_file" "foo" { `, filepath.ToSlash(outputPath)) } -func testAccArchiveFileMultiSourceConfig(outputPath string) string { +func testAccArchiveFileMultiSourceConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source { filename = "content_1.txt" content = "This is the content for content_1.txt" @@ -1421,22 +1429,22 @@ data "archive_file" "foo" { } output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } -func testAccArchiveSourceConfigMissing() string { - return ` +func testAccArchiveSourceConfigMissing(format string) string { + return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" output_path = "path" } -` +`, format) } -func testAccArchiveSourceConfigConflicting() string { - return ` +func testAccArchiveSourceConfigConflicting(format string) string { + return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source { filename = "content_1.txt" content = "This is the content for content_1.txt" @@ -1444,7 +1452,7 @@ data "archive_file" "foo" { source_dir = "test-fixtures/test-dir" output_path = "path" } -` +`, format) } //nolint:unparam diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go new file mode 100644 index 00000000..ea4c4457 --- /dev/null +++ b/internal/provider/tar_archiver.go @@ -0,0 +1,273 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "archive/tar" + "compress/gzip" + "errors" + "fmt" + "io" + "os" + "path/filepath" + "sort" + "strconv" + "time" +) + +type TarCompressionType int + +const ( + TarCompressionGz TarCompressionType = iota +) + +type TarArchiver struct { + compression TarCompressionType + filepath string + outputFileMode string // Default value "" means unset + filewriter *os.File + tarWriter *tar.Writer + compressionWriter io.WriteCloser +} + +func NewTarGzArchiver(filepath string) Archiver { + return NewTarArchiver(filepath, TarCompressionGz) +} + +func NewTarArchiver(filepath string, compression TarCompressionType) Archiver { + return &TarArchiver{ + filepath: filepath, + compression: compression, + } +} + +func (a *TarArchiver) ArchiveContent(content []byte, infilename string) error { + if err := a.open(); err != nil { + return err + } + defer a.close() + + return a.addContent(content, &tar.Header{ + Name: infilename, + Size: int64(len(content)), + ModTime: time.Time{}, + }) +} + +func (a *TarArchiver) ArchiveFile(infilename string) error { + fi, err := assertValidFile(infilename) + if err != nil { + return err + } + + if err := a.open(); err != nil { + return err + } + defer a.close() + + if err := a.addFile(infilename, filepath.ToSlash(fi.Name()), time.Time{}); err != nil { + return err + } + + return err +} + +func (a *TarArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { + _, err := assertValidDir(indirname) + if err != nil { + return err + } + + // ensure exclusions are OS compatible paths + for i := range opts.Excludes { + opts.Excludes[i] = filepath.FromSlash(opts.Excludes[i]) + } + + if err := a.open(); err != nil { + return err + } + defer a.close() + + return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts)) +} + +func (a *TarArchiver) createWalkFunc(basePath string, indirname string, opts ArchiveDirOpts) func(path string, info os.FileInfo, err error) error { + return func(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) + if err != nil { + return fmt.Errorf("error relativizing file for archival: %s", err) + } + + archivePath := filepath.Join(basePath, relname) + + isMatch := checkMatch(archivePath, opts.Excludes) + + if info.IsDir() { + if isMatch { + return filepath.SkipDir + } + return nil + } + + if isMatch { + return nil + } + + if err != nil { + return err + } + + if info.Mode()&os.ModeSymlink == os.ModeSymlink { + if !opts.ExcludeSymlinkDirectories { + path, err = filepath.EvalSymlinks(path) + if err != nil { + return err + } + + info, err = os.Stat(path) + if err != nil { + return err + } + + if info.IsDir() { + return filepath.Walk(path, a.createWalkFunc(archivePath, path, opts)) + } + } + } + + return a.addFile(path, archivePath, info.ModTime()) + } +} + +func (a *TarArchiver) ArchiveMultiple(content map[string][]byte) error { + if err := a.open(); err != nil { + return err + } + defer a.close() + + // Ensure files are processed in the same order so hashes don't change + keys := make([]string, len(content)) + i := 0 + for k := range content { + keys[i] = k + i++ + } + sort.Strings(keys) + + for _, filename := range keys { + header := &tar.Header{ + Name: filepath.ToSlash(filename), + Size: int64(len(content[filename])), + ModTime: time.Now().Round(time.Second), + } + + if err := a.addContent(content[filename], header); err != nil { + return err + } + } + return nil +} + +func (a *TarArchiver) SetOutputFileMode(outputFileMode string) { + a.outputFileMode = outputFileMode +} + +func (a *TarArchiver) open() error { + file, err := os.Create(filepath.ToSlash(a.filepath)) + if err != nil { + return err + } + + switch a.compression { + case TarCompressionGz: + a.compressionWriter = gzip.NewWriter(file) + } + + a.tarWriter = tar.NewWriter(a.compressionWriter) + return nil +} + +func (a *TarArchiver) close() { + if a.filewriter != nil { + a.filewriter.Close() + a.filewriter = nil + } + if a.tarWriter != nil { + a.tarWriter.Close() + a.tarWriter = nil + } + if a.compressionWriter != nil { + a.compressionWriter.Close() + a.compressionWriter = nil + } +} + +func (a *TarArchiver) addFile(filePath, fileName string, modTime time.Time) error { + file, err := os.Open(filePath) + if err != nil { + return fmt.Errorf("could not open file '%s', got error '%w'", filePath, err) + } + defer file.Close() + + stat, err := file.Stat() + if err != nil { + return errors.New(fmt.Sprintf("Could not get stat for file '%s', got error '%s'", filePath, err.Error())) + } + + header := &tar.Header{ + Name: fileName, + Size: stat.Size(), + Mode: int64(stat.Mode()), + ModTime: modTime, + } + + if a.outputFileMode != "" { + filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) + if err != nil { + return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode) + } + header.Mode = filemode + } + + err = a.tarWriter.WriteHeader(header) + if err != nil { + return fmt.Errorf("could not write header for file '%s', got error '%w'", filePath, err) + } + + _, err = io.Copy(a.tarWriter, file) + if err != nil { + return fmt.Errorf("error reading file for archival: %s", err) + } + + return nil +} + +func (a *TarArchiver) addContent(content []byte, header *tar.Header) error { + if header == nil { + return errors.New("tar.Header is nil") + } + + if a.outputFileMode != "" { + filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) + if err != nil { + return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode) + } + header.Mode = filemode + } + + if err := a.tarWriter.WriteHeader(header); err != nil { + return errors.New(fmt.Sprintf("Could not write header, got error '%s'", err.Error())) + } + + _, err := a.tarWriter.Write(content) + if err != nil { + return errors.New(fmt.Sprintf("Could not copy data to the tarball, got error '%s'", err.Error())) + } + + return nil +} diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go new file mode 100644 index 00000000..2a7ba238 --- /dev/null +++ b/internal/provider/tar_archiver_test.go @@ -0,0 +1,402 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "io" + "os" + "path/filepath" + "regexp" + "strconv" + "testing" + "time" +) + +func TestTarArchiver_Content(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-content.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveContent([]byte("This is some content"), "content.txt"); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "content.txt": []byte("This is some content"), + }) +} + +func TestTarArchiver_File(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-file.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveFile("./test-fixtures/test-dir/test-file.txt"); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-file.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_FileMode(t *testing.T) { + file, err := os.CreateTemp("", "archive-file-mode-test.tar.gz") + if err != nil { + t.Fatal(err) + } + + var ( + tarFilePath = file.Name() + toTarPath = filepath.FromSlash("./test-fixtures/test-dir/test-file.txt") + ) + + stringArray := [5]string{"0444", "0644", "0666", "0744", "0777"} + for _, element := range stringArray { + archiver := NewTarGzArchiver(tarFilePath) + archiver.SetOutputFileMode(element) + if err := archiver.ArchiveFile(toTarPath); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarFileMode(t, tarFilePath, element) + } +} + +func TestTarArchiver_FileModified(t *testing.T) { + var ( + tarFilePath = filepath.Join(t.TempDir(), "archive-file-modified.tar.gz") + toTarPath = filepath.FromSlash("./test-fixtures/test-dir/test-file.txt") + ) + + var tarFunc = func() { + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveFile(toTarPath); err != nil { + t.Fatalf("unexpected error: %s", err) + } + } + + tarFunc() + + expectedContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + // touch file modified, in the future just in case of weird race issues + newTime := time.Now().Add(1 * time.Hour) + if err := os.Chtimes(toTarPath, newTime, newTime); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + tarFunc() + + actualContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !bytes.Equal(expectedContents, actualContents) { + t.Fatalf("tar contents do not match, potentially a modified time issue") + } +} + +func TestTarArchiver_Dir(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir/test-dir1", ArchiveDirOpts{}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file2.txt": []byte("This is file 2"), + "file3.txt": []byte("This is file 3"), + }) +} + +func TestTarArchiver_Dir_Exclude(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-exclude.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir/test-dir1", ArchiveDirOpts{ + Excludes: []string{"file2.txt"}, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file3.txt": []byte("This is file 3"), + }) +} + +func TestTarArchiver_Dir_Exclude_With_Directory(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-exclude-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir", ArchiveDirOpts{ + Excludes: []string{"test-dir1", "test-dir2/file2.txt"}, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir2/file1.txt": []byte("This is file 1"), + "test-dir2/file3.txt": []byte("This is file 3"), + "test-file.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Multiple(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-content.tar.gz") + + content := map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file2.txt": []byte("This is file 2"), + "file3.txt": []byte("This is file 3"), + } + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveMultiple(content); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, content) +} + +func TestTarArchiver_Dir_With_Symlink_File(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-file.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures/test-dir-with-symlink-file", ArchiveDirOpts{}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-file.txt": []byte("This is test content"), + "test-symlink.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Dir_DoNotExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{}); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + ExcludeSymlinkDirectories: true, + }) + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + found := regex.Match([]byte(err.Error())) + + if !found { + t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + } +} + +func TestTarArchiver_Dir_Exclude_DoNotExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + Excludes: []string{ + "test-symlink-dir/file1.txt", + "test-symlink-dir-with-symlink-file/test-symlink.txt", + }, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + }) +} + +func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") + + archiver := NewTarGzArchiver(tarFilePath) + err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + Excludes: []string{ + "test-dir/test-dir1/file1.txt", + "test-symlink-dir-with-symlink-file/test-symlink.txt", + }, + ExcludeSymlinkDirectories: true, + }) + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + found := regex.Match([]byte(err.Error())) + + if !found { + t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + } +} + +func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte) { + t.Helper() + + f, err := os.Open(tarfilepath) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + defer f.Close() + + gzf, err := gzip.NewReader(f) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + + tarReader := tar.NewReader(gzf) + + i := 0 + for { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + t.Fatal(err) + } + + if len(wants) < i+1 { + t.Fatalf("unexpect file count in tar. expect %d", len(wants)) + } + + name := header.Name + + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + buf := bytes.Buffer{} + io.Copy(&buf, tarReader) + + wantFile, ok := wants[name] + if !ok { + t.Fatalf("missing file %s in tar", name) + } + + wantContent := string(wantFile) + gotContent := buf.String() + if gotContent != wantContent { + t.Errorf("mismatched content\ngot\n%s\nwant\n%s", gotContent, wantContent) + } + + default: + t.Fatalf("%s : %c %s %s\n", + "Yikes! Unable to figure out type", + header.Typeflag, + "in file", + name, + ) + } + + i++ + } +} + +func ensureTarFileMode(t *testing.T, tarfilepath string, outputFileMode string) { + t.Helper() + + f, err := os.Open(tarfilepath) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + defer f.Close() + + gzf, err := gzip.NewReader(f) + if err != nil { + t.Fatalf("could not open tar.gz file: %s", err) + } + + tarReader := tar.NewReader(gzf) + + filemode, err := strconv.ParseUint(outputFileMode, 0, 32) + if err != nil { + t.Fatalf("error parsing outputFileMode value: %s", outputFileMode) + } + + var osfilemode = os.FileMode(filemode) + + i := 0 + for { + header, err := tarReader.Next() + + if err == io.EOF { + break + } + + if err != nil { + t.Fatal(err) + } + + name := header.Name + + switch header.Typeflag { + case tar.TypeDir: + continue + case tar.TypeReg: + if header.FileInfo().Mode() != osfilemode { + t.Fatalf("Expected filemode \"%s\" but was \"%s\"", osfilemode, header.FileInfo().Mode()) + } + default: + t.Fatalf("%s : %c %s %s\n", + "Yikes! Unable to figure out type", + header.Typeflag, + "in file", + name, + ) + } + + i++ + } +} From 487bc5e42c2ea68b1c81eb4693d19ff5d55900d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Sun, 11 Feb 2024 08:20:58 +0100 Subject: [PATCH 13/33] add doc note MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- docs/data-sources/file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data-sources/file.md b/docs/data-sources/file.md index f390d0c9..444f34d6 100644 --- a/docs/data-sources/file.md +++ b/docs/data-sources/file.md @@ -58,7 +58,7 @@ data "archive_file" "lambda_my_function" { ### Required - `output_path` (String) The output of the archive file. -- `type` (String) The type of archive to generate. NOTE: `zip` is supported. +- `type` (String) The type of archive to generate. NOTE: `zip` or `tar.gz` is supported. ### Optional From e10f49645d2961f8aae7a40c36c0c9a0167666b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Fri, 10 May 2024 11:48:16 +0200 Subject: [PATCH 14/33] fix: inconsistent output with multiple files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver.go | 2 +- internal/provider/tar_archiver_test.go | 36 ++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index ea4c4457..faf51e98 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -163,7 +163,7 @@ func (a *TarArchiver) ArchiveMultiple(content map[string][]byte) error { header := &tar.Header{ Name: filepath.ToSlash(filename), Size: int64(len(content[filename])), - ModTime: time.Now().Round(time.Second), + ModTime: time.Time{}, } if err := a.addContent(content[filename], header); err != nil { diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 2a7ba238..71a5af58 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -168,6 +168,42 @@ func TestTarArchiver_Multiple(t *testing.T) { ensureTarContents(t, tarFilePath, content) } +func TestTarArchiver_Multiple_NoChange(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-content.tar.gz") + + content := map[string][]byte{ + "file1.txt": []byte("This is file 1"), + "file2.txt": []byte("This is file 2"), + "file3.txt": []byte("This is file 3"), + } + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveMultiple(content); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + expectedContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + time.Sleep(1 * time.Second) + + archiver = NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveMultiple(content); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + actualContents, err := os.ReadFile(tarFilePath) + if err != nil { + t.Fatalf("unexpected error: %s", err) + } + + if !bytes.Equal(expectedContents, actualContents) { + t.Fatalf("tar contents do not match, potentially a modified time issue") + } +} + func TestTarArchiver_Dir_With_Symlink_File(t *testing.T) { tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-file.tar.gz") From 48711a3e5ada85e543eab6b52c63f6bf2bea5f32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 22 Jul 2024 21:51:46 +0200 Subject: [PATCH 15/33] fix lint issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/archiver.go | 12 +++++++----- internal/provider/tar_archiver.go | 8 ++++---- internal/provider/tar_archiver_test.go | 5 ++++- internal/provider/zip_archiver.go | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/internal/provider/archiver.go b/internal/provider/archiver.go index 74ea4cad..8dcf12b2 100644 --- a/internal/provider/archiver.go +++ b/internal/provider/archiver.go @@ -43,16 +43,18 @@ func assertValidFile(infilename string) (os.FileInfo, error) { return fi, err } -func assertValidDir(indirname string) (os.FileInfo, error) { +func assertValidDir(indirname string) error { fi, err := os.Stat(indirname) if err != nil { if os.IsNotExist(err) { - return fi, fmt.Errorf("could not archive missing directory: %s", indirname) + return fmt.Errorf("could not archive missing directory: %s", indirname) } - return fi, err + return err } + if !fi.IsDir() { - return fi, fmt.Errorf("could not archive directory that is a file: %s", indirname) + return fmt.Errorf("could not archive directory that is a file: %s", indirname) } - return fi, nil + + return nil } diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index faf51e98..b9c9010e 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -74,7 +74,7 @@ func (a *TarArchiver) ArchiveFile(infilename string) error { } func (a *TarArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { - _, err := assertValidDir(indirname) + err := assertValidDir(indirname) if err != nil { return err } @@ -216,7 +216,7 @@ func (a *TarArchiver) addFile(filePath, fileName string, modTime time.Time) erro stat, err := file.Stat() if err != nil { - return errors.New(fmt.Sprintf("Could not get stat for file '%s', got error '%s'", filePath, err.Error())) + return fmt.Errorf("could not get stat for file '%s', got error '%w'", filePath, err) } header := &tar.Header{ @@ -261,12 +261,12 @@ func (a *TarArchiver) addContent(content []byte, header *tar.Header) error { } if err := a.tarWriter.WriteHeader(header); err != nil { - return errors.New(fmt.Sprintf("Could not write header, got error '%s'", err.Error())) + return fmt.Errorf("could not write header, got error '%w'", err) } _, err := a.tarWriter.Write(content) if err != nil { - return errors.New(fmt.Sprintf("Could not copy data to the tarball, got error '%s'", err.Error())) + return fmt.Errorf("could not copy data to the tarball, got error '%w'", err) } return nil diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 71a5af58..efbe3ca2 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -354,7 +354,10 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte continue case tar.TypeReg: buf := bytes.Buffer{} - io.Copy(&buf, tarReader) + _, err = io.Copy(&buf, tarReader) + if err != nil { + t.Fatalf("could not read file %s in tar: %s", name, err) + } wantFile, ok := wants[name] if !ok { diff --git a/internal/provider/zip_archiver.go b/internal/provider/zip_archiver.go index 303fa465..014cec10 100644 --- a/internal/provider/zip_archiver.go +++ b/internal/provider/zip_archiver.go @@ -104,7 +104,7 @@ func checkMatch(fileName string, excludes []string) (value bool, err error) { } func (a *ZipArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { - _, err := assertValidDir(indirname) + err := assertValidDir(indirname) if err != nil { return err } From 958ea4e87d113a017cd614611ec261800ae6fda7 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 22 Jul 2024 16:22:53 -0400 Subject: [PATCH 16/33] Regenerate provider documentation --- docs/data-sources/file.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/data-sources/file.md b/docs/data-sources/file.md index 444f34d6..7a6246a5 100644 --- a/docs/data-sources/file.md +++ b/docs/data-sources/file.md @@ -58,7 +58,7 @@ data "archive_file" "lambda_my_function" { ### Required - `output_path` (String) The output of the archive file. -- `type` (String) The type of archive to generate. NOTE: `zip` or `tar.gz` is supported. +- `type` (String) The type of archive to generate. NOTE: `zip` and `tar.gz` is supported. ### Optional From f2abe371d914e5c8629bdbf526d40c3ff5a38dfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 22 Jul 2024 23:30:42 +0200 Subject: [PATCH 17/33] fix: stabilize checksum in data resource for directories MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver.go | 51 ++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index b9c9010e..9b4fba28 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -84,15 +84,28 @@ func (a *TarArchiver) ArchiveDir(indirname string, opts ArchiveDirOpts) error { opts.Excludes[i] = filepath.FromSlash(opts.Excludes[i]) } + // Determine whether an empty archive would be generated. + isArchiveEmpty := true + + err = filepath.Walk(indirname, a.createWalkFunc("", indirname, opts, &isArchiveEmpty, true)) + if err != nil { + return err + } + + // Return an error if an empty archive would be generated. + if isArchiveEmpty { + return fmt.Errorf("archive has not been created as it would be empty") + } + if err := a.open(); err != nil { return err } defer a.close() - return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts)) + return filepath.Walk(indirname, a.createWalkFunc("", indirname, opts, &isArchiveEmpty, false)) } -func (a *TarArchiver) createWalkFunc(basePath string, indirname string, opts ArchiveDirOpts) func(path string, info os.FileInfo, err error) error { +func (a *TarArchiver) createWalkFunc(basePath, indirname string, opts ArchiveDirOpts, isArchiveEmpty *bool, dryRun bool) func(path string, info os.FileInfo, err error) error { return func(path string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("error encountered during file walk: %s", err) @@ -123,24 +136,34 @@ func (a *TarArchiver) createWalkFunc(basePath string, indirname string, opts Arc } if info.Mode()&os.ModeSymlink == os.ModeSymlink { - if !opts.ExcludeSymlinkDirectories { - path, err = filepath.EvalSymlinks(path) - if err != nil { - return err - } + realPath, err := filepath.EvalSymlinks(path) + if err != nil { + return err + } - info, err = os.Stat(path) - if err != nil { - return err - } + realInfo, err := os.Stat(realPath) + if err != nil { + return err + } - if info.IsDir() { - return filepath.Walk(path, a.createWalkFunc(archivePath, path, opts)) + if realInfo.IsDir() { + if !opts.ExcludeSymlinkDirectories { + return filepath.Walk(realPath, a.createWalkFunc(archivePath, realPath, opts, isArchiveEmpty, dryRun)) + } else { + return filepath.SkipDir } } + + info = realInfo + } + + *isArchiveEmpty = false + + if dryRun { + return nil } - return a.addFile(path, archivePath, info.ModTime()) + return a.addFile(path, archivePath, time.Time{}) } } From 01de7bd76af5411961bbc6d61b9d1bcc7ba3edb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 22 Jul 2024 23:31:57 +0200 Subject: [PATCH 18/33] fix: nil pointer in tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver_test.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index efbe3ca2..eabcc3a1 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -255,11 +255,15 @@ func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + if err == nil { + t.Fatalf("expected error, but err is nil.") + } + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) found := regex.Match([]byte(err.Error())) if !found { - t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + t.Fatalf("expected error to match %q, got: %s", regex.String(), err.Error()) } } @@ -307,7 +311,11 @@ func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures(\/|\\)test-dir-with-symlink-dir(\/|\\)test-symlink-dir: `) + if err == nil { + t.Fatalf("expected error, but err is nil.") + } + + regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) found := regex.Match([]byte(err.Error())) if !found { From 5a0b9eba2da9b7b688c1f7417dbea5aebb4e26f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 23 Jul 2024 07:16:12 +0200 Subject: [PATCH 19/33] fix: tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver_test.go | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index eabcc3a1..70ac5d5e 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -10,7 +10,6 @@ import ( "io" "os" "path/filepath" - "regexp" "strconv" "testing" "time" @@ -255,15 +254,8 @@ func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - if err == nil { - t.Fatalf("expected error, but err is nil.") - } - - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) - found := regex.Match([]byte(err.Error())) - - if !found { - t.Fatalf("expected error to match %q, got: %s", regex.String(), err.Error()) + if err != nil { + t.Errorf("expected no error: %s", err) } } @@ -311,15 +303,8 @@ func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { ExcludeSymlinkDirectories: true, }) - if err == nil { - t.Fatalf("expected error, but err is nil.") - } - - regex := regexp.MustCompile(`error reading file for archival: read test-fixtures[/\\]test-dir-with-symlink-dir[/\\]test-symlink-dir: `) - found := regex.Match([]byte(err.Error())) - - if !found { - t.Fatalf("expedted error to match %q, got: %s", regex.String(), err.Error()) + if err != nil { + t.Errorf("expected no error: %s", err) } } From c3eeece43a1f92d4d4939612db97da97b5ae453f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 23 Jul 2024 07:26:20 +0200 Subject: [PATCH 20/33] fix: tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver.go | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index 9b4fba28..f9b38f33 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -66,7 +66,14 @@ func (a *TarArchiver) ArchiveFile(infilename string) error { } defer a.close() - if err := a.addFile(infilename, filepath.ToSlash(fi.Name()), time.Time{}); err != nil { + header := &tar.Header{ + Name: filepath.ToSlash(fi.Name()), + Size: fi.Size(), + Mode: int64(fi.Mode()), + ModTime: time.Time{}, + } + + if err := a.addFile(infilename, header); err != nil { return err } @@ -163,7 +170,14 @@ func (a *TarArchiver) createWalkFunc(basePath, indirname string, opts ArchiveDir return nil } - return a.addFile(path, archivePath, time.Time{}) + header := &tar.Header{ + Name: filepath.ToSlash(archivePath), + Size: info.Size(), + Mode: int64(info.Mode()), + ModTime: time.Time{}, + } + + return a.addFile(path, header) } } @@ -230,25 +244,17 @@ func (a *TarArchiver) close() { } } -func (a *TarArchiver) addFile(filePath, fileName string, modTime time.Time) error { +func (a *TarArchiver) addFile(filePath string, header *tar.Header) error { + if header == nil { + return fmt.Errorf("tar.Header is nil") + } + file, err := os.Open(filePath) if err != nil { return fmt.Errorf("could not open file '%s', got error '%w'", filePath, err) } defer file.Close() - stat, err := file.Stat() - if err != nil { - return fmt.Errorf("could not get stat for file '%s', got error '%w'", filePath, err) - } - - header := &tar.Header{ - Name: fileName, - Size: stat.Size(), - Mode: int64(stat.Mode()), - ModTime: modTime, - } - if a.outputFileMode != "" { filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) if err != nil { From cf1a5d6e406b977a028706df4e5732c56943e13a Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 23 Jul 2024 15:09:20 -0400 Subject: [PATCH 21/33] Close gzip reader --- internal/provider/tar_archiver_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 70ac5d5e..26edb907 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -321,6 +321,7 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte if err != nil { t.Fatalf("could not open tar.gz file: %s", err) } + defer gzf.Close() tarReader := tar.NewReader(gzf) From 93506c906b80ccef91247d0bb4abe36b056c78ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 30 Jul 2024 20:53:34 +0200 Subject: [PATCH 22/33] fix windows issues --- internal/provider/tar_archiver.go | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index f9b38f33..d4c3fde8 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -26,7 +26,7 @@ type TarArchiver struct { compression TarCompressionType filepath string outputFileMode string // Default value "" means unset - filewriter *os.File + fileWriter *os.File tarWriter *tar.Writer compressionWriter io.WriteCloser } @@ -215,14 +215,16 @@ func (a *TarArchiver) SetOutputFileMode(outputFileMode string) { } func (a *TarArchiver) open() error { - file, err := os.Create(filepath.ToSlash(a.filepath)) + var err error + + a.fileWriter, err = os.Create(filepath.ToSlash(a.filepath)) if err != nil { return err } switch a.compression { case TarCompressionGz: - a.compressionWriter = gzip.NewWriter(file) + a.compressionWriter = gzip.NewWriter(a.fileWriter) } a.tarWriter = tar.NewWriter(a.compressionWriter) @@ -230,18 +232,27 @@ func (a *TarArchiver) open() error { } func (a *TarArchiver) close() { - if a.filewriter != nil { - a.filewriter.Close() - a.filewriter = nil - } if a.tarWriter != nil { - a.tarWriter.Close() + err := a.tarWriter.Close() + if err != nil { + fmt.Printf("error closing tarwriter : %s\n\n", err) + } a.tarWriter = nil } if a.compressionWriter != nil { - a.compressionWriter.Close() + err := a.compressionWriter.Close() + if err != nil { + fmt.Printf("error closing compressionWriter : %s\n\n", err) + } a.compressionWriter = nil } + if a.fileWriter != nil { + err := a.fileWriter.Close() + if err != nil { + fmt.Printf("error closing fileWriter: %s\n\n", err) + } + a.fileWriter = nil + } } func (a *TarArchiver) addFile(filePath string, header *tar.Header) error { From 9fc8a64e18de5bd1eda3247f5a6ad29a81e2060a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Tue, 30 Jul 2024 22:34:20 +0200 Subject: [PATCH 23/33] fix: conflicts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- internal/provider/tar_archiver.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/internal/provider/tar_archiver.go b/internal/provider/tar_archiver.go index d4c3fde8..14977d18 100644 --- a/internal/provider/tar_archiver.go +++ b/internal/provider/tar_archiver.go @@ -125,7 +125,10 @@ func (a *TarArchiver) createWalkFunc(basePath, indirname string, opts ArchiveDir archivePath := filepath.Join(basePath, relname) - isMatch := checkMatch(archivePath, opts.Excludes) + isMatch, err := checkMatch(archivePath, opts.Excludes) + if err != nil { + return fmt.Errorf("error checking excludes matches: %w", err) + } if info.IsDir() { if isMatch { @@ -267,11 +270,11 @@ func (a *TarArchiver) addFile(filePath string, header *tar.Header) error { defer file.Close() if a.outputFileMode != "" { - filemode, err := strconv.ParseInt(a.outputFileMode, 0, 32) + fileMode, err := strconv.ParseInt(a.outputFileMode, 0, 32) if err != nil { return fmt.Errorf("error parsing output_file_mode value: %s", a.outputFileMode) } - header.Mode = filemode + header.Mode = fileMode } err = a.tarWriter.WriteHeader(header) From ec0ede246fdf1cab9bbc21308c92b4ca50c3ed51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 18:38:29 +0200 Subject: [PATCH 24/33] Update internal/provider/tar_archiver_test.go Co-authored-by: Selena Goods --- internal/provider/tar_archiver_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 26edb907..def2d58f 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -365,10 +365,8 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte } default: - t.Fatalf("%s : %c %s %s\n", - "Yikes! Unable to figure out type", + t.Fatalf("Unable to figure out type: %c in file: %s\n", header.Typeflag, - "in file", name, ) } From 4847d785b44ccf0f80f9aa011b82edf57384d62c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 18:40:49 +0200 Subject: [PATCH 25/33] test: move assert out of loop --- internal/provider/tar_archiver_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index def2d58f..d22bd5c1 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -337,10 +337,6 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte t.Fatal(err) } - if len(wants) < i+1 { - t.Fatalf("unexpect file count in tar. expect %d", len(wants)) - } - name := header.Name switch header.Typeflag { @@ -373,6 +369,10 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte i++ } + + if len(wants) < i+1 { + t.Fatalf("unexpect file count in tar. expect %d", len(wants)) + } } func ensureTarFileMode(t *testing.T, tarfilepath string, outputFileMode string) { From 41187d394451b42dd285c53e483b7ad36d5c35eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 18:41:52 +0200 Subject: [PATCH 26/33] test: remove unused var --- internal/provider/tar_archiver_test.go | 3 --- 1 file changed, 3 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index d22bd5c1..66680972 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -398,7 +398,6 @@ func ensureTarFileMode(t *testing.T, tarfilepath string, outputFileMode string) var osfilemode = os.FileMode(filemode) - i := 0 for { header, err := tarReader.Next() @@ -427,7 +426,5 @@ func ensureTarFileMode(t *testing.T, tarfilepath string, outputFileMode string) name, ) } - - i++ } } From f3c9eb30cd1087025651547d1bf4b1f117acd6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 19:03:48 +0200 Subject: [PATCH 27/33] Update internal/provider/tar_archiver_test.go Co-authored-by: Selena Goods --- internal/provider/tar_archiver_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index 66680972..d80294b6 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -419,10 +419,8 @@ func ensureTarFileMode(t *testing.T, tarfilepath string, outputFileMode string) t.Fatalf("Expected filemode \"%s\" but was \"%s\"", osfilemode, header.FileInfo().Mode()) } default: - t.Fatalf("%s : %c %s %s\n", - "Yikes! Unable to figure out type", + t.Fatalf("Unable to figure out type: %c in file: %s\n", header.Typeflag, - "in file", name, ) } From cb4cec2a1cb47f54dbf7c00cc1080b7c74408dcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 19:04:03 +0200 Subject: [PATCH 28/33] test: add ensureTarContents to TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories and TestTarArchiver_Dir_ExcludeSymlinkDirectories --- go.mod | 7 ++-- go.sum | 14 ++++---- internal/provider/tar_archiver_test.go | 45 +++++++++++++++++++++----- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/go.mod b/go.mod index 5ce46219..a46c2a19 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/hashicorp/terraform-plugin-framework-validators v0.13.0 github.com/hashicorp/terraform-plugin-go v0.23.0 github.com/hashicorp/terraform-plugin-testing v1.9.0 + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 ) require ( @@ -54,12 +55,12 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/zclconf/go-cty v1.14.4 // indirect golang.org/x/crypto v0.25.0 // indirect - golang.org/x/mod v0.17.0 // indirect - golang.org/x/net v0.25.0 // indirect + golang.org/x/mod v0.19.0 // indirect + golang.org/x/net v0.27.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.22.0 // indirect golang.org/x/text v0.16.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/tools v0.23.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/grpc v1.63.2 // indirect diff --git a/go.sum b/go.sum index afd408fc..27fd77b0 100644 --- a/go.sum +++ b/go.sum @@ -164,15 +164,17 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -204,8 +206,8 @@ golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index d80294b6..cde9b528 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -13,6 +13,9 @@ import ( "strconv" "testing" "time" + + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" ) func TestTarArchiver_Content(t *testing.T) { @@ -257,6 +260,18 @@ func TestTarArchiver_Dir_ExcludeSymlinkDirectories(t *testing.T) { if err != nil { t.Errorf("expected no error: %s", err) } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) } func TestTarArchiver_Dir_Exclude_DoNotExcludeSymlinkDirectories(t *testing.T) { @@ -306,12 +321,23 @@ func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { if err != nil { t.Errorf("expected no error: %s", err) } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) } -func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte) { +func ensureTarContents(t *testing.T, tarFilePath string, wants map[string][]byte) { t.Helper() - f, err := os.Open(tarfilepath) + f, err := os.Open(tarFilePath) if err != nil { t.Fatalf("could not open tar.gz file: %s", err) } @@ -325,7 +351,8 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte tarReader := tar.NewReader(gzf) - i := 0 + tarFileNames := make([]string, 0, len(wants)) + for { header, err := tarReader.Next() @@ -338,6 +365,7 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte } name := header.Name + tarFileNames = append(tarFileNames, name) switch header.Typeflag { case tar.TypeDir: @@ -359,19 +387,20 @@ func ensureTarContents(t *testing.T, tarfilepath string, wants map[string][]byte if gotContent != wantContent { t.Errorf("mismatched content\ngot\n%s\nwant\n%s", gotContent, wantContent) } - default: t.Fatalf("Unable to figure out type: %c in file: %s\n", header.Typeflag, name, ) } - - i++ } - if len(wants) < i+1 { - t.Fatalf("unexpect file count in tar. expect %d", len(wants)) + wantFileNames := maps.Keys(wants) + slices.Sort(wantFileNames) + slices.Sort(tarFileNames) + + if len(wants) != len(tarFileNames) { + t.Fatalf("unexpect file count in tar\ngot\n%s\nwant\n%s", tarFileNames, wantFileNames) } } From da5931643fc1601fd0a5a5ed323c9e40f63c9a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 19:10:39 +0200 Subject: [PATCH 29/33] rebase from main --- .../provider/data_source_archive_file_test.go | 18 +++---- internal/provider/tar_archiver_test.go | 49 +++++++++++++++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/internal/provider/data_source_archive_file_test.go b/internal/provider/data_source_archive_file_test.go index 6e36426a..37496515 100644 --- a/internal/provider/data_source_archive_file_test.go +++ b/internal/provider/data_source_archive_file_test.go @@ -24,7 +24,7 @@ func TestAccArchiveFile_Basic(t *testing.T) { ProtoV5ProviderFactories: protoV5ProviderFactories(), Steps: []r.TestStep{ { - Config: testAccArchiveFileContentConfig(f), + Config: testAccArchiveFileContentConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -49,7 +49,7 @@ func TestAccArchiveFile_Basic(t *testing.T) { ), }, { - Config: testAccArchiveFileFileConfig(f), + Config: testAccArchiveFileFileConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -74,7 +74,7 @@ func TestAccArchiveFile_Basic(t *testing.T) { ), }, { - Config: testAccArchiveFileDirConfig(f), + Config: testAccArchiveFileDirConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -99,21 +99,21 @@ func TestAccArchiveFile_Basic(t *testing.T) { ), }, { - Config: testAccArchiveFileDirExcludesConfig(f), + Config: testAccArchiveFileDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), ), }, { - Config: testAccArchiveFileDirExcludesGlobConfig(f), + Config: testAccArchiveFileDirExcludesGlobConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), ), }, { - Config: testAccArchiveFileMultiSourceConfig(f), + Config: testAccArchiveFileMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), @@ -1404,15 +1404,15 @@ data "archive_file" "foo" { `, format, filepath.ToSlash(outputPath)) } -func testAccArchiveFileDirExcludesGlobConfig(outputPath string) string { +func testAccArchiveFileDirExcludesGlobConfig(format, outputPath string) string { return fmt.Sprintf(` data "archive_file" "foo" { - type = "zip" + type = "%s" source_dir = "test-fixtures/test-dir/test-dir1" excludes = ["test-fixtures/test-dir/test-dir1/file2.txt", "**/file[2-3].txt"] output_path = "%s" } -`, filepath.ToSlash(outputPath)) +`, format, filepath.ToSlash(outputPath)) } func testAccArchiveFileMultiSourceConfig(format, outputPath string) string { diff --git a/internal/provider/tar_archiver_test.go b/internal/provider/tar_archiver_test.go index cde9b528..bc19dbcb 100644 --- a/internal/provider/tar_archiver_test.go +++ b/internal/provider/tar_archiver_test.go @@ -306,6 +306,32 @@ func TestTarArchiver_Dir_Exclude_DoNotExcludeSymlinkDirectories(t *testing.T) { }) } +func TestTarArchiver_Dir_Exclude_Glob_DoNotExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar") + + archiver := NewTarGzArchiver(tarFilePath) + if err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + Excludes: []string{ + "**/file1.txt", + "**/file2.*", + "test-dir-with-symlink-dir/test-symlink-dir", + "test-symlink-dir-with-symlink-file/test-symlink.txt", + }, + }); err != nil { + t.Fatalf("unexpected error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + }) +} + func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.tar.gz") @@ -334,6 +360,29 @@ func TestTarArchiver_Dir_Exclude_ExcludeSymlinkDirectories(t *testing.T) { }) } +func TestTarArchiver_Dir_Exclude_Glob_ExcludeSymlinkDirectories(t *testing.T) { + tarFilePath := filepath.Join(t.TempDir(), "archive-dir-with-symlink-dir.zip") + + archiver := NewTarGzArchiver(tarFilePath) + err := archiver.ArchiveDir("./test-fixtures", ArchiveDirOpts{ + Excludes: []string{ + "test-dir/test-dir1/file1.txt", + "**/file[2-3].txt", + "test-dir-with-symlink-file", + }, + ExcludeSymlinkDirectories: true, + }) + + if err != nil { + t.Errorf("expected no error: %s", err) + } + + ensureTarContents(t, tarFilePath, map[string][]byte{ + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-file.txt": []byte("This is test content"), + }) +} + func ensureTarContents(t *testing.T, tarFilePath string, wants map[string][]byte) { t.Helper() From 3fefca5c3042554589e98bd6bfadd8f55a11ef6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 19:35:59 +0200 Subject: [PATCH 30/33] split tests --- .../provider/data_source_archive_file_test.go | 1112 -------------- .../data_source_archive_file_tgz_test.go | 1124 ++++++++++++++ .../data_source_archive_file_zip_test.go | 1124 ++++++++++++++ .../provider/resource_archive_file_test.go | 1352 ++--------------- .../resource_archive_file_tgz_test.go | 1149 ++++++++++++++ .../resource_archive_file_zip_test.go | 1149 ++++++++++++++ 6 files changed, 4654 insertions(+), 2356 deletions(-) create mode 100644 internal/provider/data_source_archive_file_tgz_test.go create mode 100644 internal/provider/data_source_archive_file_zip_test.go create mode 100644 internal/provider/resource_archive_file_tgz_test.go create mode 100644 internal/provider/resource_archive_file_zip_test.go diff --git a/internal/provider/data_source_archive_file_test.go b/internal/provider/data_source_archive_file_test.go index 37496515..3585b322 100644 --- a/internal/provider/data_source_archive_file_test.go +++ b/internal/provider/data_source_archive_file_test.go @@ -7,122 +7,12 @@ import ( "fmt" "os" "path/filepath" - "regexp" "testing" r "github.com/hashicorp/terraform-plugin-testing/helper/resource" "github.com/hashicorp/terraform-plugin-testing/terraform" ) -func TestAccArchiveFile_Basic(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveFileContentConfig("zip", f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_md5", "ea35f0444ea9a3d5641d8760bc2815cc", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha", "019c79c4dc14dbe1edb3e467b2de6a6aad148717", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha256", "3fb55c931a048943b8d7558dde7c2e4bfc8e04be33b1b55691053d1352391fa7", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha256", "P7VckxoEiUO411WN3nwuS/yOBL4zsbVWkQU9E1I5H6c=", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha512", "57e2d073dce214609bd61113b90b0b2b7c75034047224d56e35f363c8f2662e3acd561eebf94826a67453411181eca7e1cbf15db1f2fdd496cf13df46b7848c3", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha512", "V+LQc9ziFGCb1hETuQsLK3x1A0BHIk1W4182PI8mYuOs1WHuv5SCamdFNBEYHsp+HL8V2x8v3Uls8T30a3hIww==", - ), - ), - }, - { - Config: testAccArchiveFileFileConfig("zip", f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_md5", "59fbc9e62af3cbc2f588f97498240dae", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha", "ce4ee1450ab93ac86e11446649e44cea907b6568", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha256", "5131387f97167da47aa741df3ab2c82f182f17c514c222538d34708d04e0756b", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha256", "UTE4f5cWfaR6p0HfOrLILxgvF8UUwiJTjTRwjQTgdWs=", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha512", "eb33eb0f8cd8efe1a5a0b99acbd22ed22dbebb80817f8de6e8fed15c21c52240838d9bb46fb0938846c74f694425551ba60829a6396f91fcfe49d21a1e3bb409", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha512", "6zPrD4zY7+GloLmay9Iu0i2+u4CBf43m6P7RXCHFIkCDjZu0b7CTiEbHT2lEJVUbpggppjlvkfz+SdIaHju0CQ==", - ), - ), - }, - { - Config: testAccArchiveFileDirConfig("zip", f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_md5", "b73f64a383716070aa4a29563b8b14d4", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha", "76d20a402eefd1cfbdc47886abd4e0909616c191", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha256", "c9d07cc2dabc9caf6f43bed51fa613c281e6ca58cae3a8d6fae2094b00b3369a", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha256", "ydB8wtq8nK9vQ77VH6YTwoHmyljK46jW+uIJSwCzNpo=", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_sha512", "b96ac6b9554a04473a733be36f190422bf7162b4afdb211a0f551713eadf4092459426750646c70383ce6c8b89171b88582a608e5841bfaaafa17004a2a2ca0a", - ), - r.TestCheckResourceAttr( - "data.archive_file.foo", "output_base64sha512", "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", - ), - ), - }, - { - Config: testAccArchiveFileDirExcludesConfig("zip", f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - ), - }, - { - Config: testAccArchiveFileDirExcludesGlobConfig("zip", f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - ), - }, - { - Config: testAccArchiveFileMultiSourceConfig("zip", f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - ), - }, - }, - }) -} - func TestDataSource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { td := t.TempDir() @@ -346,1008 +236,6 @@ func TestDataSource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { }) } -func TestAccArchiveFile_SourceConfigMissing(t *testing.T) { - for _, format := range []string{"zip", "tar.gz"} { - t.Run(format, func(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigMissing(format), - ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), - }, - }, - }) - }) - } -} - -func TestAccArchiveFile_SourceConfigConflicting(t *testing.T) { - for _, format := range []string{"zip", "tar.gz"} { - t.Run(format, func(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigConflicting(format), - ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), - }, - }, - }) - }) - } -} - -// TestAccArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an -// archive which includes the file. -func TestAccArchiveFile_SymlinkFile_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkFile_Absolute verifies that a symlink to a file using an absolute path generates an -// archive which includes the file. -func TestAccArchiveFile_SymlinkFile_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectory_Relative verifies that a symlink to a directory using a relative path -// generates an archive which includes the files in the directory. -func TestAccArchiveFile_SymlinkDirectory_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "file1.txt": []byte(`This is file 1`), - "file2.txt": []byte(`This is file 2`), - "file3.txt": []byte(`This is file 3`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectory_Absolute verifies that a symlink to a directory using an absolute path -// generates an archive which includes the files in the directory. -func TestAccArchiveFile_SymlinkDirectory_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "file1.txt": []byte(`This is file 1`), - "file2.txt": []byte(`This is file 2`), - "file3.txt": []byte(`This is file 3`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_DirectoryWithSymlinkFile_Relative verifies that a relative path to a directory containing -// a symlink file generates an archive which includes the files in the directory. -func TestAccArchiveFile_DirectoryWithSymlinkFile_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_DirectoryWithSymlinkFile_Absolute verifies that an absolute path to a directory containing -// a symlink file generates an archive which includes the files in the directory. -func TestAccArchiveFile_DirectoryWithSymlinkFile_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative verifies that a relative path to a symlink -// file in a symlink directory generates an archive which includes the files in the directory. -func TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute verifies that an absolute path to a symlink -// file in a symlink directory generates an archive which includes the files in the directory. -func TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative verifies that a relative path to a -// directory containing a symlink to a directory generates an archive which includes the directory. -func TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute verifies that an absolute path to a -// directory containing a symlink to a directory generates an archive which includes the directory. -func TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_Multiple_Relative verifies that a relative path to a directory containing multiple -// directories including symlink directories generates an archive which includes the directories and files. -func TestAccArchiveFile_Multiple_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_Multiple_Absolute verifies that an absolute path to a directory containing multiple -// directories including symlink directories generates an archive which includes the directories and files. -func TestAccArchiveFile_Multiple_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a symlink to a file using a relative -// path generates an archive which includes the file. -func TestAccArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that a symlink to a file using an absolute -// path generates an archive which includes the file. -func TestAccArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestAccArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestAccArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") - if err != nil { - t.Fatal(err) - } - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestAccArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path to a -// directory containing a symlink file generates an archive which includes the files in the directory. -func TestAccArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path to a -// directory containing a symlink file generates an archive which includes the files in the directory. -func TestAccArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path -// to a symlink file in a symlink directory generates an archive which includes the files in the directory. -func TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path -// to a symlink file in a symlink directory generates an archive which includes the files in the directory. -func TestAccArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestAccArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestAccArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") - if err != nil { - t.Fatal(err) - } - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that -// symlinked directories are excluded. -func TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestAccArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that -// symlinked directories are excluded. -func TestAccArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - data "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - func testAccArchiveFileSize(filename string, fileSize *string) r.TestCheckFunc { return func(s *terraform.State) error { *fileSize = "" diff --git a/internal/provider/data_source_archive_file_tgz_test.go b/internal/provider/data_source_archive_file_tgz_test.go new file mode 100644 index 00000000..c6d20bec --- /dev/null +++ b/internal/provider/data_source_archive_file_tgz_test.go @@ -0,0 +1,1124 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "fmt" + "path/filepath" + "regexp" + "testing" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccTarGzArchiveFile_Basic(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveFileContentConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", "a09ee39e708c38ccd9ba44cc39e7cacc", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", "6c84af188d367644731196007301c9dc93914b0e", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", "ae18ec27af576dd62f29cec7ae0df130e7487c1a3cddefdec9f27d5ed3a4ca95", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", "rhjsJ69XbdYvKc7Hrg3xMOdIfBo83e/eyfJ9XtOkypU=", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", "ce93a8ba072bad42656a41def0c9ed160f9109c1a7087fb0dcf0b9fce9effc25477f9cdbf9cbc5aa593f3ded0e0db11d2c8cf67dc8d2693ff4069aa01071e68d", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", "zpOougcrrUJlakHe8MntFg+RCcGnCH+w3PC5/Onv/CVHf5zb+cvFqlk/Pe0ODbEdLIz2fcjSaT/0BpqgEHHmjQ==", + ), + ), + }, + { + Config: testAccArchiveFileFileConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", "39948f8ddedc8914ac2e42dd18dd3c06", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", "d2f26a69cbb920715f797f81c1477c41d8fc9195", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", "6f1b1a5d17e42fae154f0bdf9301a0ad43394d7fe8485b64dfdb533a0cf07784", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", "bxsaXRfkL64VTwvfkwGgrUM5TX/oSFtk39tTOgzwd4Q=", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", "e0dc636c3d11095a5b5e97c598185ee3d8a0ed7d1accb69cc28419aeeaeda22b2e774a260f71892a2e85efae1a3aee36669b61dafaed9ac0886d2ca8c5add6e9", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", "4NxjbD0RCVpbXpfFmBhe49ig7X0azLacwoQZrurtoisud0omD3GJKi6F764aOu42Zpth2vrtmsCIbSyoxa3W6Q==", + ), + ), + }, + { + Config: testAccArchiveFileDirConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", "6678fae1fe2077c767bac136861e3bdc", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", "3af6ef3c57aaa5ab3681cd25f916d6651b806cb6", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", "1b10e0f355025819486fb688aa04217939ea976cd271089bc0092e2994dbaaba", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", "GxDg81UCWBlIb7aIqgQheTnql2zScQibwAkuKZTbqro=", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", "adb56fca1e40420d4f994d031a08ca0d1ee51783f3c5d1631b6ed2b460ff2577f9154cb5f1c06edd0b0162899f7cfa7cc3d1f02ec9c9ae76f7ea64a31ba8cb81", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", "rbVvyh5AQg1PmU0DGgjKDR7lF4PzxdFjG27StGD/JXf5FUy18cBu3QsBYomffPp8w9HwLsnJrnb36mSjG6jLgQ==", + ), + ), + }, + { + Config: testAccArchiveFileDirExcludesConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileDirExcludesGlobConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileMultiSourceConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + }, + }) +} + +func TestAccTarGzArchiveFile_SourceConfigMissing(t *testing.T) { + for _, format := range []string{"tar.gz", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing(format), + ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), + }, + }, + }) + }) + } +} + +func TestAccTarGzArchiveFile_SourceConfigConflicting(t *testing.T) { + for _, format := range []string{"tar.gz", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigConflicting(format), + ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), + }, + }, + }) + }) + } +} + +// TestAccTarGzArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an +// archive which includes the file. +func TestAccTarGzArchiveFile_SymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkFile_Absolute verifies that a symlink to a file using an absolute path generates an +// archive which includes the file. +func TestAccTarGzArchiveFile_SymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectory_Relative verifies that a symlink to a directory using a relative path +// generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_SymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectory_Absolute verifies that a symlink to a directory using an absolute path +// generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_SymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Relative verifies that a relative path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Absolute verifies that an absolute path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative verifies that a relative path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute verifies that an absolute path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative verifies that a relative path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestAccTarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute verifies that an absolute path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestAccTarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_Multiple_Relative verifies that a relative path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestAccTarGzArchiveFile_Multiple_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_Multiple_Absolute verifies that an absolute path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestAccTarGzArchiveFile_Multiple_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a symlink to a file using a relative +// path generates an archive which includes the file. +func TestAccTarGzArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that a symlink to a file using an absolute +// path generates an archive which includes the file. +func TestAccTarGzArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccTarGzArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccTarGzArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestAccTarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccTarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccTarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestAccTarGzArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccTarGzArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestAccTarGzArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} diff --git a/internal/provider/data_source_archive_file_zip_test.go b/internal/provider/data_source_archive_file_zip_test.go new file mode 100644 index 00000000..c45c8663 --- /dev/null +++ b/internal/provider/data_source_archive_file_zip_test.go @@ -0,0 +1,1124 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "fmt" + "path/filepath" + "regexp" + "testing" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccZipArchiveFile_Basic(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveFileContentConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", "ea35f0444ea9a3d5641d8760bc2815cc", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", "019c79c4dc14dbe1edb3e467b2de6a6aad148717", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", "3fb55c931a048943b8d7558dde7c2e4bfc8e04be33b1b55691053d1352391fa7", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", "P7VckxoEiUO411WN3nwuS/yOBL4zsbVWkQU9E1I5H6c=", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", "57e2d073dce214609bd61113b90b0b2b7c75034047224d56e35f363c8f2662e3acd561eebf94826a67453411181eca7e1cbf15db1f2fdd496cf13df46b7848c3", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", "V+LQc9ziFGCb1hETuQsLK3x1A0BHIk1W4182PI8mYuOs1WHuv5SCamdFNBEYHsp+HL8V2x8v3Uls8T30a3hIww==", + ), + ), + }, + { + Config: testAccArchiveFileFileConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", "59fbc9e62af3cbc2f588f97498240dae", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", "ce4ee1450ab93ac86e11446649e44cea907b6568", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", "5131387f97167da47aa741df3ab2c82f182f17c514c222538d34708d04e0756b", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", "UTE4f5cWfaR6p0HfOrLILxgvF8UUwiJTjTRwjQTgdWs=", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", "eb33eb0f8cd8efe1a5a0b99acbd22ed22dbebb80817f8de6e8fed15c21c52240838d9bb46fb0938846c74f694425551ba60829a6396f91fcfe49d21a1e3bb409", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", "6zPrD4zY7+GloLmay9Iu0i2+u4CBf43m6P7RXCHFIkCDjZu0b7CTiEbHT2lEJVUbpggppjlvkfz+SdIaHju0CQ==", + ), + ), + }, + { + Config: testAccArchiveFileDirConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_md5", "b73f64a383716070aa4a29563b8b14d4", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha", "76d20a402eefd1cfbdc47886abd4e0909616c191", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha256", "c9d07cc2dabc9caf6f43bed51fa613c281e6ca58cae3a8d6fae2094b00b3369a", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha256", "ydB8wtq8nK9vQ77VH6YTwoHmyljK46jW+uIJSwCzNpo=", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_sha512", "b96ac6b9554a04473a733be36f190422bf7162b4afdb211a0f551713eadf4092459426750646c70383ce6c8b89171b88582a608e5841bfaaafa17004a2a2ca0a", + ), + r.TestCheckResourceAttr( + "data.archive_file.foo", "output_base64sha512", "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", + ), + ), + }, + { + Config: testAccArchiveFileDirExcludesConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileDirExcludesGlobConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileMultiSourceConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + ), + }, + }, + }) +} + +func TestAccZipArchiveFile_SourceConfigMissing(t *testing.T) { + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing(format), + ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir]`), + }, + }, + }) + }) + } +} + +func TestAccZipArchiveFile_SourceConfigConflicting(t *testing.T) { + for _, format := range []string{"zip", "tar.gz"} { + t.Run(format, func(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigConflicting(format), + ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), + }, + }, + }) + }) + } +} + +// TestAccZipArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an +// archive which includes the file. +func TestAccZipArchiveFile_SymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkFile_Absolute verifies that a symlink to a file using an absolute path generates an +// archive which includes the file. +func TestAccZipArchiveFile_SymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectory_Relative verifies that a symlink to a directory using a relative path +// generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_SymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectory_Absolute verifies that a symlink to a directory using an absolute path +// generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_SymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_DirectoryWithSymlinkFile_Relative verifies that a relative path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_DirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_DirectoryWithSymlinkFile_Absolute verifies that an absolute path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_DirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative verifies that a relative path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute verifies that an absolute path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_DirectoryWithSymlinkDirectory_Relative verifies that a relative path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestAccZipArchiveFile_DirectoryWithSymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute verifies that an absolute path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestAccZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_Multiple_Relative verifies that a relative path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestAccZipArchiveFile_Multiple_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_Multiple_Absolute verifies that an absolute path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestAccZipArchiveFile_Multiple_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a symlink to a file using a relative +// path generates an archive which includes the file. +func TestAccZipArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that a symlink to a file using an absolute +// path generates an archive which includes the file. +func TestAccZipArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccZipArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccZipArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccZipArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestAccZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccZipArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestAccZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestAccZipArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestAccZipArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestAccZipArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestAccZipArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + data "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("data.archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("data.archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} diff --git a/internal/provider/resource_archive_file_test.go b/internal/provider/resource_archive_file_test.go index 8c815006..3fc35bc8 100644 --- a/internal/provider/resource_archive_file_test.go +++ b/internal/provider/resource_archive_file_test.go @@ -13,115 +13,6 @@ import ( r "github.com/hashicorp/terraform-plugin-testing/helper/resource" ) -func TestAccArchiveFile_Resource_Basic(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveFileResourceContentConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "archive_file.foo", "output_md5", "ea35f0444ea9a3d5641d8760bc2815cc", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha", "019c79c4dc14dbe1edb3e467b2de6a6aad148717", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha256", "3fb55c931a048943b8d7558dde7c2e4bfc8e04be33b1b55691053d1352391fa7", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha256", "P7VckxoEiUO411WN3nwuS/yOBL4zsbVWkQU9E1I5H6c=", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha512", "57e2d073dce214609bd61113b90b0b2b7c75034047224d56e35f363c8f2662e3acd561eebf94826a67453411181eca7e1cbf15db1f2fdd496cf13df46b7848c3", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha512", "V+LQc9ziFGCb1hETuQsLK3x1A0BHIk1W4182PI8mYuOs1WHuv5SCamdFNBEYHsp+HL8V2x8v3Uls8T30a3hIww==", - ), - ), - }, - { - Config: testAccArchiveFileResourceFileConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "archive_file.foo", "output_md5", "59fbc9e62af3cbc2f588f97498240dae", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha", "ce4ee1450ab93ac86e11446649e44cea907b6568", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha256", "5131387f97167da47aa741df3ab2c82f182f17c514c222538d34708d04e0756b", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha256", "UTE4f5cWfaR6p0HfOrLILxgvF8UUwiJTjTRwjQTgdWs=", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha512", "eb33eb0f8cd8efe1a5a0b99acbd22ed22dbebb80817f8de6e8fed15c21c52240838d9bb46fb0938846c74f694425551ba60829a6396f91fcfe49d21a1e3bb409", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha512", "6zPrD4zY7+GloLmay9Iu0i2+u4CBf43m6P7RXCHFIkCDjZu0b7CTiEbHT2lEJVUbpggppjlvkfz+SdIaHju0CQ==", - ), - ), - }, - { - Config: testAccArchiveFileResourceDirConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "archive_file.foo", "output_md5", "b73f64a383716070aa4a29563b8b14d4", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha", "76d20a402eefd1cfbdc47886abd4e0909616c191", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha256", "c9d07cc2dabc9caf6f43bed51fa613c281e6ca58cae3a8d6fae2094b00b3369a", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha256", "ydB8wtq8nK9vQ77VH6YTwoHmyljK46jW+uIJSwCzNpo=", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha512", "b96ac6b9554a04473a733be36f190422bf7162b4afdb211a0f551713eadf4092459426750646c70383ce6c8b89171b88582a608e5841bfaaafa17004a2a2ca0a", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha512", "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", - ), - ), - }, - { - Config: testAccArchiveFileResourceDirExcludesConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - ), - }, - { - Config: testAccArchiveFileResourceDirExcludesGlobConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - ), - }, - { - Config: testAccArchiveFileResourceMultiSourceConfig(f), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - ), - }, - }, - }) -} - func TestResource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { td := t.TempDir() @@ -138,7 +29,7 @@ func TestResource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileResourceContentConfig(f), + Config: testAccArchiveFileResourceContentConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), @@ -155,12 +46,12 @@ func TestResource_UpgradeFromVersion2_2_0_ContentConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceContentConfig(f), + Config: testAccArchiveFileResourceContentConfig("zip", f), PlanOnly: true, }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceContentConfig(f), + Config: testAccArchiveFileResourceContentConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -194,7 +85,7 @@ func TestResource_UpgradeFromVersion2_2_0_FileConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileResourceFileConfig(f), + Config: testAccArchiveFileResourceFileConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), @@ -211,12 +102,12 @@ func TestResource_UpgradeFromVersion2_2_0_FileConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceFileConfig(f), + Config: testAccArchiveFileResourceFileConfig("zip", f), PlanOnly: true, }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceFileConfig(f), + Config: testAccArchiveFileResourceFileConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -250,7 +141,7 @@ func TestResource_UpgradeFromVersion2_2_0_DirConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileResourceDirConfig(f), + Config: testAccArchiveFileResourceDirConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), @@ -267,12 +158,12 @@ func TestResource_UpgradeFromVersion2_2_0_DirConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceDirConfig(f), + Config: testAccArchiveFileResourceDirConfig("zip", f), PlanOnly: true, }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceDirConfig(f), + Config: testAccArchiveFileResourceDirConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttr( @@ -306,7 +197,7 @@ func TestResource_UpgradeFromVersion2_2_0_DirExcludesConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileResourceDirExcludesConfig(f), + Config: testAccArchiveFileResourceDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), testExtractResourceAttr("archive_file.foo", "output_sha", &outputSha), @@ -315,12 +206,12 @@ func TestResource_UpgradeFromVersion2_2_0_DirExcludesConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceDirExcludesConfig(f), + Config: testAccArchiveFileResourceDirExcludesConfig("zip", f), PlanOnly: true, }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceDirExcludesConfig(f), + Config: testAccArchiveFileResourceDirExcludesConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttrPtr("archive_file.foo", "output_sha", &outputSha), @@ -346,7 +237,7 @@ func TestResource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { Source: "hashicorp/archive", }, }, - Config: testAccArchiveFileResourceMultiSourceConfig(f), + Config: testAccArchiveFileResourceMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( testAccArchiveFileSize(f, &fileSize), testExtractResourceAttr("archive_file.foo", "output_sha", &outputSha), @@ -355,12 +246,12 @@ func TestResource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceMultiSourceConfig(f), + Config: testAccArchiveFileResourceMultiSourceConfig("zip", f), PlanOnly: true, }, { ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceMultiSourceConfig(f), + Config: testAccArchiveFileResourceMultiSourceConfig("zip", f), Check: r.ComposeTestCheckFunc( r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), r.TestCheckResourceAttrPtr("archive_file.foo", "output_sha", &outputSha), @@ -370,69 +261,12 @@ func TestResource_UpgradeFromVersion2_2_0_SourceConfig(t *testing.T) { }) } -// TestResource_FileConfig_ModifiedContents tests that archive_file resource replaces the resource on every read. -// The contents of the source file are altered, but no aspect of the Terraform configuration is changed. -// The change in the output hashes demonstrates that the resource Read function is replacing the resource. -func TestResource_FileConfig_ModifiedContents(t *testing.T) { - td := t.TempDir() - - sourceFilePath := filepath.Join(td, "sourceFile") - outputFilePath := filepath.Join(td, "zip_file_acc_test_upgrade_file_config.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - Steps: []r.TestStep{ - { - PreConfig: func() { - alterFileContents("content", sourceFilePath) - }, - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceFileSourceFileConfig(sourceFilePath, outputFilePath), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(outputFilePath, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha256", "8inOmQJB12dXqCyRTdaRO63yP22Rmuube/A1DLDii10=", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_md5", "20d9c8096f99174d128e5042279fe576", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha", "119d3169ec43fe95bbd38a3824f4e477f4e8d4e7", - ), - ), - }, - { - PreConfig: func() { - alterFileContents("modified content", sourceFilePath) - }, - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Config: testAccArchiveFileResourceFileSourceFileConfig(sourceFilePath, outputFilePath), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(outputFilePath, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttr( - "archive_file.foo", "output_base64sha256", "OnzXDJ3jda5RPuINpxKHQsZ+jSNOupxShSmW3iUWw7Q=", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_md5", "ce31b13da062764f2975d1ef08ee56fe", - ), - r.TestCheckResourceAttr( - "archive_file.foo", "output_sha", "12c51ec24fc5dc10570abbc0b56ac5a3b4141b83", - ), - ), - }, - }, - }) -} - func TestResource_SourceConfigMissing(t *testing.T) { r.ParallelTest(t, r.TestCase{ ProtoV5ProviderFactories: protoV5ProviderFactories(), Steps: []r.TestStep{ { - Config: testResourceSourceConfigMissing(), + Config: testResourceSourceConfigMissing("zip"), ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir]`), }, }, @@ -444,1095 +278,125 @@ func TestResource_SourceConfigConflicting(t *testing.T) { ProtoV5ProviderFactories: protoV5ProviderFactories(), Steps: []r.TestStep{ { - Config: testResourceSourceConfigConflicting(), + Config: testResourceSourceConfigConflicting("zip"), ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), }, }, }) } -// TestResource_ArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an -// archive which includes the file. -func TestResource_ArchiveFile_SymlinkFile_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkFile_Absolute verifies that a symlink to a file using an absolute path generates an -// archive which includes the file. -func TestResource_ArchiveFile_SymlinkFile_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") +func alterFileContents(content, path string) { + f, err := os.Create(path) if err != nil { - t.Fatal(err) + panic(fmt.Sprintf("error creating file: %s", err)) } - var fileSize string + defer f.Close() - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) + _, err = f.Write([]byte(content)) + if err != nil { + panic(fmt.Sprintf("error writing file: %s", err)) + } } -// TestResource_ArchiveFile_SymlinkDirectory_Relative verifies that a symlink to a directory using a relative path -// generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_SymlinkDirectory_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string +func testAccArchiveFileResourceContentConfig(format, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source_content = "This is some content" + source_content_filename = "content.txt" + output_path = "%s" +} +`, format, filepath.ToSlash(outputPath)) +} - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "file1.txt": []byte(`This is file 1`), - "file2.txt": []byte(`This is file 2`), - "file3.txt": []byte(`This is file 3`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) +func testAccArchiveFileResourceFileConfig(format, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source_file = "test-fixtures/test-dir/test-file.txt" + output_path = "%s" + output_file_mode = "0666" +} +`, format, filepath.ToSlash(outputPath)) } -// TestResource_ArchiveFile_SymlinkDirectory_Absolute verifies that a symlink to a directory using an absolute path -// generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_SymlinkDirectory_Absolute(t *testing.T) { - td := t.TempDir() +func testAccArchiveFileResourceFileSourceFileConfig(format, sourceFile, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" +} +`, format, + filepath.ToSlash(sourceFile), + filepath.ToSlash(outputPath)) +} - f := filepath.Join(td, "zip_file_acc_test.zip") +func testAccArchiveFileResourceDirConfig(format, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source_dir = "test-fixtures/test-dir/test-dir1" + output_path = "%s" + output_file_mode = "0666" +} +`, format, filepath.ToSlash(outputPath)) +} - symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") - if err != nil { - t.Fatal(err) - } +func testAccArchiveFileResourceDirExcludesConfig(format, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source_dir = "test-fixtures/test-dir" + excludes = ["test-fixtures/test-dir/file2.txt"] + output_path = "%s" +} +`, format, filepath.ToSlash(outputPath)) +} - var fileSize string +func testAccArchiveFileResourceDirExcludesGlobConfig(format, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source_dir = "test-fixtures/test-dir" + excludes = ["test-fixtures/test-dir/file2.txt", "**/file[2-3].txt"] + output_path = "%s" +} +`, format, filepath.ToSlash(outputPath)) +} - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "file1.txt": []byte(`This is file 1`), - "file2.txt": []byte(`This is file 2`), - "file3.txt": []byte(`This is file 3`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) +func testAccArchiveFileResourceMultiSourceConfig(format, outputPath string) string { + return fmt.Sprintf(` +resource "archive_file" "foo" { + type = "%s" + source { + filename = "content_1.txt" + content = "This is the content for content_1.txt" + } + source { + filename = "content_2.txt" + content = "This is the content for content_2.txt" + } + output_path = "%s" +} +`, format, filepath.ToSlash(outputPath)) } -// TestResource_ArchiveFile_DirectoryWithSymlinkFile_Relative verifies that a relative path to a directory containing -// a symlink file generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_DirectoryWithSymlinkFile_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_DirectoryWithSymlinkFile_Absolute verifies that an absolute path to a directory containing -// a symlink file generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_DirectoryWithSymlinkFile_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative verifies that a relative path to a symlink -// file in a symlink directory generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute verifies that an absolute path to a symlink -// file in a symlink directory generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_DirectoryWithSymlinkDirectory_Relative verifies that a relative path to a -// directory containing a symlink to a directory generates an archive which includes the directory. -func TestResource_ArchiveFile_DirectoryWithSymlinkDirectory_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute verifies that an absolute path to a -// directory containing a symlink to a directory generates an archive which includes the directory. -func TestResource_ArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_Multiple_Relative verifies that a relative path to a directory containing multiple -// directories including symlink directories generates an archive which includes the directories and files. -func TestResource_ArchiveFile_Multiple_Relative(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_Multiple_Absolute verifies that an absolute path to a directory containing multiple -// directories including symlink directories generates an archive which includes the directories and files. -func TestResource_ArchiveFile_Multiple_Absolute(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - } - `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - "test-symlink-dir/file1.txt": []byte("This is file 1"), - "test-symlink-dir/file2.txt": []byte("This is file 2"), - "test-symlink-dir/file3.txt": []byte("This is file 3"), - "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a symlink to a file using a relative -// path generates an archive which includes the file. -func TestResource_ArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that a symlink to a file using an absolute -// path generates an archive which includes the file. -func TestResource_ArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestResource_ArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestResource_ArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") - if err != nil { - t.Fatal(err) - } - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestResource_ArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path to a -// directory containing a symlink file generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path to a -// directory containing a symlink file generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-file.txt": []byte(`This is test content`), - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path -// to a symlink file in a symlink directory generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path -// to a symlink file in a symlink directory generates an archive which includes the files in the directory. -func TestResource_ArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-symlink.txt": []byte(`This is test content`), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestResource_ArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestResource_ArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive -// is generated when trying to archive a directory which only contains a symlink to a directory. -func TestResource_ArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") - if err != nil { - t.Fatal(err) - } - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - exclude_symlink_directories = true - } - `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), - ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), - }, - }, - }) -} - -// TestResource_ArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that -// symlinked directories are excluded. -func TestResource_ArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -// TestResource_ArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that -// symlinked directories are excluded. -func TestResource_ArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) { - td := t.TempDir() - - f := filepath.Join(td, "zip_file_acc_test.zip") - - multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") - if err != nil { - t.Fatal(err) - } - - var fileSize string - - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: fmt.Sprintf(` - resource "archive_file" "foo" { - type = "zip" - source_dir = "%s" - output_path = "%s" - output_file_mode = "0666" - exclude_symlink_directories = true - } - `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), - Check: r.ComposeTestCheckFunc( - testAccArchiveFileSize(f, &fileSize), - r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), - r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { - ensureContents(t, value, map[string][]byte{ - "test-dir/test-dir1/file1.txt": []byte("This is file 1"), - "test-dir/test-dir1/file2.txt": []byte("This is file 2"), - "test-dir/test-dir1/file3.txt": []byte("This is file 3"), - "test-dir/test-dir2/file1.txt": []byte("This is file 1"), - "test-dir/test-dir2/file2.txt": []byte("This is file 2"), - "test-dir/test-dir2/file3.txt": []byte("This is file 3"), - "test-dir/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), - "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), - }) - ensureFileMode(t, value, "0666") - return nil - }), - ), - }, - }, - }) -} - -func alterFileContents(content, path string) { - f, err := os.Create(path) - if err != nil { - panic(fmt.Sprintf("error creating file: %s", err)) - } - - defer f.Close() - - _, err = f.Write([]byte(content)) - if err != nil { - panic(fmt.Sprintf("error writing file: %s", err)) - } -} - -func testAccArchiveFileResourceContentConfig(outputPath string) string { - return fmt.Sprintf(` -resource "archive_file" "foo" { - type = "zip" - source_content = "This is some content" - source_content_filename = "content.txt" - output_path = "%s" -} -`, filepath.ToSlash(outputPath)) -} - -func testAccArchiveFileResourceFileConfig(outputPath string) string { - return fmt.Sprintf(` -resource "archive_file" "foo" { - type = "zip" - source_file = "test-fixtures/test-dir/test-file.txt" - output_path = "%s" - output_file_mode = "0666" -} -`, filepath.ToSlash(outputPath)) -} - -func testAccArchiveFileResourceFileSourceFileConfig(sourceFile, outputPath string) string { - return fmt.Sprintf(` -resource "archive_file" "foo" { - type = "zip" - source_file = "%s" - output_path = "%s" - output_file_mode = "0666" -} -`, - filepath.ToSlash(sourceFile), - filepath.ToSlash(outputPath)) -} - -func testAccArchiveFileResourceDirConfig(outputPath string) string { - return fmt.Sprintf(` -resource "archive_file" "foo" { - type = "zip" - source_dir = "test-fixtures/test-dir/test-dir1" - output_path = "%s" - output_file_mode = "0666" -} -`, filepath.ToSlash(outputPath)) -} - -func testAccArchiveFileResourceDirExcludesConfig(outputPath string) string { +func testResourceSourceConfigMissing(format string) string { return fmt.Sprintf(` resource "archive_file" "foo" { - type = "zip" - source_dir = "test-fixtures/test-dir" - excludes = ["test-fixtures/test-dir/file2.txt"] - output_path = "%s" -} -`, filepath.ToSlash(outputPath)) -} - -func testAccArchiveFileResourceDirExcludesGlobConfig(outputPath string) string { - return fmt.Sprintf(` -resource "archive_file" "foo" { - type = "zip" - source_dir = "test-fixtures/test-dir" - excludes = ["test-fixtures/test-dir/file2.txt", "**/file[2-3].txt"] - output_path = "%s" -} -`, filepath.ToSlash(outputPath)) -} - -func testAccArchiveFileResourceMultiSourceConfig(outputPath string) string { - return fmt.Sprintf(` -resource "archive_file" "foo" { - type = "zip" - source { - filename = "content_1.txt" - content = "This is the content for content_1.txt" - } - source { - filename = "content_2.txt" - content = "This is the content for content_2.txt" - } - output_path = "%s" -} -`, filepath.ToSlash(outputPath)) -} - -func testResourceSourceConfigMissing() string { - return ` -resource "archive_file" "foo" { - type = "zip" + type = "%s" output_path = "path" } -` +`, format) } -func testResourceSourceConfigConflicting() string { - return ` +func testResourceSourceConfigConflicting(format string) string { + return fmt.Sprintf(` resource "archive_file" "foo" { - type = "zip" + type = "%s" source { filename = "content_1.txt" content = "This is the content for content_1.txt" @@ -1540,5 +404,5 @@ resource "archive_file" "foo" { source_dir = "test-fixtures/test-dir" output_path = "path" } -` +`, format) } diff --git a/internal/provider/resource_archive_file_tgz_test.go b/internal/provider/resource_archive_file_tgz_test.go new file mode 100644 index 00000000..7b1f0e13 --- /dev/null +++ b/internal/provider/resource_archive_file_tgz_test.go @@ -0,0 +1,1149 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "fmt" + "path/filepath" + "regexp" + "testing" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccTarGzArchiveFile_Resource_Basic(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveFileResourceContentConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "a09ee39e708c38ccd9ba44cc39e7cacc", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "6c84af188d367644731196007301c9dc93914b0e", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha256", "ae18ec27af576dd62f29cec7ae0df130e7487c1a3cddefdec9f27d5ed3a4ca95", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "rhjsJ69XbdYvKc7Hrg3xMOdIfBo83e/eyfJ9XtOkypU=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha512", "ce93a8ba072bad42656a41def0c9ed160f9109c1a7087fb0dcf0b9fce9effc25477f9cdbf9cbc5aa593f3ded0e0db11d2c8cf67dc8d2693ff4069aa01071e68d", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha512", "zpOougcrrUJlakHe8MntFg+RCcGnCH+w3PC5/Onv/CVHf5zb+cvFqlk/Pe0ODbEdLIz2fcjSaT/0BpqgEHHmjQ==", + ), + ), + }, + { + Config: testAccArchiveFileResourceFileConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "39948f8ddedc8914ac2e42dd18dd3c06", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "d2f26a69cbb920715f797f81c1477c41d8fc9195", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha256", "6f1b1a5d17e42fae154f0bdf9301a0ad43394d7fe8485b64dfdb533a0cf07784", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "bxsaXRfkL64VTwvfkwGgrUM5TX/oSFtk39tTOgzwd4Q=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha512", "e0dc636c3d11095a5b5e97c598185ee3d8a0ed7d1accb69cc28419aeeaeda22b2e774a260f71892a2e85efae1a3aee36669b61dafaed9ac0886d2ca8c5add6e9", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha512", "4NxjbD0RCVpbXpfFmBhe49ig7X0azLacwoQZrurtoisud0omD3GJKi6F764aOu42Zpth2vrtmsCIbSyoxa3W6Q==", + ), + ), + }, + { + Config: testAccArchiveFileResourceDirConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "6678fae1fe2077c767bac136861e3bdc", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "3af6ef3c57aaa5ab3681cd25f916d6651b806cb6", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha256", "1b10e0f355025819486fb688aa04217939ea976cd271089bc0092e2994dbaaba", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "GxDg81UCWBlIb7aIqgQheTnql2zScQibwAkuKZTbqro=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha512", "adb56fca1e40420d4f994d031a08ca0d1ee51783f3c5d1631b6ed2b460ff2577f9154cb5f1c06edd0b0162899f7cfa7cc3d1f02ec9c9ae76f7ea64a31ba8cb81", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha512", "rbVvyh5AQg1PmU0DGgjKDR7lF4PzxdFjG27StGD/JXf5FUy18cBu3QsBYomffPp8w9HwLsnJrnb36mSjG6jLgQ==", + ), + ), + }, + { + Config: testAccArchiveFileResourceDirExcludesConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileResourceDirExcludesGlobConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileResourceMultiSourceConfig("tar.gz", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + ), + }, + }, + }) +} + +// TestResource_TarGzFileConfig_ModifiedContents tests that archive_file resource replaces the resource on every read. +// The contents of the source file are altered, but no aspect of the Terraform configuration is changed. +// The change in the output hashes demonstrates that the resource Read function is replacing the resource. +func TestResource_TarGzFileConfig_ModifiedContents(t *testing.T) { + td := t.TempDir() + + sourceFilePath := filepath.Join(td, "sourceFile") + outputFilePath := filepath.Join(td, "tgz_file_acc_test_upgrade_file_config.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + Steps: []r.TestStep{ + { + PreConfig: func() { + alterFileContents("content", sourceFilePath) + }, + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Config: testAccArchiveFileResourceFileSourceFileConfig("tar.gz", sourceFilePath, outputFilePath), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(outputFilePath, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "7iA9Si8nqVmS7vKVzRfUxvNXNucEnbjt8qrKJIinx2A=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "9a562888c7cfc3f1b20cb68f0fb99e4e", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "159e394a629b7b603ff363158c289b0f3ccf852b", + ), + ), + }, + { + PreConfig: func() { + alterFileContents("modified content", sourceFilePath) + }, + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Config: testAccArchiveFileResourceFileSourceFileConfig("tar.gz", sourceFilePath, outputFilePath), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(outputFilePath, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "2ckWwetFiFJ9ODvHgKR+QpL0A2YtMW3aFMOIoSVWc2o=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "f42e3989922bdf3a98c6ec5836dcb775", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "b7dbd9413862c94ccb47f1a61d334a2633733705", + ), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an +// archive which includes the file. +func TestResource_TarGzArchiveFile_SymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkFile_Absolute verifies that a symlink to a file using an absolute path generates an +// archive which includes the file. +func TestResource_TarGzArchiveFile_SymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectory_Relative verifies that a symlink to a directory using a relative path +// generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_SymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectory_Absolute verifies that a symlink to a directory using an absolute path +// generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_SymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Relative verifies that a relative path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Absolute verifies that an absolute path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative verifies that a relative path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute verifies that an absolute path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative verifies that a relative path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestResource_TarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute verifies that an absolute path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestResource_TarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_Multiple_Relative verifies that a relative path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestResource_TarGzArchiveFile_Multiple_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_Multiple_Absolute verifies that an absolute path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestResource_TarGzArchiveFile_Multiple_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a symlink to a file using a relative +// path generates an archive which includes the file. +func TestResource_TarGzArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that a symlink to a file using an absolute +// path generates an archive which includes the file. +func TestResource_TarGzArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_TarGzArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_TarGzArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_TarGzArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_TarGzArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_TarGzArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestResource_TarGzArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_TarGzArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestResource_TarGzArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "tgz_file_acc_test.tar.gz") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "tar.gz" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureTarContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureTarFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} diff --git a/internal/provider/resource_archive_file_zip_test.go b/internal/provider/resource_archive_file_zip_test.go new file mode 100644 index 00000000..5cbea36d --- /dev/null +++ b/internal/provider/resource_archive_file_zip_test.go @@ -0,0 +1,1149 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package archive + +import ( + "fmt" + "path/filepath" + "regexp" + "testing" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAccZipArchiveFile_Resource_Basic(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveFileResourceContentConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "ea35f0444ea9a3d5641d8760bc2815cc", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "019c79c4dc14dbe1edb3e467b2de6a6aad148717", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha256", "3fb55c931a048943b8d7558dde7c2e4bfc8e04be33b1b55691053d1352391fa7", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "P7VckxoEiUO411WN3nwuS/yOBL4zsbVWkQU9E1I5H6c=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha512", "57e2d073dce214609bd61113b90b0b2b7c75034047224d56e35f363c8f2662e3acd561eebf94826a67453411181eca7e1cbf15db1f2fdd496cf13df46b7848c3", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha512", "V+LQc9ziFGCb1hETuQsLK3x1A0BHIk1W4182PI8mYuOs1WHuv5SCamdFNBEYHsp+HL8V2x8v3Uls8T30a3hIww==", + ), + ), + }, + { + Config: testAccArchiveFileResourceFileConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "59fbc9e62af3cbc2f588f97498240dae", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "ce4ee1450ab93ac86e11446649e44cea907b6568", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha256", "5131387f97167da47aa741df3ab2c82f182f17c514c222538d34708d04e0756b", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "UTE4f5cWfaR6p0HfOrLILxgvF8UUwiJTjTRwjQTgdWs=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha512", "eb33eb0f8cd8efe1a5a0b99acbd22ed22dbebb80817f8de6e8fed15c21c52240838d9bb46fb0938846c74f694425551ba60829a6396f91fcfe49d21a1e3bb409", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha512", "6zPrD4zY7+GloLmay9Iu0i2+u4CBf43m6P7RXCHFIkCDjZu0b7CTiEbHT2lEJVUbpggppjlvkfz+SdIaHju0CQ==", + ), + ), + }, + { + Config: testAccArchiveFileResourceDirConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "b73f64a383716070aa4a29563b8b14d4", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "76d20a402eefd1cfbdc47886abd4e0909616c191", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha256", "c9d07cc2dabc9caf6f43bed51fa613c281e6ca58cae3a8d6fae2094b00b3369a", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "ydB8wtq8nK9vQ77VH6YTwoHmyljK46jW+uIJSwCzNpo=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha512", "b96ac6b9554a04473a733be36f190422bf7162b4afdb211a0f551713eadf4092459426750646c70383ce6c8b89171b88582a608e5841bfaaafa17004a2a2ca0a", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha512", "uWrGuVVKBEc6czvjbxkEIr9xYrSv2yEaD1UXE+rfQJJFlCZ1BkbHA4PObIuJFxuIWCpgjlhBv6qvoXAEoqLKCg==", + ), + ), + }, + { + Config: testAccArchiveFileResourceDirExcludesConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileResourceDirExcludesGlobConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + ), + }, + { + Config: testAccArchiveFileResourceMultiSourceConfig("zip", f), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + ), + }, + }, + }) +} + +// TestResource_FileConfig_ModifiedContents tests that archive_file resource replaces the resource on every read. +// The contents of the source file are altered, but no aspect of the Terraform configuration is changed. +// The change in the output hashes demonstrates that the resource Read function is replacing the resource. +func TestResource_FileConfig_ModifiedContents(t *testing.T) { + td := t.TempDir() + + sourceFilePath := filepath.Join(td, "sourceFile") + outputFilePath := filepath.Join(td, "zip_file_acc_test_upgrade_file_config.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + Steps: []r.TestStep{ + { + PreConfig: func() { + alterFileContents("content", sourceFilePath) + }, + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Config: testAccArchiveFileResourceFileSourceFileConfig("zip", sourceFilePath, outputFilePath), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(outputFilePath, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "8inOmQJB12dXqCyRTdaRO63yP22Rmuube/A1DLDii10=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "20d9c8096f99174d128e5042279fe576", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "119d3169ec43fe95bbd38a3824f4e477f4e8d4e7", + ), + ), + }, + { + PreConfig: func() { + alterFileContents("modified content", sourceFilePath) + }, + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Config: testAccArchiveFileResourceFileSourceFileConfig("zip", sourceFilePath, outputFilePath), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(outputFilePath, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttr( + "archive_file.foo", "output_base64sha256", "OnzXDJ3jda5RPuINpxKHQsZ+jSNOupxShSmW3iUWw7Q=", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_md5", "ce31b13da062764f2975d1ef08ee56fe", + ), + r.TestCheckResourceAttr( + "archive_file.foo", "output_sha", "12c51ec24fc5dc10570abbc0b56ac5a3b4141b83", + ), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an +// archive which includes the file. +func TestResource_ZipArchiveFile_SymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkFile_Absolute verifies that a symlink to a file using an absolute path generates an +// archive which includes the file. +func TestResource_ZipArchiveFile_SymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectory_Relative verifies that a symlink to a directory using a relative path +// generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_SymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectory_Absolute verifies that a symlink to a directory using an absolute path +// generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_SymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "file1.txt": []byte(`This is file 1`), + "file2.txt": []byte(`This is file 2`), + "file3.txt": []byte(`This is file 3`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Relative verifies that a relative path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Absolute verifies that an absolute path to a directory containing +// a symlink file generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative verifies that a relative path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute verifies that an absolute path to a symlink +// file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_DirectoryWithSymlinkDirectory_Relative verifies that a relative path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestResource_ZipArchiveFile_DirectoryWithSymlinkDirectory_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute verifies that an absolute path to a +// directory containing a symlink to a directory generates an archive which includes the directory. +func TestResource_ZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_Multiple_Relative verifies that a relative path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestResource_ZipArchiveFile_Multiple_Relative(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_Multiple_Absolute verifies that an absolute path to a directory containing multiple +// directories including symlink directories generates an archive which includes the directories and files. +func TestResource_ZipArchiveFile_Multiple_Absolute(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-dir/test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-dir-with-symlink-dir/test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-dir-with-symlink-dir/test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + "test-symlink-dir/file1.txt": []byte("This is file 1"), + "test-symlink-dir/file2.txt": []byte("This is file 2"), + "test-symlink-dir/file3.txt": []byte("This is file 3"), + "test-symlink-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-symlink-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a symlink to a file using a relative +// path generates an archive which includes the file. +func TestResource_ZipArchiveFile_SymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that a symlink to a file using an absolute +// path generates an archive which includes the file. +func TestResource_ZipArchiveFile_SymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_ZipArchiveFile_SymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_ZipArchiveFile_SymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithRegularFilesAbs, err := filepath.Abs("test-fixtures/test-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithRegularFilesAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-file"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path to a +// directory containing a symlink file generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_DirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirWithSymlinkFilesAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-file") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirWithSymlinkFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-file.txt": []byte(`This is test content`), + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories verifies that a relative path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories verifies that an absolute path +// to a symlink file in a symlink directory generates an archive which includes the files in the directory. +func TestResource_ZipArchiveFile_SymlinkDirectoryWithSymlinkFile_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkFileInSymlinkDirAbs, err := filepath.Abs("test-fixtures/test-symlink-dir-with-symlink-file/test-symlink.txt") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_file = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkFileInSymlinkDirAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-symlink.txt": []byte(`This is test content`), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_ZipArchiveFile_DirectoryWithSymlinkDirectory_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures/test-dir-with-symlink-dir"), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories verifies that an empty archive +// is generated when trying to archive a directory which only contains a symlink to a directory. +func TestResource_ZipArchiveFile_IncludeDirectoryWithSymlinkDirectory_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + symlinkDirInRegularDirAbs, err := filepath.Abs("test-fixtures/test-dir-with-symlink-dir") + if err != nil { + t.Fatal(err) + } + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + exclude_symlink_directories = true + } + `, filepath.ToSlash(symlinkDirInRegularDirAbs), filepath.ToSlash(f)), + ExpectError: regexp.MustCompile(`.*error creating archive: error archiving directory: archive has not been\ncreated as it would be empty`), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestResource_ZipArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash("test-fixtures"), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} + +// TestResource_ZipArchiveFile_Multiple_Relative_ExcludeSymlinkDirectories verifies that +// symlinked directories are excluded. +func TestResource_ZipArchiveFile_Multiple_Absolute_ExcludeSymlinkDirectories(t *testing.T) { + td := t.TempDir() + + f := filepath.Join(td, "zip_file_acc_test.zip") + + multipleDirsAndFilesAbs, err := filepath.Abs("test-fixtures") + if err != nil { + t.Fatal(err) + } + + var fileSize string + + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: fmt.Sprintf(` + resource "archive_file" "foo" { + type = "zip" + source_dir = "%s" + output_path = "%s" + output_file_mode = "0666" + exclude_symlink_directories = true + } + `, filepath.ToSlash(multipleDirsAndFilesAbs), filepath.ToSlash(f)), + Check: r.ComposeTestCheckFunc( + testAccArchiveFileSize(f, &fileSize), + r.TestCheckResourceAttrPtr("archive_file.foo", "output_size", &fileSize), + r.TestCheckResourceAttrWith("archive_file.foo", "output_path", func(value string) error { + ensureContents(t, value, map[string][]byte{ + "test-dir/test-dir1/file1.txt": []byte("This is file 1"), + "test-dir/test-dir1/file2.txt": []byte("This is file 2"), + "test-dir/test-dir1/file3.txt": []byte("This is file 3"), + "test-dir/test-dir2/file1.txt": []byte("This is file 1"), + "test-dir/test-dir2/file2.txt": []byte("This is file 2"), + "test-dir/test-dir2/file3.txt": []byte("This is file 3"), + "test-dir/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-file.txt": []byte("This is test content"), + "test-dir-with-symlink-file/test-symlink.txt": []byte("This is test content"), + }) + ensureFileMode(t, value, "0666") + return nil + }), + ), + }, + }, + }) +} From 5d66d18bd1e3b445c36abea1d4008dccc599f2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Mon, 5 Aug 2024 23:57:14 +0200 Subject: [PATCH 31/33] remove duplicate tests --- .../data_source_archive_file_tgz_test.go | 44 ++++++++----------- .../data_source_archive_file_zip_test.go | 44 ++++++++----------- 2 files changed, 36 insertions(+), 52 deletions(-) diff --git a/internal/provider/data_source_archive_file_tgz_test.go b/internal/provider/data_source_archive_file_tgz_test.go index c6d20bec..7dd9f39d 100644 --- a/internal/provider/data_source_archive_file_tgz_test.go +++ b/internal/provider/data_source_archive_file_tgz_test.go @@ -122,35 +122,27 @@ func TestAccTarGzArchiveFile_Basic(t *testing.T) { } func TestAccTarGzArchiveFile_SourceConfigMissing(t *testing.T) { - for _, format := range []string{"tar.gz", "tar.gz"} { - t.Run(format, func(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigMissing(format), - ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), - }, - }, - }) - }) - } + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing("tar.gz"), + ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir\]`), + }, + }, + }) } func TestAccTarGzArchiveFile_SourceConfigConflicting(t *testing.T) { - for _, format := range []string{"tar.gz", "tar.gz"} { - t.Run(format, func(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigConflicting(format), - ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), - }, - }, - }) - }) - } + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing("tar.gz"), + ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), + }, + }, + }) } // TestAccTarGzArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an diff --git a/internal/provider/data_source_archive_file_zip_test.go b/internal/provider/data_source_archive_file_zip_test.go index c45c8663..10975b7f 100644 --- a/internal/provider/data_source_archive_file_zip_test.go +++ b/internal/provider/data_source_archive_file_zip_test.go @@ -122,35 +122,27 @@ func TestAccZipArchiveFile_Basic(t *testing.T) { } func TestAccZipArchiveFile_SourceConfigMissing(t *testing.T) { - for _, format := range []string{"zip", "tar.gz"} { - t.Run(format, func(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigMissing(format), - ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir]`), - }, - }, - }) - }) - } + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigMissing("zip"), + ExpectError: regexp.MustCompile(`.*At least one of these attributes must be configured:\n\[source,source_content_filename,source_file,source_dir]`), + }, + }, + }) } func TestAccZipArchiveFile_SourceConfigConflicting(t *testing.T) { - for _, format := range []string{"zip", "tar.gz"} { - t.Run(format, func(t *testing.T) { - r.ParallelTest(t, r.TestCase{ - ProtoV5ProviderFactories: protoV5ProviderFactories(), - Steps: []r.TestStep{ - { - Config: testAccArchiveSourceConfigConflicting(format), - ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), - }, - }, - }) - }) - } + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: protoV5ProviderFactories(), + Steps: []r.TestStep{ + { + Config: testAccArchiveSourceConfigConflicting("zip"), + ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), + }, + }, + }) } // TestAccZipArchiveFile_SymlinkFile_Relative verifies that a symlink to a file using a relative path generates an From 0bb5f6e4c1a792da907d7841e64c63902a09e34b Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 6 Aug 2024 14:19:40 -0400 Subject: [PATCH 32/33] Fix `TestAccTarGzArchiveFile_SourceConfigConflicting()` acceptance test --- internal/provider/data_source_archive_file_tgz_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/data_source_archive_file_tgz_test.go b/internal/provider/data_source_archive_file_tgz_test.go index 7dd9f39d..35619fe4 100644 --- a/internal/provider/data_source_archive_file_tgz_test.go +++ b/internal/provider/data_source_archive_file_tgz_test.go @@ -138,7 +138,7 @@ func TestAccTarGzArchiveFile_SourceConfigConflicting(t *testing.T) { ProtoV5ProviderFactories: protoV5ProviderFactories(), Steps: []r.TestStep{ { - Config: testAccArchiveSourceConfigMissing("tar.gz"), + Config: testAccArchiveSourceConfigConflicting("tar.gz"), ExpectError: regexp.MustCompile(`.*Attribute "source_dir" cannot be specified when "source" is specified`), }, }, From 8cb3a5379ea85449f1dd7192f979e31d0e637580 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Tue, 6 Aug 2024 14:27:20 -0400 Subject: [PATCH 33/33] Add changelog entries --- .changes/unreleased/FEATURES-20240806-142303.yaml | 5 +++++ .changes/unreleased/FEATURES-20240806-142529.yaml | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 .changes/unreleased/FEATURES-20240806-142303.yaml create mode 100644 .changes/unreleased/FEATURES-20240806-142529.yaml diff --git a/.changes/unreleased/FEATURES-20240806-142303.yaml b/.changes/unreleased/FEATURES-20240806-142303.yaml new file mode 100644 index 00000000..b5134165 --- /dev/null +++ b/.changes/unreleased/FEATURES-20240806-142303.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'data-source/archive_file: Add support for creating `tar.gz` archive files.' +time: 2024-08-06T14:23:03.200664-04:00 +custom: + Issue: "277" diff --git a/.changes/unreleased/FEATURES-20240806-142529.yaml b/.changes/unreleased/FEATURES-20240806-142529.yaml new file mode 100644 index 00000000..9ade31d5 --- /dev/null +++ b/.changes/unreleased/FEATURES-20240806-142529.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'resource/archive_file: Add support for creating `tar.gz` archive files.' +time: 2024-08-06T14:25:29.318873-04:00 +custom: + Issue: "277"