-
-
Save tamsky/c7df19684e5605023f2e to your computer and use it in GitHub Desktop.
Process YAML and write Packer JSON to STDOUT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python2 | |
# | |
# Usage: packer-config my-template.yaml | packer build - | |
# | |
# Constructs a Packer JSON configuration file from the specified YAML | |
# template file and writes it to STDOUT. | |
# | |
# The YAML template format adds some flexibility and readability by | |
# adding comments and an !include directive, allowing for the | |
# following template syntax: | |
# | |
# variables: | |
# - !include includes/common-vars.yaml # last key:value wins | |
# # so these will override common: | |
# - ssh_user: ubuntu | |
# - ssh_pass: ubuntu | |
# - hostname: host | |
# builders: | |
# - !include build/ubuntu-12.04.kvm.yaml | |
# - !include build/ubuntu-12.04.ami.yaml | |
# provisioners: | |
# - !include provision/ubuntu-12.04-base.yaml | |
# - !include provision/ops-deploy.yaml | |
# - !include provision/java7.yaml | |
# - type: shell | |
# environment_vars: [ 'HOST_NAME={{user `hostname`}}' ] | |
# script: host/setup.sh | |
# | |
# In addition to the !include directive, the resulting YAML is | |
# post-processed to flatten lists-of-lists in the top level sections, | |
# allowing includes to contain multiple entries (i.e. running two shell | |
# scripts in a common provisioner.) | |
# | |
# Author: jeremy@jongsma.org | |
# Modifications: tamsky@github | |
# License: MPLv2 | |
import sys | |
import os | |
import yaml | |
import json | |
class IncludeLoader(yaml.Loader): | |
""" | |
Custom YAML loader that adds an !include constructor for including | |
other YAML files relative to the current file being parsed. | |
Example: | |
section: !include other/file.yaml | |
""" | |
def __init__(self, stream): | |
self._root = os.path.abspath(os.path.split(stream.name)[0]) | |
super(IncludeLoader, self).__init__(stream) | |
def include(self, node): | |
path = self.construct_scalar(node) | |
filename = path if path[0] == '/' else os.path.join(self._root, path) | |
with open(filename, 'r') as f: | |
return yaml.load(f, IncludeLoader) | |
IncludeLoader.add_constructor('!include', IncludeLoader.include) | |
def parse_yaml(template): | |
""" | |
Constructs a Packer JSON configuration file from the specified YAML | |
template and returns it as a string. | |
The YAML template format adds some flexibility and readability by | |
adding comments and an !include directive, allowing for the | |
following template syntax: | |
variables: | |
- !include includes/common-vars.yaml # last key:value wins | |
# so these will override common: | |
- ssh_user: ubuntu | |
- ssh_pass: ubuntu | |
- hostname: host | |
builders: | |
- !include build/ubuntu-12.04.kvm.yaml | |
- !include build/ubuntu-12.04.ami.yaml | |
provisioners: | |
- !include provision/ubuntu-12.04-base.yaml | |
- !include provision/ops-deploy.yaml | |
- !include provision/java7.yaml | |
- type: shell | |
script: app/setup.sh | |
In addition to the !include directive, the resulting YAML is | |
post-processed to flatten lists-of-lists in the top level sections, | |
allowing includes to contain lists of multiple entries (i.e. running | |
two shell scripts in a common provisioner.) | |
""" | |
with open(template, 'r') as infile: | |
parsed = yaml.load(infile, IncludeLoader) | |
# Flatten sections to allow including lists of steps in each include | |
for section in ['variables']: | |
if section in parsed and parsed[section] is not None and isinstance(parsed[section], list): | |
finalMap = {} | |
for d in parsed[section]: | |
finalMap.update(d) | |
parsed[section]=finalMap | |
# Flatten sections to allow including lists of steps in each include | |
for section in ('builders', 'provisioners', 'post-processors'): | |
if section in parsed and parsed[section] is not None: | |
parsed[section] = [f for l in [e if isinstance(e, list) else [e] | |
for e in parsed[section]] for f in l] | |
return json.dumps(parsed, indent=2, separators=(',', ': ')) | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
sys.stderr.write("ERROR: No template specified") | |
sys.exit(1) | |
if not os.path.exists(sys.argv[1]): | |
std.stderr.write("ERROR: Template not found: %s" % sys.argv[1]) | |
sys.exit(2) | |
sys.stdout.write(parse_yaml(sys.argv[1])) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment