Files
ocf-managed-container/tests/test_lab_installer.py
Lyncoln ec2077f979 Freeze lab-tested PostgreSQL plugin engine
Extract PostgreSQL, Podman runtime and systemd lifecycle adapters while preserving existing lifecycle and scoring behavior. Update RPM layout and add guarded operator-run lab harnesses.

Record successful manual stop/start, promotion, demotion and rewind-only rejoin with pinned installed hashes. The unfenced lab does not validate automatic failover, Pacemaker-managed transitions or divergent-data rewind.

Validation: 103 offline tests, original-function differential checks, checksum manifest and whitespace checks passed.
2026-09-13 01:57:04 -04:00

79 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Exercise publication and rollback using temp files and mocked lab boundaries."""
import contextlib
import hashlib
import importlib.util
import io
import os
from pathlib import Path
import tempfile
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parents[1]
spec = importlib.util.spec_from_file_location('installer', ROOT / 'scripts/test-elitedesk-plugin.py')
installer = importlib.util.module_from_spec(spec)
spec.loader.exec_module(installer)
class Installer(unittest.TestCase):
def scenario(self, fail_monitor=False):
with tempfile.TemporaryDirectory() as directory, contextlib.ExitStack() as stack:
directory = Path(directory)
provider = directory / 'provider'; provider.mkdir()
agent = provider / 'managed-container'; agent.write_text('previous agent')
modules = provider / 'managed-container.d'; modules.mkdir()
old_module = modules / 'old.sh'; old_module.write_text('previous module')
baseline = directory / 'baseline'
baseline.write_text(''.join(hashlib.sha256(p.read_bytes()).hexdigest() + ' ' + str(p.relative_to(provider)) + '\n'
for p in [agent, old_module]))
backup = directory / 'backups'; backup.mkdir()
for name, value in dict(PROVIDER=provider, AGENT=agent, MODULES=modules,
BASELINE=baseline, BACKUP_ROOT=backup).items():
stack.enter_context(patch.object(installer, name, value))
stack.enter_context(patch.object(installer.os, 'geteuid', return_value=0))
stack.enter_context(patch.object(installer.socket, 'gethostname', return_value='DAVID-ELITEDESK'))
stack.enter_context(patch.object(installer.signal, 'signal'))
stack.enter_context(patch.object(installer, 'cluster', return_value={}))
stack.enter_context(patch.object(installer, 'observe', return_value={'unchanged': True}))
stack.enter_context(patch.dict(os.environ))
calls = []
def fake_run(args, **kwargs):
calls.append(args)
if args[0] == 'restorecon': return ''
if args[0] == 'crm_attribute': return '100'
require_action = args[1]
self.assertIn(require_action, ['meta-data', 'validate-all', 'monitor', 'status', 'start'])
if require_action == 'meta-data':
return '<resource-agent><parameters>' + '<parameter/>' * 16 + '</parameters></resource-agent>'
if fail_monitor and Path(args[0]) == agent and require_action == 'monitor':
raise RuntimeError('injected installed monitor failure')
return ''
stack.enter_context(patch.object(installer, 'run', side_effect=fake_run))
old_mask = os.umask(0o022)
try:
with contextlib.redirect_stdout(io.StringIO()):
if fail_monitor:
with self.assertRaisesRegex(RuntimeError, 'injected'):
installer.main()
self.assertEqual(agent.read_text(), 'previous agent')
self.assertEqual(old_module.read_text(), 'previous module')
else:
installer.main()
self.assertEqual(agent.read_bytes(), (ROOT / 'refactored/managed-container').read_bytes())
self.assertTrue((modules / 'applications/postgresql/plugin.sh').is_file())
self.assertFalse(old_module.exists())
finally:
os.umask(old_mask)
self.assertEqual(len(list(backup.glob('*/results.json'))), 1)
def test_success_only_runs_basic_actions(self):
self.scenario()
def test_failed_installed_monitor_restores_agent_and_modules(self):
self.scenario(fail_monitor=True)
if __name__ == '__main__':
unittest.main(verbosity=2)