Testcontainers: communication between containers + mapped outside port - java

I have such test setup:
MyService connects to PostgtreSQL
MyService endpoint is being called from test suite
Both MyService and PostgreSQL are being run with Testcontainers.
Here is the network schema I want to achieve.
At first I tried to arrange communication by exposing ports.
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_VERSION));
static final GenericContainer<?> myService = new GenericContainer<>(DockerImageName.parse(MY_SERVICE_IMAGE))
.withEnv(
Map.of(
"SPRING_DATASOURCE_URL", postgres.getJdbcUrl(),
"SPRING_DATASOURCE_USERNAME", postgres.getUsername(),
"SPRING_DATASOURCE_PASSWORD", postgres.getPassword()
)
)
.withExposedPorts(8080)
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("MyService")))
According to logs MyService couldn't establish connection to PostgreSQL.
Caused by: java.net.ConnectException: Connection refused
Then I configured both services to share the same network.
static final Network SHARED_NETWORK = Network.newNetwork();
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>(DockerImageName.parse(POSTGRES_VERSION))
.withNetwork(SHARED_NETWORK)
.withNetworkAliases("postgres");
static final GenericContainer<?> myService = new GenericContainer<>(DockerImageName.parse(MY_SERVICE_IMAGE))
.withEnv(
Map.of(
"SPRING_DATASOURCE_URL", "jdbc:postgresql://postgres:5432/" + postgres.getDatabaseName(),
"SPRING_DATASOURCE_USERNAME", postgres.getUsername(),
"SPRING_DATASOURCE_PASSWORD", postgres.getPassword()
)
)
.withExposedPorts(8080)
.withNetwork(SHARED_NETWORK)
.withNetworkAliases("MyService")
.withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("MyService")))
Now MyService has established connection with PostgreSQL successfully. But when I perform HTTP request to MyService from the test suite, I get the same error.
restTemplate.getForObject("http://" + myService.getHost() + ":" + myService.getMappedPort(8080) +"/api/endpoint", Void.class)
Caused by: java.net.ConnectException: Connection refused
My question is how can I setup the containers network to make this architecture work?

You need to specify port bindings to expose a port to the "outside world".
Example similar to what you want:
Network network = Network.newNetwork();
GenericContainer mariaDbServer = getMariaDbContainer(network);
GenericContainer flywayRunner = getFlywayContainer(network);
...
#SuppressWarnings("rawtypes")
private GenericContainer getMariaDbContainer(Network network) {
return new GenericContainer<>("mariadb:10.4.21-focal")
.withEnv(Map.of("MYSQL_ROOT_PASSWORD", "password", "MYSQL_DATABASE", "somedatabase"))
.withCommand(
"mysqld", "--default-authentication-plugin=mysql_native_password", "--character-set-server=utf8mb4",
"--collation-server=utf8mb4_unicode_ci").withNetwork(network).withNetworkAliases("somedatabasedb")
.withNetworkMode(network.getId())
.withExposedPorts(3306).withCreateContainerCmdModifier(
cmd -> cmd.withNetworkMode(network.getId()).withHostConfig(
new HostConfig()
.withPortBindings(new PortBinding(Ports.Binding.bindPort(20306), new ExposedPort(3306))))
.withNetworkMode(network.getId())).withStartupTimeout(Duration.ofMinutes(2L));
}
#SuppressWarnings("rawtypes")
private GenericContainer getFlywayContainer(Network network) {
return new GenericContainer<>("flyway/flyway:7.15.0-alpine")
.withEnv(Map.of("MYSQL_ROOT_PASSWORD", "password", "MYSQL_DATABASE", "somedatabase"))
.withCommand(
"-url=jdbc:mariadb://somedatabasedb -schemas=somedatabase-user=root -password=password -connectRetries=300 migrate")
.withFileSystemBind(Paths.get(".", "infrastructure/database/schema").toAbsolutePath().toString(),
"/flyway/sql", BindMode.READ_ONLY).withNetwork(network).waitingFor(
Wait.forLogMessage(".*Successfully applied.*", 1)
).withStartupTimeout(Duration.of(60, ChronoUnit.SECONDS));
}
Container two communicates with container one using "internal" port.
Container one exposes 20306 (that redirects to 3306) port to the "outside world".

