-
Notifications
You must be signed in to change notification settings - Fork 28
6. Openshift
- OC CLI installed
- Be connected to a running OpenShift instance -
oc login ...
- OCP 4.6+
Required Maven dependency:
<dependency>
<groupId>io.quarkus.qe</groupId>
<artifactId>quarkus-test-openshift</artifactId>
<scope>test</scope>
</dependency>
And now, we can write also scenarios to be run in OpenShift by adding the @OpenShiftScenario
annotation:
@OpenShiftScenario
public class OpenShiftPingPongResourceIT {
@QuarkusApplication
static final RestService app = new RestService();
@Test
public void shouldPingPongWorks() {
app.given().get("/ping").then().statusCode(HttpStatus.SC_OK).body(is("ping"));
app.given().get("/pong").then().statusCode(HttpStatus.SC_OK).body(is("pong"));
}
}
We can reuse all the scenarios to be run on Baremetal and OpenShift by extending scenarios, for example having written the scenario on baremetal:
@QuarkusScenario
public class PingPongResourceIT {
@QuarkusApplication
static final RestService app = new RestService();
@Test
public void shouldPingPongWorks() {
app.given().get("/ping").then().statusCode(HttpStatus.SC_OK).body(is("ping"));
app.given().get("/pong").then().statusCode(HttpStatus.SC_OK).body(is("pong"));
}
}
We can create a new scenario called OpenShiftPingPongResourceIT
as:
@OpenShiftScenario
public class OpenShiftPingPongResourceIT extends PingPongResourceIT {
}
This strategy will build the Quarkus app artifacts locally and push it into OpenShift to generate the image that will be deployed.
Example:
@OpenShiftScenario // or @OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.Build)
public class OpenShiftPingPongResourceIT {
@QuarkusApplication(classes = PingResource.class)
static final RestService pingApp = new RestService();
@Test
public void shouldPingWorks() {
pingApp.given().get("/ping").then().statusCode(HttpStatus.SC_OK).body(is("ping"));
pingApp.given().get("/pong").then().statusCode(HttpStatus.SC_NOT_FOUND);
}
}
The default template used by this strategy can be overwritten using the property ts.global.openshift.template
.
This strategy will delegate the deployment into the Quarkus OpenShift extension, so it will trigger a Maven command to run it.
Example:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension)
public class OpenShiftPingPongResourceIT {
// ...
}
In order to use this strategy, you need to add this Maven profile into the pom.xml:
<profile>
<id>deploy-to-openshift-using-extension</id>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-openshift</artifactId>
</dependency>
</dependencies>
</profile>
| Important note: This strategy does not support custom sources to be selected, this means that the whole Maven module will be deployed. Therefore, if we have:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension)
public class OpenShiftUsingExtensionPingPongResourceIT {
@QuarkusApplication(classes = PingResource.class)
static final RestService pingPongApp = new RestService();
// ...
}
The test case will fail saying that this is not supported using the Using OpenShift strategy.
It is possible to combine this strategy with @GitRepositoryQuarkusApplication
as follows:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtension)
public class OpenShiftExtensionQuickstartUsingDefaultsIT {
@GitRepositoryQuarkusApplication(repo = "https://github.com/apache/camel-quarkus-examples.git", contextDir = "file-bindy-ftp", mavenArgs = "-Dopenshift")
static final RestService app = new RestService();
}
This is an extension of the OpenShift Extension
previous deployment strategy. The only difference is that a Docker build strategy will be used:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtensionAndDockerBuildStrategy)
public class OpenShiftUsingExtensionPingPongResourceIT {
@QuarkusApplication(classes = PingResource.class)
static final RestService pingPongApp = new RestService();
// ...
}
The same limitations as in OpenShift Extension
strategy apply here too.
It is possible to combine this strategy with @GitRepositoryQuarkusApplication
as follows:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingOpenShiftExtensionAndDockerBuildStrategy)
public class OpenShiftExtensionUsingDockerBuildStrategyQuickstartUsingDefaultsIT {
@GitRepositoryQuarkusApplication(repo = "https://github.com/apache/camel-quarkus-examples.git", contextDir = "file-bindy-ftp", mavenArgs = "-Dopenshift")
static final RestService app = new RestService();
}
This strategy utilises source S2I process described by the Quarkus product documentation:
oc import-image --confirm ubi8/openjdk-11 --from=registry.access.redhat.com/ubi8/openjdk-11
oc new-app ubi8/openjdk-11 <git_path> --context-dir=<context_dir> --name=<project_name>
The application's git repository, ref, context dir and Quarkus version are all specified in @QuarkusApplication
annotation.
Example:
@OpenShiftScenario
public class OpenShiftS2iQuickstartIT {
@GitRepositoryQuarkusApplication(repo = "https://github.com/quarkusio/quarkus-quickstarts.git", contextDir = "getting-started")
static final RestService app = new RestService();
//
This scenario will work for JVM and Native builds. In order to manage the base image in use, the framework will use the standard Quarkus properties in:
- For JVM:
quarkus.openshift.base-jvm-image
. Default isregistry.access.redhat.com/ubi8/openjdk-11:latest
. - For Native:
ts.global.s2i.openshift.base-native-image
. Default isquay.io/quarkus/ubi-quarkus-native-s2i:21.2-java11
.
The way these properties are up to users. In the examples, we supply this configuration in the pom.xml as part of system properties (in the Maven failsafe plugin).
But we can provide a custom property by service in the test.properties
file. For further information about how to customise the properties, go to the Configuration section.
It's important to note that, by default, OpenShift will build the application's source code using the Red Hat maven repository https://maven.repository.redhat.com/ga/
. However, some applications might require some dependencies from other remote Maven repositories. In order to allow us to add another remote Maven repository, you can use -Dts.global.s2i.maven.remote.repository=http://host:port/repo/name
. If you only want to configure different maven repositories by service, you can do it by replacing global
to the service name, for example: -Dts.pingPong.s2i.maven.remote.repository=...
.
The test framework will automatically load a custom maven settings with the provided maven remote repository. But if you're using a custom template, all you need to do is to configure the settings-mvn
config map and the Maven args as follows:
apiVersion: build.openshift.io/v1
kind: BuildConfig
metadata:
name: myApp
spec:
source:
git:
uri: https://github.com/repo/name.git
type: Git
configMaps:
- configMap:
name: settings-mvn
destinationDir: "/configuration"
strategy:
type: Source
sourceStrategy:
env:
- name: MAVEN_ARGS
value: -s /configuration/settings.xml
// ...
The default template used by this strategy can be overwritten using the property ts.global.openshift.template
.
This strategy will build the image locally and push it to an intermediary container registry (provided by a system property). Then, the image will be pulled from the container registry in OpenShift.
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingContainerRegistry)
public class OpenShiftUsingExtensionPingPongResourceIT {
// ...
}
When running these tests, the container registry must be supplied as a system property:
mvn clean verify -Dts.container.registry-url=quay.io/<your username>
These tests can be disabled if the above system property is not set using the @DisabledIfNotContainerRegistry
annotation:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingContainerRegistry)
@DisabledIfNotContainerRegistry
public class OpenShiftUsingExtensionPingPongResourceIT {
// ...
}
The default template used by this strategy can be overwritten using the property ts.global.openshift.template
.
It is possible to combine this strategy with @GitRepositoryQuarkusApplication
as follows:
@OpenShiftScenario(deployment = OpenShiftDeploymentStrategy.UsingContainerRegistry)
public class OpenShiftContainerRegistryQuickstartUsingDefaultsIT {
@GitRepositoryQuarkusApplication(repo = "https://github.com/quarkusio/quarkus-quickstarts.git", contextDir = "getting-started")
static final RestService app = new RestService();
}
We can inject the OpenShift client to interact with OpenShift. This can be useful to cope with more complex scenarios like scale up/down services.
import io.quarkus.test.bootstrap.inject.OpenShiftClient;
import io.quarkus.test.scenarios.OpenShiftScenario;
@OpenShiftScenario
public class OpenShiftGreetingResourceIT extends GreetingResourceIT {
@Test
public void shouldInjectOpenShiftClient(OpenShiftClient client) {
// ...
client.scaleTo(app, 2);
}
}
Another option is by injecting the client directly to the test class using the @Inject
annotation:
import io.quarkus.test.bootstrap.inject.OpenShiftClient;
import io.quarkus.test.scenarios.OpenShiftScenario;
@OpenShiftScenario
public class OpenShiftGreetingResourceIT extends GreetingResourceIT {
@Inject
static OpenShiftClient client;
@Test
public void shouldInjectOpenShiftClient() {
// ...
client.scaleTo(app, 2);
}
}
| Note that the injection is only supported to static fields.
The framework will create a project for every scenario and delete it once the scenario is finished. However, sometimes due to cluster restrictions we're not allowed to create namespaces. In those cases, you can disable ephemeral namespaces and run all your tests in your current namespace.
-Dts.openshift.ephemeral.namespaces.enabled=false
Sometimes deploying a third party into OpenShift involves some complex configuration that is not required when deploying on bare metal. For these scenarios, we allow to provide a custom template via test.properties
:
ts.consul.openshift.template=/yourtemplate.yaml
| Note the custom template must contain ONLY ONE deployment config.
Moreover, if the service that is exposing the port we want to target is named differently to our service, we can provide the service name via:
ts.consul.openshift.service=consul-http-service
What about if we want to use the internal service as route (not the exposed route), we can set this behaviour by enabling the property ts.<MY_SERVICE>.openshift.use-internal-service-as-url
:
ts.consul.openshift.use-internal-service-as-url=true
By default, the framework will always delete the OpenShift project and, sometimes, it's useful to not delete the OpenShift project on failures to troubleshooting purposes. For disabling the deletion, we need to run the test using:
mvn clean verify -Dts.openshift.delete.project.after.all=false
The test framework will print the project status, events and pod logs when a test fails. This functionality is enabled by default,
however it can be disabled using the property -Dts.openshift.print.info.on.error=false
.
The OpenShift scenarios support Operator based test cases. There are two ways to deal with Operators:
- Installing the Operators as part of the
OpenShiftScenario
:
@OpenShiftScenario(
operators = @Operator(name = "strimzi-kafka-operator")
)
public class StrimziOperatorKafkaWithoutRegistryMessagingIT {
// We can now use the new Operator CRDs manually
}
- Installing and managing Custom Resource Definitions as services
First, we need to create our Custom Resource YAML file, for example, for Kafka:
apiVersion: kafka.strimzi.io/v1beta2
kind: Kafka
metadata:
name: kafka-instance
spec:
...
Now, we can create an OperatorService to load this YAML as part of an Operator installation:
@OpenShiftScenario
public class OperatorExampleIT {
@Operator(name = "my-operator", source = "...")
static final OperatorService operator = new OperatorService().withCrd("kafka-instance", "/my-crd.yaml");
@QuarkusApplication
static final RestService app = new RestService();
// ...
}
The framework will install the operator and load the YAML file by you.
Note that the framework will wait for the operator to be installed before loading the CRD yaml files, but will not wait for the CRDs to be ready. If you are working with CRDs that update conditions, then we can ease this for you by providing the custom resource definition:
@Version("v1beta2")
@Group("kafka.strimzi.io")
@Kind("Kafka")
public class KafkaInstanceCustomResource
extends CustomResource<CustomResourceSpec, CustomResourceStatus>
implements Namespaced {
}
And then registering the CRD with this type:
@OpenShiftScenario
public class OperatorExampleIT {
@Operator(name = "my-operator", source = "...")
static final OperatorService operator = new OperatorService().withCrd("kafka-instance", "/my-crd.yaml", KafkaInstanceCustomResource.class);
@QuarkusApplication
static final RestService app = new RestService();
// ...
}
Now, the framework will wait for the operator to be installed and the custom resource named kafka-instance
to be with a condition "Ready" as "True".