|
46 | 46 | SkipLocal bool |
47 | 47 | UseVersion string |
48 | 48 | WorkspaceTmpDir bool |
49 | | - TerraformDir string |
50 | 49 | OnlyOutTestToml bool |
51 | 50 | ) |
52 | 51 |
|
@@ -79,11 +78,6 @@ func init() { |
79 | 78 | // DABs in the workspace runs on the workspace file system. This flags does the same for acceptance tests |
80 | 79 | // to simulate an identical environment. |
81 | 80 | flag.BoolVar(&WorkspaceTmpDir, "workspace-tmp-dir", false, "Run tests on the workspace file system (For DBR testing).") |
82 | | - |
83 | | - // Symlinks from workspace file system to local file mount are not supported on DBR. Terraform implicitly |
84 | | - // creates these symlinks when a file_mirror is used for a provider (in .terraformrc). This flag |
85 | | - // allows us to download the provider to the workspace file system on DBR enabling DBR integration testing. |
86 | | - flag.StringVar(&TerraformDir, "terraform-dir", "", "Directory to download the terraform provider to") |
87 | 81 | flag.BoolVar(&OnlyOutTestToml, "only-out-test-toml", false, "Only regenerate out.test.toml files without running tests") |
88 | 82 | } |
89 | 83 |
|
@@ -173,14 +167,11 @@ func testAccept(t *testing.T, inprocessMode bool, singleTest string) int { |
173 | 167 |
|
174 | 168 | buildDir := getBuildDir(t, cwd, runtime.GOOS, runtime.GOARCH) |
175 | 169 |
|
176 | | - terraformDir := TerraformDir |
177 | | - if terraformDir == "" { |
178 | | - terraformDir = buildDir |
| 170 | + // Set up terraform for tests. Skip on DBR - tests with RunsOnDbr only use direct deployment. |
| 171 | + if !WorkspaceTmpDir { |
| 172 | + setupTerraform(t, cwd, buildDir, &repls) |
179 | 173 | } |
180 | 174 |
|
181 | | - // Download terraform and provider and create config. |
182 | | - RunCommand(t, []string{"python3", filepath.Join(cwd, "install_terraform.py"), "--targetdir", terraformDir}, ".", []string{}) |
183 | | - |
184 | 175 | wheelPath := buildDatabricksBundlesWheel(t, buildDir) |
185 | 176 | if wheelPath != "" { |
186 | 177 | t.Setenv("DATABRICKS_BUNDLES_WHEEL", wheelPath) |
@@ -210,7 +201,12 @@ func testAccept(t *testing.T, inprocessMode bool, singleTest string) int { |
210 | 201 | } |
211 | 202 | } |
212 | 203 |
|
213 | | - BuildYamlfmt(t) |
| 204 | + // Skip building yamlfmt when running on workspace filesystem (DBR). |
| 205 | + // This fails today on DBR. Can be looked into and fixed as a follow-up |
| 206 | + // as and when needed. |
| 207 | + if !WorkspaceTmpDir { |
| 208 | + BuildYamlfmt(t) |
| 209 | + } |
214 | 210 |
|
215 | 211 | t.Setenv("CLI", execPath) |
216 | 212 | repls.SetPath(execPath, "[CLI]") |
@@ -255,16 +251,6 @@ func testAccept(t *testing.T, inprocessMode bool, singleTest string) int { |
255 | 251 | t.Setenv("CLI_RELEASES_DIR", releasesDir) |
256 | 252 | } |
257 | 253 |
|
258 | | - terraformrcPath := filepath.Join(terraformDir, ".terraformrc") |
259 | | - t.Setenv("TF_CLI_CONFIG_FILE", terraformrcPath) |
260 | | - t.Setenv("DATABRICKS_TF_CLI_CONFIG_FILE", terraformrcPath) |
261 | | - repls.SetPath(terraformrcPath, "[DATABRICKS_TF_CLI_CONFIG_FILE]") |
262 | | - |
263 | | - terraformExecPath := filepath.Join(terraformDir, "terraform") + exeSuffix |
264 | | - t.Setenv("DATABRICKS_TF_EXEC_PATH", terraformExecPath) |
265 | | - t.Setenv("TERRAFORM", terraformExecPath) |
266 | | - repls.SetPath(terraformExecPath, "[TERRAFORM]") |
267 | | - |
268 | 254 | // do it last so that full paths match first: |
269 | 255 | repls.SetPath(buildDir, "[BUILD_DIR]") |
270 | 256 |
|
@@ -429,8 +415,8 @@ func getSkipReason(config *internal.TestConfig, configPath string) string { |
429 | 415 | return "" |
430 | 416 | } |
431 | 417 |
|
432 | | - if isTruePtr(config.SkipOnDbr) && WorkspaceTmpDir { |
433 | | - return "Disabled via SkipOnDbr setting in " + configPath |
| 418 | + if WorkspaceTmpDir && !isTruePtr(config.RunsOnDbr) { |
| 419 | + return "Disabled because RunsOnDbr is not set in " + configPath |
434 | 420 | } |
435 | 421 |
|
436 | 422 | if isTruePtr(config.Slow) && testing.Short() { |
@@ -499,6 +485,13 @@ func runTest(t *testing.T, |
499 | 485 | customEnv []string, |
500 | 486 | envFilters []string, |
501 | 487 | ) { |
| 488 | + // Check env filters early, before creating any resources like directories on the file system. |
| 489 | + // Creating / deleting too many directories causes this error on the workspace FUSE mount: |
| 490 | + // unlinkat <workspace_path>: directory not empty. Thus avoiding unnecessary directory creation |
| 491 | + // is important. |
| 492 | + testEnv := buildTestEnv(config.Env, customEnv) |
| 493 | + checkEnvFilters(t, testEnv, envFilters) |
| 494 | + |
502 | 495 | if LogConfig { |
503 | 496 | configBytes, err := json.MarshalIndent(config, "", " ") |
504 | 497 | require.NoError(t, err) |
@@ -530,7 +523,7 @@ func runTest(t *testing.T, |
530 | 523 | // If the test is being run on DBR, auth is already configured |
531 | 524 | // by the dbr_runner notebook by reading a token from the notebook context and |
532 | 525 | // setting DATABRICKS_TOKEN and DATABRICKS_HOST environment variables. |
533 | | - _, _, tmpDir = workspaceTmpDir(t.Context(), t) |
| 526 | + tmpDir = workspaceTmpDir(t.Context(), t) |
534 | 527 |
|
535 | 528 | // Run DBR tests on the workspace file system to mimic usage from |
536 | 529 | // DABs in the workspace. |
@@ -630,38 +623,12 @@ func runTest(t *testing.T, |
630 | 623 | uniqueCacheDir := filepath.Join(t.TempDir(), ".cache") |
631 | 624 | cmd.Env = append(cmd.Env, "DATABRICKS_CACHE_DIR="+uniqueCacheDir) |
632 | 625 |
|
633 | | - for _, key := range utils.SortedKeys(config.Env) { |
634 | | - if hasKey(customEnv, key) { |
635 | | - // We want EnvMatrix to take precedence. |
636 | | - // Skip rather than relying on cmd.Env order, because this might interfere with replacements and substitutions. |
637 | | - continue |
638 | | - } |
639 | | - cmd.Env = addEnvVar(t, cmd.Env, &repls, key, config.Env[key], config.EnvRepl, false) |
640 | | - } |
641 | | - |
642 | | - for _, keyvalue := range customEnv { |
643 | | - items := strings.SplitN(keyvalue, "=", 2) |
644 | | - require.Len(t, items, 2) |
645 | | - key := items[0] |
646 | | - value := items[1] |
| 626 | + for _, kv := range testEnv { |
| 627 | + key, value, _ := strings.Cut(kv, "=") |
647 | 628 | // Only add replacement by default if value is part of EnvMatrix with more than 1 option and length is 4 or more chars |
648 | 629 | // (to avoid matching "yes" and "no" values from template input parameters) |
649 | | - cmd.Env = addEnvVar(t, cmd.Env, &repls, key, value, config.EnvRepl, len(config.EnvMatrix[key]) > 1 && len(value) >= 4) |
650 | | - } |
651 | | - |
652 | | - for filterInd, filterEnv := range envFilters { |
653 | | - filterEnvKey := strings.Split(filterEnv, "=")[0] |
654 | | - for ind := range cmd.Env { |
655 | | - // Search backwards, because the latest settings is what is actually applicable. |
656 | | - envPair := cmd.Env[len(cmd.Env)-1-ind] |
657 | | - if strings.Split(envPair, "=")[0] == filterEnvKey { |
658 | | - if envPair == filterEnv { |
659 | | - break |
660 | | - } else { |
661 | | - t.Skipf("Skipping because test environment %s does not match ENVFILTER#%d: %s", envPair, filterInd, filterEnv) |
662 | | - } |
663 | | - } |
664 | | - } |
| 630 | + defaultRepl := hasKey(customEnv, key) && len(config.EnvMatrix[key]) > 1 && len(value) >= 4 |
| 631 | + cmd.Env = addEnvVar(t, cmd.Env, &repls, key, value, config.EnvRepl, defaultRepl) |
665 | 632 | } |
666 | 633 |
|
667 | 634 | absDir, err := filepath.Abs(dir) |
@@ -745,10 +712,44 @@ func runTest(t *testing.T, |
745 | 712 | } |
746 | 713 | } |
747 | 714 |
|
| 715 | +// checkEnvFilters skips the test if any env filter doesn't match testEnv. |
| 716 | +func checkEnvFilters(t *testing.T, testEnv, envFilters []string) { |
| 717 | + envMap := make(map[string]string, len(testEnv)) |
| 718 | + for _, kv := range testEnv { |
| 719 | + key, value, _ := strings.Cut(kv, "=") |
| 720 | + envMap[key] = value |
| 721 | + } |
| 722 | + for i, filter := range envFilters { |
| 723 | + key, expected, _ := strings.Cut(filter, "=") |
| 724 | + if actual, ok := envMap[key]; ok && actual != expected { |
| 725 | + t.Skipf("Skipping because test environment %s=%s does not match ENVFILTER#%d: %s", key, actual, i, filter) |
| 726 | + } |
| 727 | + } |
| 728 | +} |
| 729 | + |
| 730 | +// buildTestEnv builds the test environment from config.Env and customEnv. |
| 731 | +// customEnv (from EnvMatrix) takes precedence over config.Env. |
| 732 | +func buildTestEnv(configEnv map[string]string, customEnv []string) []string { |
| 733 | + env := make([]string, 0, len(configEnv)+len(customEnv)) |
| 734 | + |
| 735 | + // Add config.Env first (but skip keys that exist in customEnv) |
| 736 | + for _, key := range utils.SortedKeys(configEnv) { |
| 737 | + if hasKey(customEnv, key) { |
| 738 | + continue |
| 739 | + } |
| 740 | + env = append(env, key+"="+configEnv[key]) |
| 741 | + } |
| 742 | + |
| 743 | + // Add customEnv second (takes precedence) |
| 744 | + env = append(env, customEnv...) |
| 745 | + |
| 746 | + return env |
| 747 | +} |
| 748 | + |
748 | 749 | func hasKey(env []string, key string) bool { |
749 | | - for _, keyvalue := range env { |
750 | | - items := strings.SplitN(keyvalue, "=", 2) |
751 | | - if len(items) == 2 && items[0] == key { |
| 750 | + for _, kv := range env { |
| 751 | + k, _, ok := strings.Cut(kv, "=") |
| 752 | + if ok && k == key { |
752 | 753 | return true |
753 | 754 | } |
754 | 755 | } |
@@ -1383,6 +1384,22 @@ func BuildYamlfmt(t *testing.T) { |
1383 | 1384 | RunCommand(t, args, "..", []string{}) |
1384 | 1385 | } |
1385 | 1386 |
|
| 1387 | +// setupTerraform installs terraform and configures environment variables for tests. |
| 1388 | +func setupTerraform(t *testing.T, cwd, buildDir string, repls *testdiff.ReplacementsContext) { |
| 1389 | + RunCommand(t, []string{"python3", filepath.Join(cwd, "install_terraform.py"), "--targetdir", buildDir}, ".", []string{}) |
| 1390 | + |
| 1391 | + terraformrcPath := filepath.Join(buildDir, ".terraformrc") |
| 1392 | + terraformExecPath := filepath.Join(buildDir, "terraform") + exeSuffix |
| 1393 | + |
| 1394 | + t.Setenv("TF_CLI_CONFIG_FILE", terraformrcPath) |
| 1395 | + t.Setenv("DATABRICKS_TF_CLI_CONFIG_FILE", terraformrcPath) |
| 1396 | + t.Setenv("DATABRICKS_TF_EXEC_PATH", terraformExecPath) |
| 1397 | + t.Setenv("TERRAFORM", terraformExecPath) |
| 1398 | + |
| 1399 | + repls.SetPath(terraformrcPath, "[DATABRICKS_TF_CLI_CONFIG_FILE]") |
| 1400 | + repls.SetPath(terraformExecPath, "[TERRAFORM]") |
| 1401 | +} |
| 1402 | + |
1386 | 1403 | func loadUserReplacements(t *testing.T, repls *testdiff.ReplacementsContext, tmpDir string) { |
1387 | 1404 | b, err := os.ReadFile(filepath.Join(tmpDir, userReplacementsFilename)) |
1388 | 1405 | if os.IsNotExist(err) { |
|
0 commit comments