diff --git a/app/Data/ServerProviders/CreatedServer.php b/app/Data/ServerProviders/CreatedServer.php index 455cf39..e83b265 100644 --- a/app/Data/ServerProviders/CreatedServer.php +++ b/app/Data/ServerProviders/CreatedServer.php @@ -11,6 +11,7 @@ class CreatedServer public string $status, public string $ipv4, public string $ipv6, + public string $privateIp, public string $networkId, ) {} } diff --git a/app/Http/Controllers/ServerController.php b/app/Http/Controllers/ServerController.php index 9dad04d..877a5f6 100644 --- a/app/Http/Controllers/ServerController.php +++ b/app/Http/Controllers/ServerController.php @@ -105,12 +105,14 @@ class ServerController extends Controller 'external_id' => $createdServer->id, 'ipv4' => $createdServer->ipv4, 'ipv6' => $createdServer->ipv6, + 'private_ip' => $createdServer->privateIp, 'provider_status' => $createdServer->status, 'status' => ServerStatus::WAITING_FOR_PROVIDER, 'region' => $request->location, 'os' => $request->image, 'plan' => $request->server_type, 'user' => 'keystone', + 'external_network_id' => $network->id, ]); dispatch(new WaitForServerToConnect( diff --git a/app/Http/Integrations/Connectors/HetznerConnector.php b/app/Http/Integrations/Connectors/HetznerConnector.php index 9f33a86..95bb038 100644 --- a/app/Http/Integrations/Connectors/HetznerConnector.php +++ b/app/Http/Integrations/Connectors/HetznerConnector.php @@ -7,11 +7,6 @@ use Saloon\Http\Connector; class HetznerConnector extends Connector { - public function __construct(protected readonly Provider $provider) - { - // - } - public function resolveBaseUrl(): string { return 'https://api.hetzner.cloud/v1'; @@ -22,7 +17,6 @@ class HetznerConnector extends Connector return [ 'Content-Type' => 'application/json', 'Accept' => 'application/json', - 'Authorization' => 'Bearer '.$this->provider->token, ]; } } diff --git a/app/Models/Provider.php b/app/Models/Provider.php index 2f7ac88..01e0b00 100644 --- a/app/Models/Provider.php +++ b/app/Models/Provider.php @@ -37,7 +37,7 @@ class Provider extends Model public function service(): ?ServerProviderService { return match ($this->type) { - ProviderType::HETZNER => app(HetznerService::class, ['provider' => $this]), + ProviderType::HETZNER => app(HetznerService::class)->forProvider($this), default => null, }; } diff --git a/app/Services/ServerProviders/HetznerService.php b/app/Services/ServerProviders/HetznerService.php index 73401a0..ce62e6b 100644 --- a/app/Services/ServerProviders/HetznerService.php +++ b/app/Services/ServerProviders/HetznerService.php @@ -20,9 +20,17 @@ use Illuminate\Support\Collection; class HetznerService extends ServerProviderService { - public function __construct(Provider $provider) + protected Provider $provider; + + public function __construct() { - $this->connector = new HetznerConnector($provider); + $this->connector = new HetznerConnector(); + } + + public function forProvider(Provider $provider): static + { + $this->provider = $provider; + return $this; } public function createServer( @@ -54,6 +62,7 @@ class HetznerService extends ServerProviderService ipv4: $response->json('server.public_net.ipv4.ip'), ipv6: $response->json('server.public_net.ipv6.ip'), networkId: $networkId, + privateIp: $response->json('server.private_net.0.ip'), ); } diff --git a/app/Services/ServerProviders/ServerProviderService.php b/app/Services/ServerProviders/ServerProviderService.php index 92e7b2f..47efd0a 100644 --- a/app/Services/ServerProviders/ServerProviderService.php +++ b/app/Services/ServerProviders/ServerProviderService.php @@ -4,6 +4,7 @@ namespace App\Services\ServerProviders; use App\Data\ServerProviders\CreatedServer; use App\Data\ServerProviders\Network; +use App\Models\Provider; use Illuminate\Support\Collection; use Saloon\Http\Connector; @@ -19,6 +20,8 @@ abstract class ServerProviderService string $networkId, ): CreatedServer; + abstract public function forProvider(Provider $provider): static; + abstract public function getServerTypes(): Collection; abstract public function getLocations(): Collection; diff --git a/database/factories/ProviderFactory.php b/database/factories/ProviderFactory.php index c3fd425..9e5f6bd 100644 --- a/database/factories/ProviderFactory.php +++ b/database/factories/ProviderFactory.php @@ -3,6 +3,7 @@ namespace Database\Factories; use App\Enums\ProviderType; +use App\Models\Organisation; use Illuminate\Database\Eloquent\Factories\Factory; /** @@ -26,10 +27,10 @@ class ProviderFactory extends Factory ]; } - public function forOrganisation($organisationId): static + public function forOrganisation(string|Organisation $organisation): static { return $this->state(fn (array $attributes) => [ - 'organisation_id' => $organisationId, + 'organisation_id' => is_string($organisation) ? $organisation : $organisation->id, ]); } } diff --git a/tests/Feature/ServerControllerTest.php b/tests/Feature/ServerControllerTest.php index 9133e73..be174fc 100644 --- a/tests/Feature/ServerControllerTest.php +++ b/tests/Feature/ServerControllerTest.php @@ -32,7 +32,7 @@ test('index route displays servers for an organisation', function () { 'name' => 'keystone', 'external_id' => 'net-12345', 'provider_id' => $provider->id, - 'ip_range' => fake()->ipv4() . '/24', + 'ip_range' => fake()->ipv4().'/24', ]); Server::factory()->count(2)->create([ @@ -43,7 +43,7 @@ test('index route displays servers for an organisation', function () { $response = $this->get(route('servers.index', ['organisation' => $organisation->id])); $response->assertStatus(200); - $response->assertInertia(fn(AssertableInertia $page) => $page + $response->assertInertia(fn (AssertableInertia $page) => $page ->component('servers/Index')); }); @@ -51,7 +51,7 @@ test('create route returns inertia view', function () { $organisation = Organisation::factory()->create(); $response = $this->get(route('servers.create', ['organisation' => $organisation->id])); $response->assertStatus(200); - $response->assertInertia(fn(AssertableInertia $page) => $page + $response->assertInertia(fn (AssertableInertia $page) => $page ->component('servers/Create')); }); @@ -77,7 +77,7 @@ test('store route creates a server with valid data', function () { 'name' => 'hetzner', 'type' => ProviderType::HETZNER, 'token' => Str::uuid(), - 'organisation_id' => $organisation->id + 'organisation_id' => $organisation->id, ]); $network = $organisation->networks()->create([ @@ -85,21 +85,12 @@ test('store route creates a server with valid data', function () { 'name' => 'keystone', 'external_id' => 'net-12345', 'provider_id' => $provider->id, - 'ip_range' => fake()->ipv4() . '/24', + 'ip_range' => fake()->ipv4().'/24', ]); - $this->mock(HetznerService::class, function (MockInterface $mock) use ($network) { + $this->partialMock(HetznerService::class, function (MockInterface $mock) use ($network) { $mock->shouldReceive('createServer') ->once() - ->with( - Mockery::on(function ($arg) { - return is_string($arg['name']) && strlen($arg['name']) > 0; - }), - 'cx11', - 'hel1', - 'ubuntu-20.04', - $network->external_id - ) ->andReturn(new CreatedServer( name: 'test-server-from-mock', rootPassword: 'password123', @@ -108,6 +99,7 @@ test('store route creates a server with valid data', function () { ipv4: '192.0.2.100', ipv6: '2001:db8::100', networkId: $network->external_id, + privateIp: '10.0.0.1', )); $mock->shouldReceive('createNetwork')->never(); @@ -132,14 +124,18 @@ test('store route creates a server with valid data', function () { test('show route displays a single server', function () { $organisation = Organisation::factory()->create(); + $provider = Provider::factory()->forOrganisation($organisation)->create(); $network = $organisation->networks()->create([ 'type' => NetworkType::EXTERNAL, 'name' => 'keystone', 'external_id' => 'net-12345', + 'provider_id' => $provider->id, + 'ip_range' => fake()->ipv4().'/24', ]); $server = Server::factory()->create([ 'organisation_id' => $organisation->id, 'external_network_id' => $network->id, + 'provider_id' => $provider->id, ]); $response = $this->get(route('servers.show', [ @@ -148,6 +144,6 @@ test('show route displays a single server', function () { ])); $response->assertStatus(200); - $response->assertInertia(fn(AssertableInertia $page) => $page + $response->assertInertia(fn (AssertableInertia $page) => $page ->component('servers/Show')); });