Related

connection issues when running Kakfa test container with Spring Boot App

I am facing connection issues when running Kakfa test container(confluentinc/cp-kafka:5.4.3) with Spring Boot App. Wondering if someone has faced this issue as well. After kafka container starts, the Admin Client tries to connect to broker to fetch the metadata but fails to connect.
Error log:
[AdminClient clientId=adminclient-2] Connection to node -1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.
I tried following workarounds to ensure KafkaAdminClient uses right address but none of them worked:
Used boot strap server address
Used KAFKA_ADVERTISED_LISTENERS=BROKER://172.17.0.3:9092. This address was being set by testcontainers_start.sh within docker container
Used kafa.getContainerName() to form the address: Example: BROKER://t-adsad:9092
Used kafka.getHost() + “:” + kafka.getMappedPort(9092)
Test class:
#RunWith(SpringRunner.class)
#Import(KafkaTestContainersConfiguration.class)
#SpringBootTest
#DirtiesContext
public class KafkaTestContainersLiveTest {
#ClassRule
public static KafkaContainer kafka =
new KafkaContainer(DockerImageName.parse("confluentinc/cp-kafka:5.4.3"));
#BeforeClass
public static void setupBootstrapServer(){
String server = "BROKER://"+kafka.getNetworkAliases().get(0)+":9092";
System.setProperty("kafka.bootstrap.servers", server);
}
Configuration class:
#Configuration
#EnableKafka
public class KafkaTestContainersConfiguration {
#Value("${kafka.bootstrap.servers}")
private String bootstrapServer;
#Value("${kafka.topic}")
private String topic;
public final int NUM_PARTITIONS=1;
public final short REPLICATION_FACTOR=1;
#Bean
public AdminClient adminClient() {
return KafkaAdminClient.create(adminClientConfigs());
}
public Map<String, Object> adminClientConfigs() {
Map<String, Object> props = new HashMap<>();
props.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServer);
props.put(AdminClientConfig.REQUEST_TIMEOUT_MS_CONFIG, 5000);
return props;
}
}

Why I'm not able to connect to HBase running as Docker container?

