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,384 @@
<?php
declare(strict_types=1);
/**
* ML API Endpoints Test
*
* Tests all ML Management REST API endpoints:
* 1. Model registration and listing
* 2. Performance metrics retrieval
* 3. A/B testing workflows
* 4. Auto-tuning optimization
* 5. Dashboard data endpoints
*/
require_once __DIR__ . '/../../vendor/autoload.php';
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Core\ValueObjects\Version;
use App\Framework\Http\HttpRequest;
use App\Framework\Http\Method;
use App\Framework\Http\RequestBody;
use App\Framework\Http\Headers;
use App\Framework\MachineLearning\ModelManagement\ABTestingService;
use App\Framework\MachineLearning\ModelManagement\AutoTuningEngine;
use App\Framework\MachineLearning\ModelManagement\CacheModelRegistry;
use App\Framework\MachineLearning\ModelManagement\CachePerformanceStorage;
use App\Framework\MachineLearning\ModelManagement\InMemoryPerformanceStorage;
use App\Framework\MachineLearning\ModelManagement\LogAlertingService;
use App\Framework\MachineLearning\ModelManagement\ModelPerformanceMonitor;
use App\Framework\MachineLearning\ModelManagement\ValueObjects\ModelMetadata;
use App\Framework\MachineLearning\ModelManagement\ValueObjects\ModelType;
use App\Framework\Random\SecureRandomGenerator;
use App\Application\Api\MachineLearning\MLModelsController;
use App\Application\Api\MachineLearning\MLABTestingController;
use App\Application\Api\MachineLearning\MLAutoTuningController;
use App\Application\Api\MachineLearning\MLDashboardController;
use App\Framework\Cache\Driver\InMemoryCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Serializer\Php\PhpSerializer;
use App\Framework\Serializer\Php\PhpSerializerConfig;
echo "=== ML API Endpoints Test ===\n\n";
// Helper function to create HTTP requests easily
function createRequest(string $method, string $path, array $data = [], array $queryParams = []): HttpRequest
{
$methodEnum = Method::from($method);
$headers = new Headers();
$body = !empty($data) ? json_encode($data) : '';
return new HttpRequest(
method: $methodEnum,
headers: $headers,
body: $body,
path: $path,
queryParams: $queryParams,
parsedBody: new RequestBody($methodEnum, $headers, $body, $data)
);
}
try {
// ========================================================================
// Setup: Initialize services
// ========================================================================
echo "1. Initializing ML services...\n";
$cacheDriver = new InMemoryCache();
$serializer = new PhpSerializer(PhpSerializerConfig::safe());
$cache = new GeneralCache($cacheDriver, $serializer);
$registry = new CacheModelRegistry($cache, ttlDays: 7);
$storage = new InMemoryPerformanceStorage();
$alerting = new LogAlertingService();
$performanceMonitor = new ModelPerformanceMonitor($registry, $storage, $alerting);
$random = new SecureRandomGenerator();
$abTesting = new ABTestingService($random, $registry);
$autoTuning = new AutoTuningEngine($performanceMonitor, $registry, $storage);
// Initialize controllers
$modelsController = new MLModelsController($registry, $performanceMonitor);
$abTestingController = new MLABTestingController($abTesting, $registry);
$autoTuningController = new MLAutoTuningController($autoTuning, $registry);
$dashboardController = new MLDashboardController($registry, $performanceMonitor);
echo " ✓ All services initialized\n";
echo " ✓ All controllers created\n\n";
// ========================================================================
// Test 1: Model Registration (POST /api/ml/models)
// ========================================================================
echo "2. Testing model registration endpoint...\n";
$registerRequest = createRequest(
method: 'POST',
path: '/api/ml/models',
data: [
'model_name' => 'test-fraud-detector',
'type' => 'supervised',
'version' => '1.0.0',
'configuration' => [
'threshold' => 0.7,
'algorithm' => 'random_forest',
],
'performance_metrics' => [
'accuracy' => 0.92,
'precision' => 0.89,
],
]
);
$registerResponse = $modelsController->registerModel($registerRequest);
$registerData = $registerResponse->data;
echo " → POST /api/ml/models\n";
echo " Status: {$registerResponse->status->value}\n";
echo " Model: {$registerData['model_name']}\n";
echo " Version: {$registerData['version']}\n";
echo " Message: {$registerData['message']}\n\n";
// ========================================================================
// Test 2: List Models (GET /api/ml/models)
// ========================================================================
echo "3. Testing list models endpoint...\n";
// Register additional models for testing
$additionalModels = [
['name' => 'spam-classifier', 'type' => 'supervised', 'version' => '2.0.0'],
['name' => 'anomaly-detector', 'type' => 'unsupervised', 'version' => '1.5.0'],
];
foreach ($additionalModels as $modelData) {
$metadata = new ModelMetadata(
modelName: $modelData['name'],
modelType: $modelData['type'] === 'supervised' ? ModelType::SUPERVISED : ModelType::UNSUPERVISED,
version: Version::fromString($modelData['version']),
configuration: ['threshold' => 0.75],
createdAt: Timestamp::now()
);
$registry->register($metadata);
}
$listRequest = createRequest(
method: 'GET',
path: '/api/ml/models'
);
$listResponse = $modelsController->listModels($listRequest);
$listData = $listResponse->data;
echo " → GET /api/ml/models\n";
echo " Status: {$listResponse->status->value}\n";
echo " Total Models: {$listData['total_models']}\n";
foreach ($listData['models'] as $model) {
echo " - {$model['model_name']} ({$model['type']}) - {$model['versions'][0]['version']}\n";
}
echo "\n";
// ========================================================================
// Test 3: Get Model Metrics (GET /api/ml/models/{modelName}/metrics)
// ========================================================================
echo "4. Testing model metrics endpoint...\n";
// Simulate predictions for test-fraud-detector
$timestamp = Timestamp::now();
for ($i = 0; $i < 100; $i++) {
$storage->storePrediction([
'model_name' => 'test-fraud-detector',
'version' => '1.0.0',
'prediction' => $i < 92,
'actual' => $i < 92,
'confidence' => 0.85,
'features' => [],
'timestamp' => $timestamp->toDateTime(),
'is_correct' => true,
]);
}
$metricsRequest = createRequest(
method: 'GET',
path: '/api/ml/models/test-fraud-detector/metrics',
queryParams: ['version' => '1.0.0', 'timeWindow' => '1']
);
$metricsResponse = $modelsController->getMetrics('test-fraud-detector', $metricsRequest);
$metricsData = $metricsResponse->data;
echo " → GET /api/ml/models/test-fraud-detector/metrics\n";
echo " Status: {$metricsResponse->status->value}\n";
echo " Accuracy: " . sprintf("%.2f%%", $metricsData['metrics']['accuracy'] * 100) . "\n";
echo " Total Predictions: {$metricsData['metrics']['total_predictions']}\n\n";
// ========================================================================
// Test 4: A/B Test Creation (POST /api/ml/ab-test)
// ========================================================================
echo "5. Testing A/B test creation endpoint...\n";
// Register version 2.0.0 for A/B testing
$v2Metadata = new ModelMetadata(
modelName: 'test-fraud-detector',
modelType: ModelType::SUPERVISED,
version: Version::fromString('2.0.0'),
configuration: ['threshold' => 0.75],
createdAt: Timestamp::now(),
performanceMetrics: ['accuracy' => 0.95]
);
$registry->register($v2Metadata);
$abTestRequest = createRequest(
method: 'POST',
path: '/api/ml/ab-test',
data: [
'model_name' => 'test-fraud-detector',
'version_a' => '1.0.0',
'version_b' => '2.0.0',
'traffic_split_a' => 0.5,
'primary_metric' => 'accuracy',
]
);
$abTestResponse = $abTestingController->startTest($abTestRequest);
$abTestData = $abTestResponse->data;
echo " → POST /api/ml/ab-test\n";
echo " Status: {$abTestResponse->status->value}\n";
echo " Test ID: {$abTestData['test_id']}\n";
echo " Version A Traffic: " . ($abTestData['traffic_split']['version_a'] * 100) . "%\n";
echo " Version B Traffic: " . ($abTestData['traffic_split']['version_b'] * 100) . "%\n\n";
// ========================================================================
// Test 5: Rollout Plan Generation (POST /api/ml/ab-test/rollout-plan)
// ========================================================================
echo "6. Testing rollout plan generation endpoint...\n";
$rolloutRequest = createRequest(
method: 'POST',
path: '/api/ml/ab-test/rollout-plan',
data: [
'model_name' => 'test-fraud-detector',
'current_version' => '1.0.0',
'new_version' => '2.0.0',
'steps' => 4,
]
);
$rolloutResponse = $abTestingController->generateRolloutPlan($rolloutRequest);
$rolloutData = $rolloutResponse->data;
echo " → POST /api/ml/ab-test/rollout-plan\n";
echo " Status: {$rolloutResponse->status->value}\n";
echo " Total Stages: {$rolloutData['total_stages']}\n";
foreach ($rolloutData['rollout_stages'] as $stage) {
echo " Stage {$stage['stage']}: Current {$stage['current_version_traffic']}% / New {$stage['new_version_traffic']}%\n";
}
echo "\n";
// ========================================================================
// Test 6: Threshold Optimization (POST /api/ml/optimize/threshold)
// ========================================================================
echo "7. Testing threshold optimization endpoint...\n";
// Add more diverse predictions for optimization
for ($i = 0; $i < 100; $i++) {
$confidence = 0.5 + ($i / 100) * 0.4; // 0.5 to 0.9
$prediction = $confidence >= 0.7;
$actual = $i < 85;
$storage->storePrediction([
'model_name' => 'test-fraud-detector',
'version' => '1.0.0',
'prediction' => $prediction,
'actual' => $actual,
'confidence' => $confidence,
'features' => [],
'timestamp' => $timestamp->toDateTime(),
'is_correct' => $prediction === $actual,
]);
}
$optimizeRequest = createRequest(
method: 'POST',
path: '/api/ml/optimize/threshold',
data: [
'model_name' => 'test-fraud-detector',
'version' => '1.0.0',
'metric_to_optimize' => 'f1_score',
'threshold_range' => [0.5, 0.9],
'step' => 0.1,
]
);
$optimizeResponse = $autoTuningController->optimizeThreshold($optimizeRequest);
$optimizeData = $optimizeResponse->data;
echo " → POST /api/ml/optimize/threshold\n";
echo " Status: {$optimizeResponse->status->value}\n";
echo " Current Threshold: {$optimizeData['current_threshold']}\n";
echo " Optimal Threshold: {$optimizeData['optimal_threshold']}\n";
echo " Improvement: " . sprintf("%.1f%%", $optimizeData['improvement_percent']) . "\n";
echo " Tested Thresholds: {$optimizeData['tested_thresholds']}\n\n";
// ========================================================================
// Test 7: Dashboard Data (GET /api/ml/dashboard)
// ========================================================================
echo "8. Testing dashboard data endpoint...\n";
$dashboardRequest = createRequest(
method: 'GET',
path: '/api/ml/dashboard',
queryParams: ['timeWindow' => '24']
);
$dashboardResponse = $dashboardController->getDashboardData($dashboardRequest);
$dashboardData = $dashboardResponse->data;
echo " → GET /api/ml/dashboard\n";
echo " Status: {$dashboardResponse->status->value}\n";
echo " Total Models: {$dashboardData['summary']['total_models']}\n";
echo " Healthy: {$dashboardData['summary']['healthy_models']}\n";
echo " Degraded: {$dashboardData['summary']['degraded_models']}\n";
echo " Average Accuracy: " . sprintf("%.2f%%", $dashboardData['summary']['average_accuracy'] * 100) . "\n";
echo " Overall Status: {$dashboardData['summary']['overall_status']}\n";
echo " Active Alerts: " . count($dashboardData['alerts']) . "\n\n";
// ========================================================================
// Test 8: Health Indicators (GET /api/ml/dashboard/health)
// ========================================================================
echo "9. Testing health indicators endpoint...\n";
$healthResponse = $dashboardController->getHealthIndicators();
$healthData = $healthResponse->data;
echo " → GET /api/ml/dashboard/health\n";
echo " Status: {$healthResponse->status->value}\n";
echo " Overall Status: {$healthData['overall_status']}\n";
echo " Health Percentage: {$healthData['health_percentage']}%\n";
echo " Healthy Models: {$healthData['healthy_models']}\n";
echo " Degraded Models: {$healthData['degraded_models']}\n";
echo " Critical Models: {$healthData['critical_models']}\n\n";
// ========================================================================
// Test 9: Registry Summary (GET /api/ml/dashboard/registry-summary)
// ========================================================================
echo "10. Testing registry summary endpoint...\n";
$summaryResponse = $dashboardController->getRegistrySummary();
$summaryData = $summaryResponse->data;
echo " → GET /api/ml/dashboard/registry-summary\n";
echo " Status: {$summaryResponse->status->value}\n";
echo " Total Models: {$summaryData['total_models']}\n";
echo " Total Versions: {$summaryData['total_versions']}\n";
echo " By Type:\n";
foreach ($summaryData['by_type'] as $type => $count) {
echo " - {$type}: {$count}\n";
}
echo "\n";
// ========================================================================
// Test Summary
// ========================================================================
echo "=== Test Summary ===\n";
echo "✓ Model Registration: Working\n";
echo "✓ List Models: Working\n";
echo "✓ Get Model Metrics: Working\n";
echo "✓ A/B Test Creation: Working\n";
echo "✓ Rollout Plan Generation: Working\n";
echo "✓ Threshold Optimization: Working\n";
echo "✓ Dashboard Data: Working\n";
echo "✓ Health Indicators: Working\n";
echo "✓ Registry Summary: Working\n\n";
echo "API Endpoints Tested: 9\n";
echo "All endpoints returning 200/201 status codes\n\n";
echo "=== ML API Endpoints Test PASSED ===\n";
} catch (\Throwable $e) {
echo "\n!!! TEST FAILED !!!\n";
echo "Error: " . $e->getMessage() . "\n";
echo "File: " . $e->getFile() . ":" . $e->getLine() . "\n";
echo "\nStack trace:\n" . $e->getTraceAsString() . "\n";
exit(1);
}