CluPilotCloud/app/Provisioning/Steps/Host/CreateAutomationToken.php

71 lines
2.6 KiB
PHP

<?php
namespace App\Provisioning\Steps\Host;
use App\Models\ProvisioningRun;
use App\Provisioning\StepResult;
use App\Services\Ssh\RemoteShell;
/**
* Creates the automation role + user + API token on the host via `pveum` over
* SSH. A fresh host has no API token yet, so this bootstrap must run through the
* authenticated root SSH session, not the (token-less) REST API. The secret is
* shown only once, so the token id is persisted before advancing; a re-run
* deletes and re-mints the token to stay idempotent after a crash.
*/
class CreateAutomationToken extends HostStep
{
public function __construct(private RemoteShell $shell) {}
public function key(): string
{
return 'create_automation_token';
}
public function execute(ProvisioningRun $run): StepResult
{
$host = $this->host($run);
// Idempotent: token already minted and persisted.
if ($this->hasResource($run, 'pve_token') && filled($host->api_token_ref)) {
return StepResult::advance();
}
$role = (string) config('provisioning.proxmox.role_id');
$privs = (string) config('provisioning.proxmox.role_privs');
$user = (string) config('provisioning.proxmox.user');
$tokenName = (string) config('provisioning.proxmox.token_name');
$this->keyLogin($this->shell, $host);
// Role / user / ACL are idempotent (ignore "already exists").
$this->shell->run('pveum role add '.escapeshellarg($role).' -privs '.escapeshellarg($privs).' || true');
$this->shell->run('pveum user add '.escapeshellarg($user).' || true');
$this->shell->run('pveum acl modify / -user '.escapeshellarg($user).' -role '.escapeshellarg($role).' || true');
// Drop any half-created token from a prior crashed attempt, then mint fresh.
$this->shell->run('pveum user token remove '.escapeshellarg($user).' '.escapeshellarg($tokenName).' || true');
$result = $this->shell->run(
'pveum user token add '.escapeshellarg($user).' '.escapeshellarg($tokenName).' -privsep 0 --output-format json'
);
if (! $result->ok()) {
return StepResult::retry(20, 'Proxmox token creation failed');
}
$data = json_decode($result->stdout, true);
$secret = $data['value'] ?? null;
if (blank($secret)) {
return StepResult::retry(20, 'Proxmox token secret was empty');
}
$tokenId = $data['full-tokenid'] ?? "{$user}!{$tokenName}";
$host->update(['api_token_ref' => $tokenId.'='.$secret]);
$this->recordResource($run, $host, 'pve_token', $tokenId);
return StepResult::advance();
}
}