Continuous Deployment with Nuxeo and OpenShift


Thu 24 August 2017 By Damien Metzler

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 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.


Tagged: Nuxeo Platform, OpenShift, How to