add yaml helper script; refactor python testing

This commit is contained in:
Jason Ertel
2023-11-08 18:24:23 -05:00
parent 01e846ba22
commit 33a8ef1568
8 changed files with 183 additions and 32 deletions

22
pyci.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/bash
if [[ $# -ne 1 ]]; then
echo "Usage: $0 <python_script_dir>"
echo "Runs tests on all *_test.py files in the given directory."
exit 1
fi
HOME_DIR=$(dirname "$0")
TARGET_DIR=${1:-.}
PATH=$PATH:/usr/local/bin
if ! which pytest &> /dev/null || ! which flake8 &> /dev/null ; then
echo "Missing dependencies. Consider running the following command:"
echo " python -m pip install flake8 pytest pytest-cov"
exit 1
fi
pip install pytest pytest-cov
flake8 "$TARGET_DIR" "--config=${HOME_DIR}/pytest.ini"
python3 -m pytest "--cov-config=${HOME_DIR}/pytest.ini" "--cov=$TARGET_DIR" --doctest-modules --cov-report=term --cov-fail-under=100 "$TARGET_DIR"

View File

@@ -1,19 +1,9 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# Copyright 2014-2023 Security Onion Solutions, LLC # Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
# # or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
# This program is free software: you can redistribute it and/or modify # https://securityonion.net/license; you may not use this file except in compliance with the
# it under the terms of the GNU General Public License as published by # Elastic License 2.0.
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os import os
import subprocess import subprocess

View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
# Copyright Security Onion Solutions LLC and/or licensed to Security Onion Solutions LLC under one
# or more contributor license agreements. Licensed under the Elastic License 2.0 as shown at
# https://securityonion.net/license; you may not use this file except in compliance with the
# Elastic License 2.0.
import os
import sys
import time
import yaml
lockFile = "/tmp/so-yaml.lock"
def showUsage(args):
print('Usage: {} <COMMAND> <YAML_FILE> [ARGS...]'.format(sys.argv[0]))
print(' General commands:')
print(' remove - Removes a yaml top-level key, if it exists. Requires KEY arg.')
print(' help - Prints this usage information.')
print('')
print(' Where:')
print(' YAML_FILE - Path to the file that will be modified. Ex: /opt/so/conf/service/conf.yaml')
print(' KEY - Top level key only, does not support dot-notations for nested keys at this time. Ex: level1')
sys.exit(1)
def loadYaml(filename):
file = open(filename, "r")
content = file.read()
return yaml.safe_load(content)
def writeYaml(filename, content):
file = open(filename, "w")
return yaml.dump(content, file)
def remove(args):
if len(args) != 2:
print('Missing filename or key arg', file=sys.stderr)
showUsage(None)
return
filename = args[0]
content = loadYaml(filename)
content.pop(args[1], None)
writeYaml(filename, content)
return 0
def main():
args = sys.argv[1:]
if len(args) < 1:
showUsage(None)
return
commands = {
"help": showUsage,
"remove": remove,
}
code = 1
try:
lockAttempts = 0
maxAttempts = 30
while lockAttempts < maxAttempts:
lockAttempts = lockAttempts + 1
try:
f = open(lockFile, "x")
f.close()
break
except Exception:
if lockAttempts == 1:
print("Waiting for lock file to be released from another process...")
time.sleep(2)
if lockAttempts == maxAttempts:
print("Lock file (" + lockFile + ") could not be created; proceeding without lock.")
cmd = commands.get(args[0], showUsage)
code = cmd(args[1:])
finally:
try:
os.remove(lockFile)
except Exception:
print("Lock file (" + lockFile + ") already removed")
sys.exit(code)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,59 @@
from io import StringIO
import sys
from unittest.mock import patch, MagicMock
import unittest
import importlib
soyaml = importlib.import_module("so-yaml")
class TestRemove(unittest.TestCase):
def test_main_missing_input(self):
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stdout:
sys.argv = ["cmd"]
soyaml.main()
sysmock.assert_called_once_with(1)
self.assertIn(mock_stdout.getvalue(), "Usage:")
def test_main_help(self):
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stdout:
sys.argv = ["cmd", "help"]
soyaml.main()
sysmock.assert_called()
self.assertIn(mock_stdout.getvalue(), "Usage:")
def test_remove(self):
filename = "/tmp/so-yaml_test-remove.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: abc }, key2: false}")
file.close()
soyaml.remove([filename, "key1"])
file = open(filename, "r")
actual = file.read()
file.close()
expected = "key2: false\n"
self.assertEqual(actual, expected)
def test_remove_missing_args(self):
with patch('sys.exit', new=MagicMock()) as sysmock:
with patch('sys.stderr', new=StringIO()) as mock_stdout:
filename = "/tmp/so-yaml_test-remove.yaml"
file = open(filename, "w")
file.write("{key1: { child1: 123, child2: abc }, key2: false}")
file.close()
soyaml.remove([filename])
file = open(filename, "r")
actual = file.read()
file.close()
expected = "{key1: { child1: 123, child2: abc }, key2: false}"
self.assertEqual(actual, expected)
sysmock.assert_called_once_with(1)
self.assertIn(mock_stdout.getvalue(), "Missing filename or key arg\n")

View File

@@ -3,23 +3,6 @@
COMMAND=$1 COMMAND=$1
SENSORONI_CONTAINER=${SENSORONI_CONTAINER:-so-sensoroni} SENSORONI_CONTAINER=${SENSORONI_CONTAINER:-so-sensoroni}
function ci() {
HOME_DIR=$(dirname "$0")
TARGET_DIR=${1:-.}
PATH=$PATH:/usr/local/bin
if ! which pytest &> /dev/null || ! which flake8 &> /dev/null ; then
echo "Missing dependencies. Consider running the following command:"
echo " python -m pip install flake8 pytest pytest-cov"
exit 1
fi
pip install pytest pytest-cov
flake8 "$TARGET_DIR" "--config=${HOME_DIR}/pytest.ini"
python3 -m pytest "--cov-config=${HOME_DIR}/pytest.ini" "--cov=$TARGET_DIR" --doctest-modules --cov-report=term --cov-fail-under=100 "$TARGET_DIR"
}
function download() { function download() {
ANALYZERS=$1 ANALYZERS=$1
if [[ $ANALYZERS = "all" ]]; then if [[ $ANALYZERS = "all" ]]; then
@@ -36,5 +19,5 @@ function download() {
if [[ "$COMMAND" == "download" ]]; then if [[ "$COMMAND" == "download" ]]; then
download "$2" download "$2"
else else
ci ../../../../pyci.sh $@
fi fi