openstack-manuals/doc/user-guide/hot/section_software_deployment.xml
Gauvain Pocentek a8f6cc1f09 Update the user guide with the latest HOT guide content
Change-Id: If71ad2579328d22430f56f81f3d35d220be734d5
2014-12-30 10:55:05 +01:00

720 lines
33 KiB
XML

<?xml version='1.0' encoding='UTF-8'?>
<section xmlns="http://docbook.org/ns/docbook" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" xml:id="hot-software-deployment">
<!--WARNING: This file is automatically generated. Do not edit it.-->
<title>Software configuration</title>
<para>There are a variety of options to configure the software which runs on the
servers in your stack. These can be broadly divided into the following:</para>
<itemizedlist>
<listitem>
<para>Custom image building</para>
</listitem>
<listitem>
<para>User-data boot scripts and cloud-init</para>
</listitem>
<listitem>
<para>Software deployment resources</para>
</listitem>
</itemizedlist>
<para>This section will describe each of these options and provide examples for
using them together in your stacks.</para>
<section xml:id="image-building">
<?dbhtml stop-chunking?>
<title>Image building</title>
<para>The first opportunity to influence what software is configured on your servers
is by booting them with a custom-built image. There are a number of reasons
you might want to do this, including:</para>
<itemizedlist>
<listitem>
<para><emphasis role="Boot speed"/> - since the required software is already on the image there
is no need to download and install anything at boot time.</para>
</listitem>
<listitem>
<para><emphasis role="Boot reliability"/> - software downloads can fail for a number of reasons
including transient network failures and inconsistent software repositories.</para>
</listitem>
<listitem>
<para><emphasis role="Test verification"/> - custom built images can be verified in test
environments before being promoted to production.</para>
</listitem>
<listitem>
<para><emphasis role="Configuration dependencies"/> - post-boot configuration may depend on
agents already being installed and enabled</para>
</listitem>
</itemizedlist>
<para>A number of tools are available for building custom images, including:</para>
<itemizedlist>
<listitem>
<para><link xlink:href="https://github.com/openstack/diskimage-builder">diskimage-builder</link> image building tools for OpenStack</para>
</listitem>
<listitem>
<para><link xlink:href="http://imgfac.org/">imagefactory</link> builds images for a variety of operating system/cloud
combinations</para>
</listitem>
</itemizedlist>
<para>Examples in this guide which require custom images will use <link xlink:href="https://github.com/openstack/diskimage-builder">diskimage-builder</link>.</para>
</section>
<section xml:id="user-data-boot-scripts-and-cloud-init">
<?dbhtml stop-chunking?>
<title>User-data boot scripts and cloud-init</title>
<para>When booting a server it is possible to specify the contents of the user-data
to be passed to that server. This user-data is made available either from
configured config-drive or from the <link xlink:href="http://docs.openstack.org/admin-guide-cloud/content/section_metadata-service.html">Metadata service</link>.</para>
<para>How this user-data is consumed depends on the image being booted, but the most
commonly used tool for default cloud images is <link xlink:href="http://cloudinit.readthedocs.org/en/latest/">Cloud-init</link>.</para>
<para>Whether the image is using <link xlink:href="http://cloudinit.readthedocs.org/en/latest/">Cloud-init</link> or not, it should be possible to
specify a shell script in the user_data property and have it be executed by
the server during boot:</para>
<programlisting language="yaml">resources:
the_server:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data: |
#!/bin/bash
echo "Running boot script"
# ...</programlisting>
<!---->
<blockquote>
<para><emphasis role="Tip"/>: debugging these scripts it is often useful to view the boot
log using <literal>nova console-log &lt;server-id&gt;</literal> to view the progress of boot
script execution.</para>
</blockquote>
<para>Often there is a need to set variable values based on parameters or resources
in the stack. This can be done with the <literal>str_replace</literal> intrinsic function:</para>
<programlisting language="yaml">parameters:
foo:
default: bar
resources:
the_server:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data:
str_replace:
template: |
#!/bin/bash
echo "Running boot script with $FOO"
# ...
params:
$FOO: {get_param: foo}</programlisting>
<!---->
<blockquote>
<para><emphasis role="Warning"/>: If a stack-update is performed and there are any changes
at all to the content of user_data then the server will be replaced
(deleted and recreated) so that the modified boot configuration can be
run on a new server.</para>
</blockquote>
<para>When these scripts grow it can become difficult to maintain them inside the
template, so the <literal>get_file</literal> intrinsic function can be used to maintain the
script in a separate file:</para>
<programlisting language="yaml">parameters:
foo:
default: bar
resources:
the_server:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data:
str_replace:
template: {get_file: the_server_boot.sh}
params:
$FOO: {get_param: foo}</programlisting>
<!---->
<blockquote>
<para><emphasis role="Tip"/>: <literal>str_replace</literal> can replace any strings, not just strings
starting with <literal>$</literal>. However doing this for the above example is useful
because the script file can be executed for testing by passing in
environment variables.</para>
</blockquote>
<section xml:id="choosing-the-user-data-format">
<title>Choosing the user_data_format</title>
<para>The <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Nova__Server.html">OS::Nova::Server</link></literal> user_data_format property determines how the
user_data should be formatted for the server. For the default value
<literal>HEAT_CFNTOOLS</literal>, the user_data is bundled as part of the heat-cfntools
cloud-init boot configuration data. While <literal>HEAT_CFNTOOLS</literal> is the default
for <literal>user_data_format</literal>, it is considered legacy and <literal>RAW</literal> or
<literal>SOFTWARE_CONFIG</literal> will generally be more appropriate.</para>
<para>For <literal>RAW</literal> the user_data is passed to Nova unmodified. For a <link xlink:href="http://cloudinit.readthedocs.org/en/latest/">Cloud-init</link>
enabled image, the following are both valid <literal>RAW</literal> user-data:</para>
<programlisting language="yaml">resources:
server_with_boot_script:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: RAW
user_data: |
#!/bin/bash
echo "Running boot script"
# ...
server_with_cloud_config:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: RAW
user_data: |
#cloud-config
final_message: "The system is finally up, after $UPTIME seconds"</programlisting>
<para>For <literal>SOFTWARE_CONFIG</literal> user_data is bundled as part of the software config
data, and metadata is derived from any associated
<link linkend="software-deployment-resources">Software deployment resources</link>.</para>
</section>
<section xml:id="signals-and-wait-conditions">
<title>Signals and wait conditions</title>
<para>Often it is necessary to pause further creation of stack resources until the
boot configuration script has notified that it has reached a certain state.
This is usually either to notify that a service is now active, or to pass out
some generated data which is needed by another resource. The resources
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__WaitCondition.html">OS::Heat::WaitCondition</link></literal> and <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SwiftSignal.html">OS::Heat::SwiftSignal</link></literal> both perform
this function using different techniques and tradeoffs.</para>
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__WaitCondition.html">OS::Heat::WaitCondition</link></literal> is implemented as a call to the
<link xlink:href="http://developer.openstack.org/api-ref-orchestration-v1.html">Orchestration API</link> resource signal. The token is created using credentials
for a user account which is scoped only to the wait condition handle
resource. This user is created when the handle is created, and is associated
to a project which belongs to the stack, in an identity domain which is
dedicated to the orchestration service.</para>
<para>Sending the signal is a simple HTTP request, as with this example using <link xlink:href="http://curl.haxx.se/">curl</link>:</para>
<programlisting language="sh">curl -i -X POST -H 'X-Auth-Token: &lt;token&gt;' \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
'&lt;wait condition URL&gt;' --data-binary '&lt;json containing signal data&gt;'</programlisting>
<para>The JSON containing the signal data is expected to be of the following format:</para>
<programlisting language="json">{
"status": "SUCCESS",
"reason": "The reason which will appear in the 'heat event-list' output",
"data": "Data to be used elsewhere in the template via get_attr",
"id": "Optional unique ID of signal"
}</programlisting>
<para>All of these values are optional, and if not specified will be set to the
following defaults:</para>
<programlisting language="json">{
"status": "SUCCESS",
"reason": "Signal &lt;id&gt; received",
"data": null,
"id": "&lt;sequential number starting from 1 for each signal received&gt;"
}</programlisting>
<para>If <literal>status</literal> is set to <literal>FAILURE</literal> then the resource (and the stack) will go
into a <literal>FAILED</literal> state using the <literal>reason</literal> as failure reason.</para>
<para>The following template example uses the convenience attribute <literal>curl_cli</literal>
which builds a curl command with a valid token:</para>
<programlisting language="yaml">resources:
wait_condition:
type: OS::Heat::WaitCondition
properties:
handle: {get_resource: wait_handle}
# Note, count of 5 vs 6 is due to duplicate signal ID 5 sent below
count: 5
timeout: 300
wait_handle:
type: OS::Heat::WaitConditionHandle
the_server:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: RAW
user_data:
str_replace:
template: |
#!/bin/sh
# Below are some examples of the various ways signals
# can be sent to the Handle resource
# Simple success signal
wc_notify --data-binary '{"status": "SUCCESS"}'
# Or you optionally can specify any of the additional fields
wc_notify --data-binary '{"status": "SUCCESS", "reason": "signal2"}'
wc_notify --data-binary '{"status": "SUCCESS", "reason": "signal3", "data": "data3"}'
wc_notify --data-binary '{"status": "SUCCESS", "reason": "signal4", "data": "data4"}'
# If you require control of the ID, you can pass it.
# The ID should be unique, unless you intend for duplicate
# signals to overrite each other. The following two calls
# do the exact same thing, and will be treated as one signal
# (You can prove this by changing count above to 7)
wc_notify --data-binary '{"status": "SUCCESS", "id": "5"}'
wc_notify --data-binary '{"status": "SUCCESS", "id": "5"}'
# Example of sending a failure signal, optionally
# reason, id, and data can be specified as above
# wc_notify --data-binary '{"status": "FAILURE"}'
params:
wc_notify: { get_attr: [wait_handle, curl_cli] }
outputs:
wc_data:
value: { get_attr: [wait_condition, data] }
# this would return the following json
# {"1": null, "2": null, "3": "data3", "4": "data4", "5": null}
wc_data_4:
value: { get_attr: [wait_condition, data, '4'] }
# this would return "data4"</programlisting>
<!---->
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SwiftSignal.html">OS::Heat::SwiftSignal</link></literal> is implemented by creating an Object Storage
API temporary URL which is populated with signal data with an HTTP PUT. The
orchestration service will poll this object until the signal data is available.
Object versioning is used to store multiple signals.</para>
<para>Sending the signal is a simple HTTP request, as with this example using <link xlink:href="http://curl.haxx.se/">curl</link>:</para>
<programlisting language="sh">curl -i -X PUT '&lt;object URL&gt;' --data-binary '&lt;json containing signal data&gt;'</programlisting>
<para>The above template example only needs to have the <literal>type</literal> changed to the
swift signal resources:</para>
<programlisting language="yaml">resources:
signal:
type: OS::Heat::SwiftSignal
properties:
handle: {get_resource: wait_handle}
timeout: 300
signal_handle:
type: OS::Heat::SwiftSignalHandle
# ...</programlisting>
<para>The decision to use <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__WaitCondition.html">OS::Heat::WaitCondition</link></literal> or
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SwiftSignal.html">OS::Heat::SwiftSignal</link></literal> will depend on a few factors:</para>
<itemizedlist>
<listitem>
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SwiftSignal.html">OS::Heat::SwiftSignal</link></literal> depends on the availability of an Object
Storage API</para>
</listitem>
<listitem>
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__WaitCondition.html">OS::Heat::WaitCondition</link></literal> depends on whether the orchestration
service has been configured with a dedicated stack domain (which may depend
on the availability of an Identity V3 API).</para>
</listitem>
<listitem>
<para>The preference to protect signal URLs with token authentication or a
secret webhook URL.</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="software-config-resources">
<title>Software config resources</title>
<para>Boot configuration scripts can also be managed as their own resources. This
allows configuration to be defined once and run on multiple server resources.
These software-config resources are stored and retrieved via dedicated calls
to the <link xlink:href="http://developer.openstack.org/api-ref-orchestration-v1.html">Orchestration API</link>. It is not possible to modify the contents of an
existing software-config resource, so a stack-update which changes any
existing software-config resource will result in API calls to create a new
config and delete the old one.</para>
<para>The resource <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareConfig.html">OS::Heat::SoftwareConfig</link></literal> is used for storing configs
represented by text scripts, for example:</para>
<programlisting language="yaml">resources:
boot_script:
type: OS::Heat::SoftwareConfig
properties:
group: ungrouped
config: |
#!/bin/bash
echo "Running boot script"
# ...
server_with_boot_script:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: RAW
user_data: {get_resource: boot_script}</programlisting>
<para>The resource <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__CloudConfig.html">OS::Heat::CloudConfig</link></literal> allows <link xlink:href="http://cloudinit.readthedocs.org/en/latest/">Cloud-init</link> cloud-config to
be represented as template YAML rather than a block string. This allows
intrinsic functions to be included when building the cloud-config. This also
ensures that the cloud-config is valid YAML, although no further checks for
valid cloud-config are done.</para>
<programlisting language="yaml">parameters:
file_content:
type: string
description: The contents of the file /tmp/file
resources:
boot_config:
type: OS::Heat::CloudConfig
properties:
cloud_config:
write_files:
- path: /tmp/file
content: {get_param: file_content}
server_with_cloud_config:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: RAW
user_data: {get_resource: boot_config}</programlisting>
<!---->
<para>The resource <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__MultipartMime.html">OS::Heat::MultipartMime</link></literal> allows multiple
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareConfig.html">OS::Heat::SoftwareConfig</link></literal> and <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__CloudConfig.html">OS::Heat::CloudConfig</link></literal>
resources to be combined into a single <link xlink:href="http://cloudinit.readthedocs.org/en/latest/">Cloud-init</link> multi-part message:</para>
<programlisting language="yaml">parameters:
file_content:
type: string
description: The contents of the file /tmp/file
other_config:
type: string
description: The ID of a software-config resource created elsewhere
resources:
boot_config:
type: OS::Heat::CloudConfig
properties:
cloud_config:
write_files:
- path: /tmp/file
content: {get_param: file_content}
boot_script:
type: OS::Heat::SoftwareConfig
properties:
group: ungrouped
config: |
#!/bin/bash
echo "Running boot script"
# ...
server_init:
type: OS::Heat::MultipartMime
properties:
parts:
- config: {get_resource: boot_config}
- config: {get_resource: boot_script}
- config: {get_resource: other_config}
server:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: RAW
user_data: {get_resource: server_init}</programlisting>
<!---->
</section>
</section>
<section xml:id="software-deployment-resources">
<?dbhtml stop-chunking?>
<title>Software deployment resources</title>
<para>There are many situations where it is not desirable to replace the server
whenever there is a configuration change. The
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareDeployment.html">OS::Heat::SoftwareDeployment</link></literal> resource allows any number of software
configurations to be added or removed from a server throughout its life-cycle.</para>
<section xml:id="building-custom-image-for-software-deployments">
<title>Building custom image for software deployments</title>
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareConfig.html">OS::Heat::SoftwareConfig</link></literal> resources are used to store software
configuration, and a <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareDeployment.html">OS::Heat::SoftwareDeployment</link></literal> resource is used
to associate a config resource with one server. The <literal>group</literal> attribute on
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareConfig.html">OS::Heat::SoftwareConfig</link></literal> specifies what tool will consume the
config content.</para>
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareConfig.html">OS::Heat::SoftwareConfig</link></literal> has the ability to define a schema of
<literal>inputs</literal> and which the configuration script supports. Inputs are mapped to
whatever concept the configuration tool has for assigning
variables/parameters.</para>
<para>Likewise, <literal>outputs</literal> are mapped to the tool's capability to export structured
data after configuration execution. For tools which do not support this,
outputs can always be written to a known file path for the hook to read.</para>
<para>The <literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareDeployment.html">OS::Heat::SoftwareDeployment</link></literal> resource allows values to be
assigned to the config inputs, and the resource remains in an <literal>IN_PROGRESS</literal>
state until the server signals to heat what (if any) output values were
generated by the config script.</para>
</section>
<section xml:id="custom-image-script">
<title>Custom image script</title>
<para>Each of the following examples requires that the servers be booted with a
custom image. The following script uses diskimage-builder to create an image
required in later examples:</para>
<programlisting language="sh"># Clone the required repositories. Some of these are also available
# via pypi or as distro packages.
git clone https://git.openstack.org/openstack/diskimage-builder.git
git clone https://git.openstack.org/openstack/tripleo-image-elements.git
git clone https://git.openstack.org/openstack/heat-templates.git
# Required by diskimage-builder to discover element collections
export ELEMENTS_PATH=tripleo-image-elements/elements:heat-templates/hot/software-config/elements
# The base operating system element(s) provided by the diskimage-builder
# elements collection. Other values which may work include:
# centos7, debian, opensuse, rhel, rhel7, or ubuntu
export BASE_ELEMENTS="fedora selinux-permissive"
# Install and configure the os-collect-config agent to poll the heat service
# for configuration changes to execute
export AGENT_ELEMENTS="os-collect-config os-refresh-config os-apply-config"
# heat-config installs an os-refresh-config script which will invoke the
# appropriate hook to perform configuration. The element heat-config-script
# installs a hook to perform configuration with shell scripts
export DEPLOYMENT_BASE_ELEMENTS="heat-config heat-config-script"
# Install a hook for any other chosen configuration tool(s).
# Elements which install hooks include:
# heat-config-cfn-init, heat-config-puppet, or heat-config-salt
export DEPLOYMENT_TOOL=""
# The name of the qcow2 image to create, and the name of the image
# uploaded to the OpenStack image registry.
export IMAGE_NAME=fedora-software-config
# Create the image
diskimage-builder/bin/disk-image-create vm $BASE_ELEMENTS $AGENT_ELEMENTS \
$DEPLOYMENT_BASE_ELEMENTS $DEPLOYMENT_TOOL -o $IMAGE_NAME.qcow2
# Upload the image, assuming valid credentials are already sourced
glance image-create --disk-format qcow2 --container-format bare \
--name $IMAGE_NAME &lt; $IMAGE_NAME.qcow2</programlisting>
<!---->
</section>
<section xml:id="configuring-with-scripts">
<title>Configuring with scripts</title>
<para>The <link linkend="custom-image-script">Custom image script</link> already includes the <literal>heat-config-script</literal> element
so the built image will already have the ability to configure using shell
scripts.</para>
<para>Config inputs are mapped to shell environment variables. The script can
communicate outputs to heat by writing to the file
<literal>$heat_outputs_path.&lt;output name&gt;</literal>. See the following example for a script
which expects inputs <literal>foo</literal>, <literal>bar</literal> and generates an output <literal>result</literal>.</para>
<programlisting language="yaml">resources:
config:
type: OS::Heat::SoftwareConfig
properties:
group: script
inputs:
- name: foo
- name: bar
outputs:
- name: result
config: |
#!/bin/sh -x
echo "Writing to /tmp/$bar"
echo $foo &gt; /tmp/$bar
echo -n "The file /tmp/$bar contains `cat /tmp/$bar` for server $deploy_server_id during $deploy_action" &gt; $heat_outputs_path.result
echo "Written to /tmp/$bar"
echo "Output to stderr" 1&gt;&amp;2
deployment:
type: OS::Heat::SoftwareDeployment
properties:
config:
get_resource: config
server:
get_resource: server
input_values:
foo: fooooo
bar: baaaaa
server:
type: OS::Nova::Server
properties:
# flavor, image etc
user_data_format: SOFTWARE_CONFIG
outputs:
result:
value:
get_attr: [deployment, result]
stdout:
value:
get_attr: [deployment, deploy_stdout]
stderr:
value:
get_attr: [deployment, deploy_stderr]
status_code:
value:
get_attr: [deployment, deploy_status_code]</programlisting>
<!---->
<blockquote>
<para><emphasis role="Tip"/>: A config resource can be associated with multiple deployment
resources, and each deployment can specify the same or different values
for the <literal>server</literal> and <literal>input_values</literal> properties.</para>
</blockquote>
<para>As can be seen in the <literal>outputs</literal> section of the above template, the
<literal>result</literal> config output value is available as an attribute on the
<literal>deployment</literal> resource. Likewise the captured stdout, stderr and status_code
are also available as attributes.</para>
</section>
<section xml:id="configuring-with-os-apply-config">
<title>Configuring with os-apply-config</title>
<para>The agent toolchain of <literal>os-collect-config</literal>, <literal>os-refresh-config</literal> and
<literal>os-apply-config</literal> can actually be used on their own to inject heat stack
configuration data into a server running a custom image.</para>
<para>The custom image needs to have the following to use this approach:</para>
<itemizedlist>
<listitem>
<para>All software dependencies installed</para>
</listitem>
<listitem>
<para><link xlink:href="https://github.com/openstack/os-refresh-config">os-refresh-config</link> scripts to be executed on configuration changes</para>
</listitem>
<listitem>
<para><link xlink:href="https://github.com/openstack/os-apply-config">os-apply-config</link> templates to transform the heat-provided config data into
service configuration files</para>
</listitem>
</itemizedlist>
<para>The projects <link xlink:href="https://github.com/openstack/tripleo-image-elements">tripleo-image-elements</link> and <link xlink:href="https://github.com/openstack/tripleo-heat-templates">tripleo-heat-templates</link> demonstrate
this approach.</para>
</section>
<section xml:id="configuring-with-cfn-init">
<title>Configuring with cfn-init</title>
<para>Likely the only reason to use the <literal>cfn-init</literal> hook is to migrate templates
which contain <link xlink:href="http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-init.html">AWS::CloudFormation::Init</link> metadata without needing a
complete rewrite of the config metadata. It is included here as it introduces
a number of new concepts.</para>
<para>To use the <literal>cfn-init</literal> tool the <literal>heat-config-cfn-init</literal> element is required
to be on the built image, so <link linkend="custom-image-script">Custom image script</link> needs to be modified with
the following:</para>
<programlisting language="sh">export DEPLOYMENT_TOOL="heat-config-cfn-init"</programlisting>
<!---->
<para>Configuration data which used to be included in the
<literal>AWS::CloudFormation::Init</literal> section of resource metadata is instead moved
to the <literal>config</literal> property of the config resource, as in the following
example:</para>
<programlisting language="yaml">resources:
config:
type: OS::Heat::StructuredConfig
properties:
group: cfn-init
inputs:
- name: bar
config:
config:
files:
/tmp/foo:
content:
get_input: bar
mode: '000644'
deployment:
type: OS::Heat::StructuredDeployment
properties:
name: 10_deployment
signal_transport: NO_SIGNAL
config:
get_resource: config
server:
get_resource: server
input_values:
bar: baaaaa
other_deployment:
type: OS::Heat::StructuredDeployment
properties:
name: 20_other_deployment
signal_transport: NO_SIGNAL
config:
get_resource: config
server:
get_resource: server
input_values:
bar: barmy
server:
type: OS::Nova::Server
properties:
image: {get_param: image}
flavor: {get_param: flavor}
key_name: {get_param: key_name}
user_data_format: SOFTWARE_CONFIG</programlisting>
<!---->
<para>There are a number of things to note about this template example:</para>
<itemizedlist>
<listitem>
<para><literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__StructuredConfig.html">OS::Heat::StructuredConfig</link></literal> is like
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__SoftwareConfig.html">OS::Heat::SoftwareConfig</link></literal> except that the <literal>config</literal> property
contains structured YAML instead of text script. This is useful for a
number of other configuration tools including ansible, salt and
os-apply-config.</para>
</listitem>
<listitem>
<para><literal>cfn-init</literal> has no concept of inputs, so <literal>{get_input: bar}</literal> acts as a
placeholder which gets replaced with the
<literal><link xlink:href="http://docs.openstack.org/hot-reference/content/OS__Heat__StructuredDeployment.html">OS::Heat::StructuredDeployment</link></literal><literal>input_values</literal> value when the
deployment resource is created.</para>
</listitem>
<listitem>
<para><literal>cfn-init</literal> has no concept of outputs, so specifying
<literal>signal_transport: NO_SIGNAL</literal> will mean that the deployment resource will
immediately go into the <literal>CREATED</literal> state instead of waiting for a
completed signal from the server.</para>
</listitem>
<listitem>
<para>The template has 2 deployment resources deploying the same config with
different <literal>input_values</literal>. The order these are deployed in on the server
is determined by sorting the values of the <literal>name</literal> property for each
resource (10_deployment, 20_other_deployment)</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="configuring-with-puppet">
<title>Configuring with puppet</title>
<para>The <link xlink:href="http://puppetlabs.com/">puppet</link> hook makes it possible to write configuration as puppet manifests
which are deployed and run in a masterless environment.</para>
<para>To specify configuration as puppet manifests the <literal>heat-config-puppet</literal>
element is required to be on the built image, so <link linkend="custom-image-script">Custom image script</link> needs
to be modified with the following:</para>
<programlisting language="sh">export DEPLOYMENT_TOOL="heat-config-puppet"</programlisting>
<!---->
<programlisting language="yaml">resources:
config:
type: OS::Heat::SoftwareConfig
properties:
group: puppet
inputs:
- name: foo
- name: bar
outputs:
- name: result
config:
get_file: example-puppet-manifest.pp
deployment:
type: OS::Heat::SoftwareDeployment
properties:
config:
get_resource: config
server:
get_resource: server
input_values:
foo: fooooo
bar: baaaaa
server:
type: OS::Nova::Server
properties:
image: {get_param: image}
flavor: {get_param: flavor}
key_name: {get_param: key_name}
user_data_format: SOFTWARE_CONFIG
outputs:
result:
value:
get_attr: [deployment, result]
stdout:
value:
get_attr: [deployment, deploy_stdout]</programlisting>
<!---->
<para>This demonstrates the use of the <literal>get_file</literal> function, which will attach the
contents of the file <literal>example-puppet-manifest.pp</literal>, containing:</para>
<programlisting language="puppet">file { 'barfile':
ensure =&gt; file,
mode =&gt; '0644',
path =&gt; '/tmp/$::bar',
content =&gt; '$::foo',
}
file { 'output_result':
ensure =&gt; file,
path =&gt; '$::heat_outputs_path.result',
mode =&gt; '0644',
content =&gt; 'The file /tmp/$::bar contains $::foo',
}</programlisting>
<!---->
</section>
</section>
</section>