feat(Deployment): Integrate Ansible deployment via PHP deployment pipeline

- Create AnsibleDeployStage using framework's Process module for secure command execution
- Integrate AnsibleDeployStage into DeploymentPipelineCommands for production deployments
- Add force_deploy flag support in Ansible playbook to override stale locks
- Use PHP deployment module as orchestrator (php console.php deploy:production)
- Fix ErrorAggregationInitializer to use Environment class instead of $_ENV superglobal

Architecture:
- BuildStage → AnsibleDeployStage → HealthCheckStage for production
- Process module provides timeout, error handling, and output capture
- Ansible playbook supports rollback via rollback-git-based.yml
- Zero-downtime deployments with health checks
This commit is contained in:
2025-10-26 14:08:07 +01:00
parent a90263d3be
commit 3b623e7afb
170 changed files with 19888 additions and 575 deletions

View File

@@ -0,0 +1,261 @@
<?php
declare(strict_types=1);
/**
* Integration Test: Queue Anomaly Detection System
*
* Tests the full integration of:
* - QueueJobFeatureExtractor
* - JobAnomalyDetector
* - QueueAnomalyMonitor
* - Event dispatching
*/
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Framework\Core\AppBootstrapper;
use App\Framework\Queue\MachineLearning\JobAnomalyDetector;
use App\Framework\Queue\MachineLearning\QueueJobFeatureExtractor;
use App\Framework\Queue\MachineLearning\QueueAnomalyMonitor;
use App\Framework\Queue\Services\JobMetricsManager;
use App\Framework\Queue\ValueObjects\JobMetrics;
use App\Framework\Queue\ValueObjects\JobMetadata;
use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Core\ValueObjects\Score;
use App\Framework\Ulid\Ulid;
use App\Framework\DateTime\SystemClock;
echo "=== Queue Anomaly Detection Integration Test ===\n\n";
try {
// Bootstrap framework
echo "1. Bootstrapping framework...\n";
$basePath = dirname(__DIR__, 2);
$clock = new \App\Framework\DateTime\SystemClock();
$highResClock = new \App\Framework\DateTime\SystemHighResolutionClock();
$memoryMonitor = new \App\Framework\Performance\MemoryMonitor();
$collector = new \App\Framework\Performance\EnhancedPerformanceCollector(
$clock,
$highResClock,
$memoryMonitor,
enabled: false
);
$bootstrapper = new AppBootstrapper($basePath, $collector, $memoryMonitor);
$container = $bootstrapper->bootstrapWorker();
echo " ✓ Framework bootstrapped\n\n";
// Initialize components
echo "2. Initializing Queue Anomaly Detection Components...\n";
// Create detector with lower threshold for testing
$detector = new JobAnomalyDetector(
anomalyThreshold: new Score(0.4), // 40% threshold for testing
zScoreThreshold: 3.0,
iqrMultiplier: 1.5
);
echo " ✓ JobAnomalyDetector created (threshold: 40%)\n";
// Get JobMetricsManager from container
$metricsManager = $container->get(JobMetricsManager::class);
echo " ✓ JobMetricsManager retrieved\n";
// Create feature extractor
$featureExtractor = new QueueJobFeatureExtractor($metricsManager);
echo " ✓ QueueJobFeatureExtractor created\n";
// Create anomaly monitor
$logger = $container->get(\App\Framework\Logging\Logger::class);
$anomalyMonitor = new QueueAnomalyMonitor(
$detector,
$featureExtractor,
$metricsManager,
$logger
);
echo " ✓ QueueAnomalyMonitor created\n\n";
// Test Case 1: Normal Job Execution
echo "3. Test Case 1: Normal Job Execution\n";
$normalMetrics = new JobMetrics(
jobId: 'job-normal-001',
queueName: 'default',
status: 'completed',
attempts: 1,
maxAttempts: 3,
executionTimeMs: 150.0,
memoryUsageBytes: 10 * 1024 * 1024, // 10MB
errorMessage: null,
createdAt: date('Y-m-d H:i:s'),
startedAt: date('Y-m-d H:i:s'),
completedAt: date('Y-m-d H:i:s'),
failedAt: null,
metadata: ['scheduled' => false]
);
$normalMetadata = new JobMetadata(
id: new Ulid(new SystemClock()),
class: ClassName::create('NormalProcessingJob'),
type: 'job',
queuedAt: Timestamp::now(),
tags: ['normal'],
extra: []
);
$result1 = $anomalyMonitor->analyzeJobExecution($normalMetrics, $normalMetadata, 10);
echo " Result: " . ($result1->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
echo " Confidence: " . sprintf("%.2f%%", $result1->anomalyScore->value() * 100) . "\n";
echo " Severity: {$result1->getSeverity()}\n\n";
// Test Case 2: High Failure Job
echo "4. Test Case 2: High Failure Job (Multiple Retries)\n";
$highFailureMetrics = new JobMetrics(
jobId: 'job-failure-002',
queueName: 'default',
status: 'failed',
attempts: 3,
maxAttempts: 3,
executionTimeMs: 500.0,
memoryUsageBytes: 15 * 1024 * 1024, // 15MB
errorMessage: 'Database connection timeout',
createdAt: date('Y-m-d H:i:s'),
startedAt: date('Y-m-d H:i:s'),
completedAt: null,
failedAt: date('Y-m-d H:i:s'),
metadata: ['retry_reason' => 'timeout']
);
$highFailureMetadata = new JobMetadata(
id: new Ulid(new SystemClock()),
class: ClassName::create('DatabaseProcessingJob'),
type: 'job',
queuedAt: Timestamp::now(),
tags: ['database', 'critical'],
extra: ['retry_count' => 3]
);
$result2 = $anomalyMonitor->analyzeJobExecution($highFailureMetrics, $highFailureMetadata, 150);
echo " Result: " . ($result2->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
echo " Confidence: " . sprintf("%.2f%%", $result2->anomalyScore->value() * 100) . "\n";
echo " Severity: {$result2->getSeverity()}\n";
if ($result2->isAnomalous) {
echo " Primary Indicator: {$result2->primaryIndicator}\n";
echo " Detected Patterns:\n";
foreach ($result2->detectedPatterns as $pattern) {
echo " - {$pattern['type']}: " . sprintf("%.2f%% confidence", $pattern['confidence']->value() * 100) . "\n";
}
echo " Recommended Action: {$result2->getRecommendedAction()}\n";
}
echo "\n";
// Test Case 3: Performance Degradation
echo "5. Test Case 3: Performance Degradation (Slow Execution + High Memory)\n";
$slowMetrics = new JobMetrics(
jobId: 'job-slow-003',
queueName: 'default',
status: 'completed',
attempts: 1,
maxAttempts: 3,
executionTimeMs: 15000.0, // 15 seconds (very slow)
memoryUsageBytes: 200 * 1024 * 1024, // 200MB (high memory)
errorMessage: null,
createdAt: date('Y-m-d H:i:s'),
startedAt: date('Y-m-d H:i:s'),
completedAt: date('Y-m-d H:i:s'),
failedAt: null,
metadata: []
);
$slowMetadata = new JobMetadata(
id: new Ulid(new SystemClock()),
class: ClassName::create('ReportGenerationJob'),
type: 'job',
queuedAt: Timestamp::now(),
tags: ['report', 'heavy'],
extra: ['report_type' => 'annual']
);
$result3 = $anomalyMonitor->analyzeJobExecution($slowMetrics, $slowMetadata, 5);
echo " Result: " . ($result3->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
echo " Confidence: " . sprintf("%.2f%%", $result3->anomalyScore->value() * 100) . "\n";
echo " Severity: {$result3->getSeverity()}\n";
if ($result3->isAnomalous) {
echo " Primary Indicator: {$result3->primaryIndicator}\n";
echo " Top Contributors:\n";
foreach ($result3->getTopContributors(3) as $contributor) {
echo " - {$contributor['feature']}: " . sprintf("%.2f%%", $contributor['score']->value() * 100) . "\n";
}
}
echo "\n";
// Test Case 4: Queue Backlog Impact
echo "6. Test Case 4: Queue Backlog Impact (High Queue Depth)\n";
$backlogMetrics = new JobMetrics(
jobId: 'job-backlog-004',
queueName: 'default',
status: 'completed',
attempts: 2,
maxAttempts: 3,
executionTimeMs: 800.0,
memoryUsageBytes: 20 * 1024 * 1024, // 20MB
errorMessage: null,
createdAt: date('Y-m-d H:i:s'),
startedAt: date('Y-m-d H:i:s'),
completedAt: date('Y-m-d H:i:s'),
failedAt: null,
metadata: []
);
$backlogMetadata = new JobMetadata(
id: new Ulid(new SystemClock()),
class: ClassName::create('EmailNotificationJob'),
type: 'job',
queuedAt: Timestamp::now(),
tags: ['email'],
extra: []
);
$result4 = $anomalyMonitor->analyzeJobExecution($backlogMetrics, $backlogMetadata, 900); // 900 jobs in queue!
echo " Result: " . ($result4->isAnomalous ? "🚨 ANOMALOUS" : "✓ NORMAL") . "\n";
echo " Confidence: " . sprintf("%.2f%%", $result4->anomalyScore->value() * 100) . "\n";
echo " Severity: {$result4->getSeverity()}\n";
if ($result4->isAnomalous) {
echo " Primary Indicator: {$result4->primaryIndicator}\n";
echo " Immediate Attention: " . ($result4->requiresImmediateAttention() ? "YES" : "NO") . "\n";
}
echo "\n";
// Test monitoring status
echo "7. Testing Monitoring Status...\n";
$anomalyMonitor->enableMonitoring('default');
$status = $anomalyMonitor->getMonitoringStatus();
echo " ✓ Monitoring enabled for 'default' queue\n";
echo " Detector Threshold: " . sprintf("%.0f%%", $status['detector_threshold'] * 100) . "\n";
echo " Z-Score Threshold: {$status['z_score_threshold']}\n";
echo " IQR Multiplier: {$status['iqr_multiplier']}\n\n";
// Summary
echo "=== Integration Test Summary ===\n";
echo "✓ QueueJobFeatureExtractor: Working\n";
echo "✓ JobAnomalyDetector: Working\n";
echo "✓ QueueAnomalyMonitor: Working\n";
echo "✓ Event Logging: Working\n";
echo "✓ Threshold Configuration: Working\n\n";
echo "Test Results:\n";
echo " - Normal Job: " . ($result1->isAnomalous ? "ANOMALOUS" : "✓ NORMAL") . " (" . sprintf("%.2f%%", $result1->anomalyScore->value() * 100) . ")\n";
echo " - High Failure Job: " . ($result2->isAnomalous ? "🚨 ANOMALOUS" : "NORMAL") . " (" . sprintf("%.2f%%", $result2->anomalyScore->value() * 100) . ")\n";
echo " - Performance Degradation: " . ($result3->isAnomalous ? "🚨 ANOMALOUS" : "NORMAL") . " (" . sprintf("%.2f%%", $result3->anomalyScore->value() * 100) . ")\n";
echo " - Queue Backlog Impact: " . ($result4->isAnomalous ? "🚨 ANOMALOUS" : "NORMAL") . " (" . sprintf("%.2f%%", $result4->anomalyScore->value() * 100) . ")\n\n";
echo "=== Integration Test Completed Successfully ===\n";
} catch (\Throwable $e) {
echo "\n!!! INTEGRATION TEST FAILED !!!\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
exit(1);
}