![JAR search and dependency download from the Maven repository](/logo.png)
org.tbk.spring.testcontainer.eps.config.ElectrumPersonalServerContainerAutoConfiguration Maven / Gradle / Ivy
package org.tbk.spring.testcontainer.eps.config;
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.tbk.spring.testcontainer.bitcoind.BitcoindContainer;
import org.tbk.spring.testcontainer.bitcoind.config.BitcoindContainerAutoConfiguration;
import org.tbk.spring.testcontainer.core.CustomHostPortWaitStrategy;
import org.tbk.spring.testcontainer.core.MoreTestcontainers;
import org.tbk.spring.testcontainer.eps.ElectrumPersonalServerContainer;
import org.testcontainers.Testcontainers;
import org.testcontainers.containers.wait.strategy.LogMessageWaitStrategy;
import org.testcontainers.containers.wait.strategy.WaitStrategy;
import org.testcontainers.utility.DockerImageName;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import static java.util.Objects.requireNonNull;
@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(ElectrumPersonalServerContainerProperties.class)
@ConditionalOnProperty(value = "org.tbk.spring.testcontainer.eps.enabled", havingValue = "true")
@AutoConfigureAfter(BitcoindContainerAutoConfiguration.class)
public class ElectrumPersonalServerContainerAutoConfiguration {
private static final int hardcodedRpcPort = 50002;
private static final List hardcodedStandardPorts = ImmutableList.builder()
.add(hardcodedRpcPort)
.build();
private final ElectrumPersonalServerContainerProperties properties;
public ElectrumPersonalServerContainerAutoConfiguration(ElectrumPersonalServerContainerProperties properties) {
this.properties = requireNonNull(properties);
}
@Bean(name = "electrumPersonalServerContainer", destroyMethod = "stop")
@ConditionalOnMissingBean(ElectrumPersonalServerContainer.class)
@ConditionalOnBean(BitcoindContainer.class)
ElectrumPersonalServerContainer> electrumPersonalServerContainerWithBitcoindTestcontainer(BitcoindContainer> bitcoindContainer) {
String epsBitcoindConfig = buildEpsBitcoindConfig(bitcoindContainer);
return createStartedContainer(epsBitcoindConfig);
}
@Bean(name = "electrumPersonalServerContainer", destroyMethod = "stop")
@ConditionalOnMissingBean(ElectrumPersonalServerContainer.class)
ElectrumPersonalServerContainer> electrumPersonalServerContainer() {
boolean isLocalhost = "localhost".equals(this.properties.getRpchost());
boolean isLoopback = "127.0.0.1".equals(this.properties.getRpchost());
boolean isWildcard = "0.0.0.0".equals(this.properties.getRpchost());
boolean connectsToHost = isLocalhost || isLoopback || isWildcard;
if (connectsToHost) {
Testcontainers.exposeHostPorts(this.properties.getRpcport());
}
String epsBitcoindConfig = connectsToHost ?
buildLocalEpsBitcoindConfig() :
buildEpsBitcoindConfig();
return createStartedContainer(epsBitcoindConfig);
}
private WaitStrategy initPhaseWaitStrategy() {
return new LogMessageWaitStrategy()
// we are waiting for a message like: "INFO:2020-12-29 15:57:37,721: Done.\n"
.withRegEx("INFO:.+: Done\\.\n")
.withTimes(1);
}
private WaitStrategy mainPhaseWaitStrategy() {
return CustomHostPortWaitStrategy.builder()
.addPort(hardcodedRpcPort)
.build();
}
private ElectrumPersonalServerContainer> createStartedContainer(String epsBitcoindConfig) {
ElectrumPersonalServerContainer> initPhaseContainer = createContainer(epsBitcoindConfig)
.withStartupTimeout(Duration.ofMinutes(3))
.waitingFor(initPhaseWaitStrategy());
initPhaseContainer.start();
// electrum personal server exits after everything is set up correctly - we need to restart it again afterwards.
initPhaseContainer.stop();
ElectrumPersonalServerContainer> mainPhaseContainer = initPhaseContainer
.withStartupTimeout(Duration.ofMinutes(2))
.waitingFor(mainPhaseWaitStrategy());
// restart again - this time it should stay running
mainPhaseContainer.start();
// expose all mapped ports of the host so other containers can communication with the container
MoreTestcontainers.exposeAllPortsToOtherContainers(mainPhaseContainer);
return mainPhaseContainer;
}
private ElectrumPersonalServerContainer> createContainer(String epsBitcoindConfig) {
Map env = buildEnvMap(epsBitcoindConfig);
DockerImageName dockerImageName = this.properties.getImage()
.orElseThrow(() -> new RuntimeException("Container image must not be empty"));
ElectrumPersonalServerContainer> container = new ElectrumPersonalServerContainer<>(dockerImageName)
.withCreateContainerCmdModifier(cmdModifier(dockerImageName))
.withExposedPorts(hardcodedStandardPorts.toArray(new Integer[]{}))
.withEnv(env);
return container;
}
private Consumer cmdModifier(DockerImageName dockerImageName) {
String dockerContainerName = String.format("%s-%s", dockerImageName.getUnversionedPart(),
Integer.toHexString(System.identityHashCode(this)))
.replace("/", "-");
return MoreTestcontainers.cmdModifiers().withName(dockerContainerName);
}
private Map buildEnvMap(String epsBitcoindConfig) {
Map environmentWithDefaults = this.properties.getEnvironmentWithDefaults();
String epsConfigKey = "EPS_CONFIG";
String userGivenEpsConfig = environmentWithDefaults.getOrDefault(epsConfigKey, "");
String enhancedEpsConfig = epsBitcoindConfig + "\n" + userGivenEpsConfig;
Map envWithoutEpsConfig = environmentWithDefaults.entrySet().stream()
.filter(it -> !epsConfigKey.equals(it.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return ImmutableMap.builder()
.putAll(envWithoutEpsConfig)
.put(epsConfigKey, enhancedEpsConfig)
.build();
}
private String buildLocalEpsBitcoindConfig() {
return buildEpsBitcoindConfig(
MoreTestcontainers.testcontainersInternalHost(),
this.properties.getRpcport()
);
}
private String buildEpsBitcoindConfig() {
return buildEpsBitcoindConfig(
this.properties.getRpchost(),
this.properties.getRpcport()
);
}
private String buildEpsBitcoindConfig(BitcoindContainer> bitcoindContainer) {
return buildEpsBitcoindConfig(
MoreTestcontainers.testcontainersInternalHost(),
bitcoindContainer.getMappedPort(this.properties.getRpcport())
);
}
private String buildEpsBitcoindConfig(String host, int port) {
String template = "[bitcoin-rpc]\n"
+ "host = %s\n"
+ "port = %d\n"
+ "rpc_user = %s\n"
+ "rpc_password = %s\n";
return String.format(template, host, port, this.properties.getRpcuser(), this.properties.getRpcpass());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy