Skip to content

Comments

Replace docker-fixtures with testcontainers#414

Merged
jglick merged 1 commit intojenkinsci:masterfrom
strangelookingnerd:testcontainers
Jul 18, 2025
Merged

Replace docker-fixtures with testcontainers#414
jglick merged 1 commit intojenkinsci:masterfrom
strangelookingnerd:testcontainers

Conversation

@strangelookingnerd
Copy link
Contributor

@strangelookingnerd strangelookingnerd commented Jul 11, 2025

Replace docker-fixtures with testcontainers (see jenkinsci/docker-fixtures#122 (comment))

This change removes already deprecated API methods from the test jar.

Testing done

mvn clean verfiy

Submitter checklist

  • Make sure you are opening from a topic/feature/bugfix branch (right side) and not your main branch!
  • Ensure that the pull request title represents the desired changelog entry
  • Please describe what you did
  • Link to relevant issues in GitHub or Jira
  • Link to relevant pull requests, esp. upstream and downstream changes
  • Ensure you have provided tests that demonstrate the feature works or the issue is fixed

@strangelookingnerd
Copy link
Contributor Author

Once this change is merged and released it needs to be picked up by bom. I have prepared PRs for the consuming plugins and will update them once that happened.

@strangelookingnerd strangelookingnerd marked this pull request as ready for review July 11, 2025 12:37
@strangelookingnerd strangelookingnerd requested a review from a team as a code owner July 11, 2025 12:37
Copy link
Member

@basil basil left a comment

Choose a reason for hiding this comment

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

This PR is introducing technical debt, as the Dockerfile is duplicated between #414 and jenkinsci/docker-fixtures#122, which creates a maintenance burden. See this page for more information about why this is the case. I recommend refactoring the common images that used to be present in docker-fixtures into a new Jenkins repository for TestContainers, something like https://github.com/dasniko/testcontainers-keycloak.

Docker docker = new Docker();
if (!Functions.isWindows() && docker.isAvailable()) { // TODO: Windows agents on ci.jenkins.io have Docker, but cannot build the image.
return docker.build(JavaContainer.class);
public static @CheckForNull GenericContainer<?> prepareImage() {
Copy link
Member

Choose a reason for hiding this comment

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

Why are you introducing a new public method? As noted in the original Javadoc, this is no longer used outside the repo, or should not be anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I see, the comment and deprecation were in fact wrong.

@jglick
Copy link
Member

jglick commented Jul 11, 2025

I have thought of that but have mixed feelings. On the one hand, there would be less duplication. On the other hand, it makes it harder to develop and maintain tests because the repo is not self-contained: if you wish to adjust even a minor aspect of the Docker environment, you have to open a pull request on an external repo, spend time thinking about whether it is backward compatible, find someone to merge it, wait for it to be published, and only then proceed with your code changes.

Another benefit of a published image is that tests would also run faster on fresh CI machines since they would only need to download an image, not build one in steps. This can be solved by using --cache-from / --cache-to but requires additional infrastructure on the CI server for authentication, which may be impractical in @jenkinsci where plugin developers are not really trusted (every repo would need its own cache location and corresponding write credentials). Note that we already pay this cost with docker-fixtures, so switching to Testcontainers with ImageFromDockerfile does not make anything worse.

StandardUsernameCredentials creds = new UsernamePasswordCredentialsImpl(CredentialsScope.SYSTEM, "test", "desc", "test", "test");
CredentialsProvider.lookupStores(Jenkins.get()).iterator().next().addCredentials(Domain.global(), creds);
agent = new DumbSlave("test-agent", "/home/test/slave", new SSHLauncher(runningContainer.ipBound(22), runningContainer.port(22), "test"));
agent = new DumbSlave("test-agent", "/home/test/slave", new SSHLauncher(container.getHost(), container.getMappedPort(22), "test"));
Copy link
Member

Choose a reason for hiding this comment

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

I actually recommend switching from this container to a standard one. https://hub.docker.com/r/jenkins/ssh-agent could probably be used, just passing a predefined JENKINS_AGENT_SSH_PUBKEY. It may be easier to switch to an inbound https://hub.docker.com/r/jenkins/agent though. https://github.com/jenkinsci/workflow-cps-plugin/blob/b2a1d7b29c6fdd0161dbb1c7586d579d49bbe1e2/plugin/src/test/java/org/jenkinsci/plugins/workflow/cps/SerialFormTest.java#L80-L85 works (though the requirement to exposeHostPorts is admittedly ugly); note that that test is more complex than you actually need for most purposes specifically because it is testing upgrade from @LocalData. I swear I wrote a simpler version somewhere recently, but I cannot find it now.

Copy link
Member

Choose a reason for hiding this comment

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

For context, there are three common reasons to use containers in a Jenkins test:

  • To run some random service, like LDAP.
  • To run an agent with specific tools installed that it is expected to call, like a particular version of an SCM.
  • To run an agent but where the test does not care anything about the launcher or the details of the container image or even whether the agent runs in a container at all—merely that it not run in the same OS namespace as the controller (and test code), otherwise you could just use JenkinsRule.createAgent.

This test falls into the last category. The idea is to catch mistakes in remoted callables which might be presuming that a path valid on the controller is also valid on an agent or vice-versa. So we can use any inbound or outbound agent image from the past few years (just needs Java 17+ obviously) and it would be fine.

Copy link
Member

Choose a reason for hiding this comment

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

The second and third of these need a version of Java that is compatible with the controller, and every few years this changes, so it is important that we not duplicate too much code here if we can avoid it. Relying on the standard https://hub.docker.com/r/jenkins/ssh-agent sounds great to me, but it is possibly more effort to migrate tests to that approach. A more simple and direct port of the existing logic (and therefore lower-effort migration) would be to simply convert the existing images in this repository to Test Containers along the lines of https://github.com/dasniko/testcontainers-keycloak. That would still preserve the existing tech debt of having a test image distinct from the production image, but it would still reduce the test debt of using an unmaintained framework (docker-fixtures) in favor of a maintained framework (Test Containers), without having to duplicate the image in each consumer.

Copy link
Member

Choose a reason for hiding this comment

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

It is an option of course, but we would anyway want to throw out any such images sooner or later and just use the standard jenkins/{,ssh-}agent images for these cases of the third type. Since the existing docker-fixtures release is working fine as it is, I do not see any urgency in an intermediate fix: we may as well write tests the straightforward way to begin with. I can check feasibility in this PR next week.

Copy link
Member

Choose a reason for hiding this comment

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

I swear I wrote a simpler version somewhere recently, but I cannot find it now.

Ah found it at last: jenkinsci/docker-workflow-plugin#351 (comment)

@basil
Copy link
Member

basil commented Jul 11, 2025

I have thought of that but have mixed feelings. On the one hand, there would be less duplication.

The duplication has a significant negative cost for doing Java version upgrades. Each time we have done a Java version upgrade in the past, a significant amount of time has been spent hunting down old Docker images in tests and updating them to include newer Java versions. With a common definition in docker-fixtures at least this has to be done in only one place, and then Dependabot can propose updates elsewhere.

On the other hand, it makes it harder to develop and maintain tests because the repo is not self-contained: if you wish to adjust even a minor aspect of the Docker environment, you have to open a pull request on an external repo, spend time thinking about whether it is backward compatible, find someone to merge it, wait for it to be published, and only then proceed with your code changes.

For the most part these images don't change very often, only a few times in many years (for Java version upgrades mostly), so I don't see this as a real downside.

Copy link
Member

@jglick jglick left a comment

Choose a reason for hiding this comment

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

Propose #416 (and linked PRs) instead.

@jglick jglick merged commit f606866 into jenkinsci:master Jul 18, 2025
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants