Skip to content

Commit

Permalink
Merge branch 'master' into junit5
Browse files Browse the repository at this point in the history
  • Loading branch information
bsideup committed Apr 8, 2018
2 parents 774b955 + fd1a079 commit a3b9654
Show file tree
Hide file tree
Showing 86 changed files with 2,469 additions and 119 deletions.
14 changes: 14 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
@@ -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
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -9,3 +9,4 @@ Asaf Mesika <asaf.mesika@gmail.com>
Sergei Egorov <bsideup@gmail.com>
Pete Cornish <outofcoffee@gmail.com>
Miguel Gonzalez Sanchez <miguel-gonzalez@gmx.de>
Julien LAMY <julien.prolyma@gmail.com>
20 changes: 16 additions & 4 deletions CHANGELOG.md
Expand Up @@ -3,17 +3,29 @@ All notable changes to this project will be documented in this file.

## UNRELEASED

### Fixed

### Changed

## [1.7.0] - 2018-04-07

### Fixed
- Fixed extraneous insertion of `useSSL=false` in all JDBC URL strings, even for DBs that do not understand it. Usage is now restricted to MySQL by default and can be overridden by authors of `JdbcDatabaseContainer` subclasses ([\#568](https://github.com/testcontainers/testcontainers-java/issues/568))
- Fixed `getServicePort` on `DockerComposeContainer` throws NullPointerException if service instance number in not used. ([\#619](https://github.com/testcontainers/testcontainers-java/issues/619))
- Increase Ryuk's timeout and make it configurable with `ryuk.container.timeout`. ([\#621](https://github.com/testcontainers/testcontainers-java/issues/621)[\#635](https://github.com/testcontainers/testcontainers-java/issues/635))

### Changed
- Added compatibility with selenium greater than 3.X ([\#611](https://github.com/testcontainers/testcontainers-java/issues/611))
- Abstracted and changed database init script functionality to support use of SQL-like scripts with non-JDBC connections. ([\#551](https://github.com/testcontainers/testcontainers-java/pull/551))
- Added `JdbcDatabaseContainer(Future)` constructor. ([\#543](https://github.com/testcontainers/testcontainers-java/issues/543))
- Mark DockerMachineClientProviderStrategy as not persistable ([\#593](https://github.com/testcontainers/testcontainers-java/pull/593))
- Added `waitingFor(String serviceName, WaitStrategy waitStrategy)` and overloaded `withExposedService()` methods to `DockerComposeContainer` to allow user to define `WaitStrategy` for compose containers. ([\#174](https://github.com/testcontainers/testcontainers-java/issues/174) and [\#515](https://github.com/testcontainers/testcontainers-java/issues/515))
- Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy`
- Added `ContainerState` interface representing the state of a started container
- Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy`
- Added `waitingFor(String serviceName, WaitStrategy waitStrategy)` and overloaded `withExposedService()` methods to `DockerComposeContainer` to allow user to define `WaitStrategy` for compose containers. ([\#174](https://github.com/testcontainers/testcontainers-java/issues/174), [\#515](https://github.com/testcontainers/testcontainers-java/issues/515) and ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600)))
- Deprecated `WaitStrategy` and implementations in favour of classes with same names in `org.testcontainers.containers.strategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600))
- Added `ContainerState` interface representing the state of a started container ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600))
- Added `WaitStrategyTarget` interface which is the target of the new `WaitStrategy` ([\#600](https://github.com/testcontainers/testcontainers-java/pull/600))
- *Breaking:* Removed hard-coded `wnameless` Oracle database image name. Users should instead place a file on the classpath named `testcontainers.properties` containing `oracle.container.image=IMAGE`, where IMAGE is a suitable image name and tag/SHA hash. For information, the approach recommended by Oracle for creating an Oracle XE docker image is described [here](https://blogs.oracle.com/oraclewebcentersuite/implement-oracle-database-xe-as-docker-containers).
- Added `DockerHealthcheckWaitStrategy` that is based on Docker's built-in [healthcheck](https://docs.docker.com/engine/reference/builder/#healthcheck) ([\#618](https://github.com/testcontainers/testcontainers-java/pull/618)).
- Added `withLogConsumer(String serviceName, Consumer<OutputFrame> consumer)` method to `DockerComposeContainer` ([\#605](https://github.com/testcontainers/testcontainers-java/issues/605))

## [1.6.0] - 2018-01-28

Expand Down
6 changes: 5 additions & 1 deletion README.md
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
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
3 changes: 3 additions & 0 deletions core/build.gradle
Expand Up @@ -51,6 +51,7 @@ shadowJar {
[
"org.apache.http",
"org.apache.commons.lang",
"org.apache.commons.io",
"org.glassfish",
"org.aopalliance",
"org.jvnet",
Expand Down Expand Up @@ -79,6 +80,7 @@ shadowJar {
include(dependency('org.newsclub.*:.*'))
include(dependency('org.zeroturnaround:zt-exec'))
include(dependency('commons-lang:commons-lang'))
include(dependency('commons-io:commons-io'))
}
}

Expand Down Expand Up @@ -115,6 +117,7 @@ dependencies {
shaded 'org.rnorth:tcp-unix-socket-proxy:1.0.1'
shaded 'org.zeroturnaround:zt-exec:1.8'
shaded 'commons-lang:commons-lang:2.6'
shaded 'commons-io:commons-io:2.5'
// docker-java uses SslConfigurator from jersey-common for TLS
shaded ('org.glassfish.jersey.core:jersey-common:2.23.1') {
// SslConfigurator doesn't use classes from dependencies
Expand Down
Expand Up @@ -15,6 +15,8 @@

public interface ContainerState {

String STATE_HEALTHY = "healthy";

/**
* Get the IP address that this container may be reached on (may not be the local machine).
*
Expand All @@ -27,14 +29,41 @@ default String getContainerIpAddress() {
/**
* @return is the container currently running?
*/
default Boolean isRunning() {
default boolean isRunning() {
if (getContainerId() == null) {
return false;
}

try {
Boolean running = getCurrentContainerInfo().getState().getRunning();
return Boolean.TRUE.equals(running);
} catch (DockerException e) {
return false;
}
}

/**
* @return has the container health state 'healthy'?
*/
default boolean isHealthy() {
if (getContainerId() == null) {
return false;
}

try {
return getContainerId() != null && DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec().getState().getRunning();
InspectContainerResponse inspectContainerResponse = getCurrentContainerInfo();
String healthStatus = inspectContainerResponse.getState().getHealth().getStatus();

return healthStatus.equals(STATE_HEALTHY);
} catch (DockerException e) {
return false;
}
}

default InspectContainerResponse getCurrentContainerInfo() {
return DockerClientFactory.instance().client().inspectContainerCmd(getContainerId()).exec();
}

/**
* Get the actual mapped port for a first port exposed by the container.
*
Expand Down
Expand Up @@ -15,6 +15,7 @@
import org.slf4j.LoggerFactory;
import org.slf4j.profiler.Profiler;
import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.containers.startupcheck.IndefiniteWaitOneShotStartupCheckStrategy;
import org.testcontainers.containers.wait.strategy.Wait;
Expand All @@ -32,7 +33,9 @@
import java.nio.file.Paths;
import java.time.Duration;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
Expand All @@ -41,6 +44,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
Expand Down Expand Up @@ -73,6 +77,7 @@ public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>> e
private final Map<String, ComposeServiceWaitStrategyTarget> serviceInstanceMap = new ConcurrentHashMap<>();
private final Map<String, WaitAllStrategy> waitStrategyMap = new ConcurrentHashMap<>();
private final SocatContainer ambassadorContainer = new SocatContainer();
private final Map<String, List<Consumer<OutputFrame>>> logConsumers = new ConcurrentHashMap<>();

private static final Object MUTEX = new Object();

Expand Down Expand Up @@ -176,11 +181,13 @@ private void createServiceInstance(Container container) {
final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container,
ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>()));

String containerId = containerInstance.getContainerId();
if (tailChildContainers) {
LogUtils.followOutput(DockerClientFactory.instance().client(), containerInstance.getContainerId(),
new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0]));
followLogs(containerId, new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0]));
}
serviceInstanceMap.putIfAbsent(serviceName, containerInstance);
//follow logs using registered consumers for this service
logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer));
serviceInstanceMap.putIfAbsent(serviceName, containerInstance);
}

private void waitUntilServiceStarted(String serviceName, ComposeServiceWaitStrategyTarget serviceInstance) {
Expand Down Expand Up @@ -377,7 +384,7 @@ public String getServiceHost(String serviceName, Integer servicePort) {
* @return a port that can be used for accessing the service container.
*/
public Integer getServicePort(String serviceName, Integer servicePort) {
return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(serviceName).get(servicePort));
return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(getServiceInstanceName(serviceName)).get(servicePort));
}

public SELF withScaledService(String serviceBaseName, int numInstances) {
Expand Down Expand Up @@ -426,6 +433,27 @@ public SELF withTailChildContainers(boolean tailChildContainers) {
return self();
}

/**
* Attach an output consumer at container startup, enabling stdout and stderr to be followed, waited on, etc.
* <p>
* More than one consumer may be registered.
*
* @param serviceName the name of the service as set in the docker-compose.yml file
* @param consumer consumer that output frames should be sent to
* @return this instance, for chaining
*/
public SELF withLogConsumer(String serviceName, Consumer<OutputFrame> consumer) {
String serviceInstanceName = getServiceInstanceName(serviceName);
final List<Consumer<OutputFrame>> consumers = this.logConsumers.getOrDefault(serviceInstanceName, new ArrayList<>());
consumers.add(consumer);
this.logConsumers.putIfAbsent(serviceInstanceName, consumers);
return self();
}

private void followLogs(String containerId, Consumer<OutputFrame> consumer) {
LogUtils.followOutput(DockerClientFactory.instance().client(), containerId, consumer);
}

private SELF self() {
return (SELF) this;
}
Expand Down
Expand Up @@ -25,7 +25,7 @@ public class ExecInContainerPattern {
/**
* Run a command inside a running container, as though using "docker exec", and interpreting
* the output as UTF8.
* <p/>
* <p></p>
* @param containerInfo the container info
* @param command the command to execute
* @see #execInContainer(InspectContainerResponse, Charset, String...)
Expand Down
Expand Up @@ -913,7 +913,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
@@ -0,0 +1,25 @@
package org.testcontainers.containers.wait.strategy;

import org.rnorth.ducttape.TimeoutException;
import org.rnorth.ducttape.unreliables.Unreliables;
import org.testcontainers.containers.ContainerLaunchException;

import java.util.concurrent.TimeUnit;

/**
* Wait strategy leveraging Docker's built-in healthcheck mechanism.
*
* @see <a href="https://docs.docker.com/engine/reference/builder/#healthcheck">https://docs.docker.com/engine/reference/builder/#healthcheck</a>
*/
public class DockerHealthcheckWaitStrategy extends AbstractWaitStrategy {

@Override
protected void waitUntilReady() {

try {
Unreliables.retryUntilTrue((int) startupTimeout.getSeconds(), TimeUnit.SECONDS, waitStrategyTarget::isHealthy);
} catch (TimeoutException e) {
throw new ContainerLaunchException("Timed out waiting for container to become healthy");
}
}
}
Expand Up @@ -61,4 +61,13 @@ public static HttpWaitStrategy forHttps(String path) {
public static LogMessageWaitStrategy forLogMessage(String regex, int times) {
return new LogMessageWaitStrategy().withRegEx(regex).withTimes(times);
}

/**
* Convenience method to return a WaitStrategy leveraging Docker's built-in healthcheck.
*
* @return DockerHealthcheckWaitStrategy
*/
public static DockerHealthcheckWaitStrategy forHealthcheck() {
return new DockerHealthcheckWaitStrategy();
}
}

0 comments on commit a3b9654

Please sign in to comment.