Add automatic cluster pool allocation.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
keyhan
2026-05-18 23:08:06 +03:30
parent 97e4c865b6
commit 72a1519ea0
19 changed files with 983 additions and 151 deletions
@@ -23,49 +23,35 @@ export class ApplicationsService {
) {}
async create(userId: string, dto: CreateApplicationDto, userRole?: string): Promise<Application> {
// Only admin/technical users can manually select cluster or pool
// Regular users always get the default cluster assignment
if (userRole !== UserRole.ADMIN && userRole !== UserRole.TECHNICAL) {
// End users and technical staff cannot influence placement; only admins may manually assign.
const isAdmin = userRole === UserRole.ADMIN;
if (!isAdmin) {
if (dto.clusterId || dto.poolId) {
this.logger.warn(`Non-admin user ${userId} attempted manual cluster/pool selection ignoring`);
this.logger.warn(`Non-admin user ${userId} attempted manual cluster/pool selection - ignoring`);
}
dto.clusterId = undefined;
dto.poolId = undefined;
}
// Cluster assignment: 3 modes
// 1. Manual: specific clusterId provided
// 2. Pool-based LB: poolId provided → pick from pool using pool's strategy
// 3. Default fallback: no clusterId/poolId → use default cluster
let clusterId = dto.clusterId;
let poolId = dto.poolId;
let allocationLogId: string | undefined;
if (!clusterId) {
if (poolId) {
// Mode 2: Pool-based load balancing
try {
const optimal = await this.clustersService.getOptimalClusterFromPool(poolId);
clusterId = optimal.id;
this.logger.log(`Pool-assigned cluster "${optimal.name}" to app "${dto.name}" (pool LB)`);
} catch (err: any) {
this.logger.warn(`Pool assignment failed for app "${dto.name}": ${err.message}`);
poolId = undefined; // Clear invalid pool
}
}
// Mode 3: Default fallback (no manual cluster, no pool, or pool failed)
if (!clusterId) {
try {
const defaultCluster = await this.clustersService.getDefault();
clusterId = defaultCluster.id;
this.logger.log(`Default-assigned cluster "${defaultCluster.name}" to app "${dto.name}"`);
} catch {
this.logger.warn('No cluster available — app will be created without cluster assignment');
}
}
if (isAdmin && clusterId) {
await this.clustersService.findOne(clusterId);
this.logger.log(`Manual cluster assignment for app "${dto.name}" -> cluster ${clusterId}`);
} else {
this.logger.log(`Manual cluster assignment for app "${dto.name}" → cluster ${clusterId}`);
const allocationDto = { ...dto };
if (poolId) {
allocationDto.poolId = poolId;
}
const allocation = await this.clustersService.selectClusterForApplication(allocationDto, userId);
clusterId = allocation.cluster.id;
poolId = allocation.pool?.id || (isAdmin ? poolId : undefined);
allocationLogId = allocation.allocationLogId;
if (!clusterId) {
throw new BadRequestException('No eligible cluster available for this application');
}
}
// Generate database credentials if a database is requested
@@ -109,7 +95,11 @@ export class ApplicationsService {
platformDomain,
),
});
return this.appsRepository.save(app);
const saved = await this.appsRepository.save(app);
if (allocationLogId) {
await this.clustersService.attachAllocationToApplication(allocationLogId, saved.id);
}
return saved;
}
async findAllByUser(userId: string): Promise<Application[]> {