Skip to content

Commit

Permalink
Merge pull request #1 from cidrblock/add_couchdb
Browse files Browse the repository at this point in the history
Add couchDB functionality
  • Loading branch information
Bradley A. Thornton authored Nov 29, 2017
2 parents 602d887 + 34ffe9e commit f9b6e65
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 38 deletions.
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ RUN apk add gcc libffi-dev musl-dev openssl-dev sshpass make
# RUN apk add py-crypto python-dev

# Install td4a
RUN pip install td4a
RUN pip install td4a==1.2

# Clear out extras
RUN rm -rf /var/cache/apk/*

RUN mkdir /filter_plugins
# Start td4a
CMD [ "python", "-m", "td4a", "-cff", "/filter_plugins" ]
CMD [ "python", "-m", "td4a", "-f", "/filter_plugins" ]
38 changes: 36 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,22 @@ TD4A will look for custom plugins at /filter_plugins within the container. Pass
```
docker run -p 5000:5000 -v `pwd`/my_filter_plugins:/filter_plugins cidrblock/td4a
```
The docker file can be found here:

https://hub.docker.com/r/cidrblock/td4a/

pip:
#### pip:
```
$ virtualenv venv
$ source venv/bin/activate
$ pip install td4a
$ python -m td4a
```

The pip package can be found here:

https://pypi.python.org/pypi/td4a

### Usage

The interface is browser based and has been tested using Chrome. If your browser did not automatically open when TD4A was started, you can visit http://127.0.0.1:5000 to see the interface.
Expand All @@ -58,7 +64,35 @@ The UI is broken into three sections:
TD4A can load custom filters from a directory specified from the command line:

```
python -m td4a -cff ./filter_plugins
python -m td4a -f ./filter_plugins
```

### Saving docs and building links

TD4A has the ability to store data and templates in a CouchDB. This is disabled by defualt.

The CouchDB needs to previously created.

To enable link support, and add the link button to the UI, set the following environ variables:

#### docker:

```
docker run -p 5000:5000 \
-v `pwd`/my_filter_plugins:/filter_plugins \
-e "COUCH_USERNAME=admin" \
-e "COUCH_PASSWORD=password" \
-e "COUCH_URL=http://192.168.1.5:5984/td4a" \
-e "ENABLE_LINK=true" \
td4a
```

#### pip:
```
export COUCH_USERNAME=admin
export COUCH_PASSWORD=password
export COUCH_URL=http://localhost:5984/td4a
export ENABLE_LINK=True
```

### Python version
Expand Down
5 changes: 5 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ansible==2.4.1.0
Flask==0.12.2
netaddr==0.7.19
Twisted==17.9.0
requests==2.18.4
12 changes: 7 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
from setuptools import setup

setup(name='td4a',
version='1.1',
version='1.2',
description='A browser based jinja template renderer',
url='http://github.com/cidrblock/td4a',
author='Bradley A. Thornton',
author_email='brad@thethorntons.net',
license='MIT',
include_package_data=True,
packages=[
'td4a'
'td4a'
],
install_requires=[
'ansible==2.4.1.0',
'Flask==0.12.2',
'netaddr==0.7.19',
'ansible==2.4.1.0',
'Flask==0.12.2',
'netaddr==0.7.19',
'Twisted==17.9.0',
'requests==2.18.4'
],
zip_safe=False)
102 changes: 83 additions & 19 deletions td4a/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@
import traceback
import webbrowser
import ansible.plugins.filter as apf
from flask import Flask, request, jsonify, current_app
import jinja2
from jinja2 import Environment, TemplateSyntaxError, TemplateAssertionError
from flask import Flask, request, jsonify
import requests
from jinja2 import Environment, TemplateSyntaxError
import yaml

APP = Flask(__name__, static_url_path='')
Expand Down Expand Up @@ -92,15 +92,14 @@ def render_template(data, template):
"details": str(error)
}
}, None
else:
return {"Error":
{
"in": "unknown",
"title": "Unexpected error occured.",
"line_number": 'unknown',
"details": "Please see the console for details."
}
}, None
return {"Error":
{
"in": "unknown",
"title": "Unexpected error occured.",
"line_number": 'unknown',
"details": "Please see the console for details."
}
}, None

def load_data(str_data):
""" load yaml from string
Expand Down Expand Up @@ -152,24 +151,89 @@ def render():
result = ""
return jsonify({"result": result})

def main():
""" main
@APP.route('/link', methods=['POST'])
def link():
""" Save the documents in a couchdb and returns an id
"""
payload = request.json
if APP.args.username and APP.args.password and APP.args.url:
username = APP.args.username
password = APP.args.password
url = APP.args.url
response = requests.post("%s" % url, json=payload, auth=(username, password))
return jsonify({"id": response.json()['id']})
return jsonify({"error": "true"})


@APP.route('/retrieve', methods=['GET'])
def retrieve():
""" return a doc from the couchdb
"""
docid = request.args.get('id')
if APP.args.username and APP.args.password and APP.args.url:
username = APP.args.username
password = APP.args.password
url = APP.args.url
response = requests.get("%s/%s?include_docs=true" % (url, docid), auth=(username, password))
if response.status_code != 200:
return jsonify({"error": "true"})
doc = response.json()
return jsonify({"data": doc['data'], "jinja": doc['template']})
return jsonify({"error": "true"})

@APP.route('/enablelink', methods=['GET'])
def enablelink():
""" check to see if the link button should be enabled
"""
return jsonify({"enabled": APP.args.enable_links})

def parse_args():
""" parse the cli args and add environ
"""
parser = ArgumentParser(description='',
formatter_class=RawTextHelpFormatter)
parser.add_argument('-cff', action="store", dest="custom_filters",
parser.add_argument('-f', action="store", dest="custom_filters",
required=False,
help="a folder containing custom filters")
help="A folder containing custom filters.")
args = parser.parse_args()
args.username = os.environ.get('COUCH_USERNAME', False)
args.password = os.environ.get('COUCH_PASSWORD', False)
args.enable_links = os.environ.get('ENABLE_LINK', False)
args.url = os.environ.get('COUCH_URL', False)
return args

def load_filters(args):
""" load the filters
"""
ansible_filters = pack_ansible_filters()
if args.custom_filters:
custom_filters = pack_custom_filters(args.custom_filters)
APP.filters = ansible_filters + custom_filters
filters = ansible_filters + custom_filters
else:
APP.filters = ansible_filters
filters = ansible_filters
return filters

def main():
""" main
"""
APP.args = parse_args()
APP.filters = load_filters(APP.args)
reactor_args = {}
def run_twisted_wsgi():
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
resource = WSGIResource(reactor, reactor.getThreadPool(), APP)
site = Site(resource)
reactor.listenTCP(5000, site)
reactor.run(**reactor_args)
if APP.debug:
reactor_args['installSignalHandlers'] = 0
import werkzeug.serving
run_twisted_wsgi = werkzeug.serving.run_with_reloader(run_twisted_wsgi)
url = "http://127.0.0.1:5000"
threading.Timer(1.25, lambda: webbrowser.open(url)).start()
APP.run(debug=False, host='0.0.0.0')
run_twisted_wsgi()

if __name__ == '__main__':
main()
Binary file modified td4a/static/favicon.ico
Binary file not shown.
15 changes: 8 additions & 7 deletions td4a/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@
<body ng-app="mainController" ng-controller='main'>
<split direction="horizontal" ng-transclude>
<split-area size="33" ng-transclude>
<div class="menubar">
<div class="title">DATA</div>
<div class="buttons">
<div class="column">
<div class="menubar">
<div class="title">DATA</div>
<div class="buttons">
</div>
</div>
</div>
<div class="editor" id="dataEditor" ng-model="template.data" ui-codemirror="codemirror.dataOptions">
<div class="editor" id="dataEditor" ng-model="template.data" ui-codemirror="codemirror.dataOptions"></div>
</div>
</split-area>
<split-area size="33" ng-transclude>
Expand All @@ -46,8 +47,8 @@
<div class="column">
<div class="menubar">
<div class="title">RESULT</div>
<div class="buttons">
<md-button class="md-small md-raised" target="_self" ng-href="/?{{ params }}">
<div class="buttons" ng-cloak>
<md-button class="md-small md-raised" ng-disabled="!enableLink" ng-click="link()">
<md-icon aria-label="code" class="material-icons">link</md-icon>
link
</md-button>
Expand Down
60 changes: 57 additions & 3 deletions td4a/static/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,68 @@ app.controller('main', function($scope, $http, $window, $mdToast, $timeout, $rou
$scope.template.jinja = response.data
}
})
} else if ('data' in $location.search() && 'template' in $location.search()) {
$scope.template = { data: $location.search().data, jinja: $location.search().template }
} else if ('id' in $location.search()) {
$http({
method : 'GET',
url : `/retrieve?id=${$location.search().id}`,
})
.then(function(response) {
if (response.status == 200) {
if ('data' in response.data && 'jinja' in response.data) {
$scope.template = response.data
} else {
var toast = $mdToast.simple()
.textContent("Document ID not found")
.action('close')
.highlightAction(true)
.highlightClass('md-warn')
.position('top right')
.hideDelay('3000');
$mdToast.show(toast)
}
}
})
} else {
$scope.template = localStorageService.get('data')
};


$http({
method : 'GET',
url : 'enablelink',
})
.then(function(response) {
if (response.status == 200) {
$scope.enableLink = response.data.enabled
}
})

$scope.link = function() {
$http({
method : 'POST',
url : '/link',
data : { "data": $scope.template.data, "template": $scope.template.jinja },
headers : { 'Content-Type': 'application/json' }
})
.then(function(response) {
if (response.status == 200) {
if ('id' in response.data) {
$location.search(`id=${response.data.id}`)
} else {
var toast = $mdToast.simple()
.textContent("Error building link for document.")
.action('close')
.highlightAction(true)
.highlightClass('md-warn')
.position('top right')
.hideDelay('3000');
$mdToast.show(toast)
}
}
})
}; //link

$scope.render = function() {
$scope.params = `data=${encodeURIComponent($scope.template.data)}&template=${encodeURIComponent($scope.template.jinja)}`
$scope.renderButton = true;
if ('line_number' in $scope.error) {
$scope.error.codeMirrorEditor.removeLineClass($scope.error.line_number, 'wrap', 'error');
Expand Down

0 comments on commit f9b6e65

Please sign in to comment.