Skip to content

Commit

Permalink
Relocate modules (testcontainers#607)
Browse files Browse the repository at this point in the history
## General
* Docs update referring to new location of modules
* Update copyright and thanks
* Add tests for pulling various permutations of image registry/name/tag
* Include pointers to latest versions available through Maven Central

## MariaDB
* Relocate MariaDB module into main testcontainers-java repository
Also enable parallel running of Jdbc Driver tests for speed reasons

## Oracle XE
* Relocate Oracle XE module into main testcontainers-java repository
Also enable use of sha256 hashes instead of image tags, for situations
where image tags are not easily pinnable
* Extend pull/startup timeout for Oracle container
* Change rate limiter usage to prevent excessive connection creation
Intended to eliminate `ORA-12516, TNS:listener could not find available handler` error
* Add getConnectTimeoutSeconds as a separate timeout for JDBC containers to establish a JDBC connection

## Localstack
* Relocate Localstack module into main testcontainers-java repository
* Use latest localstack image

## Dynalite
* Relocate Dynalite module into main testcontainers-java repository
* Migrate dynalite image to quay.io

## MS SQL Server
* Relocate MS SQL Server module into main testcontainers-java repository
* Introduce assertion for license acceptance, e.g. for the SQL Server EULA

## Vault
* Relocate Vault module into main testcontainers-java repository
  • Loading branch information
rnorth authored Mar 26, 2018
1 parent 37456ae commit 23478fa
Show file tree
Hide file tree
Showing 60 changed files with 2,098 additions and 90 deletions.
14 changes: 14 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Each line is a file pattern followed by one or more owners.

# These owners will be the default reviewers for everything in
# the repo.

* @rnorth @bsideup @kiview

# The last matching pattern takes the most
# precedence.

# Contributed modules can have different reviewers

modules/mssqlserver/ @StefanHufschmidt @rnorth @bsideup @kiview
modules/vault/ @mikeoswald @rnorth @bsideup @kiview
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ See [LICENSE](LICENSE).

Copyright (c) 2015, 2016 Richard North and other authors.

See [AUTHORS](AUTHORS) for contributors.
MS SQL Server module is (c) 2017 - 2019 G DATA Software AG and other authors.

Hashicorp Vault module is (c) 2017 Capital One Services, LLC and other authors.

See [AUTHORS](AUTHORS) for all contributors.
33 changes: 28 additions & 5 deletions circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,24 @@ jobs:
core:
steps:
- checkout
- run: ./gradlew testcontainers:check
- run:
command: ./gradlew testcontainers:check
- run:
name: Save test results
command: |
mkdir -p ~/junit/
find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \;
when: always
- store_test_results:
path: ~/junit
modules-no-jdbc-test-no-selenium:
steps:
- checkout
- run:
command: ./gradlew check -x testcontainers:check -x selenium:check -x jdbc-test:check
environment:
# Oracle JDBC drivers require a timezone to be set
TZ: "/usr/share/zoneinfo/ETC/UTC"
- run:
name: Save test results
command: |
Expand All @@ -13,10 +30,14 @@ jobs:
when: always
- store_test_results:
path: ~/junit
modules:
modules-jdbc-test:
steps:
- checkout
- run: ./gradlew check -x testcontainers:check -x selenium:check
- run:
command: ./gradlew jdbc-test:check
environment:
# Oracle JDBC drivers require a timezone to be set
TZ: "/usr/share/zoneinfo/ETC/UTC"
- run:
name: Save test results
command: |
Expand All @@ -28,7 +49,8 @@ jobs:
selenium:
steps:
- checkout
- run: ./gradlew selenium:check
- run:
command: ./gradlew selenium:check
- run:
name: Save test results
command: |
Expand All @@ -43,5 +65,6 @@ workflows:
test_all:
jobs:
- core
- modules
- modules-no-jdbc-test-no-selenium
- modules-jdbc-test
- selenium
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ public String getDockerImageName() {
try {
return image.get();
} catch (Exception e) {
throw new ContainerFetchException("Can't get Docker image name from " + image, e);
throw new ContainerFetchException("Can't get Docker image: " + image, e);
}
}

Expand Down
65 changes: 37 additions & 28 deletions core/src/main/java/org/testcontainers/images/RemoteDockerImage.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.github.dockerjava.api.model.Image;
import com.github.dockerjava.core.command.PullImageResultCallback;
import lombok.NonNull;
import lombok.ToString;
import org.slf4j.Logger;
import org.slf4j.profiler.Profiler;
import org.testcontainers.DockerClientFactory;
Expand All @@ -14,30 +15,32 @@
import org.testcontainers.utility.DockerLoggerFactory;
import org.testcontainers.utility.LazyFuture;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@ToString
public class RemoteDockerImage extends LazyFuture<String> {

public static final Set<String> AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>();
public static final Set<DockerImageName> AVAILABLE_IMAGE_NAME_CACHE = new HashSet<>();

private final String dockerImageName;
private DockerImageName imageName;

public RemoteDockerImage(String dockerImageName) {
DockerImageName.validate(dockerImageName);
this.dockerImageName = dockerImageName;
imageName = new DockerImageName(dockerImageName);
}

public RemoteDockerImage(@NonNull String repository, @NonNull String tag) {
this.dockerImageName = repository + ":" + tag;
imageName = new DockerImageName(repository, tag);
}

@Override
protected final String resolve() {
Profiler profiler = new Profiler("Rule creation - prefetch image");
Logger logger = DockerLoggerFactory.getLogger(dockerImageName);
Logger logger = DockerLoggerFactory.getLogger(imageName.toString());
profiler.setLogger(logger);

Profiler nested = profiler.startNested("Obtaining client");
Expand All @@ -48,58 +51,64 @@ protected final String resolve() {
profiler.start("Check local images");

int attempts = 0;
DockerClientException lastException = null;
while (true) {
// Does our cache already know the image?
if (AVAILABLE_IMAGE_NAME_CACHE.contains(dockerImageName)) {
logger.trace("{} is already in image name cache", dockerImageName);
if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) {
logger.trace("{} is already in image name cache", imageName);
break;
}

// Update the cache
ListImagesCmd listImagesCmd = dockerClient.listImagesCmd();

if (Boolean.parseBoolean(System.getProperty("useFilter"))) {
listImagesCmd = listImagesCmd.withImageNameFilter(dockerImageName);
listImagesCmd = listImagesCmd.withImageNameFilter(imageName.toString());
}

List<Image> updatedImages = listImagesCmd.exec();
for (Image image : updatedImages) {
if (image.getRepoTags() != null) {
Collections.addAll(AVAILABLE_IMAGE_NAME_CACHE, image.getRepoTags());
}
}
updatedImages.stream()
.map(Image::getRepoTags)
.filter(Objects::nonNull)
.flatMap(Stream::of)
.map(DockerImageName::new)
.collect(Collectors.toCollection(() -> AVAILABLE_IMAGE_NAME_CACHE));

// And now?
if (AVAILABLE_IMAGE_NAME_CACHE.contains(dockerImageName)) {
logger.trace("{} is in image name cache following listing of images", dockerImageName);
if (AVAILABLE_IMAGE_NAME_CACHE.contains(imageName)) {
logger.trace("{} is in image name cache following listing of images", imageName);
break;
}

// Log only on first attempt
if (attempts == 0) {
logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", dockerImageName);
logger.info("Pulling docker image: {}. Please be patient; this may take some time but only needs to be done once.", imageName);
profiler.start("Pull image");
}

if (attempts++ >= 3) {
logger.error("Retry limit reached while trying to pull image: " + dockerImageName + ". Please check output of `docker pull " + dockerImageName + "`");
throw new ContainerFetchException("Retry limit reached while trying to pull image: " + dockerImageName);
logger.error("Retry limit reached while trying to pull image: {}. Please check output of `docker pull {}`", imageName, imageName);
throw new ContainerFetchException("Retry limit reached while trying to pull image: " + imageName, lastException);
}

// The image is not available locally - pull it
try {
dockerClient.pullImageCmd(dockerImageName).exec(new PullImageResultCallback()).awaitCompletion();
} catch (InterruptedException e) {
throw new ContainerFetchException("Failed to fetch container image for " + dockerImageName, e);
final PullImageResultCallback callback = new PullImageResultCallback();
dockerClient
.pullImageCmd(imageName.getUnversionedPart())
.withTag(imageName.getVersionPart())
.exec(callback);
callback.awaitSuccess();
AVAILABLE_IMAGE_NAME_CACHE.add(imageName);
break;
} catch (DockerClientException e) {
lastException = e;
}

// Do not break here, but step into the next iteration, where it will be verified with listImagesCmd().
// see https://github.com/docker/docker/issues/10708
}

return dockerImageName;
return imageName.toString();
} catch (DockerClientException e) {
throw new ContainerFetchException("Failed to get Docker client for " + dockerImageName, e);
throw new ContainerFetchException("Failed to get Docker client for " + imageName, e);
} finally {
profiler.stop().log();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
public interface FromStatementTrait<SELF extends FromStatementTrait<SELF> & DockerfileBuilderTrait<SELF>> {

default SELF from(String dockerImageName) {
DockerImageName.validate(dockerImageName);
new DockerImageName(dockerImageName).assertValid();

return ((SELF) this).withStatement(new SingleArgumentStatement("FROM", dockerImageName));
}
Expand Down
Loading

0 comments on commit 23478fa

Please sign in to comment.