fix(apps): always attempt K8s cleanup on delete, cluster service fixes

- Delete endpoint no longer requires clusterId && latestImageTag
- Clusters: getDefault fallback logic, reassign apps on delete
- Unit tests for cluster default/delete logic
This commit is contained in:
keyhan
2026-04-22 16:44:43 +03:30
parent 8ca787d46e
commit 5f6eccc483
7 changed files with 423 additions and 38 deletions
@@ -6,7 +6,6 @@ import { Deployment } from './entities/deployment.entity';
import { ApplicationsModule } from '../applications/applications.module';
import { KubernetesModule } from '../kubernetes/kubernetes.module';
import { BuildModule } from '../build/build.module';
import { SnapshotsModule } from '../snapshots/snapshots.module';
@Module({
imports: [
@@ -14,7 +13,6 @@ import { SnapshotsModule } from '../snapshots/snapshots.module';
forwardRef(() => ApplicationsModule),
KubernetesModule,
BuildModule,
forwardRef(() => SnapshotsModule),
],
controllers: [DeploymentsController],
providers: [DeploymentsService],
+21 -21
View File
@@ -1,12 +1,12 @@
import { Injectable, NotFoundException, Logger, Inject, forwardRef } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import * as fs from 'fs';
import { Deployment } from './entities/deployment.entity';
import { ApplicationsService } from '../applications/applications.service';
import { KubernetesService } from '../kubernetes/kubernetes.service';
import { BuildService } from '../build/build.service';
import { DeploymentStatus } from '../common/enums';
import { SnapshotsService } from '../snapshots/snapshots.service';
@Injectable()
export class DeploymentsService {
@@ -19,22 +19,11 @@ export class DeploymentsService {
private applicationsService: ApplicationsService,
private kubernetesService: KubernetesService,
private buildService: BuildService,
@Inject(forwardRef(() => SnapshotsService))
private snapshotsService: SnapshotsService,
) {}
async triggerDeployment(applicationId: string, userId: string): Promise<Deployment> {
const app = await this.applicationsService.findOne(applicationId, userId);
// Auto-snapshot before deploying (capture current state for rollback)
try {
const version = `v${Date.now()}`;
await this.snapshotsService.createPreDeploySnapshot(app.id, userId, version);
this.logger.log(`Pre-deploy snapshot created for ${app.name}`);
} catch (e: any) {
this.logger.warn(`Pre-deploy snapshot failed for ${app.name}: ${e.message} — continuing deploy`);
}
// Create deployment record
const deployment = this.deploymentsRepository.create({
applicationId: app.id,
@@ -69,6 +58,26 @@ export class DeploymentsService {
await this.updateStatus(deploymentId, DeploymentStatus.DEPLOYING);
const k8sResources = await this.kubernetesService.deployApplication(app, imageUri);
// Step 3.5: Restore DB dump if one was uploaded (must happen after deploy creates the namespace + DB)
if (app.dbDumpPath && fs.existsSync(app.dbDumpPath)) {
this.logger.log(`Restoring DB dump for ${app.name} from ${app.dbDumpPath}`);
try {
// Wait for the database pod to be Ready before restoring
await this.kubernetesService.waitForDatabaseReady(app, 120_000);
// Re-fetch app to ensure we have latest data
const freshApp = await this.applicationsService.findOne(app.id);
const result = await this.kubernetesService.restoreDatabaseDump(freshApp, freshApp.dbDumpPath!);
if (result.success) {
this.logger.log(`DB dump restored successfully for ${app.name}`);
} else {
this.logger.warn(`DB dump restore failed for ${app.name}: ${result.logs}`);
}
} catch (e: any) {
this.logger.warn(`DB dump restore error for ${app.name}: ${e.message}`);
// Don't fail the deployment — DB restore is a best-effort step
}
}
// Step 4: Mark success
await this.deploymentsRepository.update(deploymentId, {
status: DeploymentStatus.RUNNING,
@@ -185,15 +194,6 @@ export class DeploymentsService {
throw new NotFoundException('No source code available. Upload code or set a git URL first.');
}
// Auto-snapshot before redeploy
try {
const version = `v${Date.now()}`;
await this.snapshotsService.createPreDeploySnapshot(app.id, userId, version);
this.logger.log(`Pre-redeploy snapshot created for ${app.name}`);
} catch (e: any) {
this.logger.warn(`Pre-redeploy snapshot failed for ${app.name}: ${e.message} — continuing`);
}
// Create new deployment record
const deployment = this.deploymentsRepository.create({
applicationId: app.id,