diff --git a/test/s3tables/catalog/duckdb_deterministic_location_test.go b/test/s3tables/catalog/duckdb_deterministic_location_test.go index 8cccdddadc..256efb68c7 100644 --- a/test/s3tables/catalog/duckdb_deterministic_location_test.go +++ b/test/s3tables/catalog/duckdb_deterministic_location_test.go @@ -115,6 +115,9 @@ SELECT 'duckdb deterministic-location describe ok' AS marker; t.Logf("DuckDB output:\n%s", outputStr) if err != nil { + if testutil.IsIllegalInstruction(err) { + t.Skipf("DuckDB image cannot run on this CPU: %v", err) + } // Tolerate environments where the Iceberg extension/version is too // limited to exercise this code path. The intent is to gain // coverage where it's available, not to require it everywhere. diff --git a/test/s3tables/catalog/duckdb_oauth_test.go b/test/s3tables/catalog/duckdb_oauth_test.go index 346cd2d096..aa151f48e3 100644 --- a/test/s3tables/catalog/duckdb_oauth_test.go +++ b/test/s3tables/catalog/duckdb_oauth_test.go @@ -351,6 +351,9 @@ SELECT 'DuckDB Iceberg OAuth secret created successfully' as result; t.Logf("DuckDB output:\n%s", outputStr) if err != nil { + if testutil.IsIllegalInstruction(err) { + t.Skipf("DuckDB image cannot run on this CPU: %v", err) + } if strings.Contains(outputStr, "iceberg extension is not available") || strings.Contains(outputStr, "Failed to load") { t.Skip("Skipping: Iceberg extension not available in DuckDB Docker image") @@ -379,6 +382,9 @@ SELECT 'DuckDB Iceberg OAuth secret created successfully' as result; t.Logf("DuckDB fallback output:\n%s", fallbackStr) if fallbackErr != nil { + if testutil.IsIllegalInstruction(fallbackErr) { + t.Skipf("DuckDB image cannot run on this CPU: %v", fallbackErr) + } if strings.Contains(fallbackStr, "NotFound_404") && strings.Contains(fallbackStr, "/v1/oauth/tokens") { t.Fatal("OAuth token endpoint returned 404 - the fix is not working") } diff --git a/test/s3tables/catalog/iceberg_catalog_test.go b/test/s3tables/catalog/iceberg_catalog_test.go index 908f54b176..d8490fa629 100644 --- a/test/s3tables/catalog/iceberg_catalog_test.go +++ b/test/s3tables/catalog/iceberg_catalog_test.go @@ -574,6 +574,9 @@ SELECT 'Iceberg extension loaded successfully' as result; output, err := cmd.CombinedOutput() if err != nil { t.Logf("DuckDB output: %s", output) + if testutil.IsIllegalInstruction(err) { + t.Skipf("DuckDB image cannot run on this CPU: %v", err) + } // Check for expected errors in certain CI environments outputStr := string(output) if strings.Contains(outputStr, "iceberg extension is not available") || diff --git a/test/s3tables/catalog/issue_9103_test.go b/test/s3tables/catalog/issue_9103_test.go index be3dddbd13..0d2f86b360 100644 --- a/test/s3tables/catalog/issue_9103_test.go +++ b/test/s3tables/catalog/issue_9103_test.go @@ -218,6 +218,9 @@ SELECT * FROM test_catalog.%s.%s LIMIT 0; } if runErr != nil { + if testutil.IsIllegalInstruction(runErr) { + t.Skipf("DuckDB image cannot run on this CPU: %v", runErr) + } if strings.Contains(outStr, "does not exist because schema") && strings.Contains(outStr, namespace) { t.Fatalf("reproduced issue #9103: DuckDB cannot see namespace %q after ATTACH 's3://%s/'; see output above", diff --git a/test/s3tables/catalog/lance_client_test.go b/test/s3tables/catalog/lance_client_test.go index e44d032060..a9abac6f42 100644 --- a/test/s3tables/catalog/lance_client_test.go +++ b/test/s3tables/catalog/lance_client_test.go @@ -2,11 +2,13 @@ package catalog import ( "fmt" - "github.com/seaweedfs/seaweedfs/weed/s3api/s3tables" "os" "os/exec" "path/filepath" "testing" + + "github.com/seaweedfs/seaweedfs/test/testutil" + "github.com/seaweedfs/seaweedfs/weed/s3api/s3tables" ) // TestLanceNamespaceRealClient drives the namespace with the Lance client rather @@ -58,6 +60,9 @@ func TestLanceNamespaceRealClient(t *testing.T) { t.Logf("Running Lance client test against %s", namespaceURL) if err := cmd.Run(); err != nil { + if testutil.IsIllegalInstruction(err) { + t.Skipf("Lance client image cannot run on this CPU: %v", err) + } t.Errorf("Lance client test failed: %v", err) } } diff --git a/test/s3tables/catalog_lancedb/lancedb_catalog_test.go b/test/s3tables/catalog_lancedb/lancedb_catalog_test.go index 96613937c0..be45fd7f18 100644 --- a/test/s3tables/catalog_lancedb/lancedb_catalog_test.go +++ b/test/s3tables/catalog_lancedb/lancedb_catalog_test.go @@ -272,6 +272,9 @@ func (env *environment) runClient(t *testing.T, bucket string) { out, err := cmd.CombinedOutput() t.Logf("LanceDB client output:\n%s", out) if err != nil { + if testutil.IsIllegalInstruction(err) { + t.Skipf("LanceDB client image cannot run on this CPU: %v", err) + } t.Fatalf("the LanceDB client failed: %v", err) } if !strings.Contains(string(out), "PASS") { diff --git a/test/s3tables/catalog_risingwave/setup_test.go b/test/s3tables/catalog_risingwave/setup_test.go index 155c390eed..f2adf83433 100644 --- a/test/s3tables/catalog_risingwave/setup_test.go +++ b/test/s3tables/catalog_risingwave/setup_test.go @@ -194,6 +194,7 @@ func (env *TestEnvironment) StartRisingWave(t *testing.T) { if output, err := cmd.CombinedOutput(); err != nil { t.Fatalf("failed to start RisingWave container: %v\n%s", err, string(output)) } + env.checkRisingWaveContainer(t) // Start a sidecar postgres container for running psql commands sidecarName := "seaweed-risingwave-sidecar-" + randomString(8) @@ -205,6 +206,7 @@ func (env *TestEnvironment) StartRisingWave(t *testing.T) { "sleep", "infinity", ) if output, err := sidecarCmd.CombinedOutput(); err != nil { + env.checkRisingWaveContainer(t) t.Fatalf("failed to start postgres sidecar: %v\n%s", err, string(output)) } @@ -220,6 +222,7 @@ func (env *TestEnvironment) waitForRisingWave(timeout time.Duration) bool { deadline := time.Now().Add(timeout) env.t.Logf(">>> Waiting for RisingWave to be ready (timeout %v)...\n", timeout) for time.Now().Before(deadline) { + env.checkRisingWaveContainer(env.t) if output, err := runPostgresClientSQL(env.postgresSidecar, "SELECT 1;"); err == nil { env.t.Logf(">>> RisingWave is ready.\n") return true @@ -231,6 +234,26 @@ func (env *TestEnvironment) waitForRisingWave(timeout time.Duration) bool { return false } +func (env *TestEnvironment) checkRisingWaveContainer(t *testing.T) { + t.Helper() + out, err := exec.Command("docker", "inspect", "--format", "{{.State.Running}} {{.State.ExitCode}} {{.State.OOMKilled}}", env.risingwaveContainer).CombinedOutput() + if err != nil { + t.Fatalf("inspect RisingWave container: %v\n%s", err, out) + } + state := strings.Fields(string(out)) + if len(state) != 3 { + t.Fatalf("unexpected RisingWave container state: %s", out) + } + if state[0] == "true" { + return + } + if state[1] == "132" && state[2] == "false" { + t.Skip("RisingWave image exited with illegal instruction on this CPU") + } + logs, _ := exec.Command("docker", "logs", env.risingwaveContainer).CombinedOutput() + t.Fatalf("RisingWave container exited before it became ready (code %s, OOM killed: %s):\n%s", state[1], state[2], logs) +} + func runPostgresClientSQL(containerName, sql string) ([]byte, error) { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() diff --git a/test/testutil/helpers.go b/test/testutil/helpers.go index 8d2d6b2806..e0eafb33f3 100644 --- a/test/testutil/helpers.go +++ b/test/testutil/helpers.go @@ -51,6 +51,11 @@ func RequireImageRunnable(t *testing.T, args ...string) { } } +func IsIllegalInstruction(err error) bool { + var exitErr *exec.ExitError + return errors.As(err, &exitErr) && exitErr.ExitCode() == 132 +} + func FindBindIP() string { addrs, err := net.InterfaceAddrs() if err != nil { diff --git a/test/testutil/helpers_test.go b/test/testutil/helpers_test.go new file mode 100644 index 0000000000..efc302fc6c --- /dev/null +++ b/test/testutil/helpers_test.go @@ -0,0 +1,39 @@ +package testutil + +import ( + "errors" + "fmt" + "os" + "os/exec" + "strconv" + "testing" +) + +func TestIsIllegalInstruction(t *testing.T) { + if code := os.Getenv("SEAWEEDFS_TEST_EXIT_CODE"); code != "" { + status, _ := strconv.Atoi(code) + os.Exit(status) + } + + for _, tc := range []struct { + name string + code string + want bool + }{ + {"illegal instruction", "132", true}, + {"ordinary failure", "1", false}, + {"out of memory", "137", false}, + } { + t.Run(tc.name, func(t *testing.T) { + cmd := exec.Command(os.Args[0], "-test.run=^TestIsIllegalInstruction$") + cmd.Env = append(os.Environ(), "SEAWEEDFS_TEST_EXIT_CODE="+tc.code) + err := cmd.Run() + if got := IsIllegalInstruction(fmt.Errorf("docker run: %w", err)); got != tc.want { + t.Fatalf("IsIllegalInstruction(%v) = %v, want %v", err, got, tc.want) + } + }) + } + if IsIllegalInstruction(nil) || IsIllegalInstruction(errors.New("exit status 132")) { + t.Fatal("non-process errors must not be treated as illegal instructions") + } +}