“repeat” function for HOT templates

https://blueprints.launchpad.net/heat/+spec/repeat-function

This specification introduces a “repeat” control structure for HOT templates.

Problem description

Parameters of type “comma_delimited_list” are useful to define lists of items, but the HOT template syntax does not provide any way to map or transform those items.

For example, consider the use of a parameter to specify a list of ports to include in a security group:

parameters:
  ports:
    type: comma_delimited_list
    label: ports
    default: "80,443,8080"

The desired outcome, which is currently not possible to obtain, is that the above parameter can be used to construct a resource as follows:

resources:
  security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: web_server_security_group
      rules:
        - protocol: tcp
          port_range_min: 80
          port_range_max: 80
        - protocol: tcp
          port_range_min: 443
          port_range_max: 443
        - protocol: tcp
          port_range_min: 8080
          port_range_max: 8080

Proposed change

This proposal introduces a new function called repeat that iterates over the elements of a list, replacing each item into a given template.

Following the security group example from the previous section, the repeat function would be used as follows:

resources:
  security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: web_server_security_group
      rules:
        repeat:
          for_each:
            <%port%>: { get_param: ports }
          template:
            protocol: tcp
            port_range_min: <%port%>
            port_range_max: <%port%>

Below is another example in which this function enables a solution that is currently impossible to implement:

resources:
  my_server:
    type: OS::Nova::Server
    properties:
      networks:
        repeat:
          for_each:
            <%net_name%>: { get_param: networks }
          template:
            network: <%net_name%>

In this example a list of networks that an instance needs to be attached to is given as a list in a parameter.

Another interesting possibility is to generate permutations of two or more lists. For example, the security group example above can be extended to also support parametrized protocols as follows:

resources:
  security_group:
    type: OS::Neutron::SecurityGroup
    properties:
      name: web_server_security_group
      rules:
        repeat:
          for_each:
            <%port%>: { get_param: ports }
            <%protocol%>: { get_param: protocols }
          template:
            protocol: <%protocol%>
            port_range_min: <%port%>

The for_each argument specifies the loop variable and the list to iterate on as a key-value pair. The loop variable has to be chosen carefully, as any occurrences will be replaced with each of the items in the list in each iteration.

If more than one key/value pair is included in the for_each section, then the iterations are done over all the permutations of the elements in the given lists, similar to how nested loops work in most programming languages.

The result of the repeat function is a new list, with its elements set to the data generated in each of the loop iterations. When a single list is given, the size of the resulting list is equal to the size of the input list. When multiple lists are given as input, the size of the resulting list will be equal to the sizes of all the input lists multipled.

Alternatives

An alternative that was explored was to extend the str_replace function to accomodate this functionality, but in the end it was agreed that there are significant differences between the two usages.

Implementation

Assignee(s)

Primary assignee:
miguelgrinberg

Milestones

Target Milestone for completion:
Kilo-3

Work Items

  • Write the repeat function.
  • Documentation.
  • Unit tests.

Dependencies

None