Using Runtime Templates

Apcera supports the use of runtime templates.

A template is a flat file that exposes information about the container runtime environment to applications or jobs running inside the container. Jobs and applications may need access to this information to complete their configuration when running within a container. Templates enable you to configure your app from Apcera job environments and are usually used with frameworks that expect configuration files to reside on disk (for example, the Rails database.yml file).

You can use templates with any app or job. To template app values, you specify a template file in the app manifest. To template values in a job other than an app, you can specify them in a package script by file path or by calling the stager API. At runtime the Apcera templating engine evaluates the template attributes and replaces them with actual values.

See also the templating tutorial.

Template Parameters

The following table lists and describes the available template parameters. Refer to the example that follows this table for example template implementation.

Parameter Description
uuid Returns the UUID of the related job. Useful for correlating logs from each instance of a application.
name Return the application's name, assigned by the creator of the application.
num_instances Returns the total number of instances of this application that are running.
cpu Returns the CPU resources requested for this application.
memory Returns the bytes of memory requested for this application.
disk Returns the bytes of disk storage requested for this application.
tags Returns the list of tags applied to this application. These should be accessed via iteration. For example, to output one tag per line:
{{range tags}}
tag: {{.}}
{{end}}
has_tag Returns a boolean indicating if a tag named "name" is applied to this job, for example:
{{has_tag "production"}}
production config...
{{ end }}
routes Returns an array of routes. Accessing the route fields in the template is done as follows:
{{range routes}}
address={{.addr}}
public={{.Public}}
{{end}}
Use 'address' to deliver IP:port. These are generated from all of the routes on all of the job ports.
bindings Returns bindings to range over in the template. Accessing fields of the binding is done as follows:
{{range bindings}}
name: {{.Name}}
pipeline_config: {{.PipelineCfg}}
service: {{.Service}}
provider: {{.Provider}}
uri: {{.URI}}
{{end}}
Individual fields of the binding URI can also be accessed as follows:
scheme: {{.URI.Scheme}}
user: {{.URI.User}}
password: {{.URI.Password}}
host: {{.URI.Host}}
port: {{.URI.Port}}
path: {{.URI.Path}}
trimmedpath: {{.URI.TrimmedPath}} – same as Path, but without leading /. Useful for DB names
raw_query: {{.URI.RawQuery}
bindings_by_scheme Find all bindings with a specific scheme.
binding Returns the first binding found by the specified name. For example:
{{with binding_service "my-binding"}}
{{.URI}}
{{end}}
binding_provider Returns the first binding found by the specified provider name. For example:
{{with binding_service "postgress-provider"}}
{{.URI}}
{{end}})
binding_service Returns the first binding found by the specified service name. For example:
{{with binding_service "my-postgres"}}
{{.URI}}
{{end}}`
env_map.<KEY> Map that exposes job process environment variables. For example: {{env_map.PORT}}. Note that the PWD and OLDPWD environment variables are not exposed by this map.

Template Example

The following table shows the input (before) and output (after) of a template file evaluation:

Input Output
scalars:
uuid: {{uuid}}
name: {{name}}
num_instances: {{num_instances}}
cpu: {{cpu}}
memory: {{memory}}
disk: {{disk}}

tags:
{{range tags}}
  {{.Name}}: {{.Value}}
{{end}}

routes:
{{range routes}}
  URL: {{.URL}}
{{if .Public}}
  Port: {{.ListenPort}}
{{else}}
  UpdatePort: {{.ListenPort}}
{{end}}
{{end}}

bindings:
{{range bindings}}
  Name: {{.Name}}
  Provider: {{.Provider}}
  Service: {{.Service}}
  URI: {{.URI}}
    scheme={{.URI.Scheme}}
    user={{.URI.User}}
    password={{.URI.Password}}
    host={{.URI.Host}}
    port={{.URI.Port}}
    path={{.URI.Path}}
    raw_query={{.URI.RawQuery}}
{{end}}











scalars:
uuid: 98086f73-544e-4650-9a49-db3ca53b57dd
name: job::/sandbox/captain_jack::example-ruby-manifest
num_instances: 1
cpu: 0
memory: 268435456
disk: 805306368

tags:

  app: example-ruby-manifest


routes:

  URL: http://example-ruby-manifest.vagrant.apcera.net



  UpdatePort: 0



bindings:

  Name: binding::/sandbox/captain_jack::example-ruby-manifest_mydb1/_BACKEND
  Provider: provider::/sandbox/captain_jack::postgres
  Service: service::/sandbox/captain_jack::mydb1
  URI: postgres://hl6hulqakqhmeq4g:4JjtAuYFFtXT5Hkx@10.0.167.167/e0308bca5477424d92b14c58f6bccc39
    scheme=postgres
    user=hl6hulqakqhmeq4g
    password=4JjtAuYFFtXT5Hkx
    host=10.0.167.167
    port=0
    path=/e0308bca5477424d92b14c58f6bccc39
    raw_query=

  Name: binding::/sandbox/captain_jack::example-ruby-manifest_mydb2/_BACKEND
  Provider: provider::/sandbox/captain_jack::postgres
  Service: service::/sandbox/captain_jack::mydb2
  URI: postgres://14kg6fkuhmgomjet:kg2l7Uq330sonWpm@10.0.167.167/22a25ecb346b4d749a12ce45653502a2
    scheme=postgres
    user=14kg6fkuhmgomjet
    password=kg2l7Uq330sonWpm
    host=10.0.167.167
    port=0
    path=/22a25ecb346b4d749a12ce45653502a2
    raw_query=

Using Templates with App Manifests

The templates manifest attribute is a list of file paths that will be evaluated as templates.

For example, the following manifest snippet references the app.rb template file:

templates: [
  {
    path: "app.rb"
  },
]

The app.rb template file contains parameters whose values will be populated at runtime by Apcera, for example:

scalars:
uuid: {{uuid}}
name: {{name}}
num_instances: {{num_instances}}
cpu: {{cpu}}
memory: {{memory}}
disk: {{disk}}

Refer to "Working with Manifests" for more information on manifest files and using the template option.

Note that there may be cases when an app would benefit from templating, but the necessary files are hidden in an archive or other package. A pre-packaged Java WAR file that is deployed directly by Tomcat is an example of this situation. In these cases, the following options are available:

  • Configure the app to reference variable definitions from a file outside of the archive
  • Reference an environmental variable that can be changed on demand

Using Templates with Packages for Jobs

In addition to manifests for app creation and deployment, you can also use templates with any other type of job using Apcera's package definition framework. To do this you have two options:

Option 1) Add the /TEMPLATES file path to the package.conf file (or whatever name you have given to the package definition file)

Option 2) Do a PUT call to the Stager API from the package.conf with the path to the template file.

An example for each option is provided below. The custom package in this example involves the injection of App Dynamics into an application.

The template value delimiter for the file path option must be curly braces ({}). For the API call you can specify the delimiter similar to the way you can with mainifests.

Using a template by direct file reference

The following example demonstrates how to use templated variables for a job package using a reference to the file path.

more appDynamics.conf
name:      "appDynamics"
version:   "4.0.1.0"
namespace: "/apcera/pkg/packages"

build_depends [ { package: "build-essential" } ]
depends       [ { os: "linux" }  ]
provides      [ { package: "appDynamics" } ]

environment { "PATH": "/opt/apcera/apache-tomcat-6.0.43/bin:$PATH",
              "CATALINA_HOME": "/opt/apcera/apache-tomcat-6.0.43",
              "CATALINA_OPTS": "$CATALINA_OPTS -javaagent:/opt/appdynamics/appagent/javaagent.jar"  }

build (
  sudo wget https://www.dropbox.com/s/snjdjd117de66zf/AppServerAgent-4.0.1.0.tar?dl=1
  export INSTALLPATH=/opt/appdynamics/appagent
  sudo mkdir -p ${INSTALLPATH}
  #sudo unzip 'AppServerAgent-4.0.1.0.zip?dl=1' -d ad
  sudo mv 'AppServerAgent-4.0.1.0.tar?dl=1' AppServerAgent-4.0.1.0.tar
  sudo tar -xvf AppServerAgent-4.0.1.0.tar
  sudo cp -a AppServerAgent-4.0.1.0/. ${INSTALLPATH}
  sudo cp /opt/appdynamics/appagent/ver4.0.1.0/conf/controller-info.xml /opt/appdynamics/appagent/conf/controller-info.xml
  sudo mkdir /opt/appdynamics/appagent/ver4.0.1.0/logs
  sudo chmod a+rw /opt/appdynamics/appagent/ver4.0.1.0/logs
  sudo chmod a+rw /opt/appdynamics/appagent/ver4.0.1.0/conf
  sudo chmod a+rw /opt/appdynamics/appagent/ver4.0.1.0/conf/controller-info.xml
  sudo chmod a+rw /opt/appdynamics/appagent/conf/controller-info.xml
  sudo chmod a+rw /opt/appdynamics/appagent/conf
  sudo touch /TEMPLATES
  sudo chmod a+rw /TEMPLATES
  sudo echo "/opt/appdynamics/appagent/conf/controller-info.xml" > /TEMPLATES
  sudo echo "/opt/appdynamics/appagent/ver4.0.1.0/conf/controller-info.xml" >> /TEMPLATES
)

In the example, the controller-info.xml file uses runtime template parameters to set some values. The first file path creates (>) the /TEMPLATES file, the second appends (>>) to the file. The resulting /TEMPLATES file that is created contains those two file paths:

/opt/appdynamics/appagent/conf/controller-info.xml
/opt/appdynamics/appagent/ver4.0.1.0/conf/controller-info.xml

These XML files contain templated variables that get populated at runtime. The TEMPLATES file must be at the root level (/TEMPLATES).

The other commands you see involving TEMPLATES are for permissions and may not be applicable to your environment.

Using a template with a Stager API call

The following example demonstrates how to use templated variables for a job package using a call to the Stager API.

more appDynamics.conf
name:      "appDynamics"
version:   "4.0.1.0"
namespace: "/apcera/pkg/packages"

build_depends [ { package: "build-essential" } ]
depends       [ { os: "linux" }  ]
provides      [ { package: "appDynamics" } ]

environment { "PATH": "/opt/apcera/apache-tomcat-6.0.43/bin:$PATH",
              "CATALINA_HOME": "/opt/apcera/apache-tomcat-6.0.43",
              "CATALINA_OPTS": "$CATALINA_OPTS -javaagent:/opt/appdynamics/appagent/javaagent.jar"  }

build (
  sudo wget https://www.dropbox.com/s/snjdjd117de66zf/AppServerAgent-4.0.1.0.tar?dl=1
  export INSTALLPATH=/opt/appdynamics/appagent
  sudo mkdir -p ${INSTALLPATH}
  #sudo unzip 'AppServerAgent-4.0.1.0.zip?dl=1' -d ad
  sudo mv 'AppServerAgent-4.0.1.0.tar?dl=1' AppServerAgent-4.0.1.0.tar
  sudo tar -xvf AppServerAgent-4.0.1.0.tar
  sudo cp -a AppServerAgent-4.0.1.0/. ${INSTALLPATH}
  sudo cp /opt/appdynamics/appagent/ver4.0.1.0/conf/controller-info.xml /opt/appdynamics/appagent/conf/controller-info.xml
  sudo mkdir /opt/appdynamics/appagent/ver4.0.1.0/logs
  sudo chmod a+rw /opt/appdynamics/appagent/ver4.0.1.0/logs
  sudo chmod a+rw /opt/appdynamics/appagent/ver4.0.1.0/conf
  sudo chmod a+rw /opt/appdynamics/appagent/ver4.0.1.0/conf/controller-info.xml
  sudo chmod a+rw /opt/appdynamics/appagent/conf/controller-info.xml
  sudo chmod a+rw /opt/appdynamics/appagent/conf
  curl -X PUT -F resource=templates -F action=add -F path=/path/to -F left_delimiter=\{ right_delimiter=\} $STAGER_URL/meta
)

Using a template in an environment variable

The following example demonstrates how to assign environment variables using templates. This example assumes there is an available MySQL provider that can be used.

1) Create the MySQL service:

apc service create mysql-service -p mysql-provider

2) Create Wordpress job:

apc docker pull wordpress -i wordpress \
-e 'WORDPRESS_DB_HOST={{ (binding_service "mysql-service").URI.Host }}:{{ (binding_service "mysql-service").URI.Port }}' \
-e 'WORDPRESS_DB_USER={{ (binding_service "mysql-service").URI.User }}' \
-e 'WORDPRESS_DB_PASSWORD={{ (binding_service "mysql-service").URI.Password }}' \
-e 'WORDPRESS_DB_NAME={{ (binding_service "mysql-service").URI.TrimmedPath }}' \
--routes http://mywp.<<domain.suffix>> \
--port 80 \
--ignore-volumes

Starting with Apcera Platform release 2.2, the apc docker create command is deprecated in favor of apc docker pull.

Bind the Wordpress job to MySQL service:

apc service bind mysql-service -j wordpress
apc app start wordpress