From fe5757faa45c98288831c4f913ef548394ea7733 Mon Sep 17 00:00:00 2001 From: Shreyas Goenka Date: Mon, 4 May 2026 17:13:35 +0200 Subject: [PATCH] integration/bundle: stage Terraform + Databricks provider in TestMain The Databricks-internal CI runners that execute these tests cannot reach registry.terraform.io, so `bundle deploy` inside tests like TestGenerateFromExistingJobAndDeploy fails inside `terraform init` with: Could not retrieve the list of available versions for provider databricks/databricks: could not connect to registry.terraform.io: ... EOF acceptance/install_terraform.py already exists and downloads Terraform 1.5.5 plus the matching Databricks provider into a local filesystem mirror (driven from `acceptance` tests). Wire it into the integration/bundle package via a new TestMain and a small testutil helper: - internal/testutil/terraform.go: SetupTerraform() invokes the script against acceptance/build/{GOOS}_{GOARCH} and exports TF_CLI_CONFIG_FILE / DATABRICKS_TF_CLI_CONFIG_FILE / DATABRICKS_TF_EXEC_PATH / TERRAFORM. The CLI's existing getEnvVarWithMatchingVersion path picks these up (init.go:195) and routes terraform init at the local mirror instead of registry.terraform.io. - integration/bundle/main_test.go: TestMain calls SetupTerraform() once per package run before any test executes. Same setup runs for `go test ./integration/bundle/...` on a developer laptop and in CI. Verified locally: `go test ./integration/bundle/... -run TestNonExistent` runs TestMain, downloads terraform + provider 1.113.0 to acceptance/build/darwin_arm64/, and writes the .terraformrc with the filesystem_mirror block. CLOUD_ENV-gated tests still skip without a workspace, but TestMain has already staged Terraform by then. Co-authored-by: Isaac --- integration/bundle/main_test.go | 25 ++++++++++ internal/testutil/terraform.go | 83 +++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 integration/bundle/main_test.go create mode 100644 internal/testutil/terraform.go diff --git a/integration/bundle/main_test.go b/integration/bundle/main_test.go new file mode 100644 index 00000000000..66afcdd9113 --- /dev/null +++ b/integration/bundle/main_test.go @@ -0,0 +1,25 @@ +package bundle_test + +import ( + "fmt" + "os" + "testing" + + "github.com/databricks/cli/internal/testutil" +) + +// TestMain stages a local Terraform filesystem mirror via +// acceptance/install_terraform.py before any test in this package runs. +// +// The Databricks-internal CI runners that execute these tests cannot reach +// registry.terraform.io, so without this setup `bundle deploy` fails inside +// `terraform init` with "could not connect to registry.terraform.io: ... EOF". +// Routing through the local mirror also makes a plain `go test ./integration/bundle/...` +// work outside CI without any extra environment variables. +func TestMain(m *testing.M) { + if err := testutil.SetupTerraform(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to set up Terraform for integration tests: %v\n", err) + os.Exit(1) + } + os.Exit(m.Run()) +} diff --git a/internal/testutil/terraform.go b/internal/testutil/terraform.go new file mode 100644 index 00000000000..f83da2d4317 --- /dev/null +++ b/internal/testutil/terraform.go @@ -0,0 +1,83 @@ +package testutil + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" +) + +// SetupTerraform installs Terraform and the Databricks Terraform provider into a +// local filesystem mirror by invoking acceptance/install_terraform.py, then exports +// TF_CLI_CONFIG_FILE / DATABRICKS_TF_CLI_CONFIG_FILE / DATABRICKS_TF_EXEC_PATH / +// TERRAFORM so the CLI subprocess used by tests resolves the provider locally +// instead of contacting registry.terraform.io. +// +// Intended for use from TestMain in integration test packages: the setup runs +// once per `go test` invocation and behaves the same in CI (where +// registry.terraform.io is blocked by Databricks corp network policy) and on a +// developer's laptop. +func SetupTerraform() error { + repoRoot, err := findRepoRoot() + if err != nil { + return fmt.Errorf("locate repo root: %w", err) + } + + scriptPath := filepath.Join(repoRoot, "acceptance", "install_terraform.py") + buildDir := filepath.Join(repoRoot, "acceptance", "build", runtime.GOOS+"_"+runtime.GOARCH) + if err := os.MkdirAll(buildDir, 0o755); err != nil { + return fmt.Errorf("create build dir %s: %w", buildDir, err) + } + + cmd := exec.Command("python3", scriptPath, "--targetdir", buildDir) + cmd.Stdout = os.Stderr + cmd.Stderr = os.Stderr + if err := cmd.Run(); err != nil { + return fmt.Errorf("%s: %w", scriptPath, err) + } + + binarySuffix := "" + if runtime.GOOS == "windows" { + binarySuffix = ".exe" + } + terraformrcPath := filepath.Join(buildDir, ".terraformrc") + terraformExecPath := filepath.Join(buildDir, "terraform"+binarySuffix) + + envs := map[string]string{ + "TF_CLI_CONFIG_FILE": terraformrcPath, + "DATABRICKS_TF_CLI_CONFIG_FILE": terraformrcPath, + "DATABRICKS_TF_EXEC_PATH": terraformExecPath, + "TERRAFORM": terraformExecPath, + } + for k, v := range envs { + if err := os.Setenv(k, v); err != nil { + return fmt.Errorf("set %s: %w", k, err) + } + } + return nil +} + +// findRepoRoot walks up from the current working directory until it finds a +// directory that contains both go.mod and acceptance/install_terraform.py. The +// pair uniquely identifies the cli repo root and avoids stopping at the nested +// tools/go.mod or any other module along the way. +func findRepoRoot() (string, error) { + cwd, err := os.Getwd() + if err != nil { + return "", err + } + dir := cwd + for { + _, modErr := os.Stat(filepath.Join(dir, "go.mod")) + _, scriptErr := os.Stat(filepath.Join(dir, "acceptance", "install_terraform.py")) + if modErr == nil && scriptErr == nil { + return dir, nil + } + parent := filepath.Dir(dir) + if parent == dir { + return "", fmt.Errorf("cli repo root not found searching up from %s", cwd) + } + dir = parent + } +}