Travis ci integration (#267)

* Initial travis-ci integration

* Format code base to PEP8
This commit is contained in:
Mike Terzo
2016-07-13 20:59:07 -04:00
committed by Corey Hammerton
parent 0ac64530bf
commit faac5fa1bc
13 changed files with 211 additions and 120 deletions

3
.coveragerc Normal file
View File

@@ -0,0 +1,3 @@
[report]
exclude_lines =
pragma: notest

7
.gitignore vendored
View File

@@ -1,5 +1,8 @@
*.py[cod] *.py[cod]
# Editor tmp files
.*.sw*
# C extensions # C extensions
*.so *.so
@@ -22,6 +25,7 @@ lib64
pip-log.txt pip-log.txt
# Unit test / coverage reports # Unit test / coverage reports
.cache
.coverage .coverage
.tox .tox
nosetests.xml nosetests.xml
@@ -36,3 +40,6 @@ nosetests.xml
# PuppetDB Settings # PuppetDB Settings
/settings.py /settings.py
# Virtual Environment
venv

13
.travis.yml Normal file
View File

@@ -0,0 +1,13 @@
language: python
python:
- "2.6"
- "2.7"
install:
- pip install -r requirements.txt
- pip install -r requirements-test.txt
- pip install -q coverage coveralls --use-wheel
script: py.test --cov=puppetboard --pep8 -v
after_success:
- coveralls

2
conftest.py Normal file
View File

@@ -0,0 +1,2 @@
import puppetboard
import test

View File

@@ -25,6 +25,7 @@ from puppetboard.utils import (
jsonprint, prettyprint, Pagination jsonprint, prettyprint, Pagination
) )
DEFAULT_ORDER_BY = '[{"field": "start_time", "order": "desc"}]'
app = Flask(__name__) app = Flask(__name__)
@@ -59,12 +60,14 @@ def stream_template(template_name, **context):
rv.enable_buffering(5) rv.enable_buffering(5)
return rv return rv
def url_for_field(field, value): def url_for_field(field, value):
args = request.view_args.copy() args = request.view_args.copy()
args.update(request.args.copy()) args.update(request.args.copy())
args[field] = value args[field] = value
return url_for(request.endpoint, **args) return url_for(request.endpoint, **args)
def environments(): def environments():
envs = get_or_abort(puppetdb.environments) envs = get_or_abort(puppetdb.environments)
x = [] x = []
@@ -74,12 +77,14 @@ def environments():
return x return x
def check_env(env, envs): def check_env(env, envs):
if env != '*' and env not in envs: if env != '*' and env not in envs:
abort(404) abort(404)
app.jinja_env.globals['url_for_field'] = url_for_field app.jinja_env.globals['url_for_field'] = url_for_field
@app.context_processor @app.context_processor
def utility_processor(): def utility_processor():
def now(format='%m/%d/%Y %H:%M:%S'): def now(format='%m/%d/%Y %H:%M:%S'):
@@ -345,7 +350,8 @@ def inventory(env):
current_env=env))) current_env=env)))
@app.route('/node/<node_name>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/node/<node_name>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/node/<node_name>') @app.route('/<env>/node/<node_name>')
def node(env, node_name): def node(env, node_name):
"""Display a dashboard for a node showing as much data as we have on that """Display a dashboard for a node showing as much data as we have on that
@@ -369,7 +375,7 @@ def node(env, node_name):
reports = get_or_abort(puppetdb.reports, reports = get_or_abort(puppetdb.reports,
query=query, query=query,
limit=app.config['REPORTS_COUNT'], limit=app.config['REPORTS_COUNT'],
order_by='[{"field": "start_time", "order": "desc"}]') order_by=DEFAULT_ORDER_BY)
reports, reports_events = tee(reports) reports, reports_events = tee(reports)
report_event_counts = {} report_event_counts = {}
@@ -408,7 +414,8 @@ def node(env, node_name):
current_env=env) current_env=env)
@app.route('/reports/', defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'page': 1}) @app.route('/reports/',
defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'page': 1})
@app.route('/<env>/reports/', defaults={'page': 1}) @app.route('/<env>/reports/', defaults={'page': 1})
@app.route('/<env>/reports/page/<int:page>') @app.route('/<env>/reports/page/<int:page>')
def reports(env, page): def reports(env, page):
@@ -441,7 +448,7 @@ def reports(env, page):
reports = get_or_abort(puppetdb.reports, reports = get_or_abort(puppetdb.reports,
query=reports_query, query=reports_query,
order_by='[{"field": "start_time", "order": "desc"}]', order_by=DEFAULT_ORDER_BY,
**paging_args) **paging_args)
total = get_or_abort(puppetdb._query, total = get_or_abort(puppetdb._query,
'reports', 'reports',
@@ -488,7 +495,8 @@ def reports(env, page):
limit=paging_args.get('limit', total)))) limit=paging_args.get('limit', total))))
@app.route('/reports/<node_name>/', defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'page': 1}) @app.route('/reports/<node_name>/',
defaults={'env': app.config['DEFAULT_ENVIRONMENT'], 'page': 1})
@app.route('/<env>/reports/<node_name>', defaults={'page': 1}) @app.route('/<env>/reports/<node_name>', defaults={'page': 1})
@app.route('/<env>/reports/<node_name>/page/<int:page>') @app.route('/<env>/reports/<node_name>/page/<int:page>')
def reports_node(env, node_name, page): def reports_node(env, node_name, page):
@@ -520,7 +528,7 @@ def reports_node(env, node_name, page):
query=query, query=query,
limit=app.config['REPORTS_COUNT'], limit=app.config['REPORTS_COUNT'],
offset=(page - 1) * app.config['REPORTS_COUNT'], offset=(page - 1) * app.config['REPORTS_COUNT'],
order_by='[{"field": "start_time", "order": "desc"}]') order_by=DEFAULT_ORDER_BY)
total = get_or_abort(puppetdb._query, total = get_or_abort(puppetdb._query,
'reports', 'reports',
query=total_query) query=total_query)
@@ -565,7 +573,8 @@ def reports_node(env, node_name, page):
current_env=env) current_env=env)
@app.route('/report/<node_name>/<report_id>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/report/<node_name>/<report_id>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/report/<node_name>/<report_id>') @app.route('/<env>/report/<node_name>/<report_id>')
def report(env, node_name, report_id): def report(env, node_name, report_id):
"""Displays a single report including all the events associated with that """Displays a single report including all the events associated with that
@@ -638,7 +647,8 @@ def facts(env):
sorted_facts_dict = sorted(facts_dict.items()) sorted_facts_dict = sorted(facts_dict.items())
return render_template('facts.html', return render_template('facts.html',
facts_dict=sorted_facts_dict, facts_dict=sorted_facts_dict,
facts_len=sum(map(len,facts_dict.values())) + len(facts_dict)*5, facts_len=(sum(map(len, facts_dict.values())) +
len(facts_dict) * 5),
envs=envs, envs=envs,
current_env=env) current_env=env)
@@ -679,7 +689,8 @@ def fact(env, fact):
current_env=env))) current_env=env)))
@app.route('/fact/<fact>/<value>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/fact/<fact>/<value>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/fact/<fact>/<value>') @app.route('/<env>/fact/<fact>/<value>')
def fact_value(env, fact, value): def fact_value(env, fact, value):
"""On asking for fact/value get all nodes with that fact. """On asking for fact/value get all nodes with that fact.
@@ -713,7 +724,8 @@ def fact_value(env, fact, value):
current_env=env) current_env=env)
@app.route('/query', methods=('GET', 'POST'), defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/query', methods=('GET', 'POST'),
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/query', methods=('GET', 'POST')) @app.route('/<env>/query', methods=('GET', 'POST'))
def query(env): def query(env):
"""Allows to execute raw, user created querries against PuppetDB. This is """Allows to execute raw, user created querries against PuppetDB. This is
@@ -777,7 +789,8 @@ def metrics(env):
current_env=env) current_env=env)
@app.route('/metric/<metric>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/metric/<metric>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/metric/<metric>') @app.route('/<env>/metric/<metric>')
def metric(env, metric): def metric(env, metric):
"""Lists all information about the metric of the given name. """Lists all information about the metric of the given name.
@@ -798,6 +811,7 @@ def metric(env, metric):
envs=envs, envs=envs,
current_env=env) current_env=env)
@app.route('/catalogs', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/catalogs', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/catalogs') @app.route('/<env>/catalogs')
def catalogs(env): def catalogs(env):
@@ -819,10 +833,11 @@ def catalogs(env):
query.add(NullOperator("catalog_timestamp", False)) query.add(NullOperator("catalog_timestamp", False))
order_by_str = '[{"field": "certname", "order": "asc"}]'
nodes = get_or_abort(puppetdb.nodes, nodes = get_or_abort(puppetdb.nodes,
query=query, query=query,
with_status=False, with_status=False,
order_by='[{"field": "certname", "order": "asc"}]') order_by=oder_by_str)
nodes, temp = tee(nodes) nodes, temp = tee(nodes)
for node in temp: for node in temp:
@@ -855,7 +870,9 @@ def catalogs(env):
log.warn('Access to catalog interface disabled by administrator') log.warn('Access to catalog interface disabled by administrator')
abort(403) abort(403)
@app.route('/catalog/<node_name>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/catalog/<node_name>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/catalog/<node_name>') @app.route('/<env>/catalog/<node_name>')
def catalog_node(env, node_name): def catalog_node(env, node_name):
"""Fetches from PuppetDB the compiled catalog of a given node. """Fetches from PuppetDB the compiled catalog of a given node.
@@ -877,7 +894,9 @@ def catalog_node(env, node_name):
log.warn('Access to catalog interface disabled by administrator') log.warn('Access to catalog interface disabled by administrator')
abort(403) abort(403)
@app.route('/catalog/submit', methods=['POST'], defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/catalog/submit', methods=['POST'],
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/catalog/submit', methods=['POST']) @app.route('/<env>/catalog/submit', methods=['POST'])
def catalog_submit(env): def catalog_submit(env):
"""Receives the submitted form data from the catalogs page and directs """Receives the submitted form data from the catalogs page and directs
@@ -909,7 +928,9 @@ def catalog_submit(env):
log.warn('Access to catalog interface disabled by administrator') log.warn('Access to catalog interface disabled by administrator')
abort(403) abort(403)
@app.route('/catalogs/compare/<compare>...<against>', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/catalogs/compare/<compare>...<against>',
defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/catalogs/compare/<compare>...<against>') @app.route('/<env>/catalogs/compare/<compare>...<against>')
def catalog_compare(env, compare, against): def catalog_compare(env, compare, against):
"""Compares the catalog of one node, parameter compare, with that of """Compares the catalog of one node, parameter compare, with that of
@@ -936,6 +957,7 @@ def catalog_compare(env, compare, against):
log.warn('Access to catalog interface disabled by administrator') log.warn('Access to catalog interface disabled by administrator')
abort(403) abort(403)
@app.route('/radiator', defaults={'env': app.config['DEFAULT_ENVIRONMENT']}) @app.route('/radiator', defaults={'env': app.config['DEFAULT_ENVIRONMENT']})
@app.route('/<env>/radiator') @app.route('/<env>/radiator')
def radiator(env): def radiator(env):
@@ -966,14 +988,12 @@ def radiator(env):
query=metric_query) query=metric_query)
num_nodes = metrics[0]['count'] num_nodes = metrics[0]['count']
nodes = puppetdb.nodes( nodes = puppetdb.nodes(
query=query, query=query,
unreported=app.config['UNRESPONSIVE_HOURS'], unreported=app.config['UNRESPONSIVE_HOURS'],
with_status=True with_status=True
) )
stats = { stats = {
'changed_percent': 0, 'changed_percent': 0,
'changed': 0, 'changed': 0,
@@ -989,8 +1009,6 @@ def radiator(env):
'unreported': 0, 'unreported': 0,
} }
for node in nodes: for node in nodes:
if node.status == 'unreported': if node.status == 'unreported':
stats['unreported'] += 1 stats['unreported'] += 1
@@ -1005,14 +1023,14 @@ def radiator(env):
else: else:
stats['unchanged'] += 1 stats['unchanged'] += 1
stats['changed_percent'] = int(100 * stats['changed'] / float(num_nodes)) stats['changed_percent'] = int(100 * stats['changed'] / float(num_nodes))
stats['failed_percent'] = int(100 * stats['failed'] / float(num_nodes)) stats['failed_percent'] = int(100 * stats['failed'] / float(num_nodes))
stats['noop_percent'] = int(100 * stats['noop'] / float(num_nodes)) stats['noop_percent'] = int(100 * stats['noop'] / float(num_nodes))
stats['skipped_percent'] = int(100 * stats['skipped'] / float(num_nodes)) stats['skipped_percent'] = int(100 * stats['skipped'] / float(num_nodes))
stats['unchanged_percent'] = int(100 * stats['unchanged'] / float(num_nodes)) stats['unchanged_percent'] = int(100 * (stats['unchanged'] /
stats['unreported_percent'] = int(100 * stats['unreported'] / float(num_nodes)) float(num_nodes)))
stats['unreported_percent'] = int(100 * (stats['unreported'] /
float(num_nodes)))
return render_template( return render_template(
'radiator.html', 'radiator.html',

View File

@@ -29,6 +29,7 @@ class QueryForm(Form):
]) ])
rawjson = BooleanField('Raw JSON') rawjson = BooleanField('Raw JSON')
class CatalogForm(Form): class CatalogForm(Form):
"""The form used to compare the catalogs of different nodes.""" """The form used to compare the catalogs of different nodes."""
compare = HiddenField('compare') compare = HiddenField('compare')

