Distributions in Depth

This page delves into the details of Corus distributions: everything starts with the Corus descriptor...

The Corus Descriptor

The Corus descriptor is the XML file (named corus.xml) which must be packaged as part of Corus distributions, under the META-INF directory (which itself must be present under the root of the distribution .zip).

Here is an elaborate Corus descriptor (all elements are formally explained in the table further below):

<distribution
	xmlns="http://www.sapia-oss.org/xsd/corus/distribution-5.0.xsd" 
	name="demo" 
	version="1.0" tags="someTag" >
  <process name="echoServer" 
           maxKillRetry="3" 
           shutdownTimeout="30000" 
           deleteOnKill="false"
           invoke="true"
           pollInterval="15"
           statusInterval="45"
           tags="someOtherTag"
           propertyCategories="echoApp">
    <port name="test" />
    <magnet magnetFile="echoServerMagnet.xml" 
            profile="test"
            javaCmd="java">
      <xoption  name="ms" value="16M" />
      <dependency dist="testDist" version="1.0" process="testApp" profile="prod" />
    </magnet>
	<!-- Alternate standard Java configuration: 
	     simpler, but less powerful than Magnet -->
    <!-- java mainClass="org.sapia.corus.examples.EchoServer" 
            profile="test"
            javaCmd="java">
      <arg value="-Xms16M" />
      <dependency dist="testDist" version="1.0" process="testApp" profile="prod" />
    -->   
    
  </process>  
</distribution>

The Corus descriptor's XML schema can be dowloaded from the website. You can find below documentation for the schema's elements and attributes.

distribution

At the root, there is the <distribution> element, which has the following attributes/elements:

NameTypeRequiredDescription
name Attribute Yes The name of the distribution (the name and version of a distribution must be unique in the context of a given Corus instance).
version Attribute Yes The version of the distribution.
tags Attribute No A comma-delimited list of tags used to determine if the configured processes should be started (based on the tags of the current Corus server) – see the Tagging section.
propertyCategories Attribute No A comma-delimited list of property categories, determining which process property sets to use and it what order (the global, default property set is always used, and looked up first).
process Element Yes (1 - *) One more, each defining a so-called process configuration

process

Each <process> element corresponds to a so-called "process configuration": it holds, among others, information about how a process should be handled at runtime by Corus.

NameTypeRequiredDescription
maxKillRetry Attribute Yes The number of times Corus should attempt to kill processes corresponding to this process configuration that are deemed stalled or “down" (defaults to 3).
shutdownTimeout Attribute Yes The number of milliseconds that is given to processes to confirm their clean shutdown (defaults to 30000).
deleteOnKill Attribute No Indicates if processes corresponding to this process configuration should have their process directory deleted after shutdown (defaults to false).
invoke Attribute No Indicates if processes corresponding to this distribution will be started automatically when invoking exec for the distribution without indicating which process configuration to create processes from (if true, the value of this attribute indicates that processes corresponding to this process configuration must be invoked explicitly by passing the -n option to the exec command) – defaults to false.
pollInterval Attribute No The interval (in seconds) at which processes corresponding to this configuration are expected to poll their Corus server (defaults to 10).
statusInterval Attribute No The interval (in seconds) at which processes corresponding to this configuration are expected to provided their runtime status to the Corus server (defaults to 30).s
tags Attribute No A comma-delimited list of tags used to determine if the process should be started (based on the tags of the current Corus server) – see the Tagging section for more information.
propertyCategories Attribute No A comma-delimited list of property categories, determining which process property sets to use and in what order (the global, default property set is always used, and looked up first). The set resulting from this list is additive to the one defined at the <distribution> level.
interpolationPasses Attribute No The number of variable interpolation passes to do when rendering variables defined as part of this <process> element. This is used to make sure variables whose value results from the interpolation of other variables, and so on, get substituted properly. This strategy was chosen, as opposed to risking interpolating.
port Element No (0 - *) Indicates that a port (corresponding to a given Corus port range) should be passed to the process.
preExec Element No (0 - *) Used to hold multiple Corus CLI commands that are executed on the Corus side (see the cmd element). See the Process Pre-Execution section for more details.
java Element No (0 - 1) Encapsulates information required by the underlying Java when starting up JVMs.
magnet Element No (0 - 1) Encapsulates information required by the underlying Magnet starter when starting up JVMs.
docker Element No (0 - 1) Encapsulates information required by the underlying Docker starter when starting up Docker containers.

