Wednesday 2 October 2013

Heat Providers/Environments 101

I've recently been experimenting with some cool new features we've added to Heat over the Havana cycle, testing things out in preparation for the Havana Release.

One potentially very powerful new abstraction is the Provider Resource method of defining nested stack resources.  Combined with the new environments capability to map template resource names to non-default implementations, it provides a very flexible way for both users and those deploying Heat to define custom resources based on Heat templates.

Firstly let me clarify what nested stacks are, following on from my previous post, and why we decided to provide this native interface to the functionality, rather than something similar to the existing Cloudformation compatible resource interface.

So nested stack resources enable you to specify a URL of another heat template, which will be used to create another Heat stack, owned by the stack which defines the AWS::CloudFormation::Stack.  This provides a way to implement composed Heat templates, and to reuse logically related template snippets.

The interface looks like this (using the new native HOT template syntax):

resources:
  nested:
    type: AWS::CloudFormation::Stack
    properties:
      TemplateURL: http://somewhere/something.yaml

There are several disadvantages to this interface:
  • Hard-coded URLs in your template
  • You have to have the nested templates accessible via a web-server somewhere
  • Hard to transparently substitute different versions of the nested implementation (without sedding! ;)
  • Passing Parameters is kind-of awkward (pass a nested map of parameter values)
  • Does not provide a way to define resources based on stack templates.
So we noticed something, which was the interface to stack templates is essentially very similar to the interface to resources, stack parameters map to resource properties, and stack outputs map to resource attributes.  Provider resources leverage this symmetry to provide a more flexible (and arguably easier to use) native interface to nested stack resources.

Ok, so how does it work!  Probably the easiest explanation is a simple worked example.

Say you define a simple template, like this example in our heat-templates repo, and you want to reuse it, as a nested stack via the Providers functionality, you simply need to do this:

Define an environment

The environment is used to map resource names to templates, and optionally can be used to define common stack parameters, so that they don't need to be passed every time you create a stack.

A user may pass an environment when creating (or updating) a stack, and a global environment may also be specified by the deployer (default location is /etc/heat/environment.d), using the same syntax.  The environment may override existing resources.

The resource_registry section is used to map resource names to template files:

resource_registry:
    My::WP::Server: https://raw.github.com/openstack/heat-templates/master/hot/F18/WordPress_Native.yaml

Or you can refer to a local file (local to the user running python-heatclient, which reads the file and attaches the content to the Heat API call creating the stack in the "files" parameter of the request):

resource_registry:
    My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml

There are also some other possibilities, for example aliasing one resource name to another, which are described in our documentation.

Create Stack

Now you can simply create a stack template which references My::WP::Server:

# cat minimal_test.yaml 
heat_template_version: 2013-05-23

description: >
  Heat WordPress template, demonstrating Provider Resource.

parameters:
  user_key:
    type: string
    description : Name of a KeyPair to enable SSH access to the instance

resources:
  wordpress_instance:
    type: My::WP::Server
    properties:
        key_name: {get_param: user_key}

With an environment file:

# cat env_minimal.yaml 
resource_registry:
    My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml

And then create the stack:

# heat stack-create test_stack1 --template-file=./minimal_test.yaml --environment-file=./env_minimal.yaml --parameters="user_key=$USER_key"

Optionally you could also specify the key via the environment too:


# cat env_key.yaml 
parameters:
    user_key: userkey
resource_registry:
    My::WP::Server: file:///home/shardy/git/heat-templates/hot/F18/WordPress_Native.yaml


heat stack-create test_stack2 --template-file=./minimal_test.yaml --environment-file=./env_key.yaml

This would create the nested template with a key_name parameter of "userkey"

So hopefully that provides an overview of this new feature, for more info please see our documentation and we're planning to add some example Provider/Environment examples to our example template repository soon.

Finally, kudos to Angus Salkeld, who implemented the majority of this functionality, thanks Angus! :)