Files
trivy/magefiles/terraformplan.go
2025-07-29 07:13:54 +00:00

140 lines
3.1 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"strings"
hversion "github.com/hashicorp/go-version" //nolint:gomodguard // hc-install uses hashicorp/go-version
"github.com/hashicorp/hc-install/product"
"github.com/hashicorp/hc-install/releases"
"github.com/hashicorp/terraform-exec/tfexec"
"golang.org/x/sync/errgroup"
"github.com/aquasecurity/trivy/internal/testutil"
)
const (
terraformVersion = "1.7.3"
terraformParallelLimit = 5
tfplanFile = "tfplan"
)
func fixtureTerraformPlanSnapshots(ctx context.Context) error {
localstackC, addr, err := testutil.SetupLocalStack(ctx, "3.1.0")
if err != nil {
return err
}
defer localstackC.Terminate(ctx)
envs := []struct {
key string
val string
}{
{"AWS_DEFAULT_REGION", "us-east-1"},
{"AWS_ACCESS_KEY_ID", "test"},
{"AWS_SECRET_ACCESS_KEY", "test"},
{"AWS_ENDPOINT_URL", addr},
}
for _, env := range envs {
if err := os.Setenv(env.key, env.val); err != nil {
return err
}
}
dirs := []string{
"pkg/fanal/artifact/local/testdata/misconfig/terraformplan/snapshots",
"pkg/iac/scanners/terraformplan/snapshot/testdata",
}
var workingDirs []string
for _, dir := range dirs {
entries, err := os.ReadDir(filepath.FromSlash(dir))
if err != nil {
return err
}
for _, entry := range entries {
workingDirs = append(workingDirs, filepath.Join(dir, entry.Name()))
}
}
installer := &releases.ExactVersion{
Product: product.Terraform,
Version: hversion.Must(hversion.NewVersion(terraformVersion)),
}
execPath, err := installer.Install(ctx)
if err != nil {
return fmt.Errorf("failed to install Terraform: %w", err)
}
g, ctx := errgroup.WithContext(ctx)
g.SetLimit(terraformParallelLimit)
for _, workingDir := range workingDirs {
g.Go(func() error {
if err := os.Remove(tfplanFile); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
if err := generatePlan(ctx, execPath, workingDir); err != nil {
return fmt.Errorf("failed to generate Terraform Plan: %w", err)
}
return nil
})
}
return g.Wait()
}
func generatePlan(ctx context.Context, execPath, workingDir string) error {
if err := cleanup(workingDir); err != nil {
return err
}
defer cleanup(workingDir)
tf, err := tfexec.NewTerraform(workingDir, execPath)
if err != nil {
return fmt.Errorf("failed to run Terraform: %w", err)
}
prefix := fmt.Sprintf("tfplan:%s:", filepath.Base(workingDir))
tf.SetLogger(log.New(os.Stdout, prefix, log.LstdFlags))
if err = tf.Init(ctx, tfexec.Upgrade(true)); err != nil {
return fmt.Errorf("failed to run Init cmd: %w", err)
}
if _, err := tf.Plan(ctx, tfexec.Out(tfplanFile)); err != nil {
return fmt.Errorf("failed to run Plan cmd: %w", err)
}
return nil
}
func cleanup(workingDir string) error {
entries, err := os.ReadDir(workingDir)
if err != nil {
return err
}
for _, entry := range entries {
if entry.Name() == "terraform.tfstate" || strings.HasPrefix(entry.Name(), ".terraform") {
path := filepath.Join(workingDir, entry.Name())
if err := os.RemoveAll(path); err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
}
}
return nil
}