Corus used so-called "starters" to actually launch native OS processes (or to start containers). The starter abstraction hides the details of how a given process is started.

As hinted by the documentation above, such starters are configured under the <process> element. They correspond to the <java>, <magnet> and <docker> elements, which are more fully described in the next sections.

port

Used to indicate that network port number (corresponding to a pre-configured Corus port range) should be passed to the insances of the process.

NameTypeRequiredDescription
name Attribute Yes The name of a port range, as configured in the Corus server (see the Port Management section for futher information).

preExec

Allows specifiying multiple cmd child elements, each corresponding to a Corus CLI command. Each such command will be executed on the server-side (that is, at the Corus node) prior to the process itself being executed (see the Process Pre-Execution section for more information).

NameTypeRequiredDescription
cmd Element No (0 - *) Multiple such elements can be specified, each holding a CLI command.

cmd

The element's content is meant to hold a Corus command-line which will be executed on the server-side (at the Corus node)

Example

<cmd>deploy ${user.dir}/conf/exec.xml</cmd>

java

The <java> element configures the basic Java "starter", which supports a single-classloader scheme (as opposed to Magnet). It generates the Java command line required to start the JVM corresponding to the configured process (i.e: the process corresponding to the <process> element under which it is configured):

NameTypeRequiredDescription
mainClass Attribute Yes The name of the application class (class with a main method) to invoke upon JVM startup.
profile Attribute Yes The name of the profile under which processes are to be started.
javaHome Attribute No Allows specifying usage of a different JVM by specifiying the home of the JDK or JRE installation directory that is desired (defaults to the same Java home as the Corus server by which processes are executed).
javaCmd Attribute No Allows specifying the name of the Java executable that is to be invoked when starting JVMs (defaults to java).
vmType Attribute No Indicates the type of JVM that is to be started. Value can be either client or server for Java Hotspot)
libDirs Attribute No

Allows overridding the default lib directory expected to contain the libraries with which the JVM's classpath will be built.

The value must be a semicolon or colon-delimited list of directories (interpreted relatively to the distribution's root – that is, the root of its .zip file). The libraries (.jar files) in these directories will then instead be used to build the JVM's classpath.

To include directories directly so that their resources are available to the classloader, these must end with a forward-slash. See the Direct Java Distributions section for an example.

interopEnabled Attribute No (defaults to false) A boolean flag indicating if Corus should check for process polling or not (based on the Corus interop mechanism). Note that Corus also the diagnostic functionality to internally check process health.
interopWireFormat Attribute No Indicates which interop wire format to use has part of communication between Corus and JVM-based processes (values can be either protobuf or interop. Defaults to protobuf).
numaEnabled Attribute No (defaults to true) A boolean flag indicating if NUMA support should be enabled for the JVM process.
dependency Element No (0 - *) Use to configure dependency on one or more other processes.
arg Element No (0 - *) Corresponds to a JVM argument, such as -javaagent
option Element No (0 - *) Corresponds to a JVM option, such as -cp
xoption Element No (0 - *) Can conveniently be used in place of the arg of arguments starting with -X.
property Element No (0 - *) Allows defining system properties that are passed at the command-line, using -D.

magnet

The <magnet> element configures the Magnet "starter", which then will be used to generate the application commmand line necessary for starting that application through Magnet:

NameTypeRequiredDescription
magnetFile Attribute Yes The path to the Magnet configuration file, relatively to the root of the Corus distribution archive.
profile Attribute Yes The name of the profile under which processes are to be started.
javaHome Attribute No Allows specifying usage of a different JVM by specifiying the home of the JDK or JRE installation directory that is desired (defaults to the same Java home as the Corus server by which processes are executed).
javaCmd Attribute No Allows specifying the name of the Java executable that is to be invoked when starting JVMs (defaults to java).
vmType Attribute No Indicates the type of JVM that is to be started. Value can be either client or server for Java Hotspot)
interopEnabled Attribute No (defaults to false) A boolean flag Indicating if Corus should check for process polling or not (based on the Corus interop mechanism). Note that Corus also the diagnostic functionality to internally check process health.
interopWireFormat Attribute No Indicates which interop wire format to use has part of communication between Corus and JVM-based processes (values can be either protobuf or interop. Defaults to protobuf).
numaEnabled Attribute No (defaults to true) A boolean flag indicating if NUMA support should be enabled for the JVM process.
dependency Element No (0 - *) Use to configure dependency on one or more other processes.
arg Element No (0 - *) Corresponds to a JVM argument, such as -javaagent
option Element No (0 - *) Corresponds to a JVM option, such as -cp
xoption Element No (0 - *) Can conveniently be used in place of the arg of arguments starting with -X.
property Element No (0 - *) Allows defining system properties that are passed at the command-line, using -D.

docker

The <docker> element configures the Docker "starter", as its name implies. It is used in the context of Docker Integration.

NameTypeRequiredDescription
image Attribute No The name of the image for which to start a container. If not specified, the name of the image will be inferred from the distribution information, according to the following format: <distribution_name>:<distribution_version>.
profile Attribute Yes The name of the profile under which processes are to be started.
interopEnabled Attribute No (defaults to false) A boolean flag Indicating if Corus should check for process polling or not (based on the Corus interop mechanism). Note that Corus also the diagnostic functionality to internally check process health.
autoRemoveEnabled Attribute No (true by default) Indicates if the Docker image corresponding to this starter should be removed from the Docker daemon upon undeployment of this distributiion.
user Attribute No Allows overriding the user under which the Docker container will run (Corresponds to Docker's -u command-line switch).
command Attribute No The actual command line to execute when the Docker container starts.
networkMode Attribute No (defaults to host) Configures the Docker container's network mode (can be either host, bridge, or none). Please consult Docker's own documentation for more details about the different modes.
macAddress Attribute No The MAC address to assign to the container.
memory Attribute No Corresponds to Docker's --memory command-line switch.
memorySwap Attribute No Corresponds to Docker's --memory-swap command-line switch.
cpuShares Attribute No Corresponds to Docker's --cpu-shares command-line switch.
cpuPeriod Attribute No Corresponds to Docker's --cpu-period command-line switch.
cpuSetCpus Attribute No Corresponds to Docker's --cpuset-cpus command-line switch.
cpuQuota Attribute No Corresponds to Docker's --cpu-quota command-line switch.
blkioWeight Attribute No Corresponds to Docker's --blkio-weight command-line switch.
cgroupParent Attribute No Corresponds to Docker's --cgroup-parent command-line switch.
dependency Element No (0 - *) Use to configure dependency on one or more other processes.
portMapping Element No (0 - *) Corresponds to a Docker container port mapping.
volumeMapping Element No (0 - *) Corresponds to a Docker container volume mapping.
env Element No (0 - *) Allows specifiying one or more environment variables to pass to the Docker container.

dependency

The <dependency> element is meant to indicate that a given process depends on another (multiple such elements can be configured, thus indicating that a given process depends on multiple other ones).

NameTypeRequiredDescription
distribution or dist Attribute No The name of the distribution to which the other process belongs (if not specified, the current distribition is assumed).
process Attribute Yes The name of the process on which the current process depends.
version Attribute No The version of the distribution to which the other process belongs. If not specified, the version of the current distribution will be assumed.
profile Attribute No If not specified, the profile of the parent java or magnet element is used.

arg

The <arg> element is used to specify a JVM argument, such as -javaagent, -Xms, etc.

NameTypeRequiredDescription
value Attribute Yes Any value (the hyphen must be provided if one is required).

option

The <option> element corresponds to a JVM option, such as -cpt, -jar, etc.

NameTypeRequiredDescription
name Attribute Yes Provides the name of the option (the hyphen should be ommitted - it will be added at runtime).
value Attribute Yes Provides the value of the option (if none is expected, an empty string should be specified).

xoption

The <xoption> element can be used as a convenience, in place of the arg element, in the case of JVM arguments starting with -X, such as -Xms, -Xmx, etc. The present element allows ommitting the -X part in the option name (the part will inserted automatically at runtime.

NameTypeRequiredDescription
name Attribute Yes Provides the name of the option (the hyphen and X should be ommitted - they will be added at runtime).
value Attribute Yes Provides the value of the option (if none is expected, an empty string should be specified).

property

The <property> element corresponds to a JVM system property. At runtime, a command-line option of the form -D<name>=<value> will be generated for such an element.

NameTypeRequiredDescription
name Attribute Yes Provides the name of the system property (the hyphen should be ommitted - it will be added at runtime).
value Attribute Yes Provides the value of the option (if none is expected, an empty string should be specified).

env

The <env> element allows specifying one or more environment variables to pass to a Docker container.

NameTypeRequiredDescription
property Element No (0 - *) Allows specifying one or more properties that will be treated as an environment variable to pass to the container being started.

portMapping

The <portMapping> element allows mapping a host port to a container port.

NameTypeRequiredDescription
hostPort Attribute Yes Specifies the host port to map to the given container port.
containerPort Attribute Yes Specifies the container port to which the host port should be mapped.

volumeMapping

The <volumeMapping> is used to map a host volume to a container volume, along with the permissions to assign to the container on the said volume.

NameTypeRequiredDescription
hostVolume Attribute Yes The path of the host volume, to which to map the given container volume.
containerVolume Attribute Yes Specifies the container volume to which the host volume should be mapped.
permission Attribute No Specifies the permissions to grant to the container on the mapped volume. Defaults to rw (for read-write). Also supports ro for read-only.

Profiles

Corus supports the concept of “profile". A profile is simply a character string that identifies the “type" of execution of a process. To be more precise, imagine that you have a distribution (in the Corus sense) containing multiple process configurations corresponding to applications that are deployed in different environments, or used under different conditions. For example, you could deploy a distribution to a Corus server in your development environment, and deploy that same distribution in QA or pre-prod. By the same token, you could connect to some server simulator in a development environment, but to the real one in pre-prod or QA. A common problem is also the database, which could have different addresses across environments, and even be of a different brand (HSQLDB, Postgres) in these different environments.

The notion of profile is a simple way to work around the configuration problems that arise when working in different environments or using applications under different conditions. Based on the profile (passed around as a string, remember...) you could use different configuration files.

Therefore, Corus makes no assumption with regards to the profile; it just passes it to executed processes as a system property (the corus.process.profile system property). In addition, the Magnet starter (used by Corus to start Magnet processes) passes the profile to the JVM through the magnet.profile.name system property (this is because Magnet also supports the notion of profile and uses that system property to identify the current profile – having a look at the Magnet web site will enlighten you).

So from your application, you only need "interpreting" that system property (i.e.: implement code that acts based on the value of that system property).

Process Properties

When executing a process, Corus passes to it process properties (in fact, JVM system properties as supported in Java). Theses properties consist of all the ones specified as part of:

  • The process configuration in the Corus descriptor (corus.xml);
  • the corus_process.properties file under $CORUS_HOME/config;
  • the process properties stored in the Corus server, which can be administered through the command line – see the Corus Properties section further below.

In addition, Corus will pass the following properties to new processes:

Name Description Value
corus.server.host The address of the Corus server that started the process. A character string correspond to an IP address.
corus.server.host.name The host name of the Corus server that started the process. The name of the host on which Corus is running
corus.server.port The port of the Corus server that started the process. A port.
corus.server.domain The name of the domain of the Corus server that started the process. A domain name.
corus.distribution.name The name of the distribution to which the process corresponds. A character string correspond to the distribution name.
corus.process.id The unique identifier of the process that was started. A character string correspond to the Corus process identifier.
corus.process.name The name of the process that was started. A character string correspond to the process name
corus.process.dir The directory of the process. An absolute path, corresponding to $CORUS_HOME/deploy/<instance_dir> /<distribution_name>/<distribution_version> /process/<process_id>
corus.process.poll.interval The interval at which the process is expected to poll its Corus server. A given number of seconds.
corus.process.status.interval The interval at which the process is expected to send its status to its Corus server. A given number of seconds.
corus.process.profile The name of the profile under which the process was started. A character string corresponding to the process' profile name.
user.dir The “common" directory of all processes of the distribution of which the process is part. An absolute path, corresponding to $CORUS_HOME/deploy /<instance_dir>/<distribution_name> /<distribution_version>/common

Process Pre-Execution

It is possible to have Corus commands executed prior to the execution of a process. Commands executed in this manner are processed by a command interpreter embedded within the Corus instance: their execution is meant to be strictly in-JVM, the -cluster option is not supported in their case; and some commands make no sense in embedded mode, their use having potentially unpredictable effects (such as deploy, for example).

Typical Usage

"Pre-executable" commands (the same ones that are typically typed in the Corus CLI) are specified in the preExec element of the Corus descriptor (which is itself under the process element). A preExec element can have any number of cmd elements as children. The snippet below illustrates such an element.

<process ...>
  <preExec>
    <cmd>conf export -f ${corus.process.dir}/conf/app.properties</cmd>
  </preExec>
  <java ... libDirs="${corus.process.dir}/conf/;lib">
     ...
  </java>
</process>

In the above, prior to execution, Corus process properties are exported to the given file (${corus.process.dir}/conf/app.properties). Then, in the libDirs attribute of the java element, the directory containing that file is inserted in the classpath (note that the ending with a forward-slash is required if the whole directory is to be included in the classpath. If the directory does not end with a forward-slash, Corus will look for .jar files in that directory, and these will be added to the classpath – the Corus Descriptor section, further above, has a note to that effect).

The intent above is to make process properties available to the application being deployed, which can then load the properties file through its classloader. It is recommended to export properties in a process-specific directory, so that different processes do not overwrite each other's properties. The best guarantee then is to store such files under the folder corresponding to the one created specifically for the process – to which the corus.process.dir property is mapped.

Using conf merge

A more advanced use could be the following:

<process ...>
  <preExec>
    <cmd>conf merge -r -b ${user.dir}/conf/apps.properties -f ${corus.process.dir}/conf/app.properties</cmd>
  </preExec>
  <java ... libDirs="${corus.process.dir}/conf/;lib">
     ...
  </java>
</process>

The above uses the conf merge command, which proceeds as follows:

  1. Loads the properties specified by the -b option (the properties are considered the "base").
  2. Adds the Corus process properties to properties thus loaded in memory (the process properties will override any identically-named properties in the base properties).
  3. Saves the resulting properties to the file (know as the "target") specified by the -f option.
  4. The -r option triggers the replacement of variables in the propertie loaded from the base file, using the process properties (in a bit more, as explained just below) for looking up their corresponding value. Meaning: given such a property in the base properties: app.environment=${env}, and the following process property: env=qa, then the resulting property will be app.environment=qa.

The process properties are used for variable values, but the properties passed to the process by Corus (corus.process.id, corus.process.dir, etc.) and Corus' own system properties as well. The lookup order is as follows:

  1. The properties that are dynamically created and passed to the process by Corus (corus.process.id...).
  2. The process properties stored in Corus.
  3. Corus' own system properties.

If some variables cannot be replaced (due to having no match in terms of their name in neither of the above-define properties), then they are left as is and will be saved in this manner in the resulting target file.

The same variable resolution rules apply when processing the command-lines specified by the cmd element in the descriptor.

Invoking Ant

Ant scripts may be invoked from the CLI, through the ant command. The following illustrates such an invocation (type man ant in the CLI for more details):

<process ...>
  <preExec>
    <cmd>ant -f ${user.dir}/scripts/copy_files.xml</cmd>
  </preExec>
  <java ... libDirs="${corus.process.dir}/conf/;lib">
     ...
  </java>
</process>

Say the content of copy_files.xml is as follows:

<project name="copy_files" default="run"> 
<target name="run"> 
  <mkdir dir="${corus.process.dir}/conf" />
  <copy todir="${corus.process.dir}/conf">
    <fileset dir="${user.dir}/${corus.process.profile}">
      <include name="**/*.properties"/>
    </fileset>
  </copy>
</target> 
</project>

In the above example, a set of Java properties files is copied from a sub-directory under the root of the current Corus distribution. For the sake of the example, that directory is made to correspond to the current process' profile (note that the root directory of the Corus distribution is available through the user.dir property). The destination directory here corresponds to the directory assigned by Corus to the process (through the corus.process.dir property).

This example illustrates that an Ant script invoked this way has access to all the variables passed to a process at runtime.

Process Dependencies

It may occur that some processes depend on other processes, and that these processes themselves depend on other ones, and so on. The Corus descriptor supports declaring such dependencies, through dependency elements.

When such dependencies are detected, Corus will process them so that processes are started in the appropriate order. This will startup multiple processes consecutively, in such a case the corus.process.start-interval property should be set properly in order to not bottleneck the CPU while applications complete their initialization phase.

The excerpt below (corresponding to a Corus descriptor) shows a dependencies can be declared on a per-process basis:

<distribution  
xmlns="http://www.sapia-oss.org/xsd/corus/distribution-5.0.xsd" 
	name="demo" version="1.0">
  <process name="echoServer" 
           maxKillRetry="3" 
           shutdownTimeout="30000" 
           invoke="true">
    <java mainClass="org.sapia.corus.examples.EchoServer" profile="test" vmType="server">
      <xoption name="ms" value="16M" />
      <dependency dist="demo" version="1.0" process="configurator" />    
    </java>
  </process>  
</distribution>

Dependencies (processes on which other processes depend) are executed first, in the order in which they are declared: if process A depends on process B, and process B depends on process C, the execution order will be as such: C, B, A.

Execution Configurations

It may become tedious to start multiple processes manually. The process dependency feature may help work around this hassle, but what if some processes have no other processes that depend on them ? In such a case, if not started explicitly, they will never execute.

Corus allows defining so-called “execution configuration". These configurations consist of XML files that are uploaded (through the deploy command) to a Corus server (or to multiple servers, in cluster mode).

An example process configuration is given below:

<exec xmlns="http://www.sapia-oss.org/xsd/corus/exec-3.0.xsd" 	  
  name="test" startOnBoot="true"> 
  <process dist="web" version="1.0" name="httpServer" profile="prod" instances="1"/> 
</exec>

The <exec> element takes the following attributes:

  • name (mandatory): an arbitrary name used to refer to the configuration later on.
  • startOnBoot (defaults to false): indicates if the configured processes are to be started when the Corus server itself boots up.

In addition, the exec element contains one to many process elements, each indicating which processes should be started. Each process element takes the following attributes:

  • dist (mandatory): the distribution to which the process belongs.
  • version (mandatory): the distribution's version.
  • name (mandatory): the name of the process.
  • profile (mandatory): the profile under which to start the process.
  • instances (defaults to 1): the number of process instances that should be started.

Execution configurations can be deployed, listed, undeployed, and “executed", all through the command line. The following shows example commands – see the command line help of the appropriate command for more information.

1) To deploy

deploy -e myTestConfig.xml

2) To list the currently deployed execution configurations

ls -e

3) To undeploy

undeploy -e test

4) To execute

exec -e test

The processes corresponding to an execution configuration are treated the same way as if they had been started manually: process dependencies are resolved, and processed are started in the appropriate order. Thus, if all dependencies are declared appropriately, there is no need to specify processes on which other process depends as part of execution configurations: these will be started through the normal dependency resolution mechanisms.