Skip to content

Commit

Permalink
Runner: Added a simple templating in input variables. Example: ${ENV.…
Browse files Browse the repository at this point in the history
…USER} to see current user. Use this mechanism to pass credentials securely
  • Loading branch information
blackandred committed Oct 31, 2019
1 parent 6969273 commit 47890a0
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 7 deletions.
3 changes: 3 additions & 0 deletions infracheck/checks/false
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

exit 1
5 changes: 5 additions & 0 deletions infracheck/checks/printr
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

echo "${MESSAGE}"

exit 0
3 changes: 3 additions & 0 deletions infracheck/checks/true
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

exit 0
37 changes: 34 additions & 3 deletions infracheck/infracheck/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import subprocess
import os
import json
import re
from datetime import datetime


class Runner:
Expand All @@ -18,7 +20,7 @@ def run(self, check_name: str, input_data: dict, hooks: dict) -> tuple:
bin_path = self._get_check_path(check_name)

try:
env = {**dict(os.environ), **self._prepare_data(input_data)}
env = {**dict(os.environ), **self._prepare_data(check_name, input_data)}
output = subprocess.check_output(bin_path, env=env, stderr=subprocess.PIPE, timeout=self.timeout)
exit_status = True

Expand All @@ -31,17 +33,46 @@ def run(self, check_name: str, input_data: dict, hooks: dict) -> tuple:
return output.decode('utf-8'), exit_status, hooks_out

@staticmethod
def _prepare_data(input_data: dict):
def _prepare_data(check_name: str, input_data: dict):
""" Serialize and parse """

output_data = {}

for key, value in input_data.items():
if type(value) == dict or type(value) == list:
value = json.dumps(value)

output_data[key.upper()] = str(value)
output_data[key.upper()] = Runner._inject_variables(check_name, str(value))

return output_data

@staticmethod
def _inject_variables(check_name: str, value: str) -> str:
""" Inject variables, including environment variables from host,
to allow for example secure passing of passwords"""

matches = re.findall(r'\${([A-Za-z0-9_.]+)\}', value)

if not matches:
return value

variables = {
'checkName': check_name,
'date': datetime.now().isoformat(),
}

for env_name, env_value in os.environ.items():
variables['ENV.' + env_name.upper()] = env_value

for match in matches:
if match not in variables:
raise Exception('Invalid variable "%s" in check %s. Available variables: %s' %
(match, check_name, str(variables.keys())))

value = value.replace('${%s}' % match, variables[match])

return value

def _get_check_path(self, check_name: str):
for path in self.paths:
if os.path.isfile(path + '/' + check_name):
Expand Down
14 changes: 10 additions & 4 deletions tests/unit_test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,19 @@ def provide_data():
},
True,
'it works'
]
],
]

@data_provider(provide_data)
def test_run(self, type: str, input_data: dict, hooks: dict, expected_result: bool, expected_text: str):
runner = Runner([path + '/../example/healthchecks', path])
out = runner.run(type, input_data, hooks)
def test_run_with_hooks(self, check_type: str, input_data: dict, hooks: dict, expected_result: bool, expected_text: str):
runner = Runner([path + '/../example/healthchecks', path + '/infracheck/'])
out = runner.run(check_type, input_data, hooks)

self.assertEqual(expected_result, out[1])
self.assertEqual(expected_text, out[2])

def test_injects_variables(self):
runner = Runner([path + '/../example/healthchecks', path + '/infracheck/'])
out = runner.run('printr', {'message': 'Current user is ${ENV.USER}, running a ${checkName}'}, {})

self.assertEqual('Current user is ' + os.environ['USER'] + ', running a printr', out[0].strip())

0 comments on commit 47890a0

Please sign in to comment.