mirror of
https://github.com/vitodeploy/vito.git
synced 2025-04-20 10:21:37 +00:00
load Vultr regions and plans dynamically (#369)
This commit is contained in:
parent
57b2771c7e
commit
20944421de
@ -6,7 +6,6 @@
|
|||||||
use App\Facades\Notifier;
|
use App\Facades\Notifier;
|
||||||
use App\Notifications\FailedToDeleteServerFromProvider;
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Aws\Ec2\Ec2Client;
|
use Aws\Ec2\Ec2Client;
|
||||||
use Aws\EC2InstanceConnect\EC2InstanceConnectClient;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Illuminate\Filesystem\FilesystemAdapter;
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -16,8 +15,6 @@ class AWS extends AbstractProvider
|
|||||||
{
|
{
|
||||||
protected Ec2Client $ec2Client;
|
protected Ec2Client $ec2Client;
|
||||||
|
|
||||||
protected EC2InstanceConnectClient $ec2InstanceConnectClient;
|
|
||||||
|
|
||||||
public function createRules(array $input): array
|
public function createRules(array $input): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
@ -272,14 +269,12 @@ private function runInstance(): void
|
|||||||
/**
|
/**
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function getImageId(string $os): string
|
private function getImageId(string $os): string
|
||||||
{
|
{
|
||||||
$this->connectToEc2Client();
|
$this->connectToEc2Client();
|
||||||
|
|
||||||
$version = config('core.operating_system_versions.'.$os);
|
$version = config('core.operating_system_versions.'.$os);
|
||||||
|
|
||||||
ds($version);
|
|
||||||
|
|
||||||
$result = $this->ec2Client->describeImages([
|
$result = $this->ec2Client->describeImages([
|
||||||
'Filters' => [
|
'Filters' => [
|
||||||
[
|
[
|
||||||
@ -301,8 +296,6 @@ public function getImageId(string $os): string
|
|||||||
// Extract and display image information
|
// Extract and display image information
|
||||||
$images = $result->get('Images');
|
$images = $result->get('Images');
|
||||||
|
|
||||||
ds($images);
|
|
||||||
|
|
||||||
if (! empty($images)) {
|
if (! empty($images)) {
|
||||||
// Sort images by creation date to get the latest one
|
// Sort images by creation date to get the latest one
|
||||||
usort($images, function ($a, $b) {
|
usort($images, function ($a, $b) {
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace App\ServerProviders;
|
namespace App\ServerProviders;
|
||||||
|
|
||||||
use App\ValidationRules\RestrictedIPAddressesRule;
|
use App\ValidationRules\RestrictedIPAddressesRule;
|
||||||
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Facades\File;
|
use Illuminate\Support\Facades\File;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Validation\Rule;
|
||||||
@ -58,7 +59,7 @@ public function regions(): array
|
|||||||
|
|
||||||
public function create(): void
|
public function create(): void
|
||||||
{
|
{
|
||||||
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
|
/** @var FilesystemAdapter $storageDisk */
|
||||||
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
File::copy(
|
File::copy(
|
||||||
storage_path(config('core.ssh_private_key_name')),
|
storage_path(config('core.ssh_private_key_name')),
|
||||||
|
@ -69,8 +69,6 @@ public function plans(?string $region): array
|
|||||||
->get($this->apiUrl.'/sizes', ['per_page' => 200])
|
->get($this->apiUrl.'/sizes', ['per_page' => 200])
|
||||||
->json();
|
->json();
|
||||||
|
|
||||||
ds($region);
|
|
||||||
|
|
||||||
return collect($plans['sizes'])->filter(function ($size) use ($region) {
|
return collect($plans['sizes'])->filter(function ($size) use ($region) {
|
||||||
return in_array($region, $size['regions']);
|
return in_array($region, $size['regions']);
|
||||||
})
|
})
|
||||||
@ -133,7 +131,7 @@ public function create(): void
|
|||||||
'name' => str($this->server->name)->slug(),
|
'name' => str($this->server->name)->slug(),
|
||||||
'region' => $this->server->provider_data['region'],
|
'region' => $this->server->provider_data['region'],
|
||||||
'size' => $this->server->provider_data['plan'],
|
'size' => $this->server->provider_data['plan'],
|
||||||
'image' => config('serverproviders.digitalocean.images')[$this->server->os],
|
'image' => $this->getImageId($this->server->os, $this->server->provider_data['region']),
|
||||||
'backups' => false,
|
'backups' => false,
|
||||||
'ipv6' => false,
|
'ipv6' => false,
|
||||||
'monitoring' => false,
|
'monitoring' => false,
|
||||||
@ -195,4 +193,33 @@ public function delete(): void
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function getImageId(string $os, string $region): int
|
||||||
|
{
|
||||||
|
$version = config('core.operating_system_versions.'.$os);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = Http::withToken($this->serverProvider->credentials['token'])
|
||||||
|
->get($this->apiUrl.'/images', [
|
||||||
|
'per_page' => 200,
|
||||||
|
'type' => 'distribution',
|
||||||
|
])
|
||||||
|
->json();
|
||||||
|
|
||||||
|
$image = collect($result['images'])
|
||||||
|
->filter(function ($image) use ($region, $version) {
|
||||||
|
return in_array($region, $image['regions']) && str_contains($image['name'], $version);
|
||||||
|
})
|
||||||
|
->where('distribution', 'Ubuntu')
|
||||||
|
->where('status', 'available')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return $image['id'];
|
||||||
|
} catch (Exception) {
|
||||||
|
throw new Exception('Could not find image ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
use App\Facades\Notifier;
|
use App\Facades\Notifier;
|
||||||
use App\Notifications\FailedToDeleteServerFromProvider;
|
use App\Notifications\FailedToDeleteServerFromProvider;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use Illuminate\Filesystem\FilesystemAdapter;
|
||||||
use Illuminate\Support\Facades\Http;
|
use Illuminate\Support\Facades\Http;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
@ -15,23 +16,12 @@ class Vultr extends AbstractProvider
|
|||||||
{
|
{
|
||||||
protected string $apiUrl = 'https://api.vultr.com/v2';
|
protected string $apiUrl = 'https://api.vultr.com/v2';
|
||||||
|
|
||||||
public function createRules($input): array
|
public function createRules(array $input): array
|
||||||
{
|
{
|
||||||
$rules = [];
|
return [
|
||||||
// plans
|
'plan' => 'required',
|
||||||
$plans = [];
|
'region' => 'required',
|
||||||
foreach (config('serverproviders.vultr.plans') as $plan) {
|
];
|
||||||
$plans[] = $plan['value'];
|
|
||||||
}
|
|
||||||
$rules['plan'] = 'required|in:'.implode(',', $plans);
|
|
||||||
// regions
|
|
||||||
$regions = [];
|
|
||||||
foreach (config('serverproviders.vultr.regions') as $region) {
|
|
||||||
$regions[] = $region['value'];
|
|
||||||
}
|
|
||||||
$rules['region'] = 'required|in:'.implode(',', $regions);
|
|
||||||
|
|
||||||
return $rules;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function credentialValidationRules($input): array
|
public function credentialValidationRules($input): array
|
||||||
@ -61,7 +51,12 @@ public function data(array $input): array
|
|||||||
*/
|
*/
|
||||||
public function connect(?array $credentials = null): bool
|
public function connect(?array $credentials = null): bool
|
||||||
{
|
{
|
||||||
$connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account');
|
try {
|
||||||
|
$connect = Http::withToken($credentials['token'])->get($this->apiUrl.'/account');
|
||||||
|
} catch (Exception) {
|
||||||
|
throw new CouldNotConnectToProvider('Vultr');
|
||||||
|
}
|
||||||
|
|
||||||
if (! $connect->ok()) {
|
if (! $connect->ok()) {
|
||||||
throw new CouldNotConnectToProvider('Vultr');
|
throw new CouldNotConnectToProvider('Vultr');
|
||||||
}
|
}
|
||||||
@ -71,16 +66,43 @@ public function connect(?array $credentials = null): bool
|
|||||||
|
|
||||||
public function plans(?string $region): array
|
public function plans(?string $region): array
|
||||||
{
|
{
|
||||||
return collect(config('serverproviders.vultr.plans'))
|
try {
|
||||||
->mapWithKeys(fn ($value) => [$value['value'] => $value['title']])
|
$plans = Http::withToken($this->serverProvider->credentials['token'])
|
||||||
->toArray();
|
->get($this->apiUrl.'/plans', ['per_page' => 500])
|
||||||
|
->json();
|
||||||
|
|
||||||
|
return collect($plans['plans'])->filter(function ($plan) use ($region) {
|
||||||
|
return in_array($region, $plan['locations']);
|
||||||
|
})
|
||||||
|
->mapWithKeys(function ($value) {
|
||||||
|
return [
|
||||||
|
$value['id'] => __('server_providers.plan', [
|
||||||
|
'name' => $value['type'],
|
||||||
|
'cpu' => $value['vcpu_count'],
|
||||||
|
'memory' => $value['ram'],
|
||||||
|
'disk' => $value['disk'],
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
})
|
||||||
|
->toArray();
|
||||||
|
} catch (Exception) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function regions(): array
|
public function regions(): array
|
||||||
{
|
{
|
||||||
return collect(config('serverproviders.vultr.regions'))
|
try {
|
||||||
->mapWithKeys(fn ($value) => [$value['value'] => $value['title']])
|
$regions = Http::withToken($this->serverProvider->credentials['token'])
|
||||||
->toArray();
|
->get($this->apiUrl.'/regions', ['per_page' => 500])
|
||||||
|
->json();
|
||||||
|
|
||||||
|
return collect($regions['regions'])
|
||||||
|
->mapWithKeys(fn ($value) => [$value['id'] => $value['country'].' - '.$value['city']])
|
||||||
|
->toArray();
|
||||||
|
} catch (Exception) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,32 +111,41 @@ public function regions(): array
|
|||||||
public function create(): void
|
public function create(): void
|
||||||
{
|
{
|
||||||
// generate key pair
|
// generate key pair
|
||||||
/** @var \Illuminate\Filesystem\FilesystemAdapter $storageDisk */
|
/** @var FilesystemAdapter $storageDisk */
|
||||||
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
$storageDisk = Storage::disk(config('core.key_pairs_disk'));
|
||||||
generate_key_pair($storageDisk->path((string) $this->server->id));
|
generate_key_pair($storageDisk->path((string) $this->server->id));
|
||||||
|
|
||||||
$createSshKey = Http::withToken($this->server->serverProvider->credentials['token'])
|
try {
|
||||||
->post($this->apiUrl.'/ssh-keys', [
|
$createSshKey = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
'ssh_key' => $this->server->sshKey()['public_key'],
|
->post($this->apiUrl.'/ssh-keys', [
|
||||||
'name' => $this->server->name.'_'.$this->server->id,
|
'ssh_key' => $this->server->sshKey()['public_key'],
|
||||||
]);
|
'name' => $this->server->name.'_'.$this->server->id,
|
||||||
|
]);
|
||||||
|
} catch (Exception) {
|
||||||
|
throw new ServerProviderError('Error creating SSH Key on Vultr');
|
||||||
|
}
|
||||||
|
|
||||||
if ($createSshKey->status() != 201) {
|
if ($createSshKey->status() != 201) {
|
||||||
throw new ServerProviderError('Error creating SSH Key on Vultr');
|
throw new ServerProviderError('Error creating SSH Key on Vultr');
|
||||||
}
|
}
|
||||||
|
|
||||||
$create = Http::withToken($this->server->serverProvider->credentials['token'])
|
try {
|
||||||
->post($this->apiUrl.'/instances', [
|
$create = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
'label' => $this->server->name,
|
->post($this->apiUrl.'/instances', [
|
||||||
'region' => $this->server->provider_data['region'],
|
'label' => $this->server->name,
|
||||||
'plan' => $this->server->provider_data['plan'],
|
'region' => $this->server->provider_data['region'],
|
||||||
'os_id' => config('serverproviders.vultr.images')[$this->server->os],
|
'plan' => $this->server->provider_data['plan'],
|
||||||
'enable_ipv6' => false,
|
'os_id' => $this->getImageId($this->server->os),
|
||||||
'sshkey_id' => [$createSshKey->json()['ssh_key']['id']],
|
'enable_ipv6' => false,
|
||||||
]);
|
'sshkey_id' => [$createSshKey->json()['ssh_key']['id']],
|
||||||
|
]);
|
||||||
|
} catch (Exception) {
|
||||||
|
throw new ServerProviderError('Failed to create server on Vultr');
|
||||||
|
}
|
||||||
|
|
||||||
if ($create->status() != 202) {
|
if ($create->status() != 202) {
|
||||||
$msg = __('Failed to create server on Vultr');
|
|
||||||
Log::error('Failed to create server on Vultr', $create->json());
|
Log::error('Failed to create server on Vultr', $create->json());
|
||||||
throw new ServerProviderError($msg);
|
throw new ServerProviderError('Failed: '.$create->body());
|
||||||
}
|
}
|
||||||
$providerData = $this->server->provider_data;
|
$providerData = $this->server->provider_data;
|
||||||
$providerData['instance_id'] = $create->json()['instance']['id'];
|
$providerData['instance_id'] = $create->json()['instance']['id'];
|
||||||
@ -124,8 +155,12 @@ public function create(): void
|
|||||||
|
|
||||||
public function isRunning(): bool
|
public function isRunning(): bool
|
||||||
{
|
{
|
||||||
$status = Http::withToken($this->server->serverProvider->credentials['token'])
|
try {
|
||||||
->get($this->apiUrl.'/instances/'.$this->server->provider_data['instance_id']);
|
$status = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
|
->get($this->apiUrl.'/instances/'.$this->server->provider_data['instance_id']);
|
||||||
|
} catch (Exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (! $status->ok()) {
|
if (! $status->ok()) {
|
||||||
return false;
|
return false;
|
||||||
@ -145,12 +180,44 @@ public function isRunning(): bool
|
|||||||
public function delete(): void
|
public function delete(): void
|
||||||
{
|
{
|
||||||
if (isset($this->server->provider_data['instance_id'])) {
|
if (isset($this->server->provider_data['instance_id'])) {
|
||||||
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
try {
|
||||||
->delete($this->apiUrl.'/instances/'.$this->server->provider_data['instance_id']);
|
$delete = Http::withToken($this->server->serverProvider->credentials['token'])
|
||||||
|
->delete($this->apiUrl.'/instances/'.$this->server->provider_data['instance_id']);
|
||||||
|
} catch (Exception) {
|
||||||
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (! $delete->ok()) {
|
if (! $delete->ok()) {
|
||||||
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
Notifier::send($this->server, new FailedToDeleteServerFromProvider($this->server));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
private function getImageId(string $os): int
|
||||||
|
{
|
||||||
|
$version = config('core.operating_system_versions.'.$os);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = Http::withToken($this->serverProvider->credentials['token'])
|
||||||
|
->get($this->apiUrl.'/os', ['per_page' => 500])
|
||||||
|
->json();
|
||||||
|
|
||||||
|
$image = collect($result['os'])
|
||||||
|
->filter(function ($os) use ($version) {
|
||||||
|
return str_contains($os['name'], $version);
|
||||||
|
})
|
||||||
|
->where('family', 'ubuntu')
|
||||||
|
->where('arch', 'x64')
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return $image['id'];
|
||||||
|
} catch (Exception) {
|
||||||
|
throw new Exception('Could not find image ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user