Skip to content

Commit ae0d47e

Browse files
committed
fix(sparkapplication): backfill terminationTime when missing so TTL applies
Terminal FAILED or COMPLETED apps could omit status.terminationTime, so timeToLiveSeconds never triggered deletion. Backfill from lastSubmissionAttemptTime (or now) before IsExpired and TTL requeue. Signed-off-by: Amir Alavi <amiralavi7@gmail.com>
1 parent f9bc448 commit ae0d47e

3 files changed

Lines changed: 58 additions & 0 deletions

File tree

internal/controller/sparkapplication/controller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,8 @@ func (r *Reconciler) reconcileTerminatedSparkApplication(ctx context.Context, re
675675
return ctrl.Result{}, nil
676676
}
677677

678+
util.EnsureTerminationTimeForTerminalState(app)
679+
678680
if util.IsExpired(app) {
679681
logger.Info("Deleting expired SparkApplication", "state", app.Status.AppState.State)
680682
if err := r.client.Delete(ctx, app); err != nil {

pkg/util/sparkapplication.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,22 @@ func IsTerminated(app *v1beta2.SparkApplication) bool {
6060
app.Status.AppState.State == v1beta2.ApplicationStateFailed
6161
}
6262

63+
// EnsureTerminationTimeForTerminalState sets status.terminationTime when the app is
64+
// Completed or Failed but terminationTime was never recorded (e.g. submission failed
65+
// with FAILED set and terminationTime omitted). Without this, timeToLiveSeconds never
66+
// applies because expiry is computed from terminationTime.
67+
// It prefers lastSubmissionAttemptTime when set.
68+
func EnsureTerminationTimeForTerminalState(app *v1beta2.SparkApplication) {
69+
if !IsTerminated(app) || !app.Status.TerminationTime.IsZero() {
70+
return
71+
}
72+
if !app.Status.LastSubmissionAttemptTime.IsZero() {
73+
app.Status.TerminationTime = app.Status.LastSubmissionAttemptTime
74+
} else {
75+
app.Status.TerminationTime = metav1.Now()
76+
}
77+
}
78+
6379
// IsExpired returns whether the given SparkApplication is expired.
6480
func IsExpired(app *v1beta2.SparkApplication) bool {
6581
// The application has no TTL defined and will never expire.

pkg/util/sparkapplication_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,46 @@ var _ = Describe("IsExpired", func() {
194194
})
195195
})
196196

197+
var _ = Describe("EnsureTerminationTimeForTerminalState", func() {
198+
It("does nothing when not terminated", func() {
199+
app := &v1beta2.SparkApplication{
200+
Status: v1beta2.SparkApplicationStatus{
201+
AppState: v1beta2.ApplicationState{State: v1beta2.ApplicationStateSubmitted},
202+
},
203+
}
204+
util.EnsureTerminationTimeForTerminalState(app)
205+
Expect(app.Status.TerminationTime.IsZero()).To(BeTrue())
206+
})
207+
208+
It("does nothing when termination time is already set", func() {
209+
t := metav1.NewTime(time.Now().Add(-time.Hour))
210+
app := &v1beta2.SparkApplication{
211+
Status: v1beta2.SparkApplicationStatus{
212+
AppState: v1beta2.ApplicationState{State: v1beta2.ApplicationStateFailed},
213+
TerminationTime: t,
214+
LastSubmissionAttemptTime: metav1.NewTime(time.Now()),
215+
},
216+
}
217+
util.EnsureTerminationTimeForTerminalState(app)
218+
Expect(app.Status.TerminationTime).To(Equal(t))
219+
})
220+
221+
It("sets termination time from last submission attempt when failed and termination time is missing", func() {
222+
submitAt := metav1.NewTime(time.Date(2026, 4, 26, 16, 40, 39, 0, time.UTC))
223+
app := &v1beta2.SparkApplication{
224+
Spec: v1beta2.SparkApplicationSpec{
225+
TimeToLiveSeconds: ptr.To[int64](3600),
226+
},
227+
Status: v1beta2.SparkApplicationStatus{
228+
AppState: v1beta2.ApplicationState{State: v1beta2.ApplicationStateFailed},
229+
LastSubmissionAttemptTime: submitAt,
230+
},
231+
}
232+
util.EnsureTerminationTimeForTerminalState(app)
233+
Expect(app.Status.TerminationTime).To(Equal(submitAt))
234+
})
235+
})
236+
197237
var _ = Describe("IsDriverRunning", func() {
198238
Context("SparkApplication with completed state", func() {
199239
app := &v1beta2.SparkApplication{

0 commit comments

Comments
 (0)