Heat/Blueprints/native-tools-bootstrap-config

= Heat Bootstrap Config = --steve-stevebaker (talk) 04:36, 12 September 2013 (UTC)

NOTE: This specification is still under discussion and has not been endorsed by the heat project.

Background
The purpose of bootstrapping an orchestrated server is to provide just enough package installation and configuration to be able to hand off to a full Configuration Management tool. Traditionally this has been achieved in heat templates with cfn-init from the heat-cfntools package however this has the following issues:
 * It needs to be invoked explicitly from the template UserData
 * It is limited to providing cfn compatible functionality
 * It acts on a single block of metadata which is associated with the instance resource, which makes it difficult to write templates that are composable (ie, templates which split the application configuration from the architecture the application is being orchestrated into).

Heat's current underlying implementation relies on cloud-init to deliver the Metadata and the UserData script to the instance on boot.

This blueprint proposes that the functionality of a selection of configuration management tools be directly exposed in heat templates to fulfill server config tasks.

Relationship to HOT blueprints
Blueprint hot-software-config is flagged as depending on this blueprint. However in reality the blueprints are interdependent and need to evolve cooperatively.

For the purposes of this specification it is assumed that:
 * blueprint native-tools-bootstrap-config specifies the internal implementation of how HOT components are consumed by server/instance resources, and the interaction between heat and orchestrated servers
 * blueprint hot-software-config specifies the syntax and semantics of HOT components

However, this spec states the opinion that HOT config components should transparently expose whatever underlying bootstrap/configuration-management is being used. This has the following benefits:
 * There is no need to create and maintain yet another configuration management abstraction
 * Heat can explicitly claim that it is not itself a configuration management tool by documenting the tools it natively supports
 * Configuration management tools which use YAML as their native format have potential for tighter integration with Heat templates.

HOT examples in this spec are meant for illustration purposes only, and the specifics may change in the final implementation.

Components for Configuration Management tools
Components can be implemented to expose the features of a particular Configuration Management tool. The implementation of each component will deliver the metadata that contains the configuration data. It will also generate the appropriate cloud-init user data to perform the following actions on the orchestrated server:
 * invoke the tool to perform configuration on the supplied configuration metadata

For these components, the actual cloud-init user data is derived from the abstracted component data. This would risk the component only working on particular versions of cloud-init. In this case, the component author could choose which versions of cloud-init to support and clearly document that.

os-collect-config
Whatever configuration management tool is used, the tool needs configuration data to act on, and the tool needs to be invoked whenever that data changes (except for cloud-init, which is invoked on boot only).

Traditionally heat has delivered the initial metadata via cloud-init and cfn-hup could be used to poll for changes and trigger any actions based on those changes. os-collect-config was written for the TripleO project to be a daemon which polls for metadata and runs the configured CM tool when the metadata changes. For rest of the components described in this spec, it is assumed that os-collect-config is the tool which fetches metadata and invokes the CM tool when that metadata changes.

CM tools to consider
Implementing components for some combination of the the following tools would be enough to mark this blueprint as completed:
 * cloud-init
 * heat-cfntools cfn-init
 * os-refresh-config / os-apply-config

Other configuration management tools could be considered for implementing a HOT component in their own blueprint. They are included in this spec to demonstrate how far the scope of this cloud-init approach could be extended. Such tools include:
 * Puppet
 * Ansible
 * Chef
 * SaltStack
 * Windows PowerShell

Component type namespace
As components are a representation of self-contained configuration management, there is nothing OpenStack specific in a component. Therefore the top-level namespace for component types should be Heat:: rather than OS::Heat::.

Heat::CloudInit
It is proposed that each subclass of Component can provide its configured compute resources with some metadata, along with a collection of cloud-init user data that will perform the desired configuration. The CloudInit component will do this by directly exposing appropriate cloud-init user data types. This may be limited to #cloud-config files and user data scripts.

cloud-init version compatibility
Heat has been hindered in the past in attempts to use newer cloud-init features due to the older version of cloud-init used on the launched OS. It is hoped that this will be mitigated by transparently exposing cloud-init configuration into the template, so that it is up to the user to only use the features supported by the cloud-init of the server images they launch.

Stack updates
cloud-init is only for first-boot configuration. This means that updating a stack with different Heat::CloudInit data cannot be applied to a running server. In light of this situation, the options are:
 * do not implement Heat::CloudInit; only implement components for configuration management tools which can be run repeatedly for the lifecycle of the server
 * make the server UpdateReplace if an attempt is made to change existing Heat::CloudInit config data

Examples
cloud-init examples could be represented in HOT templates in the following way: components: # Writing out arbitrary files write_foo_bar: type: Heat::CloudInit config: cloud_config: write_files: - encoding: b64 content: foo_bar_contents owner: root:root path: /foo/bar permissions: '0644' # Adding a yum repository repo_epel_5_testing: type: Heat::CloudInit config: cloud_config: yum_repos: epel-testing: baseurl: http://download.fedoraproject.org/pub/epel/testing/5/$basearch enabled: false failovermethod: priority gpgcheck: true gpgkey: file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL name: Extra Packages for Enterprise Linux 5 - Testing # Install arbitrary packages install_things: type: Heat::CloudInit config: cloud_config: packages: - pwgen - pastebinit - [libpython2.7, 2.7.3-0ubuntu3. some_user_data: # User-Data Script type: Heat::CloudInit config: user_data: | #!/bin/sh echo "Hello World. The time is now $(date -R)!" | tee /root/output.txt ... resources: the_server: type: OS::Nova::Server properties: components: - name: install_things - name: write_foo_bar params: foo_bar_contents: get_param: the_file_contents - name: repo_epel_5_testing - name: some_user_data

Heat::SoftwareConfig
Heat software configuration is traditionally executed with cfn-init AWS::CloudFormation::Init metadata. The Heat::SoftwareConfig component would perform configuration management using cfn-init. This provides a very basic representation of software configuration, but for many users it will meet their configuration needs. Existing guides for Heat describe how to create heat-cfntools enabled images, and some distributions are now packaging heat-cfntools as part of the default install for cloud images.

We should consider not implementing this component at all, so that bootstrap config is handled by Heat::CloudInit and any further configuration is handled by the chosen configuration management component. In this case, the use of cfn-init would be limited to AWS compatible templates.

An example usage: components: install_mysql: type: Heat::SoftwareConfig config: packages: yum: mysql: [] mysql-server: [] services: systemd: mysqld: {enabled: 'true', ensureRunning: 'true'} install_wordpress: type: Heat::SoftwareConfig config: packages: yum: httpd: [] wordpress: [] services: systemd: httpd: {enabled: 'true', ensureRunning: 'true'} foo_file: type: Heat::SoftwareConfig config: files: /tmp/foo/bar: content: 'foo_bar_contents' ... resources: the_server: type: OS::Nova::Server properties: components: - name: install_mysql - name: install_wordpress - name: foo_file params: foo_bar_contents: get_param: the_file_contents

This would provide the_server with the following:
 * the metadata which contains the cfn-init AWS::CloudFormation::Init that specifies the configuration tasks
 * a script to be executed by os-collect-config which actually runs /opt/aws/bin/cfn-init

All Heat::SoftwareConfig components for a server will be combined into a single unit of AWS::CloudFormation::Init metadata. AWS::CloudFormation::Init configSets can be used to represent the work to do for each component and enforce the order they get executed in. For example, the metadata built for the_server specified above would be the json equivalent of: AWS::CloudFormation::Init: configSets: components: [install_mysql, install_wordpress, foo_file] install_mysql: packages: yum: mysql: [] mysql-server: [] services: systemd: mysqld: {enabled: 'true', ensureRunning: 'true'} install_wordpress: packages: yum: httpd: [] wordpress: [] services: systemd: httpd: {enabled: 'true', ensureRunning: 'true'} foo_file: files: /tmp/foo/bar: content: 'foo_bar_contents'

Which will be invoked by os-collect-config running

Heat::Config
os-refresh-config and os-apply-config are the lightweight configuration management tools used by the TripleO project.

TripleO have a strong preference for using golden images, where the following will already be on the image that the server is launched with:
 * os-refresh-config, os-apply-config and os-collect-config
 * all other packages required to be on the server
 * os-refresh-config run-parts scripts which are executed in order, triggered by metadata changes
 * os-apply-config templates which transform metadata into application configuration files

