mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-08-29 15:06:26 +02:00
Reorgenized test structure and added validation of inventory before deployment
This commit is contained in:
@@ -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,
|
||||
|
@@ -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:
|
Reference in New Issue
Block a user