From e79123528c1e654473942f81b14f200635feedc2 Mon Sep 17 00:00:00 2001 From: Matt Calhoun Date: Thu, 10 Oct 2024 13:26:50 -0400 Subject: [PATCH] add atmos vendor test helpers (#28) --- pkg/atmos/format.go | 38 ++++++++++++++ pkg/atmos/options.go | 4 ++ pkg/atmos/vendor_pull.go | 18 +++++++ pkg/atmos/vendor_pull_test.go | 90 +++++++++++++++++++++++++++++++++ test/fixtures/atmos/vendor.yaml | 32 ++++++++++++ 5 files changed, 182 insertions(+) create mode 100644 pkg/atmos/vendor_pull.go create mode 100644 pkg/atmos/vendor_pull_test.go create mode 100644 test/fixtures/atmos/vendor.yaml diff --git a/pkg/atmos/format.go b/pkg/atmos/format.go index 6e0ddeb..4837c2f 100644 --- a/pkg/atmos/format.go +++ b/pkg/atmos/format.go @@ -2,12 +2,14 @@ package atmos import ( "fmt" + "strings" "github.com/gruntwork-io/terratest/modules/collections" tt "github.com/gruntwork-io/terratest/modules/terraform" ) const terraformCmd = "terraform" +const vendorCmd = "vendor" // TerraformCommandsWithPlanFileSupport is a list of all the Terraform commands that support interacting with plan // files. @@ -86,6 +88,38 @@ func FormatAtmosTerraformArgs(options *Options, args ...string) []string { return terraformArgs } +// FormatAtmosVendorArgs converts the inputs to a format palatable to atmos vendor. +func FormatAtmosVendorArgs(options *Options, args ...string) []string { + var vendorArgs []string + commandType := args[0] + + vendorArgs = append(vendorArgs, "vendor", commandType) + + vendorArgs = append(vendorArgs, args[1:]...) + + if options.RedirectStrErrDestination != "" { + vendorArgs = append(vendorArgs, fmt.Sprintf("--redirect-stderr=%s", options.RedirectStrErrDestination)) + } + + if options.VendorComponent != "" { + vendorArgs = append(vendorArgs, "--component", options.VendorComponent) + } + + if options.VendorStack != "" { + vendorArgs = append(vendorArgs, "--stack", options.VendorStack) + } + + if len(options.VendorTags) > 0 { + vendorArgs = append(vendorArgs, "--tags", strings.Join(options.VendorTags, ",")) + } + + if options.VendorType != "" { + vendorArgs = append(vendorArgs, "--type", options.VendorType) + } + + return vendorArgs +} + func FormatArgs(options *Options, args ...string) []string { var atmosArgs []string commandType := args[0] @@ -94,5 +128,9 @@ func FormatArgs(options *Options, args ...string) []string { atmosArgs = append(atmosArgs, FormatAtmosTerraformArgs(options, args[1:]...)...) } + if commandType == vendorCmd { + atmosArgs = append(atmosArgs, FormatAtmosVendorArgs(options, args[1:]...)...) + } + return atmosArgs } diff --git a/pkg/atmos/options.go b/pkg/atmos/options.go index cc11a53..575f536 100644 --- a/pkg/atmos/options.go +++ b/pkg/atmos/options.go @@ -74,6 +74,10 @@ type Options struct { PluginDir string // The path of downloaded plugins to pass to the atmos init command (-plugin-dir) SetVarsAfterVarFiles bool // Pass -var options after -var-file options to Atmos commands WarningsAsErrors map[string]string // Terraform warning messages that should be treated as errors. The keys are a regexp to match against the warning and the value is what to display to a user if that warning is matched. + VendorComponent string // The component to pass to the atmos vendor command, if not passed all components will be vendored + VendorStack string // The stack to pass to the atmos vendor command, if not passed all stacks will be vendored + VendorTags []string // The tags to pass to the atmos vendor command, if not passed all tags will be vendored + VendorType string // The type of vendor to pass to the atmos vendor command, if not passed `terraform` will be used } // Clone makes a deep copy of most fields on the Options object and returns it. diff --git a/pkg/atmos/vendor_pull.go b/pkg/atmos/vendor_pull.go new file mode 100644 index 0000000..4504385 --- /dev/null +++ b/pkg/atmos/vendor_pull.go @@ -0,0 +1,18 @@ +package atmos + +import ( + "github.com/gruntwork-io/terratest/modules/testing" + "github.com/stretchr/testify/require" +) + +// Vendor runs atmos vendor with the given options and return stdout/stderr. +func VendorPull(t testing.TestingT, options *Options) string { + out, err := VendorPullE(t, options) + require.NoError(t, err) + return out +} + +// VendorPullE runs atmos vendor with the given options and return stdout/stderr. +func VendorPullE(t testing.TestingT, options *Options) (string, error) { + return RunAtmosCommandE(t, options, FormatArgs(options, "vendor", "pull")...) +} diff --git a/pkg/atmos/vendor_pull_test.go b/pkg/atmos/vendor_pull_test.go new file mode 100644 index 0000000..2d2a4c2 --- /dev/null +++ b/pkg/atmos/vendor_pull_test.go @@ -0,0 +1,90 @@ +package atmos + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/gruntwork-io/terratest/modules/files" + "github.com/stretchr/testify/require" +) + +func setupVendorTest(t *testing.T) (string, func()) { + testFolder, err := files.CopyTerraformFolderToTemp(atmosExamplePath, t.Name()) + require.NoError(t, err, "Failed to copy Terraform folder to temp directory") + + cleanup := func() { + os.RemoveAll(testFolder) + } + + return testFolder, cleanup +} + +func TestVendorPullBasic(t *testing.T) { + t.Parallel() + + testFolder, cleanup := setupVendorTest(t) + defer cleanup() + + fmt.Printf("running in %s\n", testFolder) + + options := WithDefaultRetryableErrors(t, &Options{ + AtmosBasePath: testFolder, + }) + + _, err := VendorPullE(t, options) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testFolder, "components", "terraform", "vpc")) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testFolder, "components", "terraform", "vpc-flow-logs-bucket")) + require.NoError(t, err) +} + +func TestVendorPullSingleComponent(t *testing.T) { + t.Parallel() + + testFolder, cleanup := setupVendorTest(t) + defer cleanup() + + fmt.Printf("running in %s\n", testFolder) + + options := WithDefaultRetryableErrors(t, &Options{ + AtmosBasePath: testFolder, + VendorComponent: "vpc", + }) + + _, err := VendorPullE(t, options) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testFolder, "components", "terraform", "vpc")) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testFolder, "components", "terraform", "vpc-flow-logs-bucket")) + require.Error(t, err) +} + +func TestVendorPullByTag(t *testing.T) { + t.Parallel() + + testFolder, cleanup := setupVendorTest(t) + defer cleanup() + + fmt.Printf("running in %s\n", testFolder) + + options := WithDefaultRetryableErrors(t, &Options{ + AtmosBasePath: testFolder, + VendorTags: []string{"storage"}, + }) + + _, err := VendorPullE(t, options) + require.NoError(t, err) + + _, err = os.Stat(filepath.Join(testFolder, "components", "terraform", "vpc")) + require.Error(t, err) + + _, err = os.Stat(filepath.Join(testFolder, "components", "terraform", "vpc-flow-logs-bucket")) + require.NoError(t, err) +} diff --git a/test/fixtures/atmos/vendor.yaml b/test/fixtures/atmos/vendor.yaml new file mode 100644 index 0000000..95044dd --- /dev/null +++ b/test/fixtures/atmos/vendor.yaml @@ -0,0 +1,32 @@ +apiVersion: atmos/v1 +kind: AtmosVendorConfig +metadata: + name: test-vendor-config + description: Atmos vendoring manifest for testing +spec: + # `imports` or `sources` (or both) must be defined in a vendoring manifest + imports: [] + + sources: + - component: "vpc" + source: "github.com/cloudposse/terraform-aws-components.git//modules/vpc?ref={{.Version}}" + version: "1.398.0" + targets: + - "components/terraform/vpc" + included_paths: + - "**/*.tf" + excluded_paths: + - "**/providers.tf" + tags: + - networking + - component: "vpc-flow-logs-bucket" + source: "github.com/cloudposse/terraform-aws-components.git//modules/vpc-flow-logs-bucket?ref={{.Version}}" + version: "1.398.0" + targets: + - "components/terraform/vpc-flow-logs-bucket" + included_paths: + - "**/*.tf" + excluded_paths: + - "**/providers.tf" + tags: + - storage