View File

@@ -19,9 +19,11 @@ except NameError:
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def jsonprint(value): def jsonprint(value):
return json.dumps(value, indent=2, separators=(',', ': ')) return json.dumps(value, indent=2, separators=(',', ': '))
def formatvalue(value): def formatvalue(value):
if isinstance(value, str): if isinstance(value, str):
return value return value
@@ -35,6 +37,7 @@ def formatvalue(value):
else: else:
return str(value) return str(value)
def prettyprint(value): def prettyprint(value):
html = '<table class="ui basic fixed sortable table"><thead><tr>' html = '<table class="ui basic fixed sortable table"><thead><tr>'
@@ -53,6 +56,7 @@ def prettyprint(value):
html += "</tbody></table>" html += "</tbody></table>"
return(html) return(html)
def get_or_abort(func, *args, **kwargs): def get_or_abort(func, *args, **kwargs):
"""Execute the function with its arguments and handle the possible """Execute the function with its arguments and handle the possible
errors that might occur. errors that might occur.
@@ -87,6 +91,7 @@ def yield_or_stop(generator):
except (EmptyResponseError, ConnectionError, HTTPError): except (EmptyResponseError, ConnectionError, HTTPError):
raise StopIteration raise StopIteration
class Pagination(object): class Pagination(object):
def __init__(self, page, per_page, total_count): def __init__(self, page, per_page, total_count):
@@ -111,7 +116,7 @@ class Pagination(object):
last = 0 last = 0
for num in xrange(1, self.pages + 1): for num in xrange(1, self.pages + 1):
if num <= left_edge or \ if num <= left_edge or \
(num > self.page - left_current - 1 and \ (num > self.page - left_current - 1 and
num < self.page + right_current) or \ num < self.page + right_current) or \
num > self.pages - right_edge: num > self.pages - right_edge:
if last + 1 != num: if last + 1 != num:

6
requirements-test.txt Normal file
View File

@@ -0,0 +1,6 @@
pep8==1.6.2
coverage==4.0
mock==1.3.0
pytest-pep8==1.0.5
pytest-cov==2.2.1
cov-core==1.15.0

View File

@@ -6,3 +6,20 @@ build_requires = python-setuptools
requires = python-flask requires = python-flask
python-flask-wtf python-flask-wtf
python-pypuppetdb python-pypuppetdb
[pep8]
max-line-length=100
exclude=venv,dist,build
ignore=E402
[nosetests]
with-coverage = 1
with-xunit = 1
cover-package = puppetboard
[flake8]
exclude=venv
[pytest]
addopts = --cov=puppetboard --cov-report=term-missing
norecursedirs = docs .tox venv
pep8ignore = E402

19
test/test_app.py Normal file
View File

@@ -0,0 +1,19 @@
import os
from puppetboard import app
import unittest
import tempfile
class AppTestCase(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_first_test(self):
self.assertTrue(True)
if __name__ == '__main__':
unittest.main()