Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python3.10 / hcl2 / doc fix support #98

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
__pycache__/
*.py[cod]
*$py.class
.vscode/*
.vscode

# C extensions
*.so
Expand Down
7 changes: 3 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
ARG TF_VERSION=0.12.12
ARG PYTHON_VERSION=3.7
ARG TF_VERSION=1.0.0
ARG PYTHON_VERSION=3.8.6

FROM hashicorp/terraform:$TF_VERSION AS terraform

Expand All @@ -10,12 +10,11 @@ RUN pip install -U pip ply \
COPY --from=terraform /bin/terraform /bin/terraform
COPY ./docker-entrypoint.sh /bin/docker-entrypoint.sh
RUN chmod +x /bin/docker-entrypoint.sh

WORKDIR /src
COPY . .
RUN pip install -e .

WORKDIR /data

RUN echo $(timeout 15 blast-radius --serve --port 5001; test $? -eq 124) > /output.txt
ENTRYPOINT ["/bin/docker-entrypoint.sh"]
CMD ["blast-radius", "--serve"]
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ You can read more details in the [documentation](doc/embedded.md)
## Implementation Details

*Blast Radius* uses the [Graphviz][] package to layout graph diagrams,
[PyHCL](https://github.com/virtuald/pyhcl) to parse [Terraform][] configuration,
[python-hcl2](https://github.com/amplify-education/python-hcl2) to parse [Terraform][] configuration,
and [d3.js](https://d3js.org/) to implement interactive features and animations.

## Further Reading
Expand Down
12 changes: 2 additions & 10 deletions blastradius/handlers/dot.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# standard libraries
import json
import re
import subprocess
Expand Down Expand Up @@ -316,7 +315,6 @@ def focus(self, node):
graph [fontname = "courier new",fontsize=8];
node [fontname = "courier new",fontsize=8];
edge [fontname = "courier new",fontsize=8];

{# just the root module #}
{% for cluster in clusters %}
subgraph "{{cluster}}" {
Expand All @@ -335,11 +333,9 @@ def focus(self, node):
{% endfor %}
}
{% endfor %}

{# non-root modules #}
{% for node in nodes %}
{% if node.module != 'root' %}

{% if node.collapsed %}
"{{node.label}}" [ shape=none, margin=0, id={{node.svg_id}} label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0">
{% for module in node.modules %}<TR><TD>(M) {{module}}</TD></TR>{% endfor %}
Expand All @@ -354,9 +350,7 @@ def focus(self, node):
</TABLE>>];
{% endif %}
{% endif %}

{% endfor %}

{% for edge in edges %}
{% if edge.edge_type == EdgeType.NORMAL %}"{{edge.source}}" -> "{{edge.target}}" {% if edge.fmt %} [{{edge.fmt}}] {% endif %}{% endif %}
{% if edge.edge_type == EdgeType.LAYOUT_SHOWN %}"{{edge.source}}" -> "{{edge.target}}" {% if edge.fmt %} [{{edge.fmt}}] {% endif %}{% endif %}
Expand Down Expand Up @@ -451,7 +445,7 @@ def _module(label):
try:
if not re.match(r'(\[root\]\s+)*module\..*', label):
return 'root'
m = re.match(r'(\[root\]\s+)*(?P<module>\S+)\.(?P<type>\S+)\.\S+', label)
m = re.match(r'(\[root\]\s+)*(?P<module>\S+)\.(?P<type>\S+)\.?\S+', label)
return m.groupdict()['module']
except:
raise Exception("None: ", label)
Expand Down Expand Up @@ -520,6 +514,4 @@ def __init__(self, source, target, fmt=None, edge_type=EdgeType.NORMAL):

def __iter__(self):
for key in {'source', 'target', 'svg_id', 'edge_type'}:
yield (key, getattr(self, key))


yield (key, getattr(self, key))
75 changes: 33 additions & 42 deletions blastradius/handlers/terraform.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,66 +5,57 @@
import re

# 3rd party libraries
import hcl # hashicorp configuration language (.tf)
import hcl2 as hcl # hashicorp configuration language (.tf)

class Terraform:
"""Finds terraform/hcl files (*.tf) in CWD or a supplied directory, parses
them with pyhcl, and exposes the configuration via self.config."""
them with hcl2, and exposes the configuration via self.config."""

def __init__(self, directory=None, settings=None):
self.settings = settings if settings else {}

# handle the root module first...
self.directory = directory if directory else os.getcwd()
#print(self.directory)
self.config_str = ''

self.config = {}
iterator = iglob( self.directory + '/*.tf')
for fname in iterator:
with open(fname, 'r', encoding='utf-8') as f:
self.config_str += f.read() + ' '
config_io = io.StringIO(self.config_str)
self.config = hcl.load(config_io)
try:
raw = io.StringIO(f.read())
parsed = hcl.load(raw)
self.config.update(parsed)
except Exception as e:
raise RuntimeError('Exception occurred while parsing ' + fname) from e


# then any submodules it may contain, skipping any remote modules for
# the time being.
# then any submodules it may contain
self.modules = {}
if 'module' in self.config:
for name, mod in self.config['module'].items():
for name, mod in [(k, v) for x in self.config['module'] for (k, v) in x.items()]:
if 'source' not in mod:
continue
source = mod['source']
# '//' used to refer to a subdirectory in a git repo
if re.match(r'.*\/\/.*', source):
continue
# '@' should only appear in ssh urls
elif re.match(r'.*\@.*', source):
continue
# 'github.com' special behavior.
elif re.match(r'github\.com.*', source):
continue
# points to new TFE module registry
elif re.match(r'app\.terraform\.io', source):
continue
# bitbucket public and private repos
elif re.match(r'bitbucket\.org.*', source):
continue
# git::https or git::ssh sources
elif re.match(r'^git::', source):
continue
# git:// sources
elif re.match(r'^git:\/\/', source):
continue
# Generic Mercurial repos
elif re.match(r'^hg::', source):
continue
# Public Terraform Module Registry
elif re.match(r'^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+', source):
continue
# AWS S3 buckets
elif re.match(r's3.*\.amazonaws\.com', source):
continue
# fixme path join. eek.
self.modules[name] = Terraform(directory=self.directory+'/'+source, settings=mod)
source = mod['source'][0]

path = os.path.join(self.directory, source)
if os.path.exists(path):
# local module
self.modules[name] = Terraform(directory=path, settings=mod)
else:
# remote module
# Since terraform must be init'd before use, we can
# assume remote modules have been downloaded to .terraform/modules
path = os.path.join(os.getcwd(), '.terraform', 'modules', name)

# Get the subdir if any
match = re.match(r'.*(\/\/.*)(?!:)', source)
if re.match(r'.*\/(\/.*)(?!:)', source):
path = os.path.join(path, match.groups()[0])

self.modules = Terraform(directory=path, settings=mod)




def get_def(self, node, module_depth=0):
Expand Down
170 changes: 1 addition & 169 deletions blastradius/server/static/example/demo-1/demo-1.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading