Skip to content

Commit 1bb0f27

Browse files
committed
fix: ensure PipelineRun deletion in all scenarios
1 parent a4020cb commit 1bb0f27

File tree

3 files changed

+37
-17
lines changed

3 files changed

+37
-17
lines changed

src/handler/webhook.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@
1515
use axum::{Json, extract::State, http::StatusCode, response::IntoResponse};
1616
use gitea_client::types::Event;
1717
use std::sync::Arc;
18-
use uuid::Uuid;
19-
2018
use tracing::{debug, error, info};
19+
use uuid::Uuid;
2120

2221
use crate::{
2322
context::Context,
2423
errors::{ApiError, Result},
2524
repository::CourseRepository,
2625
request::event::PipelineEvent,
27-
service::{PipelineService, RepoService, StageService},
26+
service::{PipelineCleanupGuard, RepoService, StageService},
2827
utils::crypto,
2928
};
3029

@@ -55,24 +54,25 @@ pub async fn handle_tekton_webhook(
5554
debug!("Received pipeline event: {:?}", event);
5655
let PipelineEvent { name, status, repo, course, stage, secret, tasks } = &event;
5756

57+
// Create cleanup guard - will delete pipeline when this function exits
58+
let _cleanup_guard = PipelineCleanupGuard::new(ctx.clone(), name);
59+
5860
// Verify HMAC signature to prevent request forgery
5961
let auth_secret = &ctx.config.auth_secret;
6062
let payload = format!("{}{}{}", repo, course, stage);
63+
6164
if crypto::hmac_sha256_verify(&payload, auth_secret, secret)? {
6265
error!("Received pipeline event with invalid signature");
6366
return Err(ApiError::Unauthorized("Invalid signature".into()));
6467
}
6568

6669
// Check overall pipeline status first
6770
if status != "Succeeded" {
68-
error!("Pipeline run failed, please check it.");
71+
error!("Pipeline run failed, please check it");
6972
return Ok(StatusCode::OK);
7073
}
7174

72-
// Process the pipeline event based on test task status:
73-
// - If succeeded: mark the stage as complete for the user
74-
// - If failed: log error (TODO: collect error details from Tekton and save to database)
75-
// - Other states: log and ignore non-terminal states
75+
// Process the pipeline event based on test task status
7676
match tasks.test.status.as_str() {
7777
"Succeeded" => {
7878
// Look up the course to get the user_id
@@ -85,21 +85,14 @@ pub async fn handle_tekton_webhook(
8585
}
8686
"Failed" => {
8787
info!("Test task failed: reason={}, stage={}", tasks.test.reason, stage);
88-
return Ok(StatusCode::OK);
8988
}
9089
_ => {
9190
error!(
9291
"Test task in non-terminal state: status={}, reason={}",
9392
tasks.test.status, tasks.test.reason
9493
);
95-
return Ok(StatusCode::OK);
9694
}
9795
}
9896

99-
// Delete the PipelineRun after successful processing
100-
if let Err(e) = PipelineService::new(ctx.clone()).delete(name).await {
101-
error!("Failed to delete PipelineRun {}: {}", name, e);
102-
}
103-
10497
Ok(StatusCode::OK)
10598
}

src/service/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ mod storage;
2323
// Re-exports
2424
pub use course::CourseService;
2525
pub use extension::ExtensionService;
26-
pub use pipeline::PipelineService;
26+
pub use pipeline::{PipelineCleanupGuard, PipelineService};
2727
pub use registry::RegistryService;
2828
pub use repository::RepoService;
2929
pub use stage::StageService;

src/service/pipeline.rs

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use kube::{
1919
api::{ApiResource, DeleteParams, DynamicObject, GroupVersionKind, PostParams},
2020
};
2121
use serde_json::{Error as JsonError, Value, json};
22-
use tracing::debug;
22+
use tracing::{debug, error};
2323
use uuid::Uuid;
2424

2525
use crate::{
@@ -183,3 +183,30 @@ where
183183

184184
serde_json::from_value(resource)
185185
}
186+
187+
// RAII guard to ensure PipelineRun deletion
188+
pub struct PipelineCleanupGuard<'a> {
189+
name: &'a str,
190+
ctx: Arc<Context>,
191+
}
192+
193+
impl<'a> PipelineCleanupGuard<'a> {
194+
/// Creates a new PipelineCleanupGuard
195+
pub fn new(ctx: Arc<Context>, name: &'a str) -> Self {
196+
Self { name, ctx }
197+
}
198+
}
199+
200+
impl<'a> Drop for PipelineCleanupGuard<'a> {
201+
fn drop(&mut self) {
202+
let name = self.name.to_string();
203+
let ctx = self.ctx.clone();
204+
205+
// Use tokio::spawn to handle async code in Drop
206+
tokio::spawn(async move {
207+
if let Err(e) = PipelineService::new(ctx).delete(&name).await {
208+
error!("Failed to delete PipelineRun {}: {}", name, e);
209+
}
210+
});
211+
}
212+
}

0 commit comments

Comments
 (0)