-
-
Notifications
You must be signed in to change notification settings - Fork 510
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: more robust Reusable containers experience #2768
base: main
Are you sure you want to change the base?
Conversation
Removing the sessionID label, it won't be pruned by Ryuk
✅ Deploy Preview for testcontainers-go ready!
To edit notification comments on pull requests, go to your Netlify site configuration. |
@stevenh as always, your wise 👀 are more than welcome! |
// can proceed with the creation of the container. | ||
// This is needed because we need to synchronize the creation of the container across | ||
// different test programs. | ||
c, err := p.waitContainerCreationInTimeout(ctx, hash, 5*time.Second) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see this as a potential configuration:
- a property:
testcontainers.reuse.search.timeout
, and - an env var:
TESTCONTAINERS_REUSE_SEARCH_TIMEOUT
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the PR description says:
We expect this change helps the community, but at the same time warn about its usage in parallel executions, as it could be the case that two concurrent test sessions get to the container creation at the same time, which could lead to the creation of two containers with the same request.
wouldn't we want to accept this limitation and allow for the race condition with the current implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Added that comment as an idea for a potential follow-up
I wonder if this solves this bug: #2749 I'll do a small code review here as well. Thank you for this! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done an initial pass
docker.go
Outdated
err = req.creatingHook(ctx) | ||
if err != nil { | ||
return nil, err | ||
if req.Reuse { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: Not sure about this, I would expect reused containers to still only persist while in use, is that the intent?
The edge case for shutdown while still in use, is addressed by reaper rework PRs, so it should be safe to remove this if the desired behaviour is to share between test runs that are within a reasonable window.
reuseContainerMx.Lock() | ||
defer reuseContainerMx.Unlock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: This will lock for the duration of rest of the call, is that the intention?
docker.go
Outdated
req.Labels[core.LabelContainerHash] = fmt.Sprintf("%d", hash.Hash) | ||
req.Labels[core.LabelCopiedFilesHash] = fmt.Sprintf("%d", hash.FilesHash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: do we need multiple hashes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is aligned with the Java implementation. We need these two in order to keep track of the added files before the container is started (Files attribute in the container request)
request_hash.go
Outdated
if !ok { | ||
// Calculate the hash of the file content only if it is a file. | ||
fileContent, err = os.ReadFile(f.HostFilePath) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bug: this will lead to unexplained issues.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see 🤔 . So it'd be ideal to get all the possible errors and fail the hash calculation, wouldn't it?
request_hash.go
Outdated
// the hash will be zero to avoid breaking the hash calculation. | ||
if f.Reader != nil { | ||
fileContent, err = io.ReadAll(f.Reader) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bug: this will lead to unexplained issues.
request_hash.go
Outdated
} | ||
} else { | ||
ok, err := isDir(f.HostFilePath) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bug: this will lead to unexplained issues.
) | ||
|
||
// containerHash represents the hash of the container configuration | ||
type containerHash struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: are two independent hashes needed.
@kiview I'm converting to draft until we discuss internally about the implications of switching from the container name-based approach to a hash-based approach. Will share here the discussion once it takes place. |
docker.go
Outdated
if req.Reuse { | ||
// Remove the SessionID label from the request, as we don't want Ryuk to control | ||
// the container lifecycle in the case of reusing containers. | ||
delete(req.Labels, core.LabelSessionID) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed to not remove the sessionId but an specific label when ryuk is enabled. TBD
* main: (103 commits) feat(postgres): ssl for postgres (testcontainers#2473) feat(ollama): support calling the Ollama local process (testcontainers#2923) chore(deps): bump jinja2 from 3.1.4 to 3.1.5 (testcontainers#2935) chore(deps): bump sonarsource/sonarcloud-github-action (testcontainers#2933) feat(termination)!: make container termination timeout configurable (testcontainers#2926) chore(deps): bump slackapi/slack-github-action from 1.26.0 to 2.0.0 (testcontainers#2934) chore(deps): bump github/codeql-action from 3.25.15 to 3.28.0 (testcontainers#2932) feat(wait): log sub match callback (testcontainers#2929) fix: Handle nil value in CleanupNetwork (testcontainers#2928) fix: avoid double lock in DockerProvider.DaemonHost() (testcontainers#2900) feat!: build log writer for container request (testcontainers#2925) feat(gcloud)!: add support to seed data when using RunBigQueryContainer (testcontainers#2523) security(deps): bump golang.org/x/crypto from 0.28.0 to 0.31.0 (testcontainers#2916) chore(ci): add Github labels based on PR title (testcontainers#2914) chore(gha): Use official setup-docker-action (testcontainers#2913) chore(ci): enforce conventional commits syntax in PR titles (testcontainers#2911) feat(nats): WithConfigFile - pass a configuration file to nats server (testcontainers#2905) chore: enable implicit default logger only in testing with -v (testcontainers#2877) fix: container binds syntax (testcontainers#2899) refactor(cockroachdb): to use request driven options (testcontainers#2883) ...
What does this PR do?
This PR modifies the way the Reuse mode is implemented in the project, taking tc-java as the implementation reference. For that:
github.com/mitchellh/hashstructure/v2
for the hash of the container request, with the following exceptions:hash:"ignore"
to identify them all.Files
field contains a reference to a directory in the host, it won't participate in the calculation of the hash, as we don't want to traverse the directory getting the hash for the directory tree.Files
field, we'll read their bytes and use them get a hash, which will be added to the struct hash.org.testcontainers.hash
: the hash of the container request.org.testcontainers.copied_files.hash
: the hash of the files copied to the container using theFiles
field in the container request.ReuseOrCreateContainer
from the provider struct has been deprecated for removal in the upcoming releases.Why is it important?
Create a more consistent experience around reuse, based on the container state and not forcing the user to add a container name, that can be reused by mistake in a totally different container request.
We expect this change helps the community, but at the same time warn about its usage in parallel executions, as it could be the case that two concurrent test sessions get to the container creation at the same time, which could lead to the creation of two containers with the same request.
Related issues
Docs
Render URLs:
Follow-ups
Please try out this branch, and read the docs so that they can be improved if needed 🙏