-
-
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: * 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`), but also... * For many orgs, sticking a prefix on the front of image names might be enough to use a private registry. I've added a default behaviour whereby, if a particular environment variable is present, image names are automatically substituted. e.g. `TESTCONTAINERS_IMAGE_NAME_PREFIX=my.registry.com/` would transform `redis` to `my.registry.com/redis` etc. 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
57 changed files
with
1,033 additions
and
354 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
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(); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
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,51 @@ | ||
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} followed by {@link PrefixingImageNameSubstitutor}. | ||
* <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; | ||
private final PrefixingImageNameSubstitutor prefixingImageNameSubstitutor; | ||
|
||
public DefaultImageNameSubstitutor() { | ||
configurationFileImageNameSubstitutor = new ConfigurationFileImageNameSubstitutor(); | ||
prefixingImageNameSubstitutor = new PrefixingImageNameSubstitutor(); | ||
} | ||
|
||
@VisibleForTesting | ||
DefaultImageNameSubstitutor( | ||
final ConfigurationFileImageNameSubstitutor configurationFileImageNameSubstitutor, | ||
final PrefixingImageNameSubstitutor prefixingImageNameSubstitutor | ||
) { | ||
this.configurationFileImageNameSubstitutor = configurationFileImageNameSubstitutor; | ||
this.prefixingImageNameSubstitutor = prefixingImageNameSubstitutor; | ||
} | ||
|
||
@Override | ||
public DockerImageName apply(final DockerImageName original) { | ||
return prefixingImageNameSubstitutor.apply( | ||
configurationFileImageNameSubstitutor.apply( | ||
original | ||
) | ||
); | ||
} | ||
|
||
@Override | ||
protected int getPriority() { | ||
return 0; | ||
} | ||
|
||
@Override | ||
protected String getDescription() { | ||
return "DefaultImageNameSubstitutor (composite of '" + configurationFileImageNameSubstitutor.getDescription() + "' and '" + prefixingImageNameSubstitutor.getDescription() + "')"; | ||
} | ||
} |
94 changes: 94 additions & 0 deletions
94
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,94 @@ | ||
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(); | ||
|
||
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(); | ||
} | ||
} | ||
} |
Oops, something went wrong.