Skip to content
This repository has been archived by the owner on May 29, 2024. It is now read-only.

Creator: HOWTO Create a Language Generator

Tako Schotanus edited this page Sep 3, 2019 · 6 revisions

As we already read in the page about Runtime Generators, HOWTO Create a Runtime Generator, they are meant as a basis for other Generators to build on. They provide all the necessary files (code, configuration, build etc) to create a project for a certain framework. But that's all about the code and files, and as you can imagine there are lots of different ways to write code and lots of different frameworks to choose from. You could write Runtime Generators for Vert.x, Wildfly, Spring Boot, etc, all very different, but they still have one thing in common: they all run on the JVM platform. That's where Language Generators come in.

A Language Generator is the thing that takes care of taking the code in your project and getting it compiled and running on Kubernetes or OpenShift.

⚠️ Kubernetes is mentioned here, but right now only OpenShift support has been implemented. Support for Kubernetes is currently in development.

So let's go through the steps of what it takes to create a Language Generator (this is starting to look very familiar, right?):

  1. Setup - Make sure your are set up by following the Quick Setup for Devs guide
  2. Choose a name - First we need to choose a name for our new Language Generator. Language Generators generally start with the text language-, so let's go with the very original language-example.
  3. Create a folder - And like before all the Language Generator's files need to live in a folder with the name of the Generator
$ mkdir creator/src/main/resources/META-INF/catalog/language-example
  1. Files - Language Generators don't generally need many files, but they follow the same pattern as all Generators:
language-example/
   files/
      gap

The important file here is a shell script called gap. It's a script that's used to build and deploy code. The easiest thing to do is to just copy the gap script from one of the existing Language Generators and adapt it to your specific language/platform. It's normally a simple 2-line change.

Show code

gap

#!/usr/bin/env bash

APP_NAME={{.application}}
SERVICE_NAME={{.serviceName}}

SCRIPT_DIR=$(cd "$(dirname "$BASH_SOURCE")" ; pwd -P)
OPENSHIFTIO_DIR=${SCRIPT_DIR}/.openshiftio
SOURCE_DIR=${SCRIPT_DIR}
if [[ ! -z "{{.subFolderName}}" ]]; then
    SOURCE_DIR=${SOURCE_DIR}/..
    SUB_DIR=/{{.subFolderName}}
fi

function do_deploy() {
    PARAMS=""
    REPO_URL=$(git config --get remote.origin.url || echo "")
    if [[ ! -z "${REPO_URL}" ]]; then
        PARAMS="$PARAMS -p=SOURCE_REPOSITORY_URL=${REPO_URL}"
    fi
    if [[ ! -z "${OPENSHIFT_CONSOLE_URL}" ]]; then
        PARAMS="$PARAMS -p=OPENSHIFT_CONSOLE_URL=${OPENSHIFT_CONSOLE_URL}"
    fi
    if [[ -f ${OPENSHIFTIO_DIR}/application.yaml ]]; then
        oc process -f ${OPENSHIFTIO_DIR}/application.yaml --ignore-unknown-parameters ${PARAMS} | oc apply -f -
    fi
    if [[ -f ${OPENSHIFTIO_DIR}/service.welcome.yaml ]]; then
        oc process -f ${OPENSHIFTIO_DIR}/service.welcome.yaml --ignore-unknown-parameters ${PARAMS} | oc apply -f -
    fi
}

function do_build() {
    mvn package
}

function do_clean() {
    mvn clean
}

function do_push() {
    if [[ "$1" == "--source" || "$1" == "-s" || "$1" == "--binary" || "$1" == "-b" ]]; then
        shift
        oc start-build ${SERVICE_NAME} --from-dir ${SOURCE_DIR} "$@"
    elif [[ "$1" == "--git" || "$1" == "-g" ]]; then
        shift
        oc start-build ${SERVICE_NAME} "$@"
    else
        oc start-build ${SERVICE_NAME} --from-dir ${SOURCE_DIR} "$@"
    fi
}

function do_delete() {
    oc delete all,secrets -l app=${APP_NAME}
}

while [[ $# > 0 ]]; do
    CMD=$1
    shift
    ARGS=()
    while [[ "$1" == -* ]]; do ARGS+=($1); shift; done
    case "$CMD" in
        "deploy")
            do_deploy $ARGS
            ;;
        "build")
            do_build $ARGS
            ;;
        "clean")
            do_clean $ARGS
            ;;
        "push")
            do_push $ARGS
            ;;
        "delete")
            do_delete $ARGS
            ;;
    esac
    RES=$?
    if [[ $RES > 0 ]]; then
        exit $RES
    fi
done
  1. Create metadata - Our Generator also needs an info.yaml in the root of our folder that contains some important metadata. For our example we will use the following:
type: generator
config:
  image: "registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift"

For Language Generators the most important information is the builder image specified by the image property. It's the name of a special Docker image, a so-called S2I builder image, that was created specifically to compile and run the code for your language / platform. In this case we chose a RedHat provided S2I builder image for the Java platform.

⚠️ Language Generators need an S2I Builder Image to do their work. Creating an S2I Builder Image does not fall within the scope of this documentation. See the S2I GitHub repository and the OpenShift S2I Documentation for more information.

  1. Register the image - We now need to register this image in the list of images. Just add a item to the end of the list with the exact same name we used in the image property. It would look something like this:
- id: nodeshift/centos7-s2i-nodejs
  name: Generic Node.js Code Builder
  metadata:
    language: nodejs
    isBuilder: true
    suggestedEnv:
      PORT: '8080'
- id: nodeshift/centos7-s2i-web-app
  name: Web App Node.js Code Builder
  metadata:
    language: nodejs
    isBuilder: true
- id: registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift
  name: Java Code Builder
  metadata:
    language: java
    isBuilder: true

Now you'll need to run ./creator/creator generate from the root of the Launcher project. This will generate a Yaml file which needs to be added to the project as well.

  1. Register the Generator - Finally we need to register our Generator in the GeneratorInfo list just as explained in the basic HOWTO Create a Generator.

And that's it! As you can see Language Generators are basically still Generators, but they do need a bit more work and knowledge to make work. Good thing we made them reusable!

Test your Generator

Now Generators aren't really meant to be used directly, they are the building blocks for Capabilities after all, but if you'd always have to set up a Capability just to test your work that would be a nuisance.

So to try out the effect of a single Generator you can run the following command:

$ creator apply --project testproject --name test component --generator=example-language

If everything works as it's supposed to, which it never does of course the first time, but when it does the output should look somewhat like this:

$ creator apply --project testproject --name test component --generator=example-language
Applied capability to 'testproject'
Go into that folder and type './gap deploy' while logged into OpenShift to create the application
in the currently active project. Afterwards type './gap push' at any time to push the current
application code to the project.

⚠️ The output is a bit optimistic in this case because Language generators are so very low on the Generator Food Chain that trying to run the gap command as it suggests will likely only result in errors. That's okay, you can still take a look at the project that was generated to see if the Generator was working okay

Congratulations! You just wrote your first Language Generator and created a project with it!