Skip to content

Normalize image names in prefix substitutor #8509

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public final class DockerImageName {

private static final Pattern REPO_NAME = Pattern.compile(REPO_NAME_PART + "(/" + REPO_NAME_PART + ")*");

private static final String LIBRARY_PREFIX = "library/";
static final String LIBRARY_PREFIX = "library/";

private final String rawName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ final class PrefixingImageNameSubstitutor extends ImageNameSubstitutor {
@VisibleForTesting
static final String PREFIX_PROPERTY_KEY = "hub.image.name.prefix";

@VisibleForTesting
static final String NORMALIZE_PROPERTY_KEY = "hub.image.name.normalize";

private TestcontainersConfiguration configuration = TestcontainersConfiguration.getInstance();

@VisibleForTesting
Expand All @@ -26,6 +29,9 @@ final class PrefixingImageNameSubstitutor extends ImageNameSubstitutor {
@Override
public DockerImageName apply(DockerImageName original) {
final String configuredPrefix = configuration.getEnvVarOrProperty(PREFIX_PROPERTY_KEY, "");
final boolean normalize = Boolean.parseBoolean(
configuration.getEnvVarOrProperty(NORMALIZE_PROPERTY_KEY, "false")
);

if (configuredPrefix.isEmpty()) {
log.debug("No prefix is configured");
Expand All @@ -38,13 +44,27 @@ public DockerImageName apply(DockerImageName original) {
return original;
}

log.debug("Applying changes to image name {}: applying prefix '{}'", original, configuredPrefix);
log.debug(
"Applying changes to image name {}: applying prefix '{}' with normalization: {}",
original,
configuredPrefix,
normalize
);

DockerImageName prefixAsImage = DockerImageName.parse(configuredPrefix);

return original
String repository = original.getRepository();
if (normalize && !repository.contains("/")) {
repository = DockerImageName.LIBRARY_PREFIX + repository;
}

DockerImageName substituted = original
.withRegistry(prefixAsImage.getRegistry())
.withRepository(prefixAsImage.getRepository() + original.getRepository());
.withRepository(prefixAsImage.getRepository() + repository);
if (normalize) {
substituted = substituted.asCompatibleSubstituteFor(original);
}
return substituted;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,21 @@ public void testHappyPath() {
.isEqualTo("someregistry.com/our-mirror/some/image:tag");
}

@Test
public void testNormalizeToLibraryPath() {
when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any()))
.thenReturn("someregistry.com/our-mirror/");
when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.NORMALIZE_PROPERTY_KEY), any()))
.thenReturn("true");

final DockerImageName result = underTest.apply(DockerImageName.parse("image:tag"));

assertThat(result.asCanonicalNameString())
.as("The prefix is applied")
.isEqualTo("someregistry.com/our-mirror/library/image:tag");
result.assertCompatibleWith(DockerImageName.parse("image:tag"));
}

@Test
public void hubIoRegistryIsNotChanged() {
when(mockConfiguration.getEnvVarOrProperty(eq(PrefixingImageNameSubstitutor.PREFIX_PROPERTY_KEY), any()))
Expand Down
7 changes: 7 additions & 0 deletions docs/features/image_name_substitution.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ Testcontainers will not apply the prefix to:
* non-Hub image names (e.g. where another registry is set)
* Docker Hub image names where the hub registry is explicitly part of the name (i.e. anything with a `docker.io` or `registry.hub.docker.com` host part)

If you want your registry to handle both official Docker Hub images (e.g `postgres`)
as well as images from other registries (e.g `mycompany/postgres`), you can use the
`TESTCONTAINERS_HUB_IMAGE_NAME_NORMALIZE` environment variable or the `hub.image.name.normalize`
configuration option. When set to `true`, Testcontainers will normalize the official Docker Hub
image names to start with `library/`. When this option is used, Testcontainers will additionally
disable image compatibility checks done by some containers, so it the compatibility responsibility
is on the user.
Comment on lines +80 to +86
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added this. cc @eddumelendez.

Did i get the env variable name right?
There is automatic conversion from TESTCONTAINERS_HUB_IMAGE_NAME_NORMALIZE to hub.image.name.normalize, right?

Copy link
Member

Choose a reason for hiding this comment

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

yes, the name is correct.

When this option is used, Testcontainers will additionally disable image compatibility checks done by some containers, so it the compatibility responsibility is on the user.

I don't think this is the case because the check compatibility is executed first. Can you please add a test similar to

@Test
public void testWorksWithoutConfiguredImplementation() {
Mockito.doReturn(null).when(TestcontainersConfiguration.getInstance()).getImageSubstitutorClassName();
final ImageNameSubstitutor imageNameSubstitutor = ImageNameSubstitutor.instance();
DockerImageName result = imageNameSubstitutor.apply(DockerImageName.parse("original"));
assertThat(result.asCanonicalNameString())
.as("the image has been substituted by default then configured implementations")
.isEqualTo("substituted-image:latest");
}
in order to cover it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think this is the case because the check compatibility is executed first.

that makes sense!

however, when testing this PR internally, i run into some issues until i added "as compatible substitute" call.
is it possible that there are some additional checks somewhere else? sadly, don't have stacktrace now



## Developing a custom function for transforming image names on the fly
Expand Down
Loading