From 13b636e23548f487c256427337c2497fcca83f94 Mon Sep 17 00:00:00 2001 From: Alex ATorres Date: Thu, 28 Mar 2024 11:31:15 +0100 Subject: [PATCH] feat(tfvars): Add function to get .tfvars files from a directory This commit adds the GetTFVarsFromWorkdir function to the tfvars package, which scans a directory for .tfvars files and returns their filenames. This function is essential for retrieving Terraform variable files in scenarios. It also includes a new utility function called MergeSlices for combining string slices. --- pkg/scenario/client.go | 37 ++++++++++++++++++ pkg/tfvars/tfvars.go | 34 ++++++++++++++++ pkg/utils/data.go | 11 ++++++ pkg/validation/terraform.go | 15 +++++++ .../.test-data/TerraformOptions.json | 39 ------------------- test/examples/simple/with_options_test.go | 8 ++++ 6 files changed, 105 insertions(+), 39 deletions(-) create mode 100644 pkg/tfvars/tfvars.go create mode 100644 pkg/utils/data.go delete mode 100644 test/data/tf-random/.test-data/TerraformOptions.json diff --git a/pkg/scenario/client.go b/pkg/scenario/client.go index adce6d1..1821aea 100644 --- a/pkg/scenario/client.go +++ b/pkg/scenario/client.go @@ -1,10 +1,15 @@ package scenario import ( + "fmt" "path/filepath" "testing" "time" + "github.com/Excoriate/tftest/pkg/utils" + + "github.com/Excoriate/tftest/pkg/tfvars" + "github.com/Excoriate/tftest/pkg/cloudprovider" "github.com/Excoriate/tftest/pkg/validation" @@ -118,6 +123,38 @@ func WithVarFiles(workdir string, varFiles ...string) OptFn { } } +func WithScannedTFVars(workdir, fixturesDir string) OptFn { + return func(o *Options) error { + if err := validation.IsValidTFDir(workdir); err != nil { + return err + } + + fixturesDirPath := filepath.Join(workdir, fixturesDir) + + if err := validation.IsValidTFDir(fixturesDirPath); err != nil { + return err + } + + hasTFVars, err := validation.HasTFVarFiles(fixturesDirPath) + if err != nil { + return err + } + + if !hasTFVars { + return fmt.Errorf("the Terraform module %s with this fixtures directory %s does not have any .tfvars files", workdir, fixturesDir) + } + + tfVarsPath, tfVarsErr := tfvars.GetTFVarsFromWorkdir(workdir) + if tfVarsErr != nil { + return tfVarsErr + } + + o.varFiles = utils.MergeSlices(o.varFiles, tfVarsPath) + + return nil + } +} + func NewWithOptions(t *testing.T, workdir string, opts ...OptFn) (*Client, error) { o := &Options{} for _, opt := range opts { diff --git a/pkg/tfvars/tfvars.go b/pkg/tfvars/tfvars.go new file mode 100644 index 0000000..5c4843f --- /dev/null +++ b/pkg/tfvars/tfvars.go @@ -0,0 +1,34 @@ +package tfvars + +import ( + "fmt" + "os" + "path/filepath" +) + +// GetTFVarsFromWorkdir scans the provided workdir directory for all .tfvars files +// and returns their filenames. If workdir is empty, it returns an error. +func GetTFVarsFromWorkdir(workdir string) ([]string, error) { + if workdir == "" { + return nil, fmt.Errorf("workdir cannot be empty") + } + + var tfvarFiles []string + + // Use filepath.Walk to traverse the directory tree rooted at workdir + err := filepath.Walk(workdir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if filepath.Ext(path) == ".tfvars" { + // If it does, add its filename to the slice + tfvarFiles = append(tfvarFiles, filepath.Base(path)) + } + + // Return nil to continue the walk + return nil + }) + + return tfvarFiles, err +} diff --git a/pkg/utils/data.go b/pkg/utils/data.go new file mode 100644 index 0000000..164d24f --- /dev/null +++ b/pkg/utils/data.go @@ -0,0 +1,11 @@ +package utils + +func MergeSlices(slices ...[]string) []string { + var merged []string + + for _, slice := range slices { + merged = append(merged, slice...) + } + + return merged +} diff --git a/pkg/validation/terraform.go b/pkg/validation/terraform.go index 2f84b6f..405a8de 100644 --- a/pkg/validation/terraform.go +++ b/pkg/validation/terraform.go @@ -79,3 +79,18 @@ func IsValidTFModuleDir(path string) error { return nil } + +// HasTFVarFiles checks if the given directory has Terraform variable files. +// If the directory does not have any Terraform variable files, it returns an error. +func HasTFVarFiles(path string) (bool, error) { + if path == "" { + return false, nil + } + + files, err := filepath.Glob(filepath.Join(path, "*.tfvars")) + if err != nil { + return false, fmt.Errorf("failed to list files in directory: %v", err) + } + + return len(files) > 0, nil +} diff --git a/test/data/tf-random/.test-data/TerraformOptions.json b/test/data/tf-random/.test-data/TerraformOptions.json deleted file mode 100644 index b3944b9..0000000 --- a/test/data/tf-random/.test-data/TerraformOptions.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "BackendConfig": {}, - "EnvVars": {}, - "Lock": false, - "LockTimeout": "", - "Logger": null, - "MaxRetries": 3, - "MigrateState": false, - "NoColor": true, - "NoStderr": false, - "OutputMaxLineSize": 0, - "Parallelism": 0, - "PlanFilePath": "plan.out", - "PluginDir": "", - "Reconfigure": false, - "RetryableTerraformErrors": { - ".*Error installing provider.*": "Failed to retrieve plugin due to transient network error.", - ".*Failed to query available provider packages.*": "Failed to retrieve plugin due to transient network error.", - ".*Provider produced inconsistent result after apply.*": "Provider eventual consistency error.", - ".*no provider exists with the given name.*": "Failed to retrieve plugin due to transient network error.", - ".*read: connection reset by peer.*": "Failed to reach helm charts repository.", - ".*registry service is unreachable.*": "Failed to retrieve plugin due to transient network error.", - ".*timed out waiting for server handshake.*": "Failed to retrieve plugin due to transient network error.", - ".*timeout while waiting for plugin to start.*": "Failed to retrieve plugin due to transient network error.", - ".*transport is closing.*": "Failed to reach Kubernetes API.", - ".*unable to verify checksum.*": "Failed to retrieve plugin due to transient network error.", - ".*unable to verify signature.*": "Failed to retrieve plugin due to transient network error.", - "could not query provider registry for": "Failed to retrieve plugin due to transient network error." - }, - "SetVarsAfterVarFiles": false, - "SshAgent": null, - "Targets": null, - "TerraformBinary": "", - "TerraformDir": "../../data/tf-random", - "TimeBetweenRetries": 5000000000, - "Upgrade": false, - "VarFiles": null, - "Vars": {} -} diff --git a/test/examples/simple/with_options_test.go b/test/examples/simple/with_options_test.go index 8860b3f..40e3ac3 100644 --- a/test/examples/simple/with_options_test.go +++ b/test/examples/simple/with_options_test.go @@ -53,3 +53,11 @@ func TestWithRetryOptions(t *testing.T) { s.Stg.PlanStage(t, s.GetTerraformOptions()) } + +func TestWithScannedTFVars(t *testing.T) { + workdir := "../../data/tf-random" + s, err := scenario.NewWithOptions(t, workdir, scenario.WithScannedTFVars(workdir, "fixtures")) + assert.NoErrorf(t, err, "Failed to create scenario: %s", err) + + s.Stg.PlanStage(t, s.GetTerraformOptions()) +}