From d6e799565d3eac26734f4a96ba202804f4d29720 Mon Sep 17 00:00:00 2001 From: Stefan Reimer Date: Thu, 4 Jun 2020 16:32:17 +0100 Subject: [PATCH] New feature: create-docs rendering markdown documentation for rendered stacks --- CHANGES.md | 4 ++++ cloudbender/__init__.py | 2 +- cloudbender/cli.py | 13 ++++++++++++ cloudbender/jinja.py | 12 +++++++---- cloudbender/stack.py | 47 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 73 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index ca5a6fa..6a15928 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # Changelog +## 0.8.3 +- New Feature: `create-docs` command + Renders a markdown documentation next to the rendered stack templated by parsing parameters and other relvant metadata + ## 0.8.2 - Bug fix release to allow empty stack configs again diff --git a/cloudbender/__init__.py b/cloudbender/__init__.py index 607a825..d9fa39f 100644 --- a/cloudbender/__init__.py +++ b/cloudbender/__init__.py @@ -2,7 +2,7 @@ import logging __author__ = "Stefan Reimer" __email__ = "stefan@zero-downtimet.net" -__version__ = "0.8.2" +__version__ = "0.8.3" # Set up logging to ``/dev/null`` like a library is supposed to. diff --git a/cloudbender/cli.py b/cloudbender/cli.py index b026894..80c67a4 100644 --- a/cloudbender/cli.py +++ b/cloudbender/cli.py @@ -94,6 +94,18 @@ def outputs(cb, stack_names, multi, include, values): s.get_outputs(include, values) +@click.command() +@click.argument("stack_names", nargs=-1) +@click.option("--multi", is_flag=True, help="Allow more than one stack to match") +@click.pass_obj +def create_docs(cb, stack_names, multi): + """ Parses all documentation fragments out of rendered templates creating docs/*.md file """ + + stacks = _find_stacks(cb, stack_names, multi) + for s in stacks: + s.create_docs() + + @click.command() @click.argument("stack_name") @click.argument("change_set_name") @@ -240,6 +252,7 @@ cli.add_command(delete) cli.add_command(clean) cli.add_command(create_change_set) cli.add_command(outputs) +cli.add_command(create_docs) if __name__ == '__main__': cli(obj={}) diff --git a/cloudbender/jinja.py b/cloudbender/jinja.py index 14c764e..08721fa 100644 --- a/cloudbender/jinja.py +++ b/cloudbender/jinja.py @@ -179,10 +179,14 @@ def JinjaEnv(template_locations=[]): extensions=['jinja2.ext.loopcontrols', 'jinja2.ext.do']) # undefined=SilentUndefined, - jinja_loaders = [] - for _dir in template_locations: - jinja_loaders.append(jinja2.FileSystemLoader(str(_dir))) - jenv.loader = jinja2.ChoiceLoader(jinja_loaders) + if template_locations: + jinja_loaders = [] + for _dir in template_locations: + jinja_loaders.append(jinja2.FileSystemLoader(str(_dir))) + jenv.loader = jinja2.ChoiceLoader(jinja_loaders) + + else: + jenv.loader = jinja2.BaseLoader() jenv.globals['include_raw'] = include_raw_gz jenv.globals['raise'] = raise_helper diff --git a/cloudbender/stack.py b/cloudbender/stack.py index b502b44..5861581 100644 --- a/cloudbender/stack.py +++ b/cloudbender/stack.py @@ -316,6 +316,53 @@ class Stack(object): except ClientError as e: raise e + def create_docs(self): + """ Read template, parse documentation fragments, eg. parameter description + and create a mardown doc file for the stack + same idea as eg. helm-docs for values.yaml + """ + + self.read_template_file() + + doc_template = """ +{{ name }} +=== +{{ description }} + +{% if dependencies %} +## Dependencies +{% for d in dependencies|sort %} +- {{ d }} +{% endfor %} +{% endif %} + +{% if dependencies %} +## Parameters +| Parameter | Type | Default | Format | Description | +|-----------|------|---------|--------|-------------| +{% for p in parameters.keys() %} +| {{ p }} | {{ parameters[p]['Type'] }} | {{ parameters[p]['Default'] }} | {{ parameters[p]['AllowedValues'] or parameters[p]['AllowedPattern']}} | {{ parameters[p]['Description'] }} | +{% endfor %} +{% endif %} +""" + + jenv = JinjaEnv() + template = jenv.from_string(doc_template) + data = {} + + data['name'] = self.stackname + data['description'] = self.cfn_data['Description'] + data['dependencies'] = self.dependencies + + if 'Parameters' in self.cfn_data: + data['parameters'] = self.cfn_data['Parameters'] + + doc_file = os.path.join(self.ctx['template_path'], self.rel_path, self.stackname.upper() + ".md") + + with open(doc_file, 'w') as doc_contents: + doc_contents.write(template.render(**data)) + logger.info('Wrote documentation for %s to %s', self.stackname, doc_file) + def resolve_parameters(self): """ Renders parameters for the stack based on the source template and the environment configuration """