Skip to content

Conversation

@mdelapenya
Copy link
Member

What does this PR do?

Use Run function in the Cassandra module

Why is it important?

Migrate modules to the new API

Related issues

@mdelapenya mdelapenya requested a review from a team as a code owner September 29, 2025 15:39
@mdelapenya mdelapenya added the chore Changes that do not impact the existing functionality label Sep 29, 2025
@mdelapenya mdelapenya self-assigned this Sep 29, 2025
@mdelapenya mdelapenya added the chore Changes that do not impact the existing functionality label Sep 29, 2025
@netlify
Copy link

netlify bot commented Sep 29, 2025

Deploy Preview for testcontainers-go ready!

Name Link
🔨 Latest commit 1d9cf6b
🔍 Latest deploy log https://app.netlify.com/projects/testcontainers-go/deploys/68daad56dd8f3e0008e836ec
😎 Deploy Preview https://deploy-preview-3321--testcontainers-go.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Sep 29, 2025

Summary by CodeRabbit

  • Refactor

    • Streamlined Cassandra container setup with unified options.
    • Replaced specialized port type with a string-based port.
    • Consolidated wait strategy and applied user options consistently.
    • Standardized error message on startup failure.
  • Chores

    • Cleaned up dependencies by removing a direct port-mapping library.
    • Updated module metadata to reflect indirect dependency usage.
  • Documentation

    • Corrected reference to the default Cassandra port (9042) in comments.

Walkthrough

Replaces manual ContainerRequest + GenericContainer usage in the Cassandra module with testcontainers.Run and option builders; uses string port "9042/tcp" instead of nat.Port, applies WithExposedPorts/WithEnv/WithWaitStrategy via a moduleOpts slice, updates error text, and moves a docker connections dependency to indirect in go.mod.

Changes

