Now that you have made yourself familiar with Installer concept and usage, let’s take a closer look at implementing custom ones.

Let’s take a closer look of a custom installer named Curl:

The installer can be split into description in json format and shell script.

The description has the following format:

{
  "id": "org.eclipse.che.curl",
  "version": "1.0.0",
  "name": "Curl",
  "description": "Embedded Curl",
  "dependencies": [],
  "properties": {},
  "servers": {}
}

As you can see it has the following fields:

  • id - unique value that identifier an installer;

  • version - version of the installer;

  • name - readable name of the installer;

  • description - description of the installer;

  • dependencies - declare dependencies to other installers;

  • properties - key:value map, where installer is able to store custom specific fields;

  • servers - list of servers which will be started by the installer.

The second item in resources is an installer script. It’s a shell script that will do all required system changes:

Here is an example of script that installs Curl inside a machine:

is_current_user_root() {
    test "$(id -u)" = 0
}

is_current_user_sudoer() {
    sudo -n true > /dev/null 2>&1
}

set_sudo_command() {
    if is_current_user_sudoer && ! is_current_user_root; then SUDO="sudo -E"; else unset SUDO; fi
}

set_sudo_command
unset PACKAGES

CURL_INSTALLED=false
command -v curl >/dev/null 2>&1 && CURL_INSTALLED=true

# no curl, install it
if [ ${CURL_INSTALLED} = false ]; then
  PACKAGES=${PACKAGES}" curl";
  CURL_INSTALLED=true
else
  echo "Curl is already installed"
  exit 0
fi

command -v yum >/dev/null 2>&1 && YUM_INSTALLED=true
command -v apt-get >/dev/null 2>&1 && APT_GET_INSTALLED=true
command -v dnf >/dev/null 2>&1 && DNF_INSTALLED=true
command -v zypper >/dev/null 2>&1 && ZYPPER_INSTALLED=true


if [ ${YUM_INSTALLED} = false ]; then
  ${SUDO} yum install ${PACKAGES};

elif [ ${APT_GET_INSTALLED} = false ]; then
  ${SUDO} apt-get update;
  ${SUDO} apt-get -y install ${PACKAGES};

elif [ ${DNF_INSTALLED} = false ]; then
  ${SUDO} dnf -y install ${PACKAGES};

elif [ ${ZYPPER_INSTALLED} = false ]; then
  ${SUDO} zypper install -y ${PACKAGES};

else
    >&2 echo "Any of Yum, Apt-get, Dnf, Zypper package manager is not available"
    exit 1
fi

echo "Curl is installed successfully"

And here is another installer which launches a web server and depends on another installer. Here is content of description json file:

{
  "id": "org.eclipse.che.tomcat",
  "version": "8.5.24",
  "name": "Tomcat",
  "description": "Embedded tomcat server",
  "dependencies": ["org.eclipse.che.curl"],
  "properties": {},
  "servers": {
    "tomcat": {
      "port": "8090/tcp",
      "protocol": "http",
      "path" : "/"
    }
  }
}

This installer needs curl to download tomcat binaries, so it is declared in dependencies field. If an installer depends on other installers, then all required installers will be launched automatically even if a user has not configured it. All installers which are defined as dependencies will be launched before launching the dependent one.

Bootstrapper that launches installers tries to connect to all declared servers. No user input is required. Start of a workspace fails if any of servers are not accessible on defined ports during configured timeout.

Installers servers may conflict, for example, two installers start servers on the same port. Then, infrastructure may want to be able reconfigure servers in its own way. So installer must expect environment variable with port for server launching or launch server on a default port if it is absent.

The environment variable name has the following format CHE_SERVER_{NORMALISED_SERVER_NAME}_PORT. Where NORMALISED_SERVER_NAME is a server name in the upper case where all characters which are not Latin letters or digits replaced with _ symbol.

Here is an installer script that downloads Tomcat binaries with Curl, configures to use free port and launches it.

TOMCAT_VERSION=8.5.24
TOMCAT_BINARIES_URL=http://apache.volia.net/tomcat/tomcat-8/v8.5.24/bin/apache-tomcat-$TOMCAT_VERSION.tar.gz

TARGET_FOLDER=~/tomcat
mkdir -p $TARGET_FOLDER

echo "Downloading Tomcat $TOMCAT_VERSION into $TARGET_FOLDER"
curl $TOMCAT_BINARIES_URL | tar  xzf - -C ${TARGET_FOLDER}
TOMCAT_FOLDER=$TARGET_FOLDER/apache-tomcat-$TOMCAT_VERSION

echo "Tomcat $TOMCAT_VERSION is downloaded and unpacked into $TOMCAT_FOLDER"

DEFAULT_TOMCAT_SERVER_PORT=8090
TOMCAT_SERVER_PORT=${CHE_SERVER_TOMCAT_PORT:-${DEFAULT_TOMCAT_SERVER_PORT}}

// configure Tomcat
sed -i "s/port=\"8080\"/port=\"${TOMCAT_SERVER_PORT}\"/g" $TOMCAT_FOLDER/conf/server.xml

echo "Tomcat $TOMCAT_VERSION is configured to use $TOMCAT_SERVER_PORT port"

$TOMCAT_FOLDER/bin/catalina.sh run

An installer can be included into Che assembly or added to particular Che Server via REST API.

To include an installer into Che assembly, two files are required in Che Server resources: installer description in json format and its script. The files must be named in the following way: {INSTALLER_ID}.json for description file and {INSTALLER_ID}.script.sh for script file. These files should be placed into /installers/{INSTALLER_VERSION} folder.

Here is an example of maven module structure:

curl installer module

The it is needed to add the corresponding maven module to Workspace Master War archive

<project ...>
  <parent>
    <artifactId>che-assembly-parent</artifactId>
    <groupId>org.eclipse.che</groupId>
    <version>6.0.X</version>
  </parent>
  <artifactId>assembly-wsmaster-war</artifactId>
  <packaging>war</packaging>
  <name>Che IDE :: Compiling WS Master WAR
  <dependencies>
    <dependency>
      <groupId>org.eclipse.che</groupId>
      <artifactId>installer-tomcat</artifactId>
    </dependency>
    <dependency>
      <groupId>org.eclipse.che</groupId>
      <artifactId>installer-tomcat</artifactId>
    </dependency>
  </dependencies>
</project>

Also as it was mentioned an installer can be added via REST API or using Swagger:

add installer swagger