Usage
Each release job represents a specific chunk of work that the release performs. For example a DHCP release may have a "dhcp-server" job, and a Postgres release may have "postgres" and "periodic-backup" jobs. A release can define one or more jobs.
A job typically includes:
- metadata that specifies available configuration options
- ERB configuration files
- a Monit file that describes how to start, stop and monitor processes
- start and stop scripts for each process
- additional hook scripts
Jobs are typically OS specific (Windows vs Linux); however, structure of a job remains same.
Spec file (metadata)¶
Spec file defines job metadata. It will be interpreted by the Director when the release is uploaded and when it's deployed.
--- name: http-server description: This job runs a simple HTTP server. templates: bpm.yml: config/bpm.yml config.json: config/config.json packages: - http-server properties: listen_port: description: "Port to listen on" default: 8080
Schema:
- name [String, required]: Name of the job.
- description [String, optional]: Describes purpose of the job.
- templates [Hash, optional]: Template files found in the
templates
directory of the job (keys of the Hash) and their final destinations (values of the Hash), relative to the job directory on the deployed VMs.- <key> [String, required]: the relative path and filename of the
ERB template provided by the job in the release, relative to the
templates
sub-directory. No need for any.erb
suffix, all templates are treated as ERB templates whatever their name is. - <value> [String, required]: the relative path and filename of the
rendered file, relative to the job directory (i.e.
/var/vcap/jobs/<job-name>/
) on the managed Bosh instances (a.k.a. the “deployed VMs”). By convention, executable files should be placed intobin/
directory so that the Agent can mark them as executables, and configuration files should be placed intoconfig/
directory.
- <key> [String, required]: the relative path and filename of the
ERB template provided by the job in the release, relative to the
- packages [Array, optional]: Package dependencies required by the job at runtime.
- consumes [Array, optional]: Links that are consumed by the job for
rendering ERB templates.
- name [String, required]: Name of the link to find.
- type [String, required]: Type of the link to be found. This is an
arbitrary naming. Usual and conventional types are
address
when the link goal is to expose a Bosh DNS name that allows accessing the instances of the group. Usually typed by technology, likemysql
,postgres
,cassandra
, etc. Anything that makes sense is relevant and matters. - optional [Boolean, optional]: Whether finding an matching link is
optional (when
true
) or mandatory (whenfalse
. Default isfalse
, so optional links must be explicitly declared as such.
- provides [Array, optional]: Links that are exposed to other jobs for
rendering their ERB templates.
- name [String, required]: Name of the exposed link.
- type [String, required]: Type of the exposed link.
- properties [Array, optional]: List of property keys in dot notation (same as properties.<name> below)
- properties [Hash, optional]: Configuration options supported by the job.
- <name> [String, required]: Property key in dot notation. Typical
properties include account names, passwords, shared secrets, hostnames,
IP addresses, port numbers, and descriptions.
- description [String, required]: Describes purpose of the property. This is not used by the Director, but is displayed in job configuration details provided by the release index.
- type [String, optional]: The type of the property. This is only
a convention for release authors to provide a type when they
estimate it useful. Example:
type: certificate
. - example [Any, optional]: Example value, to be displayed in the
release index. Default is
nil
. - default [Any, optional]: The default value for the property.
Default is
nil
.
- <name> [String, required]: Property key in dot notation. Typical
properties include account names, passwords, shared secrets, hostnames,
IP addresses, port numbers, and descriptions.
Note
Within a property definition, default
is used by the Director, and
description
, default
and example
are displayed by the
release index. In turns, other keys like type
are used only
for convenience, like Concourse does env
keys in the
“web” job definition. Indeed, the schema is not
formally validated by the Director when registering a release job.
Templates (ERB configuration files)¶
Release authors can define zero or more templates for each job, but typically
you need at least a template for the BPM config file, that is expected to
rendered to the config/bpm.yml
location.
Favor Bosh Process Manager (BPM) over Monit¶
Monit is a component of the Bosh architecture that was introduced in the early days, and has always been deemed to get rid of soon. But history has shown over the years that transitioning away from Monit is complex-enough for being hold back since then.
As a consequence, Release authors should avoid at all costs relying on fancy
Monit features. Instead, they should use a very simple and standard monit
file, and leverage the battle-tested and well-designed
Bosh Process Manager (BPM).
Whenever BPM would miss some required features, then contributions should be submitted as Pull Requests to its Git repository so that more use-case are covered.
BPM Configuration¶
The release needs to render a config/bpm.yml
file following the
configuration schema defined in the BPM documentation. The schema is clean and
configuring BPM is straightforward. See BPM Configuration Format
for more details.
Here is a simple example bpm.yml
config, showcased in the
Exemplar Bosh Release.
<% require "json" -%> processes: - name: sample-app executable: /var/vcap/packages/sample-app/bin/sample-app args: [] env: PORT: <%= p('port').to_json %> CF_INSTANCE_INDEX: <%= spec.index.to_json %>
Release author need a basic understanding of the isolation mechanisms enforced by BPM, especially read-only root disk remouting, and declarating read-write access to portions only of the mount space.
See the BPM Runtime Environment for more details on these topics.
Standard monit
shim with BPM¶
Here is the standard monit
file that Release authors should use. Only
replace <job-name>
by the actual job name.
check process <job-name> with pidfile /var/vcap/sys/run/bpm/<job-name>/<job-name>.pid start program "/var/vcap/jobs/bpm/bin/bpm start <job-name>" stop program "/var/vcap/jobs/bpm/bin/bpm stop <job-name>" group vcap
Legacy pattern with *_ctl
script (highly discouraged)¶
Legacy monit
files are using a *_ctl
scripts that conventionally accept
start
or stop
as first argument. We document this here only for release
author to spot this old pattern and properly
transition to the BPM pattern.
check process postgres with pidfile /var/vcap/sys/run/postgres/postgres.pid start program "/var/vcap/jobs/postgres/bin/postgres_ctl start" stop program "/var/vcap/jobs/postgres/bin/postgres_ctl stop"
This is highly discouraged, because experience has show that the *_ctl
scripts have so many small details to care about, that this pattern leads to
tremendous boiler-plate, untested and fragile script code in Bosh releases.
Would release authors not be able to use BPM for some good reason, then
leveraging the standard start-stop-daemon
utility is a cleaner and more
robust pattern.
For completeness, see the Exemplar Release with
detailed examples on the start-stop-daemon
pattern, though release authors
are encouraged to use BPM instead.
Monit expectations¶
In a typical setup, BPM is called by Monit when OS processes, whether daemons or one-off errand jobs, need to be started or stopped.
Monit expects that executing "bpm start" directive will get a process running
and output its PID into the file given by the with pidfile
declaration. Once
the process is started by BPM, Monit will monitor the daemon process, based on
the PID that can be found in the pidfile
, and if the process cease to exist,
Monit will try to start it again.
Monit also expects that executing bpm stop
will stop the running process.
BPM offers the best guarantees for that, and properly adapts to some
documented defects of Monit in that regards.
See the Monit Workarounds for more details.
Hook scripts¶
There are several job lifecycle events that a job can react to: pre-start
,
post-start
, post-deploy
, pre-stop
, post-stop
, and drain
.
See Job lifecycle for more details on the exact execution order of these hook scripts.
The Exemplar Release demonstrate state-of-the-art implementations for
post-start
or drain
, including
helpful boilerplate that provide proper timestamping of logs for hook scripts,
which has proven very useful when troubleshooting issues while developing Bosh
Releases.
Use of Properties¶
Each template file is evaluated with ERB before being sent to each instance.
Basic ERB syntax includes:
<%= "value" %>
: Inserts string "value".<% expression %>
: Evaluatesexpression
but does not insert content into the template. Useful forif/else/end
statements.<% expression -%>
: Evaluatesexpression
, does not insert any content, and remove the newline\n
character that might be after the-%>
.
Templates have access to merged job property values, built by merging default property values and operator specified property values in the deployment manifest. To access properties p
and if_p
ERB helpers are available:
<%= p("some.property") %>
: Insert the propertysome.property
value, else a default value from the job spec file. Ifsome.property
does not have a default in the spec file, error will be raised to the user specifying that property value is missing. Advanced usage:- Operator
p
can take optional parameter as a default value, e.g.<%= p("some.property", some_value) %>
. This value is used as a last resort. - The first parameter can be an array, e.g.
<%= p(["some.property1", "some.property2"], some_value) %>
. Value of the first property which is set (i.e. non-null
) will be returned.
- Operator
- A part of the template can be evaluated only when some property is provided.
<% if_p("some.property") do |prop| %>...<% end %>
evaluates the block only ifsome.property
property has been provided. The property value is available in the variableprop
.- Multiple properties can be specified:
<% if_p("some.prop1", "other.prop2") do |prop1, prop2| %>...<% end %>
, in which case the block is evaluated only if all the properties are defined.
- Multiple properties can be specified:
After the end
of an if_p
block, the .else do ... end
and
.else_if_p("other.property") do ... end
syntaxes are supported.
<% if_p("some.property") do |prop| %>...<% end.else do %>...<% end %>
- Evaluates first block ifsome.property
has been provoded (or has a default in job spec), otherwise evaluates the second block.<% if_p("some.property") do |prop| %>...<% end.else_if_p("other.property") do |prop2| %>...<% end.else do %>...<% end %>
- Evaluates first block ifsome.property
has been provided (or has a default in job spec), otherwise evaluates the second block ifother.property
has been provided (or has a default in job spec), otherwise evaluates the third block.
The link navigation syntax link()
also provides similar .p()
and .if_p()
methods, and .else_if_p()
or .else
blocks.
<%= link("relation-name").if_p("remote.prop") do |prop| %>...<% end %>
- Ifremote.prop
is defined in the job that is resolved through navigating therelation-name
link, then the block is evaluated with the value in the local variableprop
.<%= link("relation-name").if_p("remote.prop") do |prop| %>...<% end.else do %>...<% end %>
- Same as above with an.else do ... end
block.<%= link("relation-name").if_p("remote.prop") do |prop| %>...<% end.else_if_p("other.prop2") do |prop2| %>...<% end.else do %>...<% end %>
- Same as above with an.else_if_p
block that evaluates only whenother.prop2
is defined through navigating therelation-name
link.
See Links and Links Properties for more details on navigating links to fetch configuration properties from other jobs, possibly declared in different instance groups, and even possibly living in different deployments.
Using spec
¶
Each template can also access the special spec
object for instance-specific
configuration. Remember that job properties are initially defined at the
instance group level in the deployment manifest.
Release authors can the spec
object directly in the ERB templates:
<%= spec.ip %>
.
The accessible properties fall into three categories: Bosh structure information, networking setup, and instance configuration.
Structural info¶
spec.deployment
: Name of the BOSH deployment defining the instance group.spec.name
: Name of the instance group that the instance belongs to.spec.az
: The availability zone that the instance is placed into.spec.id
: Unique and immutable UUID of the instance.spec.index
: Ordinal and numeric “human friendly” instance index. Indexes usually start a0
, but with no guarantee. Gaps may appear anywhere in the numbering, and the first instance in the group may have a non-zero index.spec.bootstrap
: Boolean that istrue
if the instance is the first instance of its group.
Note
With spec.index
, Bosh doesn't guarantee that instances will be numbered
consecutively. Determining which instance is the first its group is a very
common requirement, so that certain things get bootstrapped by one single
node of a cluster, like database schema migrations, or admin password
enforcement. When facing such requirement, release authors should not
assume there is necessarily an instance with index 0
, and use
spec.bootstrap
instead.
Caveat
From within an ERB template, there is no programatic way to know the name of the job that the template is defined in. Thus release authors are forced to hardcode the job name in ERB templates that need it. Due to this limitation, there is unfortunately no way to write ERB templates that are agnostic of the job name they belong to.
Networking setup¶
spec.address
: Default network address for the instance. This can be an IPv4, an IPv6 address or a DNS record, depending on the Director's configuration. Available in bosh-release v255.4+.spec.ip
: IP address of the instance. In case multiple IP addresses are available, the IP of the addressable or default network is used. Available in bosh-release v258+.spec.dns_domain_name
: the configured root domain name for the Director, which defaults tobosh
, meaning that the configured Top-Level Domain (TLD) for Bosh DNS is.bosh
.-
spec.networks
: Entire set of network information for the instance. Example:<network-name>: type: manual ip: 10.224.0.129 netmask: 255.255.240.0 cloud_properties: name: random default: - dns - gateway gateway: 10.224.0.1 dns_record_name: 0.<instance-group>.<network>.<deployment>.bosh
Note
Release authors are encouraged to favor spec.address
over spec.ip
. The
spec.ip
property is provided only for use-cases where a numeric IP
address (either IPv4 or IPv6) is absolutely required.
Warning
When dynamic networks are being used, spec.ip
might not be
available, then the value 127.0.0.1
is provided instead. This applies
to spec.<network-name>.ip
, spec.<network-name>.netmask
and
spec.<network-name>.gateway
.
Fetching the name of the network that has the default gateway is particularily
complex, as the spec
object is not a Ruby Hash, but an OpenStruct.
As a consequence, one cannot use the .keys
method for listing the network
names. But Bosh provides a special helper method .methods(false)
on the
OpenStruct that does the trick.
So, one can use the expression spec.networks.methods(false)
in order to list
the network names. Based on this, use the following code snippet in your ERB
templates, whenever you need the name of the “default” network (i.e. the one
use for the default gateway, at least).
network = spec.networks.methods(false).find { |net_name| # Pick the network that is used for the default gateway default_for = spec.networks[net_name].default !default_for.nil? && default_for.include?("gateway") }
Obtaining the default network is absolutely necessary when building default Bosh DNS FQDNs, which include that network name.
Instance configuration¶
spec.persistent_disk
: is0
if no persistent disk is mounted to the instance. In case the deployment manifest does declare a persistent disk attached to the instances of the group, thispersistent_disk
is given a0
value when the deployment manifests instructs to remove the instance from the group and delete it (typical for scaled-in operations, as opposed to scale-out where new instances are “horizontally” added to a group).spec.release.name
: The name of the BOSH Release where the instance job is originally defined.spec.release.version
: Version of the BOSH release that the instance job relies on.
Link properties¶
Remember that the job targeted through alink can live in a different instance group of a different deployment.
- Structural info
link(name).deployment_name
: Deployment name of the linked job.link(name).instance_group
: Instance group name of the linked job.link(name).group_name
: A concatenation of the link name and link type, separated by a dash-
, i.e.<link-name>-<link-type>
.link(name).instances
: An array of details for each instance of the group.link(name).instances[].az
: the availability zone hat the instance is placed intolink(name).instances[].name
: instance group name. Alias forlink().instance_group
.link(name).instances[].id
: instance immutable UUIDlink(name).instances[].index
: human-friendly instance ordinallink(name).instances[].bootstrap
: whether the instance is the first of its group- Networking setup
link(name).default_network
: default network for the instance group.link(name).networks
: list of all networks for the instance group. TO BE TESTEDlink(name).address
: an address for the instance group, using theq-s0
prefix, indicating thesmart
health filter. See Native DNS Support for more details.link(name).domain
: the root top-level domain name suffix. Defaults tobosh
.link(name).use_link_dns_names
: applicable config for the link. TO BE TESTEDlink(name).use_short_dns_addresses
: applicable config for the link. TO BE TESTEDlink(name).instances[].address
: the instance address, that can be an IPv4, an IPv6 address or a DNS record, depending on the Director's configuration, but is usually a DNS name, ending with the suffix indicated in thelink().domain
property.link(name).instances[].addresses
: several addresses including aliases? TO BE TESTEDlink(name).instances[].dns_addresses
: same as above, but preferring DNS entry- Configuration
link(name).properties
: The job properties that are exposed by the link.
Deprecated properties accessors¶
name
: the instance group name. Alias forspec.name
(recommended).index
: the instance index in its group. Alias forspec.index
(recommended).properties
: the job properties, as defined in the instance group. Alias forspec.properties
. Doesn't provide elementary error reporting.spec.properties
: The properties defined in the deployment manifest for the instance job that the templates belongs to. Accessing properties through this object leads to poor error reporting and is highly discouraged. Bosh Release authors should use thep()
accessor instead, which implements proper error reporting, and properly prevents misconfiguration.
With Bosh v1, the term “job” was designating an “instance group”. The use of
spec.job
in ERB templates could possibly be used by legacy Bosh releases but
its usage is highly discouraged. It is documented here only to help release
authors to migrate to the standardized p()
property accessor.
spec.job
: instance group spec. This is an old Bosh v1 naming, when job did actually mean instance group.nil
when no job is defined for the instance group.spec.job.name
: name of the instance group that the template belongs to.spec.job.template
: name of the first job in the instance group, which is only relevant if it is the “default errand”, a legacy “Bosh v1” concept before it was decided that any job that defines abin/run
script can be run as an errand.spec.job.version
: version of the first job in the instance group, only relevant if it is the “default errand” (legacy concept).spec.job.templates
: an array of jobs for the instance groupspec.job.templates.*.name
: name of the jobspec.job.templates.*.version
: version as defined in the release jobs manifestsspec.job.templates.*.sha1
: digital fingerprint of the job (nowadays with asha256:
prefix for SHA256)spec.job.templates.*.blobstore_id
: where to find the job tarball in the Director's blobstore.spec.job.templates.*.logs
: an empty array of logs files, related to the legacylogs
hash in a release job spec, which is undocumented.spec.properties_need_filtering
: Whether properties from other instance groups should not be exposed to this job. This is legacy, and should not be here.