mirror of
				https://github.com/pelican-dev/panel.git
				synced 2025-10-31 02:36:52 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			117 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			117 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| namespace App\Services\Allocations;
 | |
| 
 | |
| use App\Exceptions\DisplayException;
 | |
| use App\Exceptions\Service\Allocation\AutoAllocationNotEnabledException;
 | |
| use App\Exceptions\Service\Allocation\CidrOutOfRangeException;
 | |
| use App\Exceptions\Service\Allocation\InvalidPortMappingException;
 | |
| use App\Exceptions\Service\Allocation\NoAutoAllocationSpaceAvailableException;
 | |
| use App\Exceptions\Service\Allocation\PortOutOfRangeException;
 | |
| use App\Exceptions\Service\Allocation\TooManyPortsInRangeException;
 | |
| use App\Models\Allocation;
 | |
| use App\Models\Server;
 | |
| use Webmozart\Assert\Assert;
 | |
| 
 | |
| class FindAssignableAllocationService
 | |
| {
 | |
|     /**
 | |
|      * FindAssignableAllocationService constructor.
 | |
|      */
 | |
|     public function __construct(private AssignmentService $service) {}
 | |
| 
 | |
|     /**
 | |
|      * Finds an existing unassigned allocation and attempts to assign it to the given server. If
 | |
|      * no allocation can be found, a new one will be created with a random port between the defined
 | |
|      * range from the configuration.
 | |
|      *
 | |
|      * @throws DisplayException
 | |
|      * @throws CidrOutOfRangeException
 | |
|      * @throws InvalidPortMappingException
 | |
|      * @throws PortOutOfRangeException
 | |
|      * @throws TooManyPortsInRangeException
 | |
|      */
 | |
|     public function handle(Server $server): Allocation
 | |
|     {
 | |
|         if (!config('panel.client_features.allocations.enabled')) {
 | |
|             throw new AutoAllocationNotEnabledException();
 | |
|         }
 | |
| 
 | |
|         // Attempt to find a given available allocation for a server. If one cannot be found
 | |
|         // we will fall back to attempting to create a new allocation that can be used for the
 | |
|         // server.
 | |
|         /** @var Allocation|null $allocation */
 | |
|         $allocation = $server->node->allocations()
 | |
|             ->when($server->allocation, function ($query) use ($server) {
 | |
|                 $query->where('ip', $server->allocation->ip);
 | |
|             })
 | |
|             ->whereNull('server_id')
 | |
|             ->inRandomOrder()
 | |
|             ->first();
 | |
| 
 | |
|         $allocation = $allocation ?? $this->createNewAllocation($server);
 | |
| 
 | |
|         $allocation->update(['server_id' => $server->id]);
 | |
| 
 | |
|         return $allocation->refresh();
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Create a new allocation on the server's node with a random port from the defined range
 | |
|      * in the settings. If there are no matches in that range, or something is wrong with the
 | |
|      * range information provided an exception will be raised.
 | |
|      *
 | |
|      * @throws DisplayException
 | |
|      * @throws CidrOutOfRangeException
 | |
|      * @throws InvalidPortMappingException
 | |
|      * @throws PortOutOfRangeException
 | |
|      * @throws TooManyPortsInRangeException
 | |
|      */
 | |
|     protected function createNewAllocation(Server $server): Allocation
 | |
|     {
 | |
|         $start = config('panel.client_features.allocations.range_start', null);
 | |
|         $end = config('panel.client_features.allocations.range_end', null);
 | |
| 
 | |
|         if (!$start || !$end) {
 | |
|             throw new NoAutoAllocationSpaceAvailableException();
 | |
|         }
 | |
| 
 | |
|         Assert::integerish($start);
 | |
|         Assert::integerish($end);
 | |
| 
 | |
|         // Get all the currently allocated ports for the node so that we can figure out
 | |
|         // which port might be available.
 | |
|         $ports = $server->node->allocations()
 | |
|             ->where('ip', $server->allocation->ip)
 | |
|             ->whereBetween('port', [$start, $end])
 | |
|             ->pluck('port');
 | |
| 
 | |
|         // Compute the difference of the range and the currently created ports, finding
 | |
|         // any port that does not already exist in the database. We will then use this
 | |
|         // array of ports to create a new allocation to assign to the server.
 | |
|         $available = array_diff(range($start, $end), $ports->toArray());
 | |
| 
 | |
|         // If we've already allocated all the ports, just abort.
 | |
|         if (empty($available)) {
 | |
|             throw new NoAutoAllocationSpaceAvailableException();
 | |
|         }
 | |
| 
 | |
|         // Pick a random port out of the remaining available ports.
 | |
|         /** @var int $port */
 | |
|         $port = $available[array_rand($available)];
 | |
| 
 | |
|         $this->service->handle($server->node, [
 | |
|             'allocation_ip' => $server->allocation->ip,
 | |
|             'allocation_ports' => [$port],
 | |
|         ]);
 | |
| 
 | |
|         /** @var Allocation $allocation */
 | |
|         $allocation = $server->node->allocations()
 | |
|             ->where('ip', $server->allocation->ip)
 | |
|             ->where('port', $port)
 | |
|             ->firstOrFail();
 | |
| 
 | |
|         return $allocation;
 | |
|     }
 | |
| }
 | 
