From 72a194c82e66576ddc69d1d2a0891e401e16b0de Mon Sep 17 00:00:00 2001 From: Mike Terzo Date: Mon, 24 Oct 2016 19:57:14 -0400 Subject: [PATCH 1/2] Update default_settings to use environment variables. Easier environment configuration when using docker to run puppetboard. --- README.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rst b/README.rst index 05937ef..6e1944d 100644 --- a/README.rst +++ b/README.rst @@ -128,6 +128,20 @@ image is planned for the 0.2.x series. .. _Dockerfile: https://github.com/voxpupuli/puppetboard/blob/master/Dockerfile +Usage: +.. code-block:: bash + $ docker build -t puppetboard . + $ docker run -it -p 9080:80 -v /etc/puppetlabs/puppet/ssl:/etc/puppetlabs/puppet/ssl \ + -e PUPPETDB_HOST= \ + -e PUPPETDB_PORT=8081 \ + -e PUPPETDB_SSL_VERIFY=/etc/puppetlabs/puppetdb/ssl/ca.pem \ + -e PUPPETDB_KEY=/etc/puppetlabs/puppetdb/ssl/private.pem \ + -e PUPPETDB_CERT=/etc/puppetlabs/puppetdb/ssl/public.pem \ + -e INVENTORY_FACTS='Hostname,fqdn, IP Address,ipaddress' \ + -e ENABLE_CATALOG=true \ + -e GRAPH_FACTS='architecture,puppetversion,osfamily' \ + puppetboard + Development ----------- From b96e76ff104b5639c2f86c49b9069f81b0dbb5dc Mon Sep 17 00:00:00 2001 From: Mike Terzo Date: Tue, 25 Oct 2016 22:24:42 -0400 Subject: [PATCH 2/2] Use alpine python and gunicorn Adding docker_settings.py which reads environment variables to allows for environment variable to be passed to the container --- Dockerfile | 13 ++++- puppetboard/docker_settings.py | 68 +++++++++++++++++++++++++ requirements-docker.txt | 10 ++++ test/test_docker_settings.py | 92 ++++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 2 deletions(-) create mode 100644 puppetboard/docker_settings.py create mode 100644 requirements-docker.txt create mode 100644 test/test_docker_settings.py diff --git a/Dockerfile b/Dockerfile index 7c3c916..87d9cab 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,3 +1,12 @@ -FROM grahamdumpleton/mod-wsgi-docker:python-2.7-onbuild -CMD [ "wsgi.py" ] +FROM python:2.7-alpine +ENV PUPPETBOARD_PORT 80 +ENV PUPPETBOARD_SETTINGS docker_settings.py +RUN mkdir -p /usr/src/app +WORKDIR /usr/src/app + +COPY requirements-docker.txt /usr/src/app/ +RUN pip install --no-cache-dir -r requirements-docker.txt +COPY . /usr/src/app + +CMD gunicorn -b 0.0.0.0:${PUPPETBOARD_PORT} --access-logfile=/dev/stdout puppetboard.app:app diff --git a/puppetboard/docker_settings.py b/puppetboard/docker_settings.py new file mode 100644 index 0000000..f022206 --- /dev/null +++ b/puppetboard/docker_settings.py @@ -0,0 +1,68 @@ +import os + +PUPPETDB_HOST = os.getenv('PUPPETDB_HOST', 'localhost') +PUPPETDB_PORT = int(os.getenv('PUPPETDB_PORT', '8080')) +# Since this is an env it will alwas be a string, we need +# to conver that string to a bool +SSL_VERIFY = os.getenv('PUPPETDB_SSL_VERIFY', 'True') +if SSL_VERIFY.upper() == 'TRUE': + PUPPETDB_SSL_VERIFY = True +elif SSL_VERIFY.upper() == 'FALSE': + PUPPETDB_SSL_VERIFY = False +else: + PUPPETDB_SSL_VERIFY = SSL_VERIFY + +PUPPETDB_KEY = os.getenv('PUPPETDB_KEY', None) +PUPPETDB_CERT = os.getenv('PUPPETDB_CERT', None) +PUPPETDB_TIMEOUT = int(os.getenv('PUPPETDB_TIMEOUT', '20')) +DEFAULT_ENVIRONMENT = os.getenv('DEFAULT_ENVIRONMENT', 'production') +SECRET_KEY = os.getenv('SECRET_KEY', os.urandom(24)) +DEV_LISTEN_HOST = os.getenv('DEV_LISTEN_HOST', '127.0.0.1') +DEV_LISTEN_PORT = int(os.getenv('DEV_LISTEN_PORT', '5000')) +DEV_COFFEE_LOCATION = os.getenv('DEV_COFFEE_LOCATION', 'coffee') +UNRESPONSIVE_HOURS = int(os.getenv('UNRESPONSIVE_HOURS', '2')) +ENABLE_QUERY = os.getenv('ENABLE_QUERY', 'True') + +LOCALISE_TIMESTAMP = bool(os.getenv('LOCALISE_TIMESTAMP', + 'True').upper() == 'TRUE') +LOGLEVEL = os.getenv('LOGLEVEL', 'info') +REPORTS_COUNT = int(os.getenv('REPORTS_COUNT', '10')) +OFFLINE_MODE = bool(os.getenv('OFFLINE_MODE', 'False').upper() == 'TRUE') +ENABLE_CATALOG = bool(os.getenv('ENABLE_CATALOG', 'False').upper() == 'TRUE') +OVERVIEW_FILTER = os.getenv('OVERVIEW_FILTER', None) + +GRAPH_FACTS_DEFAULT = ','.join(['architecture', 'clientversion', 'domain', + 'lsbcodename', 'lsbdistcodename', 'lsbdistid', + 'lsbdistrelease', 'lsbmajdistrelease', + 'netmask', 'osfamily', 'puppetversion', + 'processorcount']) + +GRAPH_FACTS = [x.strip() for x in os.getenv('GRAPH_FACTS', + GRAPH_FACTS_DEFAULT).split(',')] + + +# Tuples are hard to express as an environment variable, so here +# the tupple can be listed as a list of items +# export INVENTORY_FACTS="Hostname, fqdn, IP Address, ipaddress,.. etc" +# Define default array of of strings, this code is a bit neater than having +# a large string +INVENTORY_FACTS_DEFAULT = ','.join(['Hostname', 'fqdn', + 'IP Address', 'ipaddress', + 'OS', 'lsbdistdescription', + 'Architecture', 'hardwaremodel', + 'Kernel Version', 'kernelrelease', + 'Puppet Version', 'puppetversion']) + +# take either input as a list Key, Value, Key, Value, and conver it to an +# array: ['Key', 'Value'] +INV_STR = os.getenv('INVENTORY_FACTS', INVENTORY_FACTS_DEFAULT).split(',') + +# Take the Array and convert it to a tuple +INVENTORY_FACTS = [(INV_STR[i].strip(), + INV_STR[i + 1].strip()) for i in range(0, len(INV_STR), 2)] + +REFRESH_RATE = int(os.getenv('REFRESH_RATE', '30')) + +DAILY_REPORTS_CHART_ENABLED = bool(os.getenv('DAILY_REPORTS_CHART_ENABLED', + 'True').upper() == 'TRUE') +DAILY_REPORTS_CHART_DAYS = int(os.getenv('DAILY_REPORTS_CHART_DAYS', '8')) diff --git a/requirements-docker.txt b/requirements-docker.txt new file mode 100644 index 0000000..7ea7a97 --- /dev/null +++ b/requirements-docker.txt @@ -0,0 +1,10 @@ +gunicorn==19.6.0 +Flask==0.10.1 +Flask-WTF==0.12 +Jinja2==2.7.2 +MarkupSafe==0.19 +WTForms==2.1 +Werkzeug==0.11.10 +itsdangerous==0.23 +pypuppetdb==0.3.1 +requests==2.6.0 diff --git a/test/test_docker_settings.py b/test/test_docker_settings.py new file mode 100644 index 0000000..33b1432 --- /dev/null +++ b/test/test_docker_settings.py @@ -0,0 +1,92 @@ +import os +from puppetboard import docker_settings +import unittest +import tempfile +try: + import future.utils +except: + pass + +try: + from imp import reload as reload +except: + pass + + +class DockerTestCase(unittest.TestCase): + def setUp(self): + for env_var in dir(docker_settings): + if (env_var.startswith('__') or env_var.startswith('_') or + env_var.islower()): + continue + + if env_var in os.environ: + del os.environ[env_var] + reload(docker_settings) + + def test_default_host_port(self): + self.assertEqual(docker_settings.PUPPETDB_HOST, 'localhost') + self.assertEqual(docker_settings.PUPPETDB_PORT, 8080) + + def test_set_host_port(self): + os.environ['PUPPETDB_HOST'] = 'puppetdb' + os.environ['PUPPETDB_PORT'] = '9081' + reload(docker_settings) + self.assertEqual(docker_settings.PUPPETDB_HOST, 'puppetdb') + self.assertEqual(docker_settings.PUPPETDB_PORT, 9081) + + def test_cert_true_test(self): + os.environ['PUPPETDB_SSL_VERIFY'] = 'True' + reload(docker_settings) + self.assertTrue(docker_settings.PUPPETDB_SSL_VERIFY) + os.environ['PUPPETDB_SSL_VERIFY'] = 'true' + reload(docker_settings) + self.assertTrue(docker_settings.PUPPETDB_SSL_VERIFY) + + def test_cert_false_test(self): + os.environ['PUPPETDB_SSL_VERIFY'] = 'False' + reload(docker_settings) + self.assertFalse(docker_settings.PUPPETDB_SSL_VERIFY) + os.environ['PUPPETDB_SSL_VERIFY'] = 'false' + reload(docker_settings) + self.assertFalse(docker_settings.PUPPETDB_SSL_VERIFY) + + def test_cert_path(self): + ca_file = '/usr/ssl/path/ca.pem' + os.environ['PUPPETDB_SSL_VERIFY'] = ca_file + reload(docker_settings) + self.assertEqual(docker_settings.PUPPETDB_SSL_VERIFY, ca_file) + + def validate_facts(self, facts): + self.assertEqual(type(facts), type([])) + self.assertTrue(len(facts) > 0) + for map in facts: + self.assertEqual(type(map), type(())) + self.assertTrue(len(map) == 2) + + def test_inventory_facts_default(self): + self.validate_facts(docker_settings.INVENTORY_FACTS) + + def test_invtory_facts_custom(self): + os.environ['INVENTORY_FACTS'] = "A, B, C, D" + reload(docker_settings) + self.validate_facts(docker_settings.INVENTORY_FACTS) + + def test_graph_facts_defautl(self): + facts = docker_settings.GRAPH_FACTS + self.assertEqual(type(facts), type([])) + self.assertTrue('puppetversion' in facts) + + def test_graph_facts_custom(self): + os.environ['GRAPH_FACTS'] = "architecture, puppetversion, extra" + reload(docker_settings) + facts = docker_settings.GRAPH_FACTS + self.assertEqual(type(facts), type([])) + self.assertEqual(len(facts), 3) + self.assertTrue('puppetversion' in facts) + self.assertTrue('architecture' in facts) + self.assertTrue('extra' in facts) + + +if __name__ == '__main__': + unittest.main()