feat: show build logs alongside pod logs in app detail page
This commit is contained in:
@@ -18,9 +18,9 @@ export class BuildService {
|
||||
|
||||
/**
|
||||
* Builds a Docker image for the application using Kaniko inside K8s.
|
||||
* Returns the full image URI (registry/repo:tag).
|
||||
* Returns { imageUri, buildLog } — the full image URI and the build logs.
|
||||
*/
|
||||
async buildImage(app: Application): Promise<string> {
|
||||
async buildImage(app: Application): Promise<{ imageUri: string; buildLog: string }> {
|
||||
// Internal registry (used by Kaniko inside K8s for pushing)
|
||||
const internalRegistryUrl = this.configService.get<string>('registry.url') || 'registry.cloudhost-builds.svc.cluster.local:5000';
|
||||
// External registry URL (used by kubelet for pulling — NodePort or external)
|
||||
@@ -239,17 +239,26 @@ export class BuildService {
|
||||
// Wait for build to complete
|
||||
await this.waitForJobCompletion(batchApi, coreApi, buildPodName, buildNamespace!, 600);
|
||||
|
||||
// Capture build logs on success
|
||||
let buildLog = '';
|
||||
try {
|
||||
buildLog = await this.getBuildLogs(coreApi, buildPodName, buildNamespace!);
|
||||
} catch {}
|
||||
|
||||
this.logger.log(`Build completed successfully: ${pullImageUri}`);
|
||||
return pullImageUri;
|
||||
return { imageUri: pullImageUri, buildLog };
|
||||
} catch (error: any) {
|
||||
// Try to get build logs for debugging
|
||||
let buildLog = '';
|
||||
try {
|
||||
const logs = await this.getBuildLogs(coreApi, buildPodName, buildNamespace!);
|
||||
this.logger.error(`Build logs for ${buildPodName}:\n${logs}`);
|
||||
buildLog = await this.getBuildLogs(coreApi, buildPodName, buildNamespace!);
|
||||
this.logger.error(`Build logs for ${buildPodName}:\n${buildLog}`);
|
||||
} catch {}
|
||||
|
||||
this.logger.error(`Build failed for ${app.name}:`, error.body || error.message);
|
||||
throw new Error(`Image build failed: ${error.body?.message || error.message}`);
|
||||
const err = new Error(`Image build failed: ${error.body?.message || error.message}`);
|
||||
(err as any).buildLog = buildLog;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,11 +37,17 @@ export class DeploymentsController {
|
||||
}
|
||||
|
||||
@Get('applications/:appId/logs')
|
||||
@ApiOperation({ summary: 'Get application logs' })
|
||||
@ApiOperation({ summary: 'Get application pod logs' })
|
||||
async getLogs(@Param('appId') appId: string, @Request() req: any) {
|
||||
return { logs: await this.deploymentsService.getLogs(appId, req.user.id) };
|
||||
}
|
||||
|
||||
@Get('applications/:appId/build-logs')
|
||||
@ApiOperation({ summary: 'Get build logs for latest deployment' })
|
||||
async getBuildLogs(@Param('appId') appId: string, @Request() req: any) {
|
||||
return this.deploymentsService.getBuildLogs(appId, req.user.id);
|
||||
}
|
||||
|
||||
@Post('applications/:appId/stop')
|
||||
@ApiOperation({ summary: 'Stop an application' })
|
||||
async stop(@Param('appId') appId: string, @Request() req: any) {
|
||||
|
||||
@@ -45,7 +45,10 @@ export class DeploymentsService {
|
||||
try {
|
||||
// Step 1: Build image
|
||||
await this.updateStatus(deploymentId, DeploymentStatus.BUILDING);
|
||||
const imageUri = await this.buildService.buildImage(app);
|
||||
const { imageUri, buildLog } = await this.buildService.buildImage(app);
|
||||
|
||||
// Save build log
|
||||
await this.deploymentsRepository.update(deploymentId, { buildLog });
|
||||
|
||||
// Step 2: Update app with new image tag
|
||||
await this.applicationsService.updateImageTag(app.id, imageUri);
|
||||
@@ -62,9 +65,13 @@ export class DeploymentsService {
|
||||
});
|
||||
} catch (error: any) {
|
||||
this.logger.error(`Deployment ${deploymentId} failed:`, error);
|
||||
|
||||
// Save build log if available (attached by build service on failure)
|
||||
const buildLog = error.buildLog || null;
|
||||
await this.deploymentsRepository.update(deploymentId, {
|
||||
status: DeploymentStatus.FAILED,
|
||||
errorMessage: error.message,
|
||||
...(buildLog ? { buildLog } : {}),
|
||||
finishedAt: new Date(),
|
||||
});
|
||||
}
|
||||
@@ -97,6 +104,27 @@ export class DeploymentsService {
|
||||
return this.kubernetesService.getPodLogs(app);
|
||||
}
|
||||
|
||||
async getBuildLogs(applicationId: string, userId: string): Promise<{ buildLog: string | null; status: string; version: string | null; createdAt: Date }> {
|
||||
// Verify user access
|
||||
await this.applicationsService.findOne(applicationId, userId);
|
||||
|
||||
const latest = await this.deploymentsRepository.findOne({
|
||||
where: { applicationId },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
|
||||
if (!latest) {
|
||||
return { buildLog: null, status: 'no_deployment', version: null, createdAt: new Date() };
|
||||
}
|
||||
|
||||
return {
|
||||
buildLog: latest.buildLog || null,
|
||||
status: latest.status,
|
||||
version: latest.version,
|
||||
createdAt: latest.createdAt,
|
||||
};
|
||||
}
|
||||
|
||||
async stopDeployment(applicationId: string, userId: string): Promise<Deployment | null> {
|
||||
const app = await this.applicationsService.findOne(applicationId, userId);
|
||||
await this.kubernetesService.scaleDeployment(app, 0);
|
||||
|
||||
Reference in New Issue
Block a user