I have my Java Spring app that deals with HBase.
Here is my configuration:
#Configuration
public class HbaseConfiguration {
#Bean
public HbaseTemplate hbaseTemplate(#Value("${hadoop.home.dir}") final String hadoopHome,
#Value("${hbase.zookeeper.quorum}") final String quorum,
#Value("${hbase.zookeeper.property.clientPort}") final String port)
throws IOException, ServiceException {
System.setProperty("hadoop.home.dir", hadoopHome);
org.apache.hadoop.conf.Configuration configuration = HBaseConfiguration.create();
configuration.set("hbase.zookeeper.quorum", quorum);
configuration.set("hbase.zookeeper.property.clientPort", port);
HBaseAdmin.checkHBaseAvailable(configuration);
return new HbaseTemplate(configuration);
}
}
#HBASE
hbase.zookeeper.quorum = localhost
hbase.zookeeper.property.clientPort = 2181
hadoop.home.dir = C:/hadoop
Before asking the question I tried to figure out the problem on my own and found this link https://github.com/sel-fish/hbase.docker
But still, I get an error
org.apache.hadoop.net.ConnectTimeoutException: 10000 millis timeout while waiting for channel to be ready for connect. ch : java.nio.channels.SocketChannel[connection-pending remote=myhbase/192.168.99.100:60000]
Could I ask you to help me and clarify how can I connect my local Java app with HBase running in Docker?

Android smack can not connect to remote ejabberd server

I am using smack libary to connect ejabberd xmpp server, I have hosted ejabberd in my locally . And it works fine, I can connect, send text message . The code I used to connect is following
public static final String HOST = "remote_host_ip_address";
public static final int PORT = 5222;
public static final int CONNECT_TIME_OUT = 20000;
public static final String RESOURCE = "Smack";
XMPPTCPConnectionConfiguration.Builder configBuilder = XMPPTCPConnectionConfiguration.builder();
configBuilder.setHost(HOST);
configBuilder.setPort(PORT);
//configBuilder.setServiceName("localhost");
configBuilder.setServiceName("hostname.com");
configBuilder.setResource(RESOURCE);
configBuilder.setCompressionEnabled(true);
configBuilder.setConnectTimeout(CONNECT_TIME_OUT);
//configBuilder.setSecurityMode(ConnectionConfiguration.SecurityMode.disabled);
connectionConfig = configBuilder.build();
connection = new XMPPTCPConnection(connectionConfig);
accountManager = AccountManager.getInstance(connection);
chatManager = ChatManager.getInstanceFor(connection);
chatManager.addChatListener(chatManagerListener);
When I assign HOST value to my local ip address like 192.168.101.1 and use configBuilder.setServiceName("localhost"); it works perfect but if I assign configBuilder.setServiceName("hostname.com"); and HOST variable to remote server ip address it throws the following error
org.jivesoftware.smack.SmackException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
But if use any xmpp client like gajim I can connect to remote server. I have used the following dependencies
compile 'org.igniterealtime.smack:smack-android-extensions:4.1.3'
compile 'org.igniterealtime.smack:smack-tcp:4.1.3'

Connecting to Mongo database through SSH tunnel in Java

FIXED (edited code to reflect changes I made)
I'm trying to connect to a Mongo database through an SSH tunnel using Java.
I'm using the Mongo driver 3.0.2 and jcraft (JSch) to create an SSH tunnel.
The idea is that I:
connect to the machine hosting the MongoDB installation through SSH
set up port forwarding from a local port to the remote MongoDB port
connect to MongoDB remotely
My code looks like this:
// forwarding ports
private static final String LOCAL_HOST = "localhost";
private static final String REMOTE_HOST = "127.0.0.1";
private static final Integer LOCAL_PORT = 8988;
private static final Integer REMOTE_PORT = 27017; // Default mongodb port
// ssh connection info
private static final String SSH_USER = "<username>";
private static final String SSH_PASSWORD = "<password>";
private static final String SSH_HOST = "<remote host>";
private static final Integer SSH_PORT = 22;
private static Session sshSession;
public static void main(String[] args) {
try {
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
JSch jsch = new JSch();
sshSession = null;
sshSession = jsch.getSession(SSH_USER, SSH_HOST, SSH_PORT);
sshSession.setPassword(SSH_PASSWORD);
sshSession.setConfig(config);
sshSession.connect();
sshSession.setPortForwardingL(LOCAL_PORT, REMOTE_HOST, REMOTE_PORT);
MongoClient mongoClient = new MongoClient(LOCAL_HOST, LOCAL_PORT);
mongoClient.setReadPreference(ReadPreference.nearest());
MongoCursor<String> dbNames = mongoClient.listDatabaseNames().iterator();
while (dbNames.hasNext()) {
System.out.println(dbNames.next());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
sshSession.delPortForwardingL(LOCAL_PORT);
sshSession.disconnect();
}
}
This code, when run, doesn't EDIT: does work. Connecting to the SSH server works just fine, but connecting to the Mongo database behind it doesn't work and returns this error:
INFO: Exception in monitor thread while connecting to server localhost:8988
com.mongodb.MongoSocketReadException: Prematurely reached end of stream
at com.mongodb.connection.SocketStream.read(SocketStream.java:88)
at com.mongodb.connection.InternalStreamConnection.receiveResponseBuffers(InternalStreamConnection.java:491)
at com.mongodb.connection.InternalStreamConnection.receiveMessage(InternalStreamConnection.java:221)
at com.mongodb.connection.CommandHelper.receiveReply(CommandHelper.java:134)
at com.mongodb.connection.CommandHelper.receiveCommandResult(CommandHelper.java:121)
at com.mongodb.connection.CommandHelper.executeCommand(CommandHelper.java:32)
at com.mongodb.connection.InternalStreamConnectionInitializer.initializeConnectionDescription(InternalStreamConnectionInitializer.java:83)
at com.mongodb.connection.InternalStreamConnectionInitializer.initialize(InternalStreamConnectionInitializer.java:43)
at com.mongodb.connection.InternalStreamConnection.open(InternalStreamConnection.java:115)
at com.mongodb.connection.DefaultServerMonitor$ServerMonitorRunnable.run(DefaultServerMonitor.java:127)
at java.lang.Thread.run(Unknown Source)
I've tried doing this through command line as follows:
$ ssh <user>#<host> -p 22 -X -C
$ <enter requested password>
<user>#<host>$ mongo
<user>#<host>$ MongoDB shell version: 2.6.10
<user>#<host>$ connecting to: test
So this seems to work. I'm at a loss as to why the Java code (which should be doing roughly the same thing) doesn't work.
I managed to make it work (tried to forward port to "localhost" rather than "127.0.0.1", changing it fixed it) edit: I guess the server was listening specifically on localhost rather than 127.0.0.1
This code is run successfully, but the main problem is your mongo db is stopped. Please check the instance of the mongo is running or not.
sudo systemctl status mongod
if it is not running
sudo systemctl start mongod

JMX process, is it possible to call an external application to handle access rights when client attempts access

I have an application that is running on localhost:1234, I am using jconsole to connect to this. The application has a password file to handle login.
I need to allow logging in based on different AD groups of the windows user. So for example, if they are in Group1 they will be given readwrite access, if they are Group2 they are given readonly access, and group3 is not given and access.
I have created an AD group handling application that can query a list of AD groups and return the required user access level and login details.
My problem: I want to connect to the application using jconsole via the command line using something like:
jconsole localhost:1234
Obviously this will fail to connect, because it's expecting a username and password.
Is there a way in which I can have my JMX application that's running on localhost:1234 wait for an incoming connection request and run my AD group handling application to determine their access level?
My application on localhost:1234 is very basic and looks like this:
import java.lang.management.ManagementFactory;
import javax.management.InstanceAlreadyExistsException;
import javax.management.MBeanRegistrationException;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.NotCompliantMBeanException;
import javax.management.ObjectName;
public class SystemConfigManagement {
private static final int DEFAULT_NO_THREADS = 10;
private static final String DEFAULT_SCHEMA = "default";
public static void main(String[] args)
throws MalformedObjectNameException, InterruptedException,
InstanceAlreadyExistsException, MBeanRegistrationException,
NotCompliantMBeanException{
//Get the MBean server
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
//register the mBean
SystemConfig mBean = new SystemConfig(DEFAULT_NO_THREADS, DEFAULT_SCHEMA);
ObjectName name = new ObjectName("com.barc.jmx:type=SystemConfig");
mbs.registerMBean(mBean, name);
do{
Thread.sleep(2000);
System.out.println(
"Thread Count = " + mBean.getThreadCount()
+ ":::Schema Name = " + mBean.getSchemaName()
);
}while(mBean.getThreadCount() != 0);
}
}
and
package com.test.jmx;
public class SystemConfig implements SystemConfigMBean {
private int threadCount;
private String schemaName;
public SystemConfig(int numThreads, String schema){
this.threadCount = numThreads;
this.schemaName = schema;
}
#Override
public void setThreadCount(int noOfThreads) {
this.threadCount = noOfThreads;
}
#Override
public int getThreadCount() {
return this.threadCount;
}
#Override
public void setSchemaName(String schemaName) {
this.schemaName = schemaName;
}
#Override
public String getSchemaName() {
return this.schemaName;
}
#Override
public String doConfig() {
return "No of Threads=" + this.threadCount + " and DB Schema Name = " + this.schemaName;
}
}
[source : http://www.journaldev.com/1352/what-is-jmx-mbean-jconsole-tutorial]
Is there somewhere in main() where I can create this query to validate the user details using the AD group handling application?
The default RMI connector server cannot do that very well (you can provide your own JAAS module (UC3) or Authenticator (UC4)).
You might be better off using another protocol/implementation which does already delegate authentication. There are some webservice, REST- and even jboss remoting connectors and most of them can be authenticated via a container mechanism. However I think most of them are not easy to integrate.
If you use for example Jolokia (servlet), you could also use hawt.io as a very nice "AJAX" console. (I am not sure if jolokia actually ships a JMX client connector which you can use in JConsole but there are many alternative clients which are most of the time better for integration/automation).

Categories

Resources