In this case, the only purpose of a Heat::Config component would be to deliver the metadata without any modification or cloud-init user data, for example: components: mysql_configure: type: Heat::Config config: mysql: create-users: - database: keystone username: keystone password: KeystoneDBPassword - database: heat username: heat password: HeatDBPassword ... resources: the_server: type: OS::Nova::Server properties: components: - name: mysql_configure params: KeystoneDBPassword: get_param: KeystoneDBPassword HeatDBPassword: get_param: HeatDBPassword

As Heat::Config has no underlying tool which executes configuration scripts with passed variables, the component parameters KeystoneDBPassword and HeatDBPassword get substituted with their real values using HOT function fn_replace style substitution. Alternatively, os-apply-config could be modified to support passing variables on invocation.

For the cases where the user can't or won't use golden images, it should be possible for Heat::Config to work on pristine images which lack installed packages, os-refresh-config scripts and os-apply-config templates. These could be installed with a Heat::CloudInit component. Consider the following example: components: mysql_install: type: Heat::CloudInit config: cloud_config: packages: - mysql-server-5.5 - mysql-client-5.5 write_files: - path: /opt/stack/os-apply-config/templates/etc/mysql/dbusers.json content: - path: /opt/stack/os-config-refresh/post-configure.d/50-mysql-users permissions: '0755' content: | #!/usr/bin/python # Assert users that came from metadata config # ...  mysql_configure: type: Heat::Config config: mysql: create-users: - database: keystone username: keystone password: KeystoneDBPassword - database: heat username: heat password: HeatDBPassword ... resources: the_server: type: OS::Nova::Server properties: components: - name: mysql_install - name: mysql_configure params: KeystoneDBPassword: get_param: KeystoneDBPassword HeatDBPassword: get_param: HeatDBPassword

os-apply-config currently acts on a single top-level block of metadata, which means that some design decisions will need to be made to handle multiple Heat::Config components on a server. Some options are:
 * A shallow or deep merge strategy to end up with a single block of metadata to invoke os-config-applier
 * Modify os-apply-config to accept a list of metadata blocks to apply in order instead of a single block

Heat::Puppet
A component which allows Puppet to be used for configuration of the server, either by specifying a puppet master or by allowing a manifest to be specified and applied on boot.

Puppet with master server
components: puppet_slave: type: Heat::Puppet ... resources: the_server: type: OS::Nova::Server properties: components: - name: puppet_slave params: puppet_server: get_param: puppet_server puppet_certname: "%i.%f" puppet_ca_cert: get_param: puppet_cert

The params puppet_server, puppet_certname and puppet_ca_cert will be documented special params which configure the puppet agent. It is up to the Heat::Puppet implementation to ensure that if puppet_server changes during a stack update, the puppet agent on the server will be reconfigured with the new puppet_server.

Puppet serverless
components: the_component: type: Heat::Puppet config: manifest: | file {'testfile': path   => '/tmp/testfile', ensure => present, mode   => 0640, content => $testfile_content, } ... resources: the_server: type: OS::Nova::Server properties: components: - name: the_component params: testfile_content: 'I am a test file.'

Since puppet has its own concept of manifest variables, values passed into the component params will be mapped to puppet variables when the manifest is invoked.

Heat::Ansible
Ansible is generally considered to be an agent-less configuration tool which configures servers by SSHing into them from where the playbook is invoked. However Ansible also supports pull-mode playbooks where each server repeatedly polls for a playbook, and executes the playbook in local mode if it has changed.

This mode of operation maps well to Heat, where the playbook can be delivered on boot via cloud-init and subsequent playbook changes can be picked up by os-collect-config to re-run ansible-playbook.

Given that Ansible is written in python and the playbook format is YAML, it has the potential to integrate well with Heat.

components: the_component: type: Heat::Ansible config: user: root tasks: - name: ensure apache is at the latest version yum: pkg=httpd state=latest - name: write the apache config file template: src=/srv/httpd.j2 dest=/etc/httpd.conf notify: - restart apache - name: ensure apache is running service: name=httpd state=started handlers: - name: restart apache service: name=httpd state=restarted ... resources: the_server: type: OS::Nova::Server properties: components: - name: the_component params: http_port: get_param: apache_port max_clients: get_param: max_clients

Ansible also has its own concept of variables, so as for puppet, variables can be set with the values of the specified component params.