TNB provides several helper interfaces that add specific capabilities to your deployment implementations. These are designed as mix-ins that can be combined with the main deployment interfaces.
WithName
Adds identity and discovery capabilities for named OpenShift resources.
Interface signature
public interface WithName
Methods
Returns the name of the deployment resource. This name is used for pod selection and deployment identification.
Returns a predicate to filter pods belonging to this deployment based on labels.default Predicate<Pod> podSelector() {
return p -> OpenshiftClient.get().hasLabels(p,
Map.of(OpenshiftConfiguration.openshiftDeploymentLabel(), name()));
}
Checks if the deployment exists in OpenShift and is not marked for deletion.default boolean isDeployed() {
final Deployment deployment = OpenshiftClient.get()
.apps().deployments().withName(name()).get();
return deployment != null && !deployment.isMarkedForDeletion();
}
Example
public class OpenshiftRedis implements OpenshiftDeployable, WithName {
@Override
public String name() {
return "redis";
}
@Override
public Predicate<Pod> podSelector() {
return WithName.super.podSelector();
}
@Override
public void create() {
// Create deployment with name "redis"
}
}
WithDockerImage
Provides configurable Docker image selection with system property overrides.
Interface signature
public interface WithDockerImage
Methods
Specifies the default Docker image to use if no override is provided.
Returns the Docker image to use, checking for system property overrides first.default String image() {
return TestConfiguration.getProperty(
String.format("tnb.%s.image",
ReflectionUtil.getSuperClassName(this.getClass()).toLowerCase()),
defaultImage()
);
}
Images can be overridden using the property pattern:
Example
public class OpenshiftMongoDB implements OpenshiftDeployable, WithDockerImage {
@Override
public String defaultImage() {
return "mongo:7.0";
}
@Override
public void create() {
// Use image() to get the configured image
String mongoImage = image();
// Create container with the image
OpenshiftClient.get().apps().deployments()
.createOrReplace(new DeploymentBuilder()
// ... deployment configuration
.addNewContainer()
.withImage(mongoImage)
.endContainer()
.build());
}
}
Override the image:
mvn test -Dtnb.mongodb.image=mongo:6.0
WithExternalHostname
Provides the hostname for external client connections (e.g., from test code to the service).
Interface signature
public interface WithExternalHostname
Methods
Returns the hostname used for connections from outside the cluster. Often “localhost” when using port-forwarding.String externalHostname();
Use case
This hostname represents how test code (running outside OpenShift) connects to the service. Commonly used with port-forwarding:
public class OpenshiftKafka implements OpenshiftDeployable, WithExternalHostname {
private PortForward portForward;
@Override
public String externalHostname() {
return "localhost";
}
@Override
public void openResources() {
// Set up port forwarding
portForward = OpenshiftClient.get().services()
.withName("kafka")
.portForward(9092);
}
public String getBootstrapServers() {
return externalHostname() + ":" + portForward.getLocalPort();
}
}
WithInClusterHostname
Provides the internal cluster hostname for pod-to-pod communication.
Interface signature
public interface WithInClusterHostname
Methods
Returns the internal cluster hostname in the format name.namespace.svc.cluster.local. Requires the implementing class to also implement WithName.default String inClusterHostname() {
if (this instanceof WithName) {
Function<WithName, String> getId = WithName::name;
try {
return OpenshiftClient.get().getClusterHostname(
getId.apply((WithName) this)
);
} catch (Exception e) {
throw new RuntimeException(
"Unable to cast " + this.getClass().getSimpleName() + " to WithName"
);
}
} else {
throw new IllegalArgumentException(
"Class " + this.getClass().getSimpleName() +
" does not implement WithName, you need to override the default implementation"
);
}
}
Use case
Used when configuring one service to connect to another within the cluster:
public class OpenshiftApplication implements OpenshiftDeployable,
WithName,
WithInClusterHostname {
private OpenshiftPostgreSQL database;
@Override
public String name() {
return "my-app";
}
@Override
public void create() {
// Configure application to connect to database using internal hostname
String dbUrl = String.format(
"jdbc:postgresql://%s:5432/mydb",
database.inClusterHostname()
);
// Create deployment with environment variable
OpenshiftClient.get().apps().deployments()
.createOrReplace(new DeploymentBuilder()
// ...
.addNewContainer()
.addNewEnv()
.withName("DATABASE_URL")
.withValue(dbUrl)
.endEnv()
.endContainer()
.build());
}
}
Hostname format:
postgresql.test-namespace.svc.cluster.local
WithLogs
Provides access to service logs for debugging.
Interface signature
public interface WithLogs
Methods
Returns the logs from the service.
Most deployment interfaces (like ContainerDeployable and OpenshiftDeployable) already implement WithLogs through the Deployable interface.
Example
@Test
void testServiceBehavior() {
try {
// Test that might fail
service.performOperation();
} catch (Exception e) {
// Print logs for debugging
System.out.println("Service logs:");
System.out.println(service.getLogs());
throw e;
}
}
Combining multiple helpers
These interfaces are designed to be used together:
public class OpenshiftRabbitMQ implements OpenshiftDeployable,
WithName,
WithDockerImage,
WithExternalHostname,
WithInClusterHostname {
private PortForward portForward;
@Override
public String name() {
return "rabbitmq";
}
@Override
public String defaultImage() {
return "rabbitmq:3.12-management";
}
@Override
public String externalHostname() {
return "localhost";
}
@Override
public void create() {
OpenshiftClient.get().apps().deployments()
.createOrReplace(new DeploymentBuilder()
.withNewMetadata()
.withName(name()) // From WithName
.endMetadata()
.withNewSpec()
.withNewTemplate()
.withNewSpec()
.addNewContainer()
.withImage(image()) // From WithDockerImage
.withName("rabbitmq")
.addNewPort()
.withContainerPort(5672)
.endPort()
.endContainer()
.endSpec()
.endTemplate()
.endSpec()
.build());
}
@Override
public void openResources() {
portForward = OpenshiftClient.get().services()
.withName(name())
.portForward(5672);
}
@Override
public void closeResources() {
if (portForward != null) {
portForward.close();
}
}
@Override
public Predicate<Pod> podSelector() {
return WithName.super.podSelector();
}
// External connection for tests
public String getExternalConnectionUrl() {
return String.format(
"amqp://%s:%d",
externalHostname(), // From WithExternalHostname
portForward.getLocalPort()
);
}
// Internal connection for other services
public String getInternalConnectionUrl() {
return String.format(
"amqp://%s:5672",
inClusterHostname() // From WithInClusterHostname
);
}
}