Reorgenized test structure and added validation of inventory before deployment

This commit is contained in:
2025-07-04 01:14:00 +02:00
parent fe04f1955f
commit a9f55579a2
37 changed files with 241 additions and 42 deletions

View File

@@ -4,8 +4,9 @@ import argparse
import subprocess
import os
import datetime
import sys
def run_ansible_playbook(inventory, playbook, modes, limit=None, password_file=None, verbose=0, skip_tests:bool=False):
def run_ansible_playbook(inventory, playbook, modes, limit=None, password_file=None, verbose=0, skip_tests=False):
start_time = datetime.datetime.now()
print(f"\n▶️ Script started at: {start_time.isoformat()}\n")
@@ -94,6 +95,10 @@ def main():
"--skip-tests", action="store_true",
help="Skip running 'make test' even if tests are normally enabled."
)
parser.add_argument(
"--skip-validation", action="store_true",
help="Skip inventory validation before deployment."
)
parser.add_argument(
"-v", "--verbose", action="count", default=0,
help="Increase verbosity level. Multiple -v flags increase detail (e.g., -vvv for maximum log output)."
@@ -101,6 +106,17 @@ def main():
args = parser.parse_args()
if not args.skip_validation:
print("\n🔍 Validating inventory before deployment...\n")
try:
subprocess.run(
[sys.executable, os.path.join(script_dir, "validate_inventory.py"), os.path.dirname(args.inventory)],
check=True
)
except subprocess.CalledProcessError:
print("\n❌ Inventory validation failed. Deployment aborted.\n", file=sys.stderr)
sys.exit(1)
modes = {
"mode_reset": args.reset,
"mode_test": args.test,

View File

@@ -46,6 +46,22 @@ def compare_application_keys(applications, defaults, source_file):
return errors
def compare_user_keys(users, default_users, source_file):
errors = []
for username, user_conf in users.items():
if username not in default_users:
print(f"Warning: {source_file}: Unknown user '{username}' (not in default_users)", file=sys.stderr)
continue
default_conf = default_users.get(username, {})
for key in user_conf:
if key in ("password", "credentials", "mailu_token"):
continue # ignore credentials/password
if key not in default_conf:
raise Exception(f"{source_file}: Missing default for user '{username}': key '{key}'")
return errors
def load_inventory_files(inventory_dir):
all_data = {}
inventory_path = Path(inventory_dir)
@@ -69,32 +85,51 @@ def load_inventory_files(inventory_dir):
return all_data
def find_defaults_applications_file():
candidates = list(Path("group_vars/all").glob("*_applications.yml"))
def find_single_file(pattern):
candidates = list(Path("group_vars/all").glob(pattern))
if len(candidates) != 1:
raise RuntimeError(f"Expected exactly one *_applications.yml file in group_vars/all, found {len(candidates)}")
raise RuntimeError(f"Expected exactly one {pattern} file in group_vars/all, found {len(candidates)}")
return candidates[0]
def main():
parser = argparse.ArgumentParser(description="Verify application variable consistency with defaults.")
parser = argparse.ArgumentParser(description="Verify application and user variable consistency with defaults.")
parser.add_argument("inventory_dir", help="Path to inventory directory (contains inventory.yml and *_vars/)")
args = parser.parse_args()
defaults_path = find_defaults_applications_file()
defaults_path = find_single_file("*_applications.yml")
users_path = find_single_file("*users.yml")
defaults_data = load_yaml_file(defaults_path)
default_users_data = load_yaml_file(users_path)
defaults = defaults_data.get("defaults_applications", {}) if defaults_data else {}
default_users = default_users_data.get("default_users", {}) if default_users_data else {}
if not defaults:
print(f"Error: No 'defaults_applications' found in {defaults_path}.", file=sys.stderr)
sys.exit(1)
if not default_users:
print(f"Error: No 'default_users' found in {users_path}.", file=sys.stderr)
sys.exit(1)
all_errors = []
inventory_files = load_inventory_files(args.inventory_dir)
for source_path, app_data in inventory_files.items():
errors = compare_application_keys(app_data, defaults, str(source_path))
all_errors.extend(errors)
# Load all users.yml files from inventory
for path in Path(args.inventory_dir).rglob("*.yml"):
data = load_yaml_file(path)
if isinstance(data, dict) and "users" in data:
try:
compare_user_keys(data["users"], default_users, str(path))
except Exception as e:
print(e, file=sys.stderr)
sys.exit(1)
if all_errors:
print("Validation failed with the following issues:")
for err in all_errors: