Monday 5 August 2013

Heat Nested Resource Introspection

The following topic has come up a couple of times lately on IRC, so I thought I'd put down some details describing $subject, in a more permanent place :)

Nested Stack Resources, Primer/Overview

So, Heat has a really powerful feature, which is the ability to nest stack definitions, such that one top-level stack definition may recursively define one or more nested stacks.

There are two ways to define a nested stack:
  1. Explicitly reference a nested stack template in the parent template (via our implementation of the AWS::CloudFormation::Stack resource type, see this example template)
  2. Create a new resource type, which internally defines a nested stack (an example of this is our simple loadbalancer resource, our implementation of the AWS::ElasticLoadBalancing::LoadBalancer resource)
There is actually a third way (Provider templates), but that's a bleeding-edge feature so I'm not considering it in this post.

In both cases, what Heat creates internally is a real stack, referenced by the parent stack via a unique ID. Since the Heat API allows you to request details for a specific stack using a stack UUID, that means you can use the heat API introspection operations to access information about the nested stack in the exact same way as you do for the top level stack.


Worked Example

If I create a stack, I can use various introspection operations, either via the Heat ReST API, or more conveniently via the "heat" CLI tool provided by python-heatclient (which uses the Heat ReST API):

> heat list
+--------------------------------------+------------+---------------+----------------------+
| id                                   | stack_name | stack_status  | creation_time        |
+--------------------------------------+------------+---------------+----------------------+
| faaca636-ed2f-44d9-b228-909c35b37215 | as123      | CREATE_FAILED | 2013-08-05T09:29:49Z |
+--------------------------------------+------------+---------------+----------------------+

I can use the heat introspection operations using either the stack_name (which Heat requires to be unique per tenant) or the unique id interchangeably:

E.g

heat stack-show as123

provides the exact same information as

heat stack-show faaca636-ed2f-44d9-b228-909c35b37215

If the stack contains a resource based on a nested stack (or a directly defined nested stack), we can look up the stack ID like this:

heat resource-list as123
+--------------------------+-----------------------------------------+-----------------+-------------
| logical_resource_id      | resource_type                           | resource_status | updated_time       
+--------------------------+-----------------------------------------+-----------------+-------------
| CfnUser                  | AWS::IAM::User                          | CREATE_COMPLETE | 2013-08-05T09:29:52Z |
| LaunchConfig             | AWS::AutoScaling::LaunchConfiguration   | CREATE_COMPLETE | 2013-08-05T09:29:56Z |
| WebServerKeys            | AWS::IAM::AccessKey                     | CREATE_COMPLETE | 2013-08-05T09:29:56Z |
| ElasticLoadBalancer      | AWS::ElasticLoadBalancing::LoadBalancer | CREATE_FAILED   | 2013-08-05T09:40:49Z |
| MEMAlarmHigh             | AWS::CloudWatch::Alarm                  | INIT_COMPLETE   | 2013-08-05T16:57:54Z |
| MEMAlarmLow              | AWS::CloudWatch::Alarm                  | INIT_COMPLETE   | 2013-08-05T16:57:54Z |
| WebServerGroup           | AWS::AutoScaling::AutoScalingGroup      | INIT_COMPLETE   | 2013-08-05T16:57:54Z |
| WebServerScaleDownPolicy | AWS::AutoScaling::ScalingPolicy         | INIT_COMPLETE   | 2013-08-05T16:57:54Z |
| WebServerScaleUpPolicy   | AWS::AutoScaling::ScalingPolicy         | INIT_COMPLETE   | 2013-08-05T16:57:54Z |
+--------------------------+-----------------------------------------+-----------------+--------

Here we can see we have a resource "ElasticLoadBalancer", which is of type "AWS::ElasticLoadBalancing::LoadBalancer", which as I mentioned earlier is defined internally via a nested stack, whose ID we can access via the heat resource-show option, which gives details of the specified resource:

> heat resource-show as123 ElasticLoadBalancer
+------------------------+--------------------------------
| Property               | Value                                                                            
+------------------------+--------------------------------
| description            |                                                                                                                                           |
| links                  | http://localhost:8004/v1/1938f0707fe04b58b0053040d4a0fe06/stacks/as123/faaca636-ed2f-44d9-b228-909c35b37215/resources/ElasticLoadBalancer |
|                        | http://localhost:8004/v1/1938f0707fe04b58b0053040d4a0fe06/stacks/as123/faaca636-ed2f-44d9-b228-909c35b37215                               |
| logical_resource_id    | ElasticLoadBalancer                                                                                                                       |
| physical_resource_id   | 60a1ee88-61fe-4bfb-a020-5837e35a42c9                                                                                                      |
| required_by            | WebServerGroup                                                                                                                            |
| resource_status        | CREATE_FAILED                                                                                                                             |
| resource_status_reason | Error: Resource create failed: WaitConditionTimeout: 0 of 1 received                                                                      |
| resource_type          | AWS::ElasticLoadBalancing::LoadBalancer                                                                                                   |
| updated_time           | 2013-08-05T09:40:49Z                                                                                                                      |
+------------------------+--------------------------------

Aha! physical_resource_id provides a UUID for the resource, which just so happens to be the UUID of the underlying nested stack ;)

So you can use that UUID to do introspection operations on the nested stack, e.g:

heat resource-list 60a1ee88-61fe-4bfb-a020-5837e35a42c9
+---------------------+------------------------------------------+-----------------+----------------------+
| logical_resource_id | resource_type                            | resource_status | updated_time         |
+---------------------+------------------------------------------+-----------------+----------------------+
| CfnLBUser           | AWS::IAM::User                           | CREATE_COMPLETE | 2013-08-05T09:29:52Z |
| WaitHandle          | AWS::CloudFormation::WaitConditionHandle | CREATE_COMPLETE | 2013-08-05T09:29:52Z |
| latency_watcher     | AWS::CloudWatch::Alarm                   | CREATE_COMPLETE | 2013-08-05T09:29:52Z |
| CfnLBAccessKey      | AWS::IAM::AccessKey                      | CREATE_COMPLETE | 2013-08-05T09:29:54Z |
| LB_instance         | AWS::EC2::Instance                       | CREATE_COMPLETE | 2013-08-05T09:30:49Z |
| WaitCondition       | AWS::CloudFormation::WaitCondition       | CREATE_FAILED   | 2013-08-05T09:40:49Z |
+---------------------+------------------------------------------+-----------------+----------------------+

So we can see that the reason the stack failed was the nested stack WaitCondition resource failed (which we already knew from the top level status string, but hopefully you get the point ;)