CluPilotCloud/app/Provisioning/Steps/Customer/CustomerStep.php

96 lines
2.8 KiB
PHP

<?php
namespace App\Provisioning\Steps\Customer;
use App\Models\Instance;
use App\Models\Order;
use App\Models\ProvisioningRun;
use App\Models\Subscription;
use App\Provisioning\Contracts\ProvisioningStep;
use App\Provisioning\Steps\ManagesRunResources;
use App\Services\Proxmox\ProxmoxClient;
use RuntimeException;
/**
* Shared helpers for customer-provisioning steps: subject/instance/plan
* resolution, guest-agent command execution, and idempotency breadcrumbs.
*/
abstract class CustomerStep implements ProvisioningStep
{
use ManagesRunResources;
public function label(): string
{
return 'provisioning.step.'.$this->key();
}
public function maxDuration(): int
{
return 300;
}
protected function order(ProvisioningRun $run): Order
{
/** @var Order $order */
$order = $run->subject;
return $order;
}
protected function instance(ProvisioningRun $run): ?Instance
{
$id = $run->context('instance_id');
return $id ? Instance::query()->find($id) : null;
}
/** The contract this run is carrying out, or null if none was opened. */
protected function subscription(ProvisioningRun $run): ?Subscription
{
return Subscription::query()->where('order_id', $this->order($run)->id)->first();
}
/**
* What the customer actually bought — read from their frozen snapshot, never
* from config('provisioning.plans').
*
* The catalogue says what we sell TODAY. Sizing a machine from it would mean
* that shrinking a plan resizes the VM of a customer who bought the larger
* one, on their next run. Empty when no contract exists, so ValidateOrder
* can fail the run closed instead of quietly falling back to the catalogue.
*
* @return array<string, mixed>
*/
protected function plan(ProvisioningRun $run): array
{
$subscription = $this->subscription($run);
if ($subscription === null) {
return [];
}
return $subscription->only([
'plan', 'term', 'price_cents', 'currency', 'quota_gb', 'traffic_gb',
'seats', 'ram_mb', 'cores', 'disk_gb', 'performance', 'template_vmid', 'tier',
]);
}
/**
* Run a command inside the guest via qemu-guest-agent. Throws on a non-zero
* exit code so the runner turns it into a retry.
*/
protected function guest(ProxmoxClient $pve, ProvisioningRun $run, string $command): string
{
$node = (string) $run->context('node');
$vmid = (int) $run->context('vmid');
$result = $pve->guestExec($node, $vmid, $command);
if ((int) ($result['exitcode'] ?? 1) !== 0) {
throw new RuntimeException("guest exec failed (exit {$result['exitcode']}): {$command}");
}
return (string) ($result['out-data'] ?? '');
}
}