At Nuxeo, we always put effort in making life easier for developers. For instance, we have Nuxeo Studio, which allows our customers (and their developers) to customize their business logic with a few clicks by defining new document types, new workflows and rules. However, sometimes when the use cases call for significant customizations, the developers need to write some custom code in Java and deploy that code in the Nuxeo Platform through new bundles. For that, we provide our custom Nuxeo packaging that makes the deployment of marketplace very easy.
If you follow our blogs, you may have noticed that we are showing a lot of interest in the OpenShift Container Platform. So, when it came to deployment, we looked into OpenShift as well. We wanted to provide a continuous deployment option with OpenShift, which would mean that when you write your new code and new tests, and push your code to GitHub, it will be deployed on your Nuxeo development instance. Let’s take a detailed look at how we can achieve this.
OpenShift Source To Image
One of the strategies to create deployment in OpenShift is to use the sourceStrategy
. It basically points to a GitHub repository where it’s supposed to build the project.
By default it knows how to build projects with a Dockerfile
, but you can specify a build image that knows how to build the project, package it and create a Docker image containing the project. That process is known as Source To Image. You have some code (Java, for instance) as input and you have a Docker image ready to run as output.
In the Docker meme Build, Ship, Run, Source To Image handles the Build part. Once built, the image is then pushed as an ImageStream in OpenShift, which is the Ship part. And then finally, you’re able to use that ImageStream in a DeploymentConfig, which is the Run part.
Nuxeo S2I Image
In order to be able to run that complete deployment cycle, we built a Nuxeo S2I (Source to Image) Docker image that knows how to build a Nuxeo project, providing a JDK, Maven and Node tools.
Let’s create a dummy Nuxeo project with the help of the Nuxeo CLI:
# mkdir nuxeo-s2i-sample
# cd nuxeo-s2i-sample
# nuxeo bootstrap single-module
...
info You'll be prompted for generation of:
info nuxeo-s2i-sample-core: single-module
create Generate Module: nuxeo-s2i-sample-core
create Generating Single module
info Parameters: Parent group, Parent artifact, Parent version, Nuxeo version, Project group, Project artifact, Project version, Project description
? Parent Group id (use white space to cancel default value.): org.nuxeo
? Parent Artifact id: nuxeo-addons-parent
? Parent Version: 9.2
? Project Group id: org.nuxeo.sample
? Project Artifact id: nuxeo-s2i-sample
? Project version: 1.0-SNAPSHOT
? Project description: Sample project to demonstrate S2I usage
create pom.xml
create src/main/resources/META-INF/MANIFEST.MF
create src/main/java/org/nuxeo/sample/package-info.java
create src/test/resources/jndi.properties
create src/test/resources/log4j.xml
info You can start editing code or you can continue with calling another generator (nuxeo bootstrap [<generator>..])
This will create a sample empty Nuxeo project that you can build with Maven. Instead of doing that, we will invoke the s2i
client to build a Nuxeo Docker image containing that bundle.
s2i build . nuxeo/s2i:9.2 nuxeo-s2i-sample
After a while, it will create nuxeo-s2i-sample
Docker image that you can run like the Nuxeo image with:
docker run --rm -ti -p 8080:8080 nuxeo-s2i-sample
We can verify whether our package has been deployed by going to the Admin Center http://localhost:8080/nuxeo/nxadmin/default/defau[email protected]_admin?tabIds=NUXEO_ADMIN%3ASystemInfo%3ANuxeoInfo
in the distribution information.
Using Source to Image in OpenShift
OpenShift knows how to use s2i
. We just have to create some resource that references our Nuxeo S2I image. This is done via an ImageStream:
apiVersion: v1
kind: ImageStream
metadata:
name: nuxeo-s2i
annotations:
openshift.io/display-name: Nuxeo Source to image
spec:
tags:
- name: "latest"
annotations:
openshift.io/display-name: Nuxeo (latest)
description: |
Build and run a Nuxeo application. For more information about using this builder image, see https://github.com/nuxeo-sandbox/nuxeo-s2i
WARNING: By selecting this tag, your application will automatically update to use the latest version of Ruby available on OpenShift, including major versions updates.
iconClass: icon-java
tags: builder, java, nuxeo
supports: nuxeo
sampleRepo: https://github.com/nuxeo/nuxeo-sample-project
from:
kind: ImageStreamTag
name: "9.3-SNAPSHOT"
- name: "9.3-SNAPSHOT"
annotations:
openshift.io/display-name: Nuxeo 9.3-SNAPSHOT
description: |
Build and run a Nuxeo application. For more information about using this builder image, see https://github.com/nuxeo-sandbox/nuxeo-s2i
iconClass: icon-java
tags: builder, java, nuxeo
supports: nuxeo
sampleRepo: https://github.com/nuxeo/nuxeo-sample-project
from:
kind: DockerImage
name: nuxeo/s2i:master
After that, we can use the Add project button to create a new Nuxeo project by selecting the ImageStream that has just been created.
We will reference our Nuxeo project GitHub repository address:
And that’s it! OpenShift will invoke the S2I image, build our Maven project, find the artifact in the target directory, add it to the bundles
directory of our Nuxeo Docker image, push the image and then deploy it into a DeploymentConfig.
Once the Nuxeo Platform is launched, we can check whether the package is installed by rsh
into the container and listing the packages installed in the bundles
directory.
# oc get po
NAME READY STATUS RESTARTS AGE
nuxeosample-1-7z7s2 1/1 Running 0 5m
# oc rsh nuxeosample-1-7z7s2
nux...z7s2:/opt/nuxeo/server$ ls /opt/nuxeo/server/nxserver/bundles | grep sample
nuxeo-s2i-sample-1.0-SNAPSHOT.jar
Continuous Deployment
Our Nuxeo project is empty and we want to add some behavior into it and deploy it in place of the current pod. In order to do that, we could push our changes to GitHub and launch a new build which would then trigger a new deployment. But we can also avoid that manual build step by using a webhook.
In OpenShift, we will edit the Nuxeo build, and open the advanced settings panel. There we can look for Triggers
and copy the GitHub webhook endpoint.
Then in GitHub, we will setup a webhook that will target that endpoint on each commit.
Now we will create a listener that will update the description of each newly created note. When we will create a Note
document in Nuxeo, its description will be updated to “Happily deployed thru s2i.”
# nuxeo bootstrap listener
...
create Generating Listener
info Parameters: Listener package, Listener class name, Trigger on event, Custom events, Is it an asynchronous listener, Is it a post commit listener
? Listener package: org.nuxeo.sample
? Listener class name: NoteCreationListener
? Trigger on events: aboutToCreate
? Is it an asynchronous Listener? No
? Is it a post-commit Listener? No
info Maven dependency: org.nuxeo.runtime:nuxeo-runtime-test:::test
info Maven dependency: org.nuxeo.ecm.platform:nuxeo-platform-test:::test
info Maven dependency: org.nuxeo.ecm.core:nuxeo-core-event
force pom.xml
force src/main/resources/META-INF/MANIFEST.MF
create src/main/java/org/nuxeo/sample/NoteCreationListener.java
create src/test/java/org/nuxeo/sample/TestNoteCreationListener.java
create src/main/resources/OSGI-INF/notecreationlistener-listener-contrib.xml
info You can start editing code or you can continue with calling another generator (nuxeo bootstrap [<generator>..])
Then you can edit the Java code of src/main/java/org/nuxeo/sample/NoteCreationListener.java
so that the listener updates the document.
public class NoteCreationListener implements EventListener {
@Override
public void handleEvent(Event event) {
EventContext ctx = event.getContext();
if (!(ctx instanceof DocumentEventContext)) {
return;
}
DocumentEventContext docCtx = (DocumentEventContext) ctx;
DocumentModel doc = docCtx.getSourceDocument();
// Add some logic starting from here.
doc.setPropertyValue("dc:description","Happily deployed thru s2i");
}
}
Once we commit and push to GitHub, the OpenShift build starts and the deployment follows. In order to check whether it works, we can create a test Note
document and voilà, our description will be updated!
Going Further with the S2I Image
In its basic form, the S2I image just takes any JAR found in the target
directory and copies it to the $NUXEO_HOME/nxserver/bundles
folder. If you have a multi-module maven project, this may not be suitable. To make things more configurable, you can add a .nuxeo-s2i
manifest file at the root of the project that specifies what has to be deployed. And you can of course specify a Nuxeo package. A sample file can look like this:
# Directory where to find JAR artifacts to deploy
#ARTIFACT_DIR=target
# Nuxeo package to deploy
#NUXEO_PACKAGE=sample-package/target/sample-package-*.zip
# Whether to start Nuxeo in a test phase and do some smoke tests
#NUXEO_SMOKE_TEST=false
This is just the beginning and we will surely add some other features, such as, running the Maven integration tests on the deployed Nuxeo instance. Feel free to send us your feedback!
Lastly, if our assemble
script doesn’t fit your needs, you can still specify your own by following the source-to-image instructions.
A Continuous Flow of Development
S2I can be seen as a very simple tool that transforms code into a runnable Docker image. But with OpenShift it allows us to completely automate our development and deployment experience.
The Nuxeo S2I image is still a work in progress. When this is combined with Nuxeo Studio, you have a complete chain of rapid application development! Don’t forget to send us your feedback on your experiments with the various options we discussed today.