Cohort / File(s) Summary
Cassandra module refactor
modules/cassandra/cassandra.go
Replaced manual ContainerRequest + GenericContainer flow with testcontainers.Run(ctx, image, moduleOpts...); switched port representation to the string "9042/tcp" (removed nat.Port), built moduleOpts using WithExposedPorts, WithEnv, WithWaitStrategy, appended user opts, mapped returned container to CassandraContainer.Container, and changed run error message to "run cassandra: %w".
Dependency classification
modules/cassandra/go.mod
Moved github.com/docker/go-connections v0.6.0 from direct require to an indirect (// indirect) entry; no API or behavioral changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Caller as Caller
  participant Module as Cassandra Module
  participant RunAPI as testcontainers.Run
  participant Engine as Container Engine

  Caller->>Module: Run(ctx, image, userOpts...)
  Module->>Module: Build moduleOpts (WithExposedPorts "9042/tcp", WithEnv, WithWaitStrategy, +userOpts)
  Module->>RunAPI: Run(ctx, image, moduleOpts...)
  RunAPI->>Engine: create & start container
  Engine-->>RunAPI: container handle / error
  RunAPI-->>Module: container (ctr) / error
  Module-->>Caller: CassandraContainer (with Container=ctr) / error
  note right of Module: Port as string "9042/tcp" and wait strategy via WithWaitStrategy
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • stevenh

Poem

A rabbit hops in code and sings,
Ports as strings and neat small things.
Options stacked like crunchy hay,
Run wakes Cassandra for the day.
Thump, the tests begin to play. 🥕

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Title Check ✅ Passed The title follows conventional commit styling and succinctly conveys the core change of migrating the Cassandra module to use the new Run function, making it clear and specific to the main update.
Description Check ✅ Passed The description clearly states the purpose and scope of the PR by explaining that it uses the Run function in the Cassandra module and why this migration to the new API is important, which directly relates to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
modules/cassandra/cassandra.go (1)

23-27: Fix comment: Cassandra native port is 9042, not 9000.

Adjust the doc to avoid confusion for users reading the API.

-// ConnectionHost returns the host and port of the cassandra container, using the default, native 9000 port, and
+// ConnectionHost returns the host and port of the Cassandra container, using the default native 9042 port, and
🧹 Nitpick comments (3)
modules/cassandra/cassandra.go (3)

33-41: YAML config shouldn’t be executable; use 0644 instead of 0755.

No need to grant execute bits to cassandra.yaml.

-            FileMode:          0o755,
+            FileMode:          0o644,

45-64: Avoid collisions when multiple scripts share the same basename.

Copying all scripts to "/" risks overwriting if basenames repeat. Either prefix to make paths unique or fail fast on duplicates.

 func WithInitScripts(scripts ...string) testcontainers.CustomizeRequestOption {
   return func(req *testcontainers.GenericContainerRequest) error {
-    var initScripts []testcontainers.ContainerFile
+    var initScripts []testcontainers.ContainerFile
     var execs []testcontainers.Executable
-    for _, script := range scripts {
+    // prevent overwriting when basenames collide
+    seen := make(map[string]struct{}, len(scripts))
+    for _, script := range scripts {
+      base := filepath.Base(script)
+      if _, ok := seen[base]; ok {
+        return fmt.Errorf("duplicate init script basename %q; please rename or ensure unique basenames", base)
+      }
+      seen[base] = struct{}{}
-      cf := testcontainers.ContainerFile{
-        HostFilePath:      script,
-        ContainerFilePath: "/" + filepath.Base(script),
+      cf := testcontainers.ContainerFile{
+        HostFilePath:      script,
+        ContainerFilePath: "/" + base,
         FileMode:          0o755,
       }
       initScripts = append(initScripts, cf)

       execs = append(execs, initScript{File: cf.ContainerFilePath})
     }

     req.Files = append(req.Files, initScripts...)
     return testcontainers.WithAfterReadyCommand(execs...)(req)
   }
 }

84-91: Increase wait deadline; Cassandra often takes >60s to bootstrap.

testcontainers.WithWaitStrategy defaults to 60s. Cassandra initialization can exceed that, causing flaky tests. Recommend using a longer deadline.

+import (
+    // ...
+    "time"
+)
@@
-        testcontainers.WithWaitStrategy(wait.ForAll(
+        testcontainers.WithWaitStrategyAndDeadline(3*time.Minute, wait.ForAll(
             wait.ForListeningPort(port),
             wait.ForExec([]string{"cqlsh", "-e", "SELECT bootstrapped FROM system.local"}).WithResponseMatcher(func(body io.Reader) bool {
                 data, _ := io.ReadAll(body)
                 return strings.Contains(string(data), "COMPLETED")
             }),
-        )),
+        )),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c3b4d4c and e3a0ef6.

📒 Files selected for processing (2)
  • modules/cassandra/cassandra.go (2 hunks)
  • modules/cassandra/go.mod (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-09-29T15:08:18.668Z
Learnt from: mdelapenya
PR: testcontainers/testcontainers-go#3320
File: modules/artemis/artemis.go:98-103
Timestamp: 2025-09-29T15:08:18.668Z
Learning: In testcontainers-go, nat.Port is a type alias for string, so untyped string constants can be passed directly to functions expecting nat.Port (like wait.ForListeningPort) without explicit type conversion - the Go compiler handles the implicit conversion automatically.

Applied to files:

  • modules/cassandra/cassandra.go
📚 Learning: 2025-09-18T08:24:27.479Z
Learnt from: mdelapenya
PR: testcontainers/testcontainers-go#3254
File: .github/dependabot.yml:21-21
Timestamp: 2025-09-18T08:24:27.479Z
Learning: In the testcontainers-go repository, submodules like atlaslocal that are part of a parent module (e.g., mongodb) share the same go.mod file and should not have separate Dependabot entries. They are already monitored through the parent module's Dependabot configuration entry.

Applied to files:

  • modules/cassandra/go.mod
🧬 Code graph analysis (1)
modules/cassandra/cassandra.go (5)
options.go (4)
  • ContainerCustomizer (22-24)
  • WithExposedPorts (464-469)
  • WithEnv (75-85)
  • WithWaitStrategy (376-378)
wait/all.go (1)
  • ForAll (44-48)
wait/host_port.go (1)
  • ForListeningPort (67-69)
wait/exec.go (1)
  • ForExec (71-73)
modules/azure/azurite/azurite.go (2)
  • Run (74-119)
  • Container (31-34)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: lint (modules/cassandra) / lint: modules/cassandra
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
modules/cassandra/cassandra.go (2)

15-16: Port constant as string is fine here.

Passing a string to wait.ForListeningPort works because nat.Port is a type alias for string. No need for explicit conversion.

Based on learnings


74-76: Migration to Run with composed options looks solid.

Cleaner option composition, opts appended in the right order, error wrapping is explicit, and container wrapping is safe.

Please ensure no behavior regressions in CI by running a Cassandra smoke test that exercises:

  • WithConfigFile + init scripts
  • Custom image override via Run(ctx, img, ...)

Also applies to: 93-106

modules/cassandra/go.mod (1)

26-26: Approve dependency scope change; no direct imports detected.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
modules/cassandra/cassandra.go (3)

29-43: YAML shouldn’t be executable; fix mode and align comment with behavior

  • Set cassandra.yaml to 0644 instead of 0755.
  • The comment says this also sets a "configFile" CLI parameter, but the code only copies the file. Update the comment for accuracy.
-// It will also set the "configFile" parameter to the path of the config file
-// as a command line argument to the container.
+// It copies the provided YAML into /etc/cassandra/cassandra.yaml inside the container.
@@
-            FileMode:          0o755,
+            FileMode:          0o644,

74-84: Env setup is fine; consider avoiding duplicate snitch vars (optional)

You set both CASSANDRA_SNITCH and CASSANDRA_ENDPOINT_SNITCH to the same value. If not required for backward compatibility, drop one to reduce ambiguity (I’d keep CASSANDRA_ENDPOINT_SNITCH).

-            "CASSANDRA_SNITCH":          "GossipingPropertyFileSnitch",

If older images in your matrix depend on CASSANDRA_SNITCH, keep both. Please confirm test coverage across images.


84-91: Increase wait deadline to prevent flakes; minor matcher hardening

Default WithWaitStrategy uses a 60s deadline, which is tight for Cassandra bootstrap. Bump to a more forgiving deadline (e.g., 3m). Also, make the matcher resilient to read errors.

-        testcontainers.WithWaitStrategy(wait.ForAll(
+        testcontainers.WithWaitStrategyAndDeadline(3*time.Minute, wait.ForAll(
             wait.ForListeningPort(port),
             wait.ForExec([]string{"cqlsh", "-e", "SELECT bootstrapped FROM system.local"}).WithResponseMatcher(func(body io.Reader) bool {
-                data, _ := io.ReadAll(body)
-                return strings.Contains(string(data), "COMPLETED")
+                data, err := io.ReadAll(body)
+                return err == nil && strings.Contains(string(data), "COMPLETED")
             }),
-        )),
+        )),

Add missing import:

 import (
     "context"
     "fmt"
     "io"
+    "time"
     "path/filepath"
     "strings"

I can parameterize the deadline via an option if you want this configurable per test.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e3a0ef6 and 1d9cf6b.

📒 Files selected for processing (1)
  • modules/cassandra/cassandra.go (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: mdelapenya
PR: testcontainers/testcontainers-go#3319
File: modules/arangodb/arangodb.go:46-57
Timestamp: 2025-09-29T13:57:14.629Z
Learning: In testcontainers-go ArangoDB module, the wait strategy combines port listening check with HTTP readiness check using wait.ForAll - both strategies are required and complementary, not redundant.
📚 Learning: 2025-09-29T15:08:18.668Z
Learnt from: mdelapenya
PR: testcontainers/testcontainers-go#3320
File: modules/artemis/artemis.go:98-103
Timestamp: 2025-09-29T15:08:18.668Z
Learning: In testcontainers-go, nat.Port is a type alias for string, so untyped string constants can be passed directly to functions expecting nat.Port (like wait.ForListeningPort) without explicit type conversion - the Go compiler handles the implicit conversion automatically.

Applied to files:

  • modules/cassandra/cassandra.go
🧬 Code graph analysis (1)
modules/cassandra/cassandra.go (4)
modules/azure/azurite/azurite.go (2)
  • Container (31-34)
  • Run (74-119)
options.go (4)
  • ContainerCustomizer (22-24)
  • WithExposedPorts (464-469)
  • WithEnv (75-85)
  • WithWaitStrategy (376-378)
wait/host_port.go (1)
  • ForListeningPort (67-69)
wait/exec.go (1)
  • ForExec (71-73)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (1.25.x, modules/cassandra) / test: modules/cassandra/1.25.x
  • GitHub Check: test (1.24.x, modules/cassandra) / test: modules/cassandra/1.24.x
  • GitHub Check: Analyze (go)
🔇 Additional comments (4)
modules/cassandra/cassandra.go (4)

15-15: Port as string is correct and idiomatic

Using a string constant for the port works with both WithExposedPorts and wait.ForListeningPort because nat.Port is a type alias for string. No explicit conversion needed. Based on learnings


23-27: Doc tweak looks good

Docstring now reflects the native 9042 port and ConnectionHost correctly delegates to PortEndpoint.


93-93: Good: user opts applied after defaults

Appending opts after module defaults preserves sensible defaults while allowing overrides.


95-103: Run pattern and error wrapping look solid

Wrapping the returned container even on error mirrors other modules and helps with cleanup; error context “run cassandra” is clear.

@mdelapenya mdelapenya merged commit 9dcbf8b into testcontainers:main Sep 29, 2025
16 checks passed
@mdelapenya mdelapenya deleted the use-run-cassandra branch September 29, 2025 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Changes that do not impact the existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant