Refactor subuser permissions and enhance artifact management
This commit is contained in:
+44
-3
@@ -6,6 +6,7 @@ use Exception;
|
||||
|
||||
class edge_gateway_agent_artifact_locator
|
||||
{
|
||||
private const EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY = 'build/install';
|
||||
private const ROUTER_ARTIFACT_DIRECTORY = 'resources/edge-gateway-agent';
|
||||
private const DEFAULT_MOUNTED_ARTIFACT_DIRECTORY = '/services/edge-agent/php-agent';
|
||||
private const DEFAULT_BAKED_ARTIFACT_DIRECTORY = '/opt/truckwash-edge-agent-artifacts';
|
||||
@@ -28,6 +29,10 @@ class edge_gateway_agent_artifact_locator
|
||||
$candidateDirectories[] = self::normalizePath($configuredDirectory);
|
||||
}
|
||||
|
||||
foreach (self::edgeAgentBuildDirectories($basePath) as $directory) {
|
||||
$candidateDirectories[] = $directory;
|
||||
}
|
||||
|
||||
$candidateDirectories[] = self::routerArtifactDirectory($basePath);
|
||||
|
||||
$mountedArtifactDirectory = $mountedArtifactDirectory ?? self::mountedArtifactDirectory();
|
||||
@@ -40,9 +45,9 @@ class edge_gateway_agent_artifact_locator
|
||||
$candidateDirectories[] = self::normalizePath($bakedArtifactDirectory);
|
||||
}
|
||||
|
||||
$candidateDirectories[] = self::normalizePath(dirname($basePath, 3) . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent');
|
||||
$candidateDirectories[] = self::normalizePath(dirname($basePath, 2) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent');
|
||||
$candidateDirectories[] = self::normalizePath(dirname($basePath) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent');
|
||||
foreach (self::legacyPhpAgentDirectories($basePath) as $directory) {
|
||||
$candidateDirectories[] = $directory;
|
||||
}
|
||||
|
||||
$paths = [];
|
||||
foreach (array_values(array_unique($candidateDirectories)) as $directory) {
|
||||
@@ -102,6 +107,42 @@ class edge_gateway_agent_artifact_locator
|
||||
return self::normalizePath($basePath . DIRECTORY_SEPARATOR . self::ROUTER_ARTIFACT_DIRECTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private static function edgeAgentBuildDirectories(string $basePath): array
|
||||
{
|
||||
$relative = str_replace('/', DIRECTORY_SEPARATOR, self::EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY);
|
||||
$directories = [
|
||||
dirname($basePath, 4) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
|
||||
dirname($basePath, 3) . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
|
||||
dirname($basePath, 2) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
|
||||
dirname($basePath) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . $relative,
|
||||
];
|
||||
|
||||
if (DIRECTORY_SEPARATOR === '/') {
|
||||
array_unshift($directories, '/edge-agent/' . self::EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY);
|
||||
$directories[] = '/services/edge-agent/' . self::EDGE_AGENT_BUILD_ARTIFACT_DIRECTORY;
|
||||
}
|
||||
|
||||
return array_values(array_unique(array_map(
|
||||
static fn(string $directory): string => self::normalizePath($directory),
|
||||
$directories
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int,string>
|
||||
*/
|
||||
private static function legacyPhpAgentDirectories(string $basePath): array
|
||||
{
|
||||
return [
|
||||
self::normalizePath(dirname($basePath, 3) . DIRECTORY_SEPARATOR . 'services' . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent'),
|
||||
self::normalizePath(dirname($basePath, 2) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent'),
|
||||
self::normalizePath(dirname($basePath) . DIRECTORY_SEPARATOR . 'edge-agent' . DIRECTORY_SEPARATOR . 'php-agent'),
|
||||
];
|
||||
}
|
||||
|
||||
private static function mountedArtifactDirectory(): ?string
|
||||
{
|
||||
if (DIRECTORY_SEPARATOR !== '/') {
|
||||
|
||||
@@ -7,6 +7,7 @@ use Exception;
|
||||
class edge_gateway_install_service
|
||||
{
|
||||
private const ARTIFACTS = [
|
||||
'manifest.json' => 'application/json; charset=utf-8',
|
||||
'agent.php' => 'application/x-httpd-php; charset=utf-8',
|
||||
'lan-worker.php' => 'application/x-httpd-php; charset=utf-8',
|
||||
'auto-updater.php' => 'application/x-httpd-php; charset=utf-8',
|
||||
@@ -42,6 +43,10 @@ class edge_gateway_install_service
|
||||
*/
|
||||
public function readArtifact(string $fileName): string
|
||||
{
|
||||
if ($fileName === 'manifest.json') {
|
||||
return $this->buildManifest();
|
||||
}
|
||||
|
||||
$path = $this->artifactPath($fileName);
|
||||
$contents = file_get_contents($path);
|
||||
if ($contents === false) {
|
||||
@@ -66,13 +71,48 @@ class edge_gateway_install_service
|
||||
*/
|
||||
public function artifactPath(string $fileName): string
|
||||
{
|
||||
if (!array_key_exists($fileName, self::ARTIFACTS)) {
|
||||
if (!array_key_exists($fileName, self::ARTIFACTS) || $fileName === 'manifest.json') {
|
||||
throw new Exception('Unknown edge agent artifact');
|
||||
}
|
||||
|
||||
return edge_gateway_agent_artifact_locator::resolve($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function buildManifest(): string
|
||||
{
|
||||
$artifacts = [];
|
||||
foreach (self::ARTIFACTS as $fileName => $contentType) {
|
||||
if ($fileName === 'manifest.json') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $this->artifactPath($fileName);
|
||||
$sha256 = hash_file('sha256', $path);
|
||||
$bytes = filesize($path);
|
||||
if ($sha256 === false || $bytes === false) {
|
||||
throw new Exception('Unable to inspect edge agent artifact: ' . $fileName);
|
||||
}
|
||||
|
||||
$artifacts[] = [
|
||||
'name' => $fileName,
|
||||
'sha256' => $sha256,
|
||||
'bytes' => $bytes,
|
||||
'content_type' => $contentType,
|
||||
];
|
||||
}
|
||||
|
||||
return json_encode([
|
||||
'schema_version' => 1,
|
||||
'package' => 'truckwash-edge-agent',
|
||||
'version' => edge_gateway_manager::DEFAULT_INSTALL_VERSION,
|
||||
'generated_at' => gmdate('c'),
|
||||
'artifacts' => $artifacts,
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL;
|
||||
}
|
||||
|
||||
private function manager(): edge_gateway_manager
|
||||
{
|
||||
return $this->manager ?? new edge_gateway_manager();
|
||||
|
||||
@@ -33,6 +33,7 @@ class edge_gateway_manager
|
||||
public const STATUS_DEGRADED = 'DEGRADED';
|
||||
public const STATUS_OFFLINE = 'OFFLINE';
|
||||
public const DEFAULT_RELEASE_CHANNEL = 'stable';
|
||||
public const DEFAULT_INSTALL_VERSION = 'compose-php-agent-v3';
|
||||
public const DEFAULT_AGENT_SERVICE_NAME = 'truckwash-edge-agent.service';
|
||||
public const DEFAULT_STACK_SERVICE_NAME = 'truckwash-edge-gateway-stack.service';
|
||||
public const DEFAULT_COMPOSE_STACK_FILE = 'docker-compose.gateway.yml';
|
||||
@@ -2309,6 +2310,7 @@ class edge_gateway_manager
|
||||
'minioBaseImage' => self::DEFAULT_MINIO_BASE_IMAGE,
|
||||
'heartbeatIntervalSeconds' => 15,
|
||||
'operationPollTimeoutSeconds' => self::COMMAND_POLL_TIMEOUT_SECONDS,
|
||||
'installedVersion' => self::DEFAULT_INSTALL_VERSION,
|
||||
], JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$script = <<<'BASH'
|
||||
@@ -2543,6 +2545,42 @@ fetch_http() {
|
||||
[ "$cleanup_body" -eq 1 ] && rm -f "$body_path"
|
||||
rm -f "$headers_path"
|
||||
}
|
||||
verify_manifest_artifact() {
|
||||
local manifest_path="$1"
|
||||
local artifact_name="$2"
|
||||
local artifact_path="$3"
|
||||
log_info "Verifying ${artifact_name} checksum"
|
||||
php -r '
|
||||
$manifestPath = $argv[1];
|
||||
$artifactName = $argv[2];
|
||||
$artifactPath = $argv[3];
|
||||
$manifest = json_decode((string)file_get_contents($manifestPath), true);
|
||||
if (!is_array($manifest)) {
|
||||
fwrite(STDERR, "Invalid artifact manifest: " . $manifestPath . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
$expected = null;
|
||||
foreach ((array)($manifest["artifacts"] ?? []) as $artifact) {
|
||||
if (is_array($artifact) && ($artifact["name"] ?? null) === $artifactName) {
|
||||
$expected = (string)($artifact["sha256"] ?? "");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($expected === null || $expected === "") {
|
||||
fwrite(STDERR, "Artifact missing from manifest: " . $artifactName . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
if (!is_file($artifactPath)) {
|
||||
fwrite(STDERR, "Downloaded artifact is missing: " . $artifactPath . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
$actual = hash_file("sha256", $artifactPath);
|
||||
if ($actual === false || !hash_equals($expected, $actual)) {
|
||||
fwrite(STDERR, "Artifact checksum mismatch for " . $artifactName . PHP_EOL);
|
||||
exit(1);
|
||||
}
|
||||
' "$manifest_path" "$artifact_name" "$artifact_path"
|
||||
}
|
||||
cleanup_existing_installation() {
|
||||
if [ "$INSTALL_DIR" != "/opt/truckwash-edge-agent" ]; then
|
||||
echo "Refusing to remove unexpected install directory: $INSTALL_DIR" >&2
|
||||
@@ -2707,6 +2745,7 @@ run_step "Updating package lists" apt-get update
|
||||
run_step "Installing base packages" apt-get install -y curl ca-certificates docker.io php-cli php-curl php-mbstring php-sqlite3
|
||||
run_step "Installing Docker Compose runtime" install_compose_runtime
|
||||
begin_install_phase "DOWNLOAD_ARTIFACTS" "Downloading edge gateway artifacts"
|
||||
fetch_http "Download artifact manifest" "__MANIFEST_URL__" "$INSTALL_DIR/manifest.json"
|
||||
fetch_http "Download PHP edge agent" "__AGENT_URL__" "$INSTALL_DIR/agent.php"
|
||||
fetch_http "Download LAN worker" "__WORKER_URL__" "$INSTALL_DIR/lan-worker.php"
|
||||
fetch_http "Download auto-updater" "__AUTO_UPDATER_URL__" "$INSTALL_DIR/auto-updater.php"
|
||||
@@ -2717,6 +2756,17 @@ fetch_http "Download auto-updater Dockerfile" "__AUTO_UPDATER_DOCKERFILE_URL__"
|
||||
fetch_http "Download gateway launcher" "__LAUNCHER_URL__" "$INSTALL_DIR/gateway-launcher.sh"
|
||||
fetch_http "Download compose stack service unit" "__STACK_SERVICE_URL__" "$INSTALL_DIR/truckwash-edge-gateway-stack.service"
|
||||
fetch_http "Download compatibility service unit" "__LEGACY_SERVICE_URL__" "$INSTALL_DIR/truckwash-edge-agent.service"
|
||||
begin_install_phase "VERIFY_ARTIFACTS" "Verifying edge gateway artifacts"
|
||||
run_step "Verifying PHP edge agent" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "agent.php" "$INSTALL_DIR/agent.php"
|
||||
run_step "Verifying LAN worker" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "lan-worker.php" "$INSTALL_DIR/lan-worker.php"
|
||||
run_step "Verifying auto-updater" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "auto-updater.php" "$INSTALL_DIR/auto-updater.php"
|
||||
run_step "Verifying compose stack" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "docker-compose.gateway.yml" "$INSTALL_DIR/docker-compose.gateway.yml"
|
||||
run_step "Verifying edge-agent Dockerfile" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "Dockerfile.edge-agent" "$INSTALL_DIR/Dockerfile.edge-agent"
|
||||
run_step "Verifying lan-worker Dockerfile" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "Dockerfile.lan-worker" "$INSTALL_DIR/Dockerfile.lan-worker"
|
||||
run_step "Verifying auto-updater Dockerfile" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "Dockerfile.auto-updater" "$INSTALL_DIR/Dockerfile.auto-updater"
|
||||
run_step "Verifying gateway launcher" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "gateway-launcher.sh" "$INSTALL_DIR/gateway-launcher.sh"
|
||||
run_step "Verifying compose stack service unit" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "truckwash-edge-gateway-stack.service" "$INSTALL_DIR/truckwash-edge-gateway-stack.service"
|
||||
run_step "Verifying compatibility service unit" verify_manifest_artifact "$INSTALL_DIR/manifest.json" "truckwash-edge-agent.service" "$INSTALL_DIR/truckwash-edge-agent.service"
|
||||
begin_install_phase "WRITE_CONFIG" "Writing gateway configuration"
|
||||
cat > "$CONFIG_TEMPLATE_PATH" <<'EOF_JSON'
|
||||
__CONFIG_JSON__
|
||||
@@ -2745,6 +2795,7 @@ BASH;
|
||||
'__INSTALL_TOKEN__' => $plainToken,
|
||||
'__VERIFY_URL__' => $this->buildInstallTokenVerifyUrl($plainToken),
|
||||
'__STATUS_URL__' => rtrim($this->getApiBaseUrl(), '/') . '/edge-agent/install-token/status',
|
||||
'__MANIFEST_URL__' => $this->buildAgentArtifactUrl('manifest.json'),
|
||||
'__AGENT_URL__' => $this->buildAgentArtifactUrl('agent.php'),
|
||||
'__WORKER_URL__' => $this->buildAgentArtifactUrl(self::DEFAULT_LAN_WORKER_ARTIFACT),
|
||||
'__AUTO_UPDATER_URL__' => $this->buildAgentArtifactUrl(self::DEFAULT_AUTO_UPDATER_ARTIFACT),
|
||||
|
||||
@@ -81,6 +81,7 @@ class edgeGatewaysRoute
|
||||
$this->get('/edge-agent/install-token/verify', fn() => $this->handleInstallTokenVerify());
|
||||
$this->post('/edge-agent/install-token/status', fn() => $this->handleAgentInstallTokenStatus());
|
||||
$this->get('/edge-agent/install.sh', fn() => $this->renderInstallScript());
|
||||
$this->get('/edge-agent/artifacts/manifest.json', fn() => $this->renderArtifact('manifest.json'));
|
||||
$this->get('/edge-agent/artifacts/agent.php', fn() => $this->renderArtifact('agent.php'));
|
||||
$this->get('/edge-agent/artifacts/lan-worker.php', fn() => $this->renderArtifact('lan-worker.php'));
|
||||
$this->get('/edge-agent/artifacts/auto-updater.php', fn() => $this->renderArtifact('auto-updater.php'));
|
||||
|
||||
Reference in New Issue
Block a user