diff --git a/Dockerfile b/Dockerfile index e462f05..02eaf75 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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" ] diff --git a/README.md b/README.md index 69b1986..eccb49f 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,11 @@ 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 @@ -35,6 +37,10 @@ $ 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. @@ -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 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..847b3da --- /dev/null +++ b/requirements.txt @@ -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 diff --git a/setup.py b/setup.py index dbd9548..3863a58 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ 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', @@ -9,11 +9,13 @@ 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) diff --git a/td4a/__main__.py b/td4a/__main__.py index b54381a..2cafd59 100644 --- a/td4a/__main__.py +++ b/td4a/__main__.py @@ -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='') @@ -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 @@ -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() diff --git a/td4a/static/favicon.ico b/td4a/static/favicon.ico index 3a37317..3244c07 100644 Binary files a/td4a/static/favicon.ico and b/td4a/static/favicon.ico differ diff --git a/td4a/static/index.html b/td4a/static/index.html index c481e99..5e15f61 100644 --- a/td4a/static/index.html +++ b/td4a/static/index.html @@ -20,12 +20,13 @@ -