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

blast-radius extended inital commit #97

Open
wants to merge 1 commit 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
120 changes: 75 additions & 45 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,82 @@
# Blast Radius

[![CircleCI](https://circleci.com/gh/28mm/blast-radius/tree/master.svg?style=svg)](https://circleci.com/gh/28mm/blast-radius/tree/master)
[![PyPI version](https://badge.fury.io/py/BlastRadius.svg)](https://badge.fury.io/py/BlastRadius)

[terraform]: https://www.terraform.io/
[examples]: https://28mm.github.io/blast-radius-docs/
[overlayfs]:

_Blast Radius_ is a tool for reasoning about [Terraform][] dependency graphs
with interactive visualizations.
_Blast Radius_ is a tool for reasoning about [Terraform][] dependency graphs with interactive visualizations.

Use _Blast Radius_ to:

* __Learn__ about *Terraform* or one of its providers through real [examples][]
* __Document__ your infrastructure
* __Reason__ about relationships between resources and evaluate changes to them
* __Interact__ with the diagram below (and many others) [in the docs][examples]
* __Interact__ with the diagrams below (and many others) [in the docs][examples]

![screenshot](doc/blastradius-interactive.png)
---

## Blast Radius
![screenshot](doc/blastradiusext.png)

---

## Prerequisites

* [Graphviz](https://www.graphviz.org/)
* [Python](https://www.python.org/) 3.7 or newer
* [Terraform][] 0.12.x or newer

> __Note:__ For macOS you can `brew install graphviz`
---

## Quickstart

The fastest way to get up and running with *Blast Radius* is to install it with
`pip` to your pre-existing environment:
For fastest way to get up and running with blast-radius is as follows:

```sh
pip install blastradius
```
* Download and install the wheel files from the [release](https://github.com/nishubharti/blast-radius/releases)

Once installed just point *Blast Radius* at any initialized *Terraform*
directory:
```
copy the blastradius/server/static/images folder to the terraform directory
```
install the wheel file
```
easy_install blastradius-0.1.25.1-py3-none-any.whl
```
or
```
pip3 blastradius-0.1.25.1-py3-none-any.whl
```

```sh
blast-radius --serve /path/to/terraform/directory
```
* Enrich the Blast Radius diagrams with the outcome of Terraform plan actions:
```
terraform plan --out tfplan.binary
terraform show -json tfplan.binary > tfplan.json
```
for including cost and policy information into blast-radius cost.json and policy.json file need to be stored into the working directory.

* Once installed just point Blast Radius at any initialized Terraform directory:
```sh
blast-radius --serve /path/to/terraform/directory
```

* Go to the browser link http://127.0.0.1:5000/ to view the Blast Radius diagram for the terraform file.

![BlastRadius](doc/blastradiusext.png)

The enrichments include - information from the Plan file, State file , cost file and time file .
Click the columns adjacent to the Resource Names to view these enrichment in the side panel view.

![BlastRadiusExt](doc/blast-radius-ext.png)

And you will shortly be rewarded with a browser link http://127.0.0.1:5000/.
---

## Build your own wheel file

* Create wheel file of this repo
```sh
python3 setup.py sdist bdist_wheel
```
---

## Docker

Expand All @@ -50,34 +85,28 @@ And you will shortly be rewarded with a browser link http://127.0.0.1:5000/.

To launch *Blast Radius* for a local directory by manually running:

```sh
docker run --rm -it -p 5000:5000 \
-v $(pwd):/data:ro \
--security-opt apparmor:unconfined \
--cap-add=SYS_ADMIN \
28mm/blast-radius
```
* create a dockerhub account
```sh
docker build -t <dockerhub_username>/blast-radius:v1 .
docker push <dockerhub_username>/blast-radius:v1
```

A slightly more customized variant of this is also available as an example
[docker-compose.yml](./examples/docker-compose.yml) usecase for Workspaces.
```sh
docker run --cap-add=SYS_ADMIN -dit -p 5000:5000 -v <path>:/data:ro <dockerhub_username>/blast-radius:v1
```

### Docker configurations

*Terraform* module links are saved as _absolute_ paths in relative to the
project root (note `.terraform/modules/<uuid>`). Given these paths will vary
betwen Docker and the host, we mount the volume as read-only, assuring we don't
ever interfere with your real environment.
betwen Docker and the host, we mount the volume as read-only, assuring we don't ever interfere with your real environment.

However, in order for *Blast Radius* to actually work with *Terraform*, it needs
to be initialized. To accomplish this, the container creates an [overlayfs][]
that exists within the container, overlaying your own, so that it can operate
However, in order for *Blast Radius* to actually work with *Terraform*, it needs to be initialized as well as planned compulsory. To accomplish this, the container creates an [overlayfs][] that exists within the container, overlaying your own, so that it can operate
independently. To do this, certain runtime privileges are required --
specifically `--cap-add=SYS_ADMIN`.

For more information on how this works and what it means for your host, check
out the [runtime privileges][privileges] documentation.

#### Docker & Subdirectories
### Docker & Subdirectories

If you organized your *Terraform* project using stacks and modules,
*Blast Radius* must be called from the project root and reference them as
Expand All @@ -101,14 +130,12 @@ It consists of 3 modules `foo`, `bar` and `dead`, followed by one `beef` stack.
To apply *Blast Radius* to the `beef` stack, you would want to run the container
with the following:

```sh
$ cd project
$ docker run --rm -it -p 5000:5000 \
-v $(pwd):/data:ro \
--security-opt apparmor:unconfined \
--cap-add=SYS_ADMIN \
28mm/blast-radius --serve stacks/beef
```
```sh
$ cd project
$ docker run --cap-add=SYS_ADMIN -dit -p 5000:5000 -v <pathofdirectory>:/data:ro <dockerhub_username>/blast-radius:v1
```

---

## Embedded Figures

Expand All @@ -121,11 +148,14 @@ You will need the following:

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,
and [d3.js](https://d3js.org/) to implement interactive features and animations.
[hcl] to parse [Terraform][] configuration, and [d3.js](https://d3js.org/) to implement interactive features and animations.

---

## Further Reading

Expand Down
21 changes: 14 additions & 7 deletions bin/blast-radius
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ from blastradius.handlers.apply import Apply
from blastradius.handlers.terraform import Terraform
from blastradius.server.server import app

from blastradius.server.server import simple_graph

def main():

parser = parser = argparse.ArgumentParser(description='blast-radius: Interactive Terraform Graph Visualizations')
Expand All @@ -28,7 +30,8 @@ def main():
output_group.add_argument('--dot', action='store_const', const=True, default=False, help='print the graphviz/dot representation of the Terraform graph')
output_group.add_argument('--svg', action='store_const', const=True, default=False, help='print the svg representation of the Terraform graph')
output_group.add_argument('--serve', action='store_const', const=True, default=False, help='spins up a webserver with an interactive Terraform graph')

output_group.add_argument('--svg-ext', action='store_const', const=True, default=False, help='download the simple svg of modified svg representation of the Terraform graph')

parser.add_argument('--graph', type=str, help='`terraform graph` output (defaults to stdin)', default=sys.stdin)

# options to limit, re-focus, and re-center presentation of larger graphs.
Expand All @@ -49,11 +52,11 @@ def main():
app.run(host='0.0.0.0',port=args.port)
sys.exit(0)

elif args.json or args.dot or args.svg:
elif args.json or args.dot or args.svg or args.svg_ext:
if args.graph is sys.stdin:
dot = DotGraph('', file_contents=sys.stdin.read())
else:
dot = DotGraph(args.graph)
dot = DotGraph('',file_contents=simple_graph())

# we might not want to show every node in the depedency graph
# specifying --module-depth is an easy way to limit detail
Expand All @@ -76,18 +79,22 @@ def main():
parser.print_help()
sys.exit(1)
dot.focus(f_node)

if args.json:
tf = Terraform(args.directory)
for node in dot.nodes:
node.definition = tf.get_def(node)

if args.json:
print(dot.json())
f = open("visualization.json", "a")
f.write(dot.json())
f.close()
elif args.dot:
print(dot.dot())
elif args.svg:
print(dot.svg())
elif args.svg_ext:
f = open("visualization.svg", "a")
f.write(dot.svg())
f.close()
else:
parser.print_help()

Expand Down
88 changes: 34 additions & 54 deletions blastradius/handlers/apply.py
Original file line number Diff line number Diff line change
@@ -1,55 +1,35 @@
# standard libraries
import re
from __future__ import print_function
import json

# 1st party libraries
from blastradius.graph import Graph, Node, Edge
from blastradius.handlers.dot import DotNode
from blastradius.util import Re

class Apply(Graph):
def __init__(self, filename):
self.filename = filename
self.contents = ''
self.nodes = [] # we can populate this,
self.edges = [] # but not this!

ansi_escape = re.compile(r'\x1b[^m]*m')
with open(filename, 'r') as f:
self.contents = ansi_escape.sub('', f.read())

# example output:
#
# aws_vpc.default: Creation complete after 4s (ID: vpc-024f7a64)
# ...
# aws_key_pair.auth: Creating...
# fingerprint: "" => "<computed>"
# key_name: "" => "default-key"
# ...
# aws_instance.web: Still creating... (10s elapsed)
# aws_instance.web: Still creating... (20s elapsed)
# aws_instance.web (remote-exec): Connecting to remote host via SSH...
# aws_instance.web (remote-exec): Host: 1.2.3.4
# aws_instance.web (remote-exec): User: ubuntu
# ...

node_begin_re =r'(?P<name>\S+)\:\s+Creating...'
node_compl_re = r'(?P<name>\S+)\:\s+Creation\s+complete\s+after\s+(?P<duration>\S+)\s+'
node_still_re = r'(?P<name>\S+)\:\s+Still\s+creating\.\.\.\s+\((?P<duration>\S+)\s+'

for line in self.contents.splitlines():

r = Re()
if r.match(node_begin_re, line):




break




print(self.contents)


import sys
import jinja2
import json
import subprocess
import os.path
from os import path

class Apply():
def __init__(self,filename=None):
self.apply_resource_info = []
#reading from state file
if filename == None:
if(path.exists("terraform.tfstate")):
with open("terraform.tfstate", 'r') as f:
data = json.load(f)
else:
data = ""
else:
with open(filename, 'r') as f:
data = json.load(f)

if (data) :
for _, var in enumerate(data["resources"]):
temp_data = dict()
temp_data = var
self.apply_resource_info.append(temp_data)

else:
self.apply_resource_info.append("not applied")

def json(self):
my_json_string = json.dumps(self.apply_resource_info,indent=4, sort_keys=True)
return my_json_string
30 changes: 30 additions & 0 deletions blastradius/handlers/controls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import json
import os.path
from os import path

class Controls():
def __init__(self,filename=None):
self.resource_controls_info = []
if filename == None:
if(path.exists("policy.json")):
with open("policy.json", 'r') as f:
data = json.load(f)
else:
data = ""
else:
with open(filename, 'r') as f:
data = json.load(f)

if (data) :
for _, var in enumerate(data["resources"]):
temp_data = dict()
temp_data = var
self.resource_controls_info.append(temp_data)

else:
self.resource_controls_info.append("not available")


def json(self):
controls_json = json.dumps(self.resource_controls_info,indent=4, sort_keys=True)
return controls_json
Loading