From 97203d5918dfc5bf7088090545f880e658dcf5d4 Mon Sep 17 00:00:00 2001 From: Julien Duchesne Date: Tue, 25 Jun 2024 23:12:19 -0400 Subject: [PATCH] Config Generation: TF Install settings Allow configuring the install dir and version. The benefit is that you can install it only once and reuse the executable --- cmd/generate/main.go | 25 ++++++++++++++++++++- pkg/generate/config.go | 14 ++++++++++-- pkg/generate/generate.go | 2 ++ pkg/generate/generate_test.go | 6 +++++ pkg/generate/terraform.go | 42 ++++++++++++++++++++++++++++++----- 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/cmd/generate/main.go b/cmd/generate/main.go index 796c79f5a..ee9979b1a 100644 --- a/cmd/generate/main.go +++ b/cmd/generate/main.go @@ -9,6 +9,7 @@ import ( "github.com/grafana/terraform-provider-grafana/v3/pkg/generate" "github.com/fatih/color" + goVersion "github.com/hashicorp/go-version" "github.com/urfave/cli/v2" ) @@ -74,6 +75,18 @@ This supports a glob format. Examples: EnvVars: []string{"TFGEN_OUTPUT_CREDENTIALS"}, Value: false, }, + &cli.StringFlag{ + Name: "terraform-install-dir", + Usage: `Directory to install Terraform to. If not set, a temporary directory will be created.`, + EnvVars: []string{"TFGEN_TERRAFORM_INSTALL_DIR"}, + Required: false, + }, + &cli.StringFlag{ + Name: "terraform-install-version", + Usage: `Version of Terraform to install. If not set, the latest version _tested in this tool_ will be installed.`, + EnvVars: []string{"TFGEN_TERRAFORM_INSTALL_VERSION"}, + Required: false, + }, // Grafana OSS flags &cli.StringFlag{ @@ -184,6 +197,16 @@ func parseFlags(ctx *cli.Context) (*generate.Config, error) { StackServiceAccountName: ctx.String("cloud-stack-service-account-name"), }, IncludeResources: ctx.StringSlice("include-resources"), + TerraformInstallConfig: generate.TerraformInstallConfig{ + InstallDir: ctx.String("terraform-install-dir"), + }, + } + var err error + if tfVersion := ctx.String("terraform-install-version"); tfVersion != "" { + config.TerraformInstallConfig.Version, err = goVersion.NewVersion(ctx.String("terraform-install-version")) + if err != nil { + return nil, fmt.Errorf("terraform-install-version must be a valid version: %w", err) + } } if config.ProviderVersion == "" { @@ -191,7 +214,7 @@ func parseFlags(ctx *cli.Context) (*generate.Config, error) { } // Validate flags - err := newFlagValidations(). + err = newFlagValidations(). atLeastOne("grafana-url", "cloud-access-policy-token"). conflicting( []string{"grafana-url", "grafana-auth", "synthetic-monitoring-url", "synthetic-monitoring-access-token", "oncall-url", "oncall-access-token"}, diff --git a/pkg/generate/config.go b/pkg/generate/config.go index f96c176d8..26924d4d1 100644 --- a/pkg/generate/config.go +++ b/pkg/generate/config.go @@ -1,6 +1,9 @@ package generate -import "github.com/hashicorp/terraform-exec/tfexec" +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-exec/tfexec" +) type OutputFormat string @@ -29,6 +32,11 @@ type CloudConfig struct { StackServiceAccountName string } +type TerraformInstallConfig struct { + InstallDir string + Version *version.Version +} + type Config struct { // IncludeResources is a list of patterns to filter resources by. // If a resource name matches any of the patterns, it will be included in the output. @@ -43,5 +51,7 @@ type Config struct { ProviderVersion string Grafana *GrafanaConfig Cloud *CloudConfig - Terraform *tfexec.Terraform + + TerraformInstallConfig TerraformInstallConfig + Terraform *tfexec.Terraform } diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go index 04e118407..8dbc85323 100644 --- a/pkg/generate/generate.go +++ b/pkg/generate/generate.go @@ -63,6 +63,7 @@ func Generate(ctx context.Context, cfg *Config) error { cfg.Terraform = tf if cfg.Cloud != nil { + log.Printf("Generating cloud resources") stacks, err := generateCloudResources(ctx, cfg) if err != nil { return err @@ -86,6 +87,7 @@ func Generate(ctx context.Context, cfg *Config) error { onCallToken: cfg.Grafana.OnCallAccessToken, onCallURL: cfg.Grafana.OnCallURL, } + log.Printf("Generating Grafana resources") if err := generateGrafanaResources(ctx, cfg, stack, true); err != nil { return err } diff --git a/pkg/generate/generate_test.go b/pkg/generate/generate_test.go index bdb155aa2..f7d24a1f4 100644 --- a/pkg/generate/generate_test.go +++ b/pkg/generate/generate_test.go @@ -22,6 +22,9 @@ func TestAccGenerate(t *testing.T) { } testutils.CheckOSSTestsEnabled(t) + // Install Terraform to a temporary directory to avoid reinstalling it for each test case. + installDir := t.TempDir() + cases := []struct { name string config string @@ -175,6 +178,9 @@ func TestAccGenerate(t *testing.T) { URL: "http://localhost:3000", Auth: "admin:admin", }, + TerraformInstallConfig: generate.TerraformInstallConfig{ + InstallDir: installDir, + }, } if tc.generateConfig != nil { tc.generateConfig(&config) diff --git a/pkg/generate/terraform.go b/pkg/generate/terraform.go index 97994adfc..17c2449ac 100644 --- a/pkg/generate/terraform.go +++ b/pkg/generate/terraform.go @@ -5,11 +5,13 @@ import ( "encoding/json" "errors" "fmt" + "log" "os" "path/filepath" "strings" "github.com/hashicorp/go-version" + "github.com/hashicorp/hc-install/fs" "github.com/hashicorp/hc-install/product" "github.com/hashicorp/hc-install/releases" "github.com/hashicorp/hcl/v2" @@ -20,14 +22,42 @@ import ( ) func setupTerraform(cfg *Config) (*tfexec.Terraform, error) { - installer := &releases.ExactVersion{ - Product: product.Terraform, - Version: version.Must(version.NewVersion("1.8.4")), + var err error + + tfVersion := cfg.TerraformInstallConfig.Version + if tfVersion == nil { + // Not using latest to avoid unexpected breaking changes + log.Printf("No Terraform version specified, defaulting to version 1.8.5") + tfVersion = version.Must(version.NewVersion("1.8.5")) } - execPath, err := installer.Install(context.Background()) - if err != nil { - return nil, fmt.Errorf("error installing Terraform: %s", err) + // Check if Terraform is already installed + var execPath string + if cfg.TerraformInstallConfig.InstallDir != "" { + finder := fs.ExactVersion{ + Product: product.Terraform, + Version: tfVersion, + ExtraPaths: []string{ + cfg.TerraformInstallConfig.InstallDir, + }, + } + + if execPath, err = finder.Find(context.Background()); err == nil { + log.Printf("Terraform %s already installed at %s", tfVersion, execPath) + } + } + + // Install Terraform if not found + if execPath == "" { + log.Printf("Installing Terraform %s", tfVersion) + installer := &releases.ExactVersion{ + Product: product.Terraform, + Version: tfVersion, + InstallDir: cfg.TerraformInstallConfig.InstallDir, + } + if execPath, err = installer.Install(context.Background()); err != nil { + return nil, fmt.Errorf("error installing Terraform: %s", err) + } } tf, err := tfexec.NewTerraform(cfg.OutputDir, execPath)