@Override @Deprecated public void starting(Description description) { start(); }
@Override public void start() { synchronized (MUTEX) { registerContainersForShutdown(); if (pull) { try { pullImages(); } catch (ContainerLaunchException e) { logger().warn("Exception while pulling images, using local images if available", e); } } applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); startAmbassadorContainers(); waitUntilServiceStarted(); } }
@BeforeEach void setup() { host = composeContainer.getServiceHost("whoami_1", 80); port = composeContainer.getServicePort("whoami_1", 80); }
/** Specify the {@link WaitStrategy} to use to determine if the container is ready. * * @see org.testcontainers.containers.wait.strategy.Wait#defaultWaitStrategy() * @param serviceName the name of the service to wait for * @param waitStrategy the WaitStrategy to use * @return this */ public SELF waitingFor(String serviceName, @NonNull WaitStrategy waitStrategy) { String serviceInstanceName = getServiceInstanceName(serviceName); addWaitStrategy(serviceInstanceName, waitStrategy); 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(); }
@Test public void test() { try (DockerComposeContainer compose = new DockerComposeContainer(composeFiles) .withLocalCompose(localMode) .withExposedService(SERVICE_NAME, SERVICE_PORT)) { compose.start(); BufferedReader br = Unreliables.retryUntilSuccess(10, TimeUnit.SECONDS, () -> { Uninterruptibles.sleepUninterruptibly(1, TimeUnit.SECONDS); Socket socket = new Socket(compose.getServiceHost(SERVICE_NAME, SERVICE_PORT), compose.getServicePort(SERVICE_NAME, SERVICE_PORT)); return new BufferedReader(new InputStreamReader(socket.getInputStream())); }); Unreliables.retryUntilTrue(10, TimeUnit.SECONDS, () -> { while (br.ready()) { String line = br.readLine(); if (line.contains(expectedEnvVar)) { pass("Mapped environment variable was found"); return true; } } info("Mapped environment variable was not found yet - process probably not ready"); Uninterruptibles.sleepUninterruptibly(100, TimeUnit.MILLISECONDS); return false; }); } }
@Test public void usesLocalImageEvenWhenPullFails() throws InterruptedException { tagImage("redis:4.0.10", "redis-local", "latest"); DockerComposeContainer composeContainer = new DockerComposeContainer(new File("src/test/resources/local-compose-test.yml")) .withExposedService("redis", 6379); composeContainer.start(); }
private void createServiceInstance(Container container) { String serviceName = getServiceNameFromContainer(container); final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); String containerId = containerInstance.getContainerId(); if (tailChildContainers) { followLogs(containerId, new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); } //follow logs using registered consumers for this service logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); serviceInstanceMap.putIfAbsent(serviceName, containerInstance); }
@Override public void stop() { synchronized (MUTEX) { try { // shut down the ambassador container ambassadorContainer.stop(); // Kill the services using docker-compose try { runWithCompose("down -v"); // If we reach here then docker-compose down has cleared networks and containers; // we can unregister from ResourceReaper spawnedContainerIds.forEach(ResourceReaper.instance()::unregisterContainer); spawnedNetworkIds.forEach(ResourceReaper.instance()::unregisterNetwork); } catch (Exception e) { // docker-compose down failed; use ResourceReaper to ensure cleanup // kill the spawned service containers spawnedContainerIds.forEach(ResourceReaper.instance()::stopAndRemoveContainer); // remove the networks after removing the containers spawnedNetworkIds.forEach(ResourceReaper.instance()::removeNetworkById); } spawnedContainerIds.clear(); spawnedNetworkIds.clear(); } finally { project = randomProjectId(); } } }
public DockerComposeContainer(String identifier, List<File> composeFiles) { this.composeFiles = composeFiles; // Use a unique identifier so that containers created for this compose environment can be identified this.identifier = identifier; project = randomProjectId(); this.dockerClient = DockerClientFactory.instance().client(); }
/** * Get the port that an exposed service can be found at, from the host machine * (i.e. should be the machine that's running this Java process). * <p> * The service must have been declared using DockerComposeContainer#withExposedService. * * @param serviceName the name of the service as set in the docker-compose.yml file. * @param servicePort the port exposed by the service container. * @return a port that can be used for accessing the service container. */ public Integer getServicePort(String serviceName, Integer servicePort) { return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(getServiceInstanceName(serviceName)).get(servicePort)); }
private void waitUntilServiceStarted() { listChildContainers().forEach(this::createServiceInstance); serviceInstanceMap.forEach(this::waitUntilServiceStarted); }
@Test public void testGetServicePort() { int serviceWithInstancePort = environment.getServicePort("redis_1", REDIS_PORT); assertNotNull("Port is set for service with instance number", serviceWithInstancePort); int serviceWithoutInstancePort = environment.getServicePort("redis", REDIS_PORT); assertNotNull("Port is set for service with instance number", serviceWithoutInstancePort); assertEquals("Service ports are the same", serviceWithInstancePort, serviceWithoutInstancePort); } }
private void createServiceInstance(Container container) { String serviceName = getServiceNameFromContainer(container); final ComposeServiceWaitStrategyTarget containerInstance = new ComposeServiceWaitStrategyTarget(container, ambassadorContainer, ambassadorPortMappings.getOrDefault(serviceName, new HashMap<>())); String containerId = containerInstance.getContainerId(); if (tailChildContainers) { followLogs(containerId, new Slf4jLogConsumer(logger()).withPrefix(container.getNames()[0])); } //follow logs using registered consumers for this service logConsumers.getOrDefault(serviceName, Collections.emptyList()).forEach(consumer -> followLogs(containerId, consumer)); serviceInstanceMap.putIfAbsent(serviceName, containerInstance); }
public SELF withExposedService(String serviceName, int servicePort, @NonNull WaitStrategy waitStrategy) { String serviceInstanceName = getServiceInstanceName(serviceName); /* * For every service/port pair that needs to be exposed, we register a target on an 'ambassador container'. * * The ambassador container's role is to link (within the Docker network) to one of the * compose services, and proxy TCP network I/O out to a port that the ambassador container * exposes. * * This avoids the need for the docker compose file to explicitly expose ports on all the * services. * * {@link GenericContainer} should ensure that the ambassador container is on the same network * as the rest of the compose environment. */ // Ambassador container will be started together after docker compose has started int ambassadorPort = nextAmbassadorPort.getAndIncrement(); ambassadorPortMappings.computeIfAbsent(serviceInstanceName, __ -> new ConcurrentHashMap<>()).put(servicePort, ambassadorPort); ambassadorContainer.withTarget(ambassadorPort, serviceInstanceName, servicePort); ambassadorContainer.addLink(new FutureContainer(this.project + "_" + serviceInstanceName), serviceInstanceName); addWaitStrategy(serviceInstanceName, waitStrategy); return self(); }
public DockerComposeContainer(String identifier, List<File> composeFiles) { this.composeFiles = composeFiles; // Use a unique identifier so that containers created for this compose environment can be identified this.identifier = identifier; project = randomProjectId(); this.dockerClient = DockerClientFactory.instance().client(); }
/** * Get the port that an exposed service can be found at, from the host machine * (i.e. should be the machine that's running this Java process). * <p> * The service must have been declared using DockerComposeContainer#withExposedService. * * @param serviceName the name of the service as set in the docker-compose.yml file. * @param servicePort the port exposed by the service container. * @return a port that can be used for accessing the service container. */ public Integer getServicePort(String serviceName, Integer servicePort) { return ambassadorContainer.getMappedPort(ambassadorPortMappings.get(getServiceInstanceName(serviceName)).get(servicePort)); }
listChildContainers().forEach(this::createServiceInstance); serviceInstanceMap.forEach(this::waitUntilServiceStarted);
@Override public void start() { synchronized (MUTEX) { registerContainersForShutdown(); if (pull) { try { pullImages(); } catch (ContainerLaunchException e) { logger().warn("Exception while pulling images, using local images if available", e); } } applyScaling(); // scale before up, so that all scaled instances are available first for linking createServices(); startAmbassadorContainers(); waitUntilServiceStarted(); } }
@Before public void setupClients() { for (int i = 0; i < 3; i++) { String name = String.format("redis_%d", i + 1); clients[i] = new Jedis(environment.getServiceHost(name, REDIS_PORT), environment.getServicePort(name, REDIS_PORT)); } }