OpenShift cartridges provide the necessary command and control for the functionality of software that is running users' applications. OpenShift currently has many language cartridges (JBoss EAP, JBoss EWS, PHP, Ruby, Rails, etc.) as well as many DB cartridges (Postgres, Mysql, Mongo, etc.). Before writing your own cartridge, you should search the current list of Red Hat and OpenShift Community provided cartridges.

Cartridge configuration and setup is convention based, with an emphasis on minimizing external dependencies in your cartridge code.

1. Hierarchy

In some cases, multiple cartridges can be co-located or combined in the same gear. To support these scenarios, cartridges enforce a hierarchy that must consist of a single, primary cartridge and any number of embedded cartridges. The primary cartridge controls the build lifecycle, responds to scaling events and is the cartridge responsible for providing some external network accessibility. embedded cartridges play a supporting role to the primary cartridge, adding capabilities in a more limited fashion. One example of a primary and embedded cartridge relationship is between the jenkins cartridge and the jenkins-client cartridge. The jenkins cartridge provides a fully functional Jenkins service that can be accessed via a web browser. The jenkins-client, on the other hand, needs to be embedded with an existing web application as its role is to simply offload builds to an existing Jenkins service. The client by itself provides no value without being combined with an existing primary cartridge.

2. Cartridge Directory Structure

This is an example structure to which your cartridge is expected to conform when written to disk. Failure to meet these expectations will cause your cartridge to not function when either installed or used on OpenShift. You may have additional directories or files as required to meet the needs of the software you are packaging and the application developers using your cartridge.

[cartridge name]
 +- bin                        (required) (1)
 |  +- setup                   (optional) (2)
 |  +- install                 (optional)
 |  +- post_install            (optional)
 |  +- teardown                (optional)
 |  +- control                 (required)
 |- hooks                      (optional)
 |  +- set-db-connection-info  (discretionary) (3)
 +- versions                   (discretionary)
 |  +- `software version`
 |  |  +- bin
 |  |     +- ...
 |  |  +- data
 |  |     +- template          (optional)
 |  |        +- .openshift
 |  |        |   +- ...
 |  |        +- ... (directory/file tree)
 |  |     +- template.git       (discretionary)
 |  |        +- ... (git bare repo)
 |  +- ...
 +- env                        (required)
 |  +- *.erb
 +- template                   (optional)
 |  +- ... (directory/file tree)
 +  template.git               (discretionary)
 +  +- ... (bare git repository)
 +- usr                        (optional)
 |  +- ...
 +- metadata                   (required)
 |  +- manifest.yml            (required)
 |  +- managed_files.yml       (optional)
 +- conf.d                     (discretionary)
 |  +- openshift.conf.erb
 +- conf                       (discretionary)
 |  +- magic
1 required items must exist for minimal OpenShift support of your cartridge
2 optional exist to support additional functionality
3 discretionary should be considered best practices for your cartridge and work. E.g., conf.d is the usual name for where a web framework would install its httpd configuration.

To support multiple software versions within one cartridge, you may create symlinks between the bin/control and the versions/{software version}/bin/control file. Or, you may choose to use the bin/control file as a shim to call the correct versioned control file.

When creating an instance of your cartridge for use by a gear, OpenShift will copy the files, links, and directories from the cartridge library with the exclusion of the usr directory. The usr directory will be symlinked into the gear’s cartridge instance. This allows for the sharing of libraries and other data across all cartridge instances.

Later (see Cartridge Locking) we’ll describe how, as the cartridge author, you can customize a cartridge instance.

3. Cartridge Metadata

The manifest.yml file is used by OpenShift to determine what features your cartridge requires and in turn publishes. OpenShift also uses fields in the manifest.yml to determine what data to present to the cartridge user about your cartridge.

An example manifest.yml file:

Name: PHP
Cartridge-Short-Name: PHP
Cartridge-Version: '1.0.1'
Compatible-Versions:
  - '1.0.1'
Cartridge-Vendor: redhat
Display-Name: PHP 5.3
Description: "PHP is a general-purpose server-side scripting language..."
Version: '5.3'
Versions:
  - '5.3'
License: "The PHP License, version 3.0"
License-Url: http://www.php.net/license/3_0.txt
Vendor: PHP Group
Categories:
  - service
  - php
  - web_framework
Website: http://www.php.net
Help-Topics:
  "Developer Center": https://www.openshift.com/developers
Cart-Data:
  - Key: OPENSHIFT_...
    Type: environment
    Description: "How environment variable should be used"
Provides:
  - php-5.3
  - "php"
Publishes:
  get-php-ini:
    Type: "FILESYSTEM:php-ini"
  publish-http-url:
    Type: "NET_TCP:httpd-proxy-info"
  publish-gear-endpoint:
    Type: "NET_TCP:gear-endpoint-info"
Subscribes:
  set-db-connection-info:
    Type: "NET_TCP:db:connection-info"
    Required: false
  set-nosql-db-connection-info:
    Type: "NET_TCP:nosqldb:connection-info"
    Required: false
  set-mysql-connection-info:
    Type: "NET_TCP:db:mysql"
    Required : false
  set-postgres-connection-info:
    Type: "NET_TCP:db:postgres"
    Required : false
  set-doc-url:
    Type: "STRING:urlpath"
    Required : false
Scaling:
  Min: 1
  Max: -1
Group-Overrides:
  - components:
    - php-5.3
    - web_proxy
Endpoints:
  - Private-IP-Name:   IP1
    Private-Port-Name: HTTP_PORT
    Private-Port:      8080
    Public-Port-Name:  PROXY_HTTP_PORT
    Mappings:
      - Frontend:      "/front"
        Backend:       "/back"
Additional-Control-Actions:
  - threaddump

3.1. Cartridge-Short-Name

OpenShift creates a number of environment variables for you when installing your cartridge. This shortened name is used when creating those variables. For example, using the example manifest, the following environment variables would be created:

OPENSHIFT_PHP_DIR
OPENSHIFT_PHP_IP
OPENSHIFT_PHP_PORT
OPENSHIFT_PHP_PROXY_PORT

3.2. Cartridge-Version

The Cartridge-Version element is a version number identifying a release of your cartridge to OpenShift. The value follows the format:

<number>[.<number>[.<number>[...]]]

When you publish new versions of your cartridge to OpenShift, this number will be used to determine what is necessary to upgrade the application developer’s application. YAML will assume number.number is a float; be sure to enclose it in quotes so it is read as a string.

3.3. Compatible-Versions

Compatible-Versions is a list of past cartridge versions that are compatible with this version. To be compatible with a previous version, the code changes you made in this version do not require the cartridge to be restarted or the application developer’s application to be restarted.

Compatible-Versions: ['1.0.1']

By not requiring a restart, you improve the application user’s experience since no downtime will be incurred from your changes. If the cartridge’s current version is not in the list when upgraded, the cartridge will be stopped, the new code will be installed, setup will be run, and the cartridge started.

Today this is a simple list and string matching is used to determine compatible versions. If this list proves to be unmanageable, future versions of OpenShift may implement maven dependency range style checking.

3.4. Cartridge-Vendor

The Cartridge-Vendor element is used to differentiate cartridges when installed in the system. As an individual, you should use the same unique value for all your cartridges to identify yourself; otherwise, use your company name.

3.5. Version

The Version element is the default or only version of the software packaged by this cartridge.

Version: '5.3'

3.6. Versions

Versions is the list of the versions of the software packaged by this cartridge.

Versions: ['5.3']

3.7. Categories

Categories represent a list of classifications for a given cartridge. Categories are broken into two distinct groups:

1 system categories have special meaning to the platform and influence system behavior.
2 descriptive categories are arbitrary classifications used to improve searching for cartridges in the web console and client tools.

3.7.1. System Categories

system categories consist of the following reserved terms:

  • web_framework

  • web_proxy

  • service

  • plugin

  • embedded

  • domain_scope

Web Framework Category

The web_framework category is used to describe a primary cartridge that accepts inbound HTTP and HTTPS as well as WebSocket requests. An application can have a single cartridge with the web_framework category. Lastly, when using a web_framework category, SSL termination occurs at the platform layer, before the cartridge interaction takes place and the original inbound protocol is passed using the X-Forwarded-Proto header to the cartridge.

Web Proxy Category

The web_proxy category is used to describe a cartridge that is responsible for routing web traffic to the application’s gears. If a scalable application is created with a cartridge that has the web_framework category, a web_proxy cartridge is also added to it to enable auto-scaling. Subsequently, whenever the web_framework cartridge needs to scale beyond a single gear, the web_proxy cartridge will automatically route to the end point described by the Public-Port-Name with a value of PROXY_PORT. The web_proxy will also be automatically updated with routing rules to address the new gears over HTTP as they are added. An application can have a single cartridge with the web_proxy category.

Service Category

The service category is used to describe a primary cartridge that is not necessarily HTTP-based. This means that the cartridge can scale independently but is not necessarily addressable outside of the platform. Because of this, when creating an application in OpenShift, there is a restriction that at least one web_framework cartridge be present in the application so that the DNS registration for the application contains at least one well known addressable endpoint for the application over HTTP. However, in many cases, an application might need to consist of a web_framework cartridge and other service cartridges such as MySQL. By using the category of service for a cartridge like MySQL, it will install the cartridge on separate gears from the web_framework cartridge and allow it to scale independently as well.

Embedded Category

The embedded category is used to describe whether a cartridge can be co-located with a primary cartridge. It is relevant only in case of non-scalable applications. This category allows the cartridge to always be co-located or installed with any other primary cartridge. An example of this would be the Jenkins client cartridge which can always be combined with any web application cartridge to offload the builds to a Jenkins service.

Plugin Category

The plugin category is the equivalent of the embedded category for scalable applications. A plugin cartridge is designed to be co-located with another cartridge in a scalable application. It relies upon Group-Overrides being defined to determine which cartridge it should co-locate with. An example of this is the cron cartridge that is a plugin and specifies through Group-Overrides that it needs to co-locate with the web_framework cartridge.

Domain Scope Category

The domain_scope category describes a cartridge that can only have a single instance within the domain. For example, the jenkins server cartridge has the domain_scope category to ensure that there is a single jenkins server application within the entire domain. All other applications embed the jenkins client cartridge to enable builds that are handled by the jenkins server.

3.7.2. Descriptive Categories

The descriptive categories are primarily used in the OpenShift web console and the rhc client tools to improve the user experience. In the web console, descriptive categories show up as tags which allow users to search and quickly filter the available cartridges. When using the client tools, these categories are used to apply matching logic on cartridge related operations. For example, if a user ran:

rhc add-cartridge php

The descriptive categories will be searched in addition to the names of the cartridges.

3.8. Group-Overrides

Group-Overrides is applicable in case of scalable applications. By default, for scalable applications, each cartridge resides on its own gears within its own group instance. However, sometimes it is required/preferred to have two cartridges be located together on the same set of gears. Group-Overrides lets you do this. For example, if you create a cron cartridge, and you want it to colocate with the web_framework cartridge, you can specify:

Group-Overrides:
- components:
  - web_framework
  - cron

Similarly, if you would like the web_framework cartridge to be located along with the web_proxy cartridge, then you can specify:

Group-Overrides:
- components:
  - web_proxy
  - web_framework

3.9. Scaling

This section defines the scaling parameters for a cartridge and is applicable when the cartridge is added to a scalable application. The Min and Max parameters define the scaling limits for the cartridge. Setting both the Min and Max equal to 1 indicates that the cartridge cannot scale. On the other hand, if Max is specified as -1, then there is no maximum scaling limit and the cartridge can scale up as long as the user’s gear limit is not exceeded. The scaling limits are enforced during auto-scaling as well as when setting the cartridge scaling limits manually.

When using Group-Overrides to co-locate two or more cartridges that can all scale, it is important to ensure that their scaling limits match. There are occasions, however, when this is not desirable, such as in the case of a web_proxy that is co-located with the web_framework. In this case it doesn’t make sense to have the web_proxy cartridge be present on every gear where the web_framework gear is located. The Multiplier parameter enables this by allowing the cartridge to be placed on fewer gears within the group instance. For example, if the Multiplier is set to 3, then every third gear within the group instance will have the cartridge installed. Similarly, if the Multiplier is set to 1, then all gears within the group instance will have the cartridge installed on them.

3.10. Provides

Provides is a list of features or functionalities that the cartridge provides to the application. For example, the php-5.3 cartridge provides php-5.3 as well as, more generically, php.

Provides:
  - php-5.3
  - php

3.11. Requires

Requires is a list of features or functionalities that this cartridge depends upon for its operation. These dependencies would be matched against other available cartridges to find the ones that provide them. For example, a framework cartridge like Rails could require a language/runtime cartridge like Ruby. In this case, if an application is being created with the Rails cartridge, based on the Requires specification, a cartridge that provides Ruby would be automatically added to the application.

The functionality specified in the Requires section must identify a single cartridge. In case multiple cartridges are matched, then the cartridge cannot be added and an error is raised to the user.

3.12. Source-Url

Source-Url is used when you self-distribute your cartridges. They are downloaded at the time the application is created.

Table 1. non-Git URL support
Scheme Method Expected Inputs

https

GET

extensions zip, tar, tag.gz, tgz

http

GET

extensions zip, tar, tag.gz, tgz

file

file copy

cartridge directory tree expected

All Git schemes are supported. The cartridge source will be cloned from the given repository.

Source-Url: https://github.com/example/killer-cartridge.git
Source-Url: git://github.com/chrisk/fakeweb.git
Source-Url: https://www.example.com/killer-cartridge.zip
Source-Url: https://github.com/example/killer-cartridge/archive/master.zip

3.13. Source-Md5

If Source-Md5 is provided and a non-Git scheme is used for downloading your cartridge, OpenShift will verify the downloaded file against this MD5 digest.

Source-Md5: 835ed97b00a61f0dae2e2b7a75c672db

3.14. Additional-Control-Actions

The Additional-Control-Actions element is a list of optional actions supported by your cartridge. threaddump is an example of one such action. OpenShift will only call optional actions if they are included in this element. Supported optional actions:

threaddump

3.15. Endpoints

See below.

4. Managed Files

The metadata/managed_files.yml file provides an array of files or strings that are managed or used during different stages of your cartridge lifecycle. The keys for the entries (such as locked_files) can be specified as either strings or Ruby symbols. For example:

locked_files:
- env/
- ~/.foorc
snapshot_exclusions:
- mydir/*
restore_transforms:
- s|${OPENSHIFT_GEAR_NAME}/data|app-root/data|
processed_templates:
- '**/*.erb'
setup_rewritten:
- conf/*

4.1. Entry Values

Most entries will use file patterns. These patterns are treated like Shell globs. Any entry that contains one or more * will be processed by Dir.glob (with the File::FNM_DOTMATCH flag). Any entry that ends in a / is treated as a directory; otherwise it will be treated as a single file.

Any lines starting with ~/ will be anchored at the gear directory; otherwise, they will be anchored to your cartridge directory.

4.2. Strings

Some entries allow for string values in the arrays. In this case, the values will be directly returned without any modification.

4.3. Allowed Entries

Currently, the following entries are supported:

Table 2. Supported entries
Entry Type Usage

locked_files

File Pattern

snapshot_exclusions

File Pattern

restore_transforms

Strings

setup_rewritten

File Pattern

processed_templates

File Pattern

5. Cartridge Locking

Cartridge instances within a gear will be either locked or unlocked at any given time. Locking a cartridge allows the cartridge scripts to have additional access to the gear’s files and directories. Other scripts and hooks written by the application developer will not be able to override decisions you make as the cartridge author.

The lock state is controlled by OpenShift. Cartridges are locked and unlocked at various points in the cartridge lifecycle.

If you fail to provide a locked_files entry in metadata/managed_files.yml or the file is empty, your cartridge will remain always unlocked. For very simple cartridges, this may be sufficient.

Cartridge file locking is not intended to be a security measure. It is a mechanism to help prevent application developers from inadvertently breaking their application by modifying files reserved for use by you, the cartridge author.

5.1. Lock Configuration

The metadata/managed_files.yml locked_files entry lists the files and directories, one per line, that will be provided to the cartridge author with read/write access while the cartridge is unlocked, but only read access to the application developer while the cartridge is locked.

Any non-existent files that are included in the list will be created before your setup script is called. Any missing parent directories will be created as needed. The list is anchored at the cartridge’s directory. An entry ending in slash is processed as a directory. Entries ending in asterisk are a list of files. Entries ending in any other character are considered files. OpenShift will not attempt to change files to directories or vice versa, and your cartridge may fail to operate if files are miscategorized and you depend on OpenShift to create them.

Example 1. Lock Configuration Example

Here is a locked_files entry for a PHP cartridge:

locked_files:
- ~/.pearrc (1)
- bin/ (2)
- conf/* (3)
1 The file ~/.pearrc will be created, if it does not exist, and be made editable by you.
2 The directory php/bin is locked but not the files it contains. While you can add files, both you and the application developer can edit any files contained.
3 The files in php/conf are locked but the directory itself is not, so you or the application developer can add files, but only you can edit them.

Directories like ~/.node-gyp and ~/.npm in nodejs are NOT candidates to be created in this manner as they require the application developer to have read and write access while the application is deploying and running. These directories would need to be created by the nodejs setup or install scripts.

5.2. Reserved Directories

The following list is reserved by OpenShift in the gear’s home directory:

  • ~/.ssh

  • ~/.sandbox

  • ~/.tmp

  • ~/.env

  • any non-hidden directory or file

You may create any hidden file or directory (one that starts with a period) not in the reserved list in the gear’s home directory while the cartridge is unlocked.

6. Template Directories for Language Cartridges

The template or template.git directory should provide an minimal example of an application written in the language/framework your cartridge is packaging. Your application should welcome the application developer to your cartridge and let them see that your cartridge has indeed been installed and operates. If you provide a template directory, OpenShift will transform it into a bare git repository for use by the application developer. If you provide a template.git directory, OpenShift will copy the directory for use by the application developer.

Your setup and install scripts should assume that template directories may be converted to template.git during the packaging of your cartridge for use by OpenShift. The PaaS operator may choose to convert all template directories to bare git repositories template.git to obtain the performance gain when adding your cartridge to a gear. One good workflow point to make this change is when your cartridge is packaged into an RPM.

A ruby 1.8 with Passenger support would have a public sub-directory and a config.ru file to define the web application.

+- template
|  +- config.ru
|  +- public
|  |  +- .gitignore
|  .openshift
|  +- markers
|  |- ...
A .gitignore file should be placed in empty directories to ensure they survive when the file tree is loaded into a git repository.

6.1. Marker Example Files

The sub-directory .openshift/markers may contain example files for the application developer. These files denote behavior you are expected to honor in your cartridge’s lifecycle. Current examples from a Ruby 1.8 cartridge include:

Table 3. Marker File Descriptions
Marker Action

force_clean_build

Remove and previously built artifacts (gems/maven artifacts/etc) before starting the next build. Note: Turning off auto deploy on your application and specify force_clean_build on a per deploy basis is generally preferred over this option.

hot_deploy

Perform the minimal restart to pick up code changes. Skips any non required packaging steps such as bundle installing gems. Note: Turning off auto deploy on your application and specify hot_deploy on a per deploy basis is generally preferred over this option.

disable_auto_scaling

Will prevent scalable applications from scaling up or down according to application load.

You may add additional markers to allow an application developer to control aspects of your cartridge.

6.2. Application Developer Action Hooks

The sub-directory .openshift/action_hooks will contain code the application developer wishes to be run during lifecycle changes. Examples would be:

pre_start_`cartridge name`
post_start_`cartridge name`
pre_stop_`cartridge name`
...

As a cartridge author you do not need to execute the default action_hooks. OpenShift will call them during lifecycle changes based on the actions given to the control script. If you wish to add additional hooks, you are expected to document them and you will need to run them explicitly in your control script.

7. Exposing Services / TCP Endpoints

Most cartridges provide a service by binding to one or many ports. Cartridges must explicitly declare which ports they will bind to, and provide meaningful variable names to describe the following:

  • Any IP addresses necessary for binding

  • The gear-local ports to which the cartridge services will bind

  • (Optional) Publicly proxied ports which expose gear-local ports for use by the application’s users or intra-gear. These endpoint ports are only created when the application is scalable.

In addition to IP and port definitions, Endpoints are where front-end httpd mappings for your cartridge are declared to route traffic from the outside world to your cartridge’s services.

These declarations represent Endpoints, and are defined in the cartridge manifest.yml in the Endpoints section using the following format:

Endpoints:
  - Private-IP-Name:   <name of IP variable>
    Private-Port-Name: <name of port variable>
    Private-Port:      <port number>
    Public-Port-Name:  <name of public port variable>
    Mappings:
      - Frontend:      "<frontend path>"
        Backend:       "<backend path>"
        Options:       { ... }
      - <...>
  - <...>

During cartridge installation within a gear, IP addresses will be automatically allocated and assigned to each distinct IP variable name, with the guarantee that the specified port will be bindable on the allocated address.

If an endpoint specifies a public port variable, a public port proxy mapping will be created using a random external port accessible via the gear’s DNS entry.

Each portion of the Endpoint definition becomes available via environment variables located within the gear and accessible to cartridge scripts and application code. The names of these variables are prefixed with OpenShift namespacing information in the follow the format:

OPENSHIFT_{Cartridge-Short-Name}_{name of IP variable} (1)
OPENSHIFT_{Cartridge-Short-Name}_{name of port variable} (2)
OPENSHIFT_{Cartridge-Short-Name}_{name of public port variable} (3)
1 Assigned internal IP
2 Endpoint-specified port
3 Assigned external port

Cartridge-Short-Name is the Cartridge-Short-Name element from the cartridge manifest file.

If an Endpoint specifies a Mappings section, each mapping entry will be used to create a frontend httpd route to your cartridge using the provided options. The Frontend key represents a frontend path element to be connected to a backend URI specified by the Backend key. The optional Options hash for a mapping allows the route to be configured in a variety of ways:

  • websocket - Enable web sockets on a particular path

  • gone - Mark the path as gone (uri is ignored)

  • forbidden - Mark the path as forbidden (uri is ignored)

  • noproxy - Mark the path as not proxied (uri is ignored)

  • redirect - Use redirection to uri instead of proxy (uri must be a path)

  • file - Ignore request and load file path contained in uri (must be path)

  • tohttps - Redirect request to https and use the path contained in the uri (must be path)

While more than one option is allowed, the above options conflict with each other.

Example 2. "CustomCart" Endpoint Configuration

Given a cartridge named CustomCart and the following entry in manifest.yml:

Name: CustomCart
Cartridge-Short-Name: CUSTOMCART
# ...
Endpoints:
  - Private-IP-Name:   HTTP_IP
    Private-Port-Name: WEB_PORT
    Private-Port:      8080
    Public-Port-Name:  WEB_PROXY_PORT
    Mappings:
      - Frontend:      "/web_front"
        Backend:       "/web_back"
      - Frontend:      "/socket_front"
        Backend:       "/socket_back"
        Options:       { "websocket": true }

  - Private-IP-Name:   HTTP_IP
    Private-Port-Name: ADMIN_PORT
    Private-Port:      9000
    Public-Port-Name:  ADMIN_PROXY_PORT
    Mappings:
      - Frontend:      "/admin_front"
      - Backend:       "/admin_back"

  - Private-IP-Name:   INTERNAL_SERVICE_IP
    Private-Port-Name: 5544
    Public-Port-Name:  INTERNAL_SERVICE_PORT

The following environment variables will be generated:

# Internal IP/port allocations
OPENSHIFT_CUSTOMCART_HTTP_IP=<assigned internal IP 1>
OPENSHIFT_CUSTOMCART_WEB_PORT=8080
OPENSHIFT_CUSTOMCART_ADMIN_PORT=9000
OPENSHIFT_CUSTOMCART_INTERNAL_SERVICE_IP=<assigned internal IP 2>
OPENSHIFT_CUSTOMCART_INTERNAL_SERVICE_PORT=5544

# Public proxy port mappings
OPENSHIFT_CUSTOMCART_WEB_PROXY_PORT=<assigned public port 1>
OPENSHIFT_CUSTOMCART_ADMIN_PROXY_PORT=<assigned public port 2>

In the above example, the public proxy port mappings are as follows:

<assigned external IP>:<assigned public port 1> => OPENSHIFT_CUSTOMCART_HTTP_IP:OPENSHIFT_CUSTOMCART_WEB_PORT
<assigned external IP>:<assigned public port 2> => OPENSHIFT_CUSTOMCART_HTTP_IP:OPENSHIFT_CUSTOMCART_ADMIN_PORT

And finally, the following frontend httpd routes will be created:

http://<app dns>/web_front    => http://OPENSHIFT_CUSTOMCART_HTTP_IP:8080/web_back
http://<app dns>/socket_front => http://OPENSHIFT_CUSTOMCART_HTTP_IP:8080/socket_back
http://<app dns>/admin_front  => http://OPENSHIFT_CUSTOMCART_HTTP_IP:9000/admin_back

8. Cartridge Scripts

How you implement the cartridge scripts in the bin directory is up to you as the author. For easily configured software where your cartridge is just installing one version, these scripts may include all the necessary code. For complex configurations or multi-version support, you may choose to write these scripts as shim code to setup the necessary environment before calling additional scripts you write. Or, you may choose to create symlinks from these names to a name of your choosing. Your API is the scripts and their associated actions.

8.1. Notes on Script Execution

The scripts will be run directly from the home directory of the cartridge. They need to have the executable bit turned on, and they should have UNIX-friendly line endings (\n), not DOS ones (\r\n).

To ensure this, consider setting the following git options (just once) so that the files have correct line endings in the git repository:

git config --global core.autocrlf input # use `true` on Windows
git config --global core.safecrlf true

To ensure that the excutable bit is on, on UNIX-like systems, run:

chmod +x bin/*
On Windows, you can achieve this by running git update-index --chmod=+x bin/* in the cartridge directory.

8.2. Mandatory Script

A cartridge must implement the following script:

Script Name Usage

control

Command cartridge to report or change state

8.3. Optional Scripts

A cartridge may implement the following scripts:

Script Name Usage

setup

Prepare this instance of cartridge to be operational for the initial install and each incompatible upgrade

install

Prepare this instance of cartridge to be operational for the initial install

post_install

An opportunity for configuration after the cartridge has been started for the initial install

teardown

Prepare this instance of cartridge to be removed

8.4. Exit Status Codes

OpenShift follows the convention that your scripts should return zero for success and non-zero for failure. Additionally, OpenShift supports special handling of the following non-zero exit codes:

Exit Code Usage

127

TODO

131

TODO

These exit status codes will allow OpenShift to refine its behavior when returning HTTP status codes for the REST API, whether an internal operation can continue or should aborted, etc. Should your script return a value not included in this table, OpenShift will assume the problem is fatal to your cartridge.

8.5. OpenShift Cartridge Installation Process

The initial install process for a cartridge is as follows:

  1. The new cartridge is overlaid in the gear.

  2. The cartridge Environment Variables are populated.

  3. The cartridge directory is secured.

  4. The private Endpoints are created.

  5. The cartridge directory is unlocked.

  6. If the cartridge provides a setup script, that script is executed.

  7. The erb templates for the cartridge are processed.

  8. If the cartridge provides an install script, that script is executed.

  9. The gear git repository is populated.

  10. The cartridge directory is locked.

  11. The public Endpoints are creted.

  12. The start script is run.

  13. The frontend is connected.

  14. If the cartridge provides a post_install script, that script is executed.

8.6. ERB Processing

In order to provide flexible configuration and environment variables, you may provide some values as ERB templates.

Your templates will be rendered at safe_level 2. and are processed in 2 passes.

  1. The first pass processes any entries in your env directory. This pass happens before bin/setup is called and is mandatory.

  2. The second pass processes any entries specified in the processed_templates entry of metadata/managed_files.yml. This pass happens after bin/setup but before bin/install. This allows bin/setup to create or modify ERB templates if needed. It also allows for bin/install to use these values or processed files.

Example 3. ERB Examples
  1. Given env/OPENSHIFT_MONGODB_DB_LOG_DIR.erb containing:

<%= ENV['OPENSHIFT_HOMEDIR'] + "/mongodb/log/" %>

becomes env/OPENSHIFT_MONGODB_DB_LOG_DIR containing:

/var/lib/openshift/aa9e0f66e6451791f86904eef0939e/mongodb/log/
  1. Given conf/php.ini.erb containing:

upload_tmp_dir = "<%= ENV['OPENSHIFT_HOMEDIR'] %>php/tmp/"   session.save_path = "<%= ENV['OPENSHIFT_HOMEDIR'] %>php/sessions/"

becomes conf/php.ini containing:

upload_tmp_dir = "/var/lib/openshift/aa9e0f66e6451791f86904eef0939e/php/tmp/"   session.save_path = "/var/lib/openshift/aa9e0f66e6451791f86904eef0939e/php/sessions/"

Other candidates for templates are httpd configuration files for includes, configuring databases to store persistent data in OPENSHIFT_DATA_DIR, and setting the application name in the pom.xml file.

8.7. bin/setup

Synopsis

setup [--version <version>]

Options

--version <version>: Selects which version of cartridge to install. If no version is provided, the version denoted by the Version element from manifest.yml will be installed.

Description

The setup script is responsible for creating and/or configuring the files that were copied from the cartridge repository into the gear’s directory. Setup must also be reentrant and will be called on every incompatible upgrade. Any logic you want to occur only once should be added to install.

Any files created during setup should be added to setup_rewritten section of metadata/managed_files.yml. These files will be deleted prior to setup being run during upgrades.

If you have used ERB templates for software configuration those files will be processed for environment variable substitution after setup is run.

Lock Context

unlocked

8.8. bin/install

Synopsis

install [--version <version>]

Options

--version <version>: Selects which version of cartridge to install. If no version is provided, the version denoted by the Version element from manifest.yml will be installed.

Description

The install script is responsible for creating and/or configuring the files that were copied from the cartridge repository into the gear’s directory. install will only be called on the initial install of a cartridge.

  • Any one-time operations, such as generating passwords, creating ssh keys, or adding environment variables, should occur in install.

  • Additionally, any client results/messages should also be reported in install rather than setup.

  • install may substitute a version dependent of the template or template.git directories.

Lock Context

unlocked

8.9. bin/post_install

Synopsis

post_install [--version <version>]

Options

--version <version>: Selects which version of cartridge to install. If no version is provided, the version denoted by the Version element from manifest.yml will be installed.

Description

The post_install script is an opportunity to configure your cartridge after the cartridge has been started and is only called for the initial install of the cartridge.

Lock Context

locked

8.10. bin/teardown

Synopsis

teardown

Options

None

Description

The teardown script prepares the gear for the cartridge to be removed. This script will not be called when the gear is destroyed. The teardown script is only run when a cartridge is to be removed from the gear. The gear is expected to continue to operate minus the functionality of your cartridge cartridge.

Lock Context

unlocked

8.11. bin/control

Synopsis

control <action>

Options

action: which operation the cartridge should perform.

Description

The control script allows OpenShift or user to control the state of the cartridge.

Lock Context

locked

Here is the list of operations your cartridge may be called to perform:

Operation Behavior

update-configuration, pre-build, build, deploy, or post-deploy

described in the OpenShift Builds section

start

Start the software your cartridge controls

stop

Stop the software your cartridge controls

status

Return an 0 exit status if your cartridge code is running

reload

Your cartridge and the packaged software needs to re-read their configuration information (this operation will only be called if your cartridge is running)

restart

Stop current process and start a new one for the code your cartridge packages

threaddump

If applicable, your cartridge should signal the packaged software to perform a thread dump

tidy

All unused resources should be released (it is at your discretion to determine what should be done; be frugal as on some systems resources may be very limited)

pre-snapshot

Prepare the cartridge for a snapshot, e.g. dump database to flat file

post-snapshot

Clean up the cartridge after snapshot, e.g. remove database dump file

pre-restore

Prepare the cartridge for restore

post-restore

Clean up the cartridge after being restored, load database with data from flat file

8.11.1. The tidy Action

Some possible tidy behaviors:

  • rm $OPENSHIFT_{Cartridge-Short_Name}_DIR/logs/log.[0-9]

  • cd $OPENSHIFT_REPO_DIR ; mvn clean

OpenShift has the following default tidy behaviors:

  • the Git repository will be garbage collected

  • all files will be removed from the /tmp directory

8.11.2. The status Action

For a number of reasons, the application developer will want to be able to query whether the software your cartridge packages is running and behaving as expected. A 0 exit status implies that the software is running correctly.

You may direct information to the application developer by writing to stdout. Errors may be return on stderr with a non-zero exit status.

OpenShift maintains the expected state of the gear/application in ~/app-root/runtime/.state. You may not use this to determine the status of the software you are packaging. That software may have crashed so you would be returning an invalid status if you used this file’s value. Future versions of OpenShift may combine the results from the status action and the value of the .state file to automatically restart failed applications. For completeness, see the following .state values:

Value Meaning

building

Application is currently being built

deploying

Application is currently being deployed

idle

Application has been shutdown because of no activity

new

Gear has been created, but no application has been installed

started

Application has been commanded to start

stopped

Application has been commanded to stop

8.12. Messaging to OpenShift from Cartridge

Your cartridge may provide one or more services that are consumed by multiple gears in one application. OpenShift provides the orchestration necessary for you to publish this service or services. Each message is written to stdout, one message per line.

  • ENV_VAR_ADD: <variable name>=<value>

  • CART_DATA: <variable name>=<value>

  • CART_PROPERTIES: <key>=<value>

  • APP_INFO: <value>

9. Custom HTTP Services

Your cartridge may expose services using the application’s URL by providing one or more snippets of Apache configuration code using ERB templates in the httpd.d directory. The httpd.d directory and its contents are optional. After OpenShift has run your setup script, it will render each ERB template and write the contents of the node’s httpd configuration.

An example of mongodb.conf.erb:

Alias /health <%= ENV['OPENSHIFT_HOMEDIR'] + "/mongodb/httpd.d/health.html" %>
Alias / <%= ENV['OPENSHIFT_HOMEDIR'] + "/mongodb/httpd.d/index.html" %>

10. Environment Variables

Environment variables are used to communicate information between this cartridge and others, as well as to OpenShift. The cartridge controlled variables are stored in the env directory and will be loaded after system-provided environment variables but before your code is called. OpenShift-provided environment variables will be loaded and available to be used for all cartridge entry points.

You cannot override system provided variables by creating new copies in your cartridge env directory. If you attempt to do so, when an application developer attempts to instantiate your cartridge the system will raise an exception and refuse to do so. With the exception of OPENSHIFT_SECRET_TOKEN, these variables may not be overridden using rhc env-set.

10.1. System Provided Variables (Read Only)

Name Value

HOME

Alias for OPENSHIFT_HOMEDIR

HISTFILE

Bash history file

OPENSHIFT_APP_DNS

The application’s fully qualified domain name that your cartridge is a part of

OPENSHIFT_APP_NAME

The validated user assigned name for the application (black list is system dependent)

OPENSHIFT_APP_UUID

OpenShift-assigned UUID for the application

OPENSHIFT_DATA_DIR

The directory where your cartridge may store data

OPENSHIFT_GEAR_DNS

The gear’s fully qualified domain name that your cartridge is a part of (may or may not be equal to OPENSHIFT_APP_DNS)

OPENSHIFT_GEAR_NAME

OpenShift-assigned name for the gear (may or may not be equal to OPENSHIFT_APP_NAME)

OPENSHIFT_GEAR_UUID

OpenShift-assigned UUID for the gear

OPENSHIFT_HOMEDIR

OpenShift-assigned directory for the gear

OPENSHIFT_REPO_DIR

The directory where the developer’s application is "archived" to and will be run from

OPENSHIFT_SECRET_TOKEN

A unique 128 character string that is unique to your application that may be used for authentication: joining clusters, initial passwords, etc. This variable may be overridden using rhc set-env.

OPENSHIFT_TMP_DIR

The directory where your cartridge may store temporary data

TMPDIR

Alias for OPENSHIFT_TMP_DIR

TMP

Alias for OPENSHIFT_TMP_DIR

10.2. System Provided Cartridge Variables (Read Only)

  • OPENSHIFT_{Cartridge-Short-Name}_DIR

  • OPENSHIFT_{Cartridge-Short-Name}_IDENT

  • OPENSHIFT_PRIMARY_CARTRIDGE_DIR

10.2.1. Examples of Cartridge Variables

These are variables provided to you for communicating to the application developer. You may add additional variables for your cartridge’s or the packaged software’s needs. You may provide these files in your cartridge’s env directory or choose to create them in your setup and install scripts.

  • OPENSHIFT_MYSQL_DB_HOST Backwards compatibility (ERB populate from OPENSHIFT_MYSQL_DB_IP)

  • OPENSHIFT_MYSQL_DB_IP

  • OPENSHIFT_MYSQL_DB_LOG_DIR

  • OPENSHIFT_MYSQL_DB_PASSWORD

  • OPENSHIFT_MYSQL_DB_PORT

  • OPENSHIFT_MYSQL_DB_SOCKET

  • OPENSHIFT_MYSQL_DB_URL

  • OPENSHIFT_MYSQL_DB_USERNAME

  • OPENSHIFT_PHP_LOG_DIR

  • OPENSHIFT_PHP_DIR

Some variables may be dictated by the software you are packaging:

  • JENKINS_URL

  • JENKINS_USERNAME

  • JENKINS_PASSWORD

Your environment variables should be prefixed with OPENSHIFT_{cartridge short name}_ to prevent overwriting other cartridge variables in the packaged software’s process environment space.

By convention, an environment variable whose value is a directory should have a name that ends in _DIR and the value should have a trailing slash. The software you are packaging may have environment variable requirements of its own, for example: JENKINS_URL; these would be added to your env directory or included in shim code in your bin scripts.

Cartridge-provided environment variables are not validated by the system. Your cartridge may fail to function if you write invalid data to these files.

You may provide ERB templates in the env directory (see above for details). ERB templates in the env directory will be processed before setup is called.

The PATH variable is set by OpenShift with the base being /etc/openshift/env/PATH. If you provide an OPENSHIFT_{Cartridge-Short-Name}_PATH_ELEMENT, OpenShift will include the value when building the PATH when your scripts are run or an application developer does an interactive log on.

11. Cartridge Events

Cartridges may need to act when another cartridge is added or removed from an application. OpenShift supports a simple publish/subscribe system which allows cartridges to communicate in the context of these events.

The Publishes and Subscribes sections of the cartridge manifest.yml are used to express the event support for a given cartridge.

11.1. Cartridge Event Publishing

Publish events are defined via the manifest.yml for the cartridge, in the following format:

Publishes:   <event name>:     Type: "<event type>"   ...

When a cartridge is added to an application, each entry in the Publishes section of the manifest is used to construct events dispatched to other cartridges in the application. For each publish entry, OpenShift will attempt to execute a script named hooks/<event name>:

hooks/<event name> <gear name> <namespace> <gear uuid>

All lines of output (on stdout) produced by the script will be joined by single spaces and used as the input to matching subscriber scripts. All cartridges which declare a subscription whose Type matches that of the publish event will be notified.

11.2. Cartridge Event Subscriptions

Subscriptions to events published by other carts are defined via the manifest.yml for the cartridge, in the following format:

Subscribes:   <event name>     Type: "<event type>"   ...

When a cartridge publish event is fired, the subscription entries in the Subscribes section whose Type matches that of the publish event will be processed. Subscriptions which have a Type that starts with ENV: are processed differently, as described below. For each matching subscription event, OpenShift will attempt to execute a script named hooks/<event name>:

hooks/<event name> <gear name> <namespace> <gear uuid> <publish output>

The format of the <publish output> input to the subscription script is defined by the implementation of the publisher script, and so the cartridge subscription script must have an awareness of the output format of the matching publish script.

11.2.1. ENV: Subscription Type

Subscription types that start with ENV: have special designation as environment variable subscriptions. For these subscriptions the event hook script hooks/<event name> is optional. If this script is not present or present but not executable, a specialized built-in event hook is used.

The built-in event hook imports environment variables from any matching Publishes sections of other cartridges added to the application in question. A typical example where this is useful would be setting up connection credentials in a web cartridge for a database add-on cartridge.

There are two forms of this subscription type: the wildcard type, which is usually what you want to use, and the targeted type.

Wildcard ENV:* Subscription Type

It’s often useful to pull in environment variables from all add-on cartridges within an application, particularly for web cartridges. For these instances, the ENV:* subscription type is provided. When a cartridge with this this subscription type is added to an application, all other cartridges in the application are scanned for ENV: type event publications. These are then processed automatically as detailed above.

The convention for adding the ENV:* subscription to a cartridge manifest is as follows:

Subscribes:  set-env:    Type: ENV:*    Required: false
Targeted ENV: Subscription Type

In most cases, it is appropriate to use the special wildcard subscription type format described above. For the small remainder of cases, there is the targeted ENV: subscription form. This allows a cartridge author to control specifically which published environment variable event types a cartridge will use to pull in environment variables.

A targeted ENV: subscription takes the same format as a normal subscription event, with a particular event type specified as in the following example:

Suppose the fictitious "AwesomeSQL" cartridge publishes environment variables with the following manifest entry:

Publishes:   publish-awesomesql-connection-info:     Type: "ENV:NET_TCP:db:awesomesql"

The corresponding subscription event would be written thus:

Subscribes:   set-awesomesql-connection-info:     Type: "ENV:NET_TCP:db:awesomesql"

11.3. Cartridge Event Example

Consider a simple example of a PHP cartridge which can react when MySQL is added to an application, so that it can set environment variables on the gear to be able to connect to the newly added MySQL cartridge on a different gear.

This requires a Subscribes section in the PHP cartridge manifest.yml:

Subscribes:   set-mysql-connection-info:     Type: "NET_TCP:db:mysql"

And a Publishes section in the MySQL cartridge manifest.yml:

Publishes:   publish-mysql-connection-info:     Type: "NET_TCP:db:mysql"

The PHP cartridge implements a script in hooks/set-mysql-connection-info, and the MySQL cartridge implements a script in hooks/publish-mysql-connection-info.

These events and scripts are matched on the basis of the string value in Type ("NET_TCP:db:mysql").

The publish-mysql-connection-info script could output the host, port, and password to connect to the MySQL instance, and it will be fed as input to the set-mysql-connection-info script in the PHP cart when MySQL is added to an application that has PHP installed.

For example, consider the following output from the publish-mysql-connection-info in the MySQL cartridge:

OPENSHIFT_MYSQL_DB_USERNAME=username;
OPENSHIFT_MYSQL_DB_PASSWORD=password;
OPENSHIFT_MYSQL_DB_HOST=hostname;
OPENSHIFT_MYSQL_DB_PORT=port;
OPENSHIFT_MYSQL_DB_URL=url;

This would be fed as input to hooks/set-mysql-connection-info in the PHP cartridge, as follows:

hooks/set-mysql-connection-info gear_name namespace gear_uuid 'OPENSHIFT_MYSQL_DB_USERNAME=username;OPENSHIFT_MYSQL_DB_PASSWORD=password;OPENSHIFT_MYSQL_DB_HOST=hostname;OPENSHIFT_MYSQL_DB_PORT=port;OPENSHIFT_MYSQL_DB_URL=url;'

The set-mysql-connection-info is responsible for being capable of parsing the final argument and extracting the values provided.

12. Backing Up and Restoring Your Cartridge

OpenShift provides a snapshot/restore feature for user applications. This feature is meant to allow OpenShift application developers to:

  1. Capture the state (snapshot) of their application and produce an archive of that state.

  2. Use a previously taken snapshot of an application to restore the application to the state in the snapshot.

  3. Use a previously taken snapshot of an application to restore a new application to the state in the snapshot. This could be merely renaming an application or copying an application.

OpenShift uses the tar command when backing up and restoring the gear that contains your cartridge. The file metadata/managed_files.yml snapshot_exclusions entry contains an array of patterns of files that will not be backed up or restored. If you exclude files from being backed up and restored you need to ensure those files are not required for your cartridge’s operation.

The file metadata/managed_files.yml restore_transforms entry contains scripts that will be used to transform file names during restore.

Both entries are optional and may be omitted. Empty files will be ignored. Patterns are from the OPENSHIFT_HOMEDIR directory rather than your cartridge’s directory. See the man page for tar (the --transform and --exclude-from options) for more details.

Cartridge developers should preserve the state of the cartridge in all (pre|post)-(restore|snapshot) scripts. Cartridge state should be set back to the original state before this script was invoked.

12.1. Understanding OpenShift Behavior: Snapshot

OpenShift creates an archive during snapshot as follows:

  1. OpenShift stops the application by invoking gear stop.

  2. OpenShift invokes control pre-snapshot for each installed cartridge in the gear. Cartridges may control their serialization in the snapshot by implementing this control action in conjunction with exclusions (example: cartridge authors want to snapshot/restore to/from a database dump instead of a database file).

  3. OpenShift builds a list of exclusions by reading the snapshot_exclusions list from the metadata/managed_files.yml file for each cartridge in the gear.

  4. OpenShift creates an archive in tar.gz format and writes it to STDOUT for consumption by the client tools. The following exclusions are used in addition to the list created from cartridges:

    • Gear user .tmp, .ssh, .sandbox

    • Application state file (app-root/runtime/.state)

    • Bash history file ($OPENSHIFT_DATA_DIR/.bash_history)

  5. OpenShift invokes control post-snapshot for each installed cartridge in the gear.

  6. Based on the state of application before snapshot, OpenShift will either stop or start the gear.

12.2. Understanding OpenShift Behavior: Restore

OpenShift restores an application from an archive as follows:

  1. OpenShift prepares the application for restoration.

  2. If the archive contains a git repo, the platform invokes gear prereceive.

  3. Otherwise, the platform invokes gear stop.

  4. OpenShift invokes control pre-restore for each installed cartridge in the gear. This allows cartridges that control their snapshotted state to prepare their cartridges for restoration (example: delete old database dump, if present).

  5. OpenShift builds a list of transforms to apply by reading the restore_transforms entries from the metadata/managed_files.yml file of each cartridge installed in the gear.

  6. OpenShift extracts the archive into the gear user’s home directory, overwriting existing files, and applying the transformations obtained from cartridges.

  7. OpenShift invokes control post-restore for each installed cartridge in the gear (example: delete new database dump that the db was restored from).

  8. OpenShift resumes the application.

  9. If the archive contains a git repo, OpenShift invokes gear postreceive.

  10. Based on the state of application before restore, OpenShift will either stop or start the gear.

13. Sample conf.d/openshift.conf.erb

ServerRoot "<%= ENV['OPENSHIFT_HOMEDIR'] + "/ruby-1.8" %>"
DocumentRoot "<%= ENV['OPENSHIFT_REPO_DIR'] + "/public" %>"
Listen <%= ENV['OPENSHIFT_RUBY_IP'] + ':' + ENV['OPENSHIFT_RUBY_PORT'] %>
User <%= ENV['OPENSHIFT_GEAR_UUID'] %>
Group <%= ENV['OPENSHIFT_GEAR_UUID'] %>

ErrorLog "|/usr/sbin/rotatelogs <%= ENV['OPENSHIFT_HOMEDIR']%>/ruby-1.8/logs/error_log-%Y%m%d-%H%M%S-%Z 86400"
CustomLog "|/usr/sbin/rotatelogs <%= ENV['OPENSHIFT_HOMEDIR']%>/logs/access_log-%Y%m%d-%H%M%S-%Z 86400" combined

PassengerUser <%= ENV['OPENSHIFT_GEAR_UUID'] %>
PassengerPreStart http://<%= ENV['OPENSHIFT_RUBY_IP'] + ':' + ENV['OPENSHIFT_RUBY_PORT'] %>/
PassengerSpawnIPAddress <%= ENV['OPENSHIFT_RUBY_IP'] %>
PassengerUseGlobalQueue off
<Directory <%= ENV['OPENSHIFT_REPO_DIR]%>/public>
  AllowOverride all
  Options -MultiViews
</Directory>

14. OpenShift Builds

When changes are pushed to an application’s Git repository, OpenShift will build and deploy the application using the updated changes from the repository. The specific build lifecycle which manages the build process changes depending on the presence of a builder cartridge within the application.

14.1. Default Build Lifecycle

When no builder cartridge has been added to the application, changes pushed to the application Git repository result in the execution of the default build lifecycle. The default lifecycle consists of a build and deploy phase, each of which aggregates several steps.

In this lifecycle, OpenShift manages the start and stop of the application, as well as moves the newly committed code into $OPENSHIFT_REPO_DIR. All other specific behaviors are defined by the primary cartridge as well as any user action hooks present.

User action hooks are assumed to reside in $OPENSHIFT_REPO_DIR/.openshift/action_hooks.

During the build phase:

  1. The application is stopped.

  2. The primary cartridge pre-receive control action is executed.

  3. The primary cartridge pre-repo-archive control action is executed.

  4. A new deployment directory $OPENSHIFT_HOMEDIR/app-deployments/$date_$time is created with repo and dependencies subdirectories.

    • The dependencies directory can be referred to as $OPENSHIFT_DEPENDENCIES_DIR in any of the cartridge scripts including setup/install/post_install. Contents placed in this directory are synced to additional gears (both for CI and for scale-up). If your cartridge requires a particular directory structure for dependencies, you should symlink your cartridge directory structure into $OPENSHIFT_DEPENDENCIES_DIR.

    • There is also an $OPENSHIFT_BUILD_DEPENDENCIES_DIR which is the appropriate place to put build time only dependencies such as maven artifacts.

  5. All dependencies from the active deployment ($OPENSHIFT_HOMEDIR/app-root/runtime/dependencies) are copied (or moved if $OPENSHIFT_KEEP_DEPLOYMENTS == 1) to $OPENSHIFT_HOMEDIR/app-deployments/$date_$time/dependencies

  6. Starting with the oldest deployment, previous deployments are removed until the number of deployments in app-deployments ⇐ the value of $OPENSHIFT_KEEP_DEPLOYMENTS (if necessary)

  7. The contents of the git repo for the current deployment branch are unpacked into $OPENSHIFT_HOMEDIR/app-deployments/$date_$time/repo. This step is the only time the application source code is copied by OpenShift during this lifecycle.

  8. The primary cartridge pre-build control action is executed.

  9. The pre_build user action hook is executed, if present.

  10. The primary cartridge build control action is executed.

  11. The build user action hook is executed.

Next, during the prepare phase:

  1. The prepare user action hook is executed, if present.

  2. The deployment id and checksum of deployment contents are calculated

  3. $OPENSHIFT_HOMEDIR/app-deployments/by-id/$deployment_id is created and points to ../app-deployments/$date_time

Next, during the distribute phase:

  1. If the app is scalable, the new deployment will be synced to all child gears

Next, during the activate phase:

  1. $OPENSHIFT_HOMEDIR/app-root/runtime/repo is updated to point at ../../app-deployments/$date_$time/repo

  2. $OPENSHIFT_HOMEDIR/app-root/runtime/dependencies is updated to point at ../../app-deployments/$date_$time/dependencies

  3. The primary cartridge update-configuration control action is executed.

  4. All secondary cartridges in the application are started.

  5. The primary cartridge deploy control action is executed.

  6. The deploy user action hook is executed, if present.

  7. The primary cartridge is started (the application is now fully started).

  8. The primary cartridge post-deploy control action is executed.

  9. The post_deploy user action hook is executed, if present.

  10. If the app is scalable, SSH to each child gear and execute gear activate $deployment_id which performs all the activation steps (except this one)

  11. Write activation time to $OPENSHIFT_HOMEDIR/app-deployments/$date_$time/metadata.json

At this point, the application has been fully built and restarted.

14.2. Archiving Deployments

The current deployment can be archived, creating an artifact which can be re-deployed later with rhc archive-deployment.

14.3. Binary Deploy

From a cartridge perspective, binary deploy is very similar to build and deploy without the build. Instead the built artifacts and dependencies are provided and the deploy steps start at prepare. Binary deployment must be enabled with rhc env-set OPENSHIFT_DEPLOYMENT_TYPE=binary.

14.4. Builder Cartridge Lifecycle

If a builder cartridge is present in the application, changes pushed to the application Git repository will execute using an alternate build lifecycle which hands over operations to the builder cartridge. In this lifecycle, OpenShift provides no specific behavior for the build beyond giving the builder cartridge the opportunity to perform work. The sequence of events follows:

During the Git pre-receive hook:

  1. The builder cartridge pre-receive control action is executed.

During the Git post-receive hook:

  1. The builder cartridge post-receive control action is executed.

14.5. Builder Tips

Any build implementation should take care to avoid duplicating source or copying artifacts any more than necessary. The space a cartridge’s build implementation consumes during the build cycle is the application developer’s, and so cartridge authors should take care to be as conservative as possible.

[cart_locking]: #cartridge-locking [snapshot]: #backing-up-and-restoring-your-cartridge [erb_processing]: #erb-processing [erb]: http://ruby-doc.org/stdlib-1.9.3/libdoc/erb/rdoc/ERB.html [locking_ruby]: http://www.ruby-doc.org/docs/ProgrammingRuby/html/taint.html).

15. OpenShift Upgrades

The OpenShift runtime contains an upgrade system used to upgrade the cartridges in a gear to the latest available version and to apply gear-scoped changes which are orthogonal to cartridges to a gear. The oo-admin-upgrade command provides the CLI for the upgrade system and can be used to upgrade all gears in an OpenShift environment, all gears on a node, or a single gear. This command queries the openshift broker to determine the locations of the indicated gears to migrate and makes mcollective calls to trigger the upgrade for a gear.

During upgrades, OpenShift follows the following high-level process to upgrade a gear:

  1. Load the gear upgrade extension, if configured.

  2. Inspect the gear state.

  3. Run the gear extension’s pre-upgrade method, if it exists.

  4. Compute the upgrade itinerary for the gear.

  5. If the itinerary contains an incompatible upgrade, stop the gear.

  6. Upgrade the cartridges in the gear according to the itinerary.

  7. Run the gear extension’s post-upgrade method, if it exists.

  8. If the itinerary contains an incompatible upgrade, restart and validate the gear.

  9. Clean up after the upgrade by deleting pre-upgrade state and upgrade metadata.

15.1. Upgrade Itinerary

The upgrade process must be re-entrant; if it fails or times out, a subsequent upgrade operation must pick up where the last one left off without losing any data about which operations must be performed to fully upgrade a gear. The upgrade itinerary stores information about which cartridges in a gear must be upgraded and which type of upgrade to perform.

There are two types of cartridge upgrade process: compatible and incompatible. Whether an upgrade from version X to version Y is compatible is driven by the presence of version X in version Y’s Compatible-Versions manifest element. Though compatible and incompatible upgrades differ in various ways, the chief difference is that when an incompatible upgrade is to be applied to any cartridge in a gear, that gear is stopped before the cartridge upgrades are performed and restarted after all cartridges have been upgraded.

The upgrade itinerary is computed as follows for each cartridge in a gear:

  1. Read in the current IDENT of the cartridge.

  2. Select the name and software version of the cartridge from the cartridge repository; this will yield the manifest for the latest version of the cartridge. If the manifest does not exist in the cartridge repository or does not include the software version, skip the cartridge.

  3. If the latest manifest is for the same cartridge version as that currently installed in the gear, skip the cartridge unless the ignore_cartridge_version parameter is set. If the ignore_cartridge_version parameter is set, record an incompatible upgrade for the cartridge in the itinerary. (TODO: case where manifest declares itself as compatible version).

  4. If the latest manifest includes the current cartridge version in the Compatible-Versions element, record a compatible upgrade for the cartridge in the itinerary. Otherwise, record an incompatible upgrade for the cartridge in the itinerary.

15.2. Compatible Upgrades

The compatible upgrade process for a cartridge is as follows:

  1. The new version of the cartridge is overlaid in the gear.

  2. The files declared in the Processed-Templates section of the cartridge’s managed-files.yml are removed.

  3. The cartridge directory is unlocked.

  4. The cartridge directory is secured.

  5. If the cartridge provides an upgrade script, that script is executed.

  6. The cartridge directory is locked.

15.3. Incompatible Upgrades

The incompatible upgrade process for a cartridge is as follows:

  1. The files and directories declared in the setup_rewritten section of the cartridge’s managed_files.yml are removed.

  2. The new version of the cartridge is overlaid in the gear.

  3. The cartridge directory is unlocked.

  4. The cartridge directory is secured.

  5. If the cartridge provides an upgrade script, that script is executed.

  6. If the cartridge provides a setup script, that script is executed.

  7. The erb templates for the cartridge are processed.

  8. The cartridge directory is locked.

  9. New endpoints for the cartridge are created.

  10. The frontend is connected.

15.4. Cartridge Upgrade Script

A cartridge may provide an upgrade script in the bin directory which will be executed during the upgrade process. The purpose of this script is to allow for arbitrary actions to occur during the upgrade process which are not accounted for by the compatible or incompatible processes. If the upgrade script is provided, it will be passed the following arguments:

  1. The software version of the cartridge.

  2. The current cartridge version.

  3. The cartridge version being upgraded to.

A non-zero exit code from this script will result in the upgrade operation failing until the exit code is corrected.