#!/usr/bin/env python
# -*- coding: utf-8 -*-

import os
import sys

import json
import yaml


def merge_dicts(a, b):
    if not (isinstance(a, dict) and isinstance(b, dict)):
        raise ValueError("Error merging variables: '{}' and '{}'".format(
            type(a).__name__, type(b).__name__
        ))

    result = a.copy()
    for key, val in b.items():
        if isinstance(result.get(key), dict):
            result[key] = merge_dicts(a[key], b[key])
        else:
            result[key] = val

    return result


def load_manifest(manifest_file):
    with open(manifest_file) as f:
        manifest = yaml.load(f)

    if 'inherit' in manifest:
        inherit_file = os.path.join(os.path.dirname(manifest_file), manifest.pop('inherit'))
        manifest = merge_dicts(load_manifest(inherit_file), manifest)

    return manifest


def load_variables(vars_files):
    variables = {}
    for vars_file in vars_files:
        with open(vars_file) as f:
            variables = merge_dicts(variables, json.load(f))

    return {
        k.upper(): json.dumps(v) if isinstance(v, (dict, list)) else v
        for k, v in variables.items()
    }


def paas_manifest(manifest_file, *vars_files):
    """Generate a PaaS manifest file from a Jinja2 template"""

    manifest = load_manifest(manifest_file)
    variables = load_variables(vars_files)

    manifest['env'].update(variables)

    return yaml.dump(manifest, default_flow_style=False, allow_unicode=True)


if __name__ == "__main__":
    print(paas_manifest(*sys.argv[1:]))

Add a script to generate PaaS manifest with environment variables
Add a script to generate PaaS manifest with environment variables

`./scripts/generate_manifest.py` takes a path to a PaaS manifest file
and a list of variable files and prints a single CloudFoundry manifest.

The generated manifest replaces all `inherit` keys by loading the data
from parent manifests. This allows us to pipe the script output directly
to CF CLI, without saving it to disk, which minimises the risk of it
being accidentally included in the deployment artefact. The combined
manifest might differ from the results produced by CF CLI itself, so
the original manifest shouldn't normally be used on its own.

After combining the manifests the script will load and parse all listed
variable files and add them to the manifest's `env` by merging the files
together in the order they were listed (so in case of any key conflicts
the latest file overwrites previous values), upper-casing keys and
processing any list or dictionary values with `json.dumps`, so that they
can be set as environment variables.

This gives us a full list of environment variables that were previously
parsed from the CloudFoundry user services data.
