All Downloads are FREE. Search and download functionalities are using the official Maven repository.

software.xdev.tci.portfixation.PortFixation Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2024 XDEV Software (https://xdev.software)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package software.xdev.tci.portfixation;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;

import org.slf4j.Logger;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.DockerLoggerFactory;


/**
 * Utility class for getting a random port using a dummy Docker container.
 * 

* Created as a workaround for moby/moby#44137 */ public final class PortFixation { private PortFixation() { } static TriConsumer, Integer, Integer> addFixedExposedPortFunc; public static void makeExposedPortsFix(final GenericContainer container) { // Cache if(addFixedExposedPortFunc == null) { initAddFixedExposedPortFunc(); } Stream.concat( container.getExposedPorts().stream(), Optional.of(container) .filter(AdditionalPortsForFixedExposingContainer.class::isInstance) .map(AdditionalPortsForFixedExposingContainer.class::cast) .map(AdditionalPortsForFixedExposingContainer::getAdditionalPortsForFixedExposing) .stream() .flatMap(Collection::stream)) .distinct() .map(cPort -> CompletableFuture.runAsync(() -> addFixedExposedPortFunc.accept(container, PortFixation.getRandomFreePort(), cPort))) .toList() .forEach(CompletableFuture::join); } @SuppressWarnings("java:S3011") static synchronized void initAddFixedExposedPortFunc() { if(addFixedExposedPortFunc != null) { return; } try { final Method mAddFixedExposedPort = GenericContainer.class.getDeclaredMethod("addFixedExposedPort", int.class, int.class); mAddFixedExposedPort.setAccessible(true); addFixedExposedPortFunc = (container, hostPort, containerPort) -> { try { mAddFixedExposedPort.invoke(container, hostPort, containerPort); } catch(final IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException("Unable to invoke", e); } }; } catch(final NoSuchMethodException e) { throw new IllegalStateException("Unable to find underlying method", e); } } private static int getRandomFreePort() { try(final GetPortContainer container = new GetPortContainer()) { container.start(); return container.getPort(); } } static class GetPortContainer extends GenericContainer { protected static final DockerImageName IMAGE = DockerImageName.parse("alpine:3"); protected static final int PORT = 5000; public GetPortContainer() { super(IMAGE); // Create a netcat server listener so that the startup check succeeds this.setCommand("/bin/sh", "-c", "while true; do nc -v -lk -p " + PORT + "; done"); this.addExposedPort(PORT); } public Integer getPort() { return this.getMappedPort(PORT); } @Override protected Logger logger() { return DockerLoggerFactory.getLogger("container.getport"); } } @FunctionalInterface interface TriConsumer { void accept(T k, U v, V s); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy