-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Builds upon #3021 and #3411: * adds a pluggable image substitution mechanism using ServiceLoader, enabling users to perform custom substitution/auditing of images being used by their tests * provides a default implementation that behaves similarly to legacy `TestcontainersConfiguration` approach (`testcontainers.properties`) Notes: * behaviour is similar but not quite identical to `TestcontainersConfiguration`: use of a configured custom image for, e.g. Kafka/Pulsar that does not have a tag specified causes the substitution to take effect for all usages. It seems very unlikely that people would be using a mix of the config file image overrides in some places _and_ specific images specified in code in others. * Duplication of default image names in modules vs `TestcontainersConfiguration` class is intentional: specifying image overrides in `testcontainers.properties` should be removed in the future. * ~Add log deprecation warnings when `testcontainers.properties` image overrides are used.~ Defer to a future release?
- Loading branch information
Showing
34 changed files
with
575 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
core/src/main/java/org/testcontainers/utility/ConfigurationFileImageNameSubstitutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package org.testcontainers.utility; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
/** | ||
* {@link ImageNameSubstitutor} which takes replacement image names from configuration. | ||
* See {@link TestcontainersConfiguration} for the subset of image names which can be substituted using this mechanism. | ||
* <p> | ||
* WARNING: this class is not intended to be public, but {@link java.util.ServiceLoader} | ||
* requires it to be so. Public visibility DOES NOT make it part of the public API. | ||
*/ | ||
@Slf4j | ||
public class ConfigurationFileImageNameSubstitutor extends ImageNameSubstitutor { | ||
|
||
private final TestcontainersConfiguration configuration; | ||
|
||
public ConfigurationFileImageNameSubstitutor() { | ||
this(TestcontainersConfiguration.getInstance()); | ||
} | ||
|
||
@VisibleForTesting | ||
ConfigurationFileImageNameSubstitutor(TestcontainersConfiguration configuration) { | ||
this.configuration = configuration; | ||
} | ||
|
||
@Override | ||
public DockerImageName apply(final DockerImageName original) { | ||
final DockerImageName result = configuration | ||
.getConfiguredSubstituteImage(original) | ||
.asCompatibleSubstituteFor(original); | ||
|
||
if (!result.equals(original)) { | ||
log.warn("Image name {} was substituted by configuration to {}. This approach is deprecated and will be removed in the future", | ||
original, | ||
result | ||
); | ||
} | ||
|
||
return result; | ||
} | ||
|
||
@Override | ||
protected int getPriority() { | ||
return -2; | ||
} | ||
|
||
@Override | ||
protected String getDescription() { | ||
return getClass().getSimpleName(); | ||
} | ||
} |
43 changes: 43 additions & 0 deletions
43
core/src/main/java/org/testcontainers/utility/DefaultImageNameSubstitutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package org.testcontainers.utility; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
/** | ||
* Testcontainers' default implementation of {@link ImageNameSubstitutor}. | ||
* Delegates to {@link ConfigurationFileImageNameSubstitutor}. | ||
* <p> | ||
* WARNING: this class is not intended to be public, but {@link java.util.ServiceLoader} | ||
* requires it to be so. Public visibility DOES NOT make it part of the public API. | ||
*/ | ||
@Slf4j | ||
public class DefaultImageNameSubstitutor extends ImageNameSubstitutor { | ||
|
||
private final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor; | ||
|
||
public DefaultImageNameSubstitutor() { | ||
configurationFileImageNameSubstitutor = new ConfigurationFileImageNameSubstitutor(); | ||
} | ||
|
||
@VisibleForTesting | ||
DefaultImageNameSubstitutor( | ||
final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor | ||
) { | ||
this.configurationFileImageNameSubstitutor = configurationFileImageNameSubstitutor; | ||
} | ||
|
||
@Override | ||
public DockerImageName apply(final DockerImageName original) { | ||
return configurationFileImageNameSubstitutor.apply(original); | ||
} | ||
|
||
@Override | ||
protected int getPriority() { | ||
return 0; | ||
} | ||
|
||
@Override | ||
protected String getDescription() { | ||
return "DefaultImageNameSubstitutor (delegates to '" + configurationFileImageNameSubstitutor.getDescription() + "')"; | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
core/src/main/java/org/testcontainers/utility/ImageNameSubstitutor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package org.testcontainers.utility; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
import java.util.ServiceLoader; | ||
import java.util.function.Function; | ||
import java.util.stream.StreamSupport; | ||
|
||
import static java.util.Comparator.comparingInt; | ||
|
||
/** | ||
* An image name substitutor converts a Docker image name, as may be specified in code, to an alternative name. | ||
* This is intended to provide a way to override image names, for example to enforce pulling of images from a private | ||
* registry. | ||
*/ | ||
@Slf4j | ||
public abstract class ImageNameSubstitutor implements Function<DockerImageName, DockerImageName> { | ||
|
||
@VisibleForTesting | ||
static ImageNameSubstitutor instance; | ||
|
||
public synchronized static ImageNameSubstitutor instance() { | ||
if (instance == null) { | ||
final ServiceLoader<ImageNameSubstitutor> serviceLoader = ServiceLoader.load(ImageNameSubstitutor.class); | ||
|
||
instance = StreamSupport.stream(serviceLoader.spliterator(), false) | ||
.peek(it -> log.debug("Found ImageNameSubstitutor using ServiceLoader: {} (priority {}) ", it, it.getPriority())) | ||
.max(comparingInt(ImageNameSubstitutor::getPriority)) | ||
.map(ImageNameSubstitutor::wrapWithLogging) | ||
.orElseThrow(() -> new RuntimeException("Unable to find any ImageNameSubstitutor using ServiceLoader")); | ||
|
||
log.info("Using ImageNameSubstitutor: {}", instance); | ||
} | ||
|
||
return instance; | ||
} | ||
|
||
private static ImageNameSubstitutor wrapWithLogging(final ImageNameSubstitutor wrappedInstance) { | ||
return new LogWrappedImageNameSubstitutor(wrappedInstance); | ||
} | ||
|
||
/** | ||
* Substitute a {@link DockerImageName} for another, for example to replace a generic Docker Hub image name with a | ||
* private registry copy of the image. | ||
* | ||
* @param original original name to be replaced | ||
* @return a replacement name, or the original, as appropriate | ||
*/ | ||
public abstract DockerImageName apply(DockerImageName original); | ||
|
||
/** | ||
* Priority of this {@link ImageNameSubstitutor} compared to other instances that may be found by the service | ||
* loader. The highest priority instance found will always be used. | ||
* | ||
* @return a priority | ||
*/ | ||
protected abstract int getPriority(); | ||
|
||
protected abstract String getDescription(); | ||
|
||
/** | ||
* Wrapper substitutor which logs which substitutions have been performed. | ||
*/ | ||
static class LogWrappedImageNameSubstitutor extends ImageNameSubstitutor { | ||
@VisibleForTesting | ||
final ImageNameSubstitutor wrappedInstance; | ||
|
||
public LogWrappedImageNameSubstitutor(final ImageNameSubstitutor wrappedInstance) { | ||
this.wrappedInstance = wrappedInstance; | ||
} | ||
|
||
@Override | ||
public DockerImageName apply(final DockerImageName original) { | ||
final String className = wrappedInstance.getClass().getName(); | ||
final DockerImageName replacementImage = wrappedInstance.apply(original); | ||
|
||
if (!replacementImage.equals(original)) { | ||
log.info("Using {} as a substitute image for {} (using image substitutor: {})", replacementImage.asCanonicalNameString(), original.asCanonicalNameString(), className); | ||
return replacementImage; | ||
} else { | ||
log.debug("Did not find a substitute image for {} (using image substitutor: {})", original.asCanonicalNameString(), className); | ||
return original; | ||
} | ||
} | ||
|
||
@Override | ||
protected int getPriority() { | ||
return wrappedInstance.getPriority(); | ||
} | ||
|
||
@Override | ||
protected String getDescription() { | ||
return wrappedInstance.getDescription(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.