Paillasse/portail/app/Services/Labs/ClientCorrecteur.php

66 lines
2.1 KiB
PHP
Raw Normal View History

<?php
namespace App\Services\Labs;
use RuntimeException;
/**
* Parle au service correcteur (Go, sur l'hyperviseur) par son socket Unix.
* Le portail n'envoie que des identifiants : jamais une commande. Les
* commandes sont lues dans le lab.yaml par le correcteur lui-même.
*/
class ClientCorrecteur
{
/**
* @return array{reussi: bool, erreur: ?string}
*/
public function verifier(string $instanceId, string $lab, string $verification): array
{
// 10 s par vérification côté correcteur, plus une marge.
$reponse = $this->envoyer(['instance' => $instanceId, 'lab' => $lab, 'verification' => $verification], 15);
return ['reussi' => $reponse['reussi'], 'erreur' => $reponse['erreur'] ?? null];
}
/**
* Sonde de santé (self-healing) : état libvirt de la VM et réponse de son
* agent invité. Rien n'est exécuté dans la VM.
*
* @return array{repond: bool, etat: ?string, erreur: ?string}
*/
public function sante(string $instanceId): array
{
// 8 s maximum côté correcteur, plus une marge.
$reponse = $this->envoyer(['instance' => $instanceId, 'sante' => true], 10);
return ['repond' => $reponse['reussi'], 'etat' => $reponse['etat'] ?? null, 'erreur' => $reponse['erreur'] ?? null];
}
/**
* @param array<string, string|bool> $requete
* @return array{reussi: bool, erreur?: string, etat?: string}
*/
private function envoyer(array $requete, int $delai): array
{
$flux = @stream_socket_client('unix://'.config('labs.correcteur.socket'), $codeErreur, $messageErreur, 2);
if ($flux === false) {
throw new RuntimeException("Correcteur injoignable : {$messageErreur}");
}
stream_set_timeout($flux, $delai);
fwrite($flux, json_encode($requete)."\n");
$ligne = fgets($flux);
fclose($flux);
$reponse = is_string($ligne) ? json_decode($ligne, true) : null;
if (! is_array($reponse) || ! is_bool($reponse['reussi'] ?? null)) {
throw new RuntimeException('Réponse illisible du correcteur');
}
return $reponse;
}
}