Skip to content

fix: Resolve ServiceAccount deletion race condition (thanks @nss10!)#1421

Merged
lbeckman314 merged 7 commits into
developfrom
fix/service-account-deletion
Jun 3, 2026
Merged

fix: Resolve ServiceAccount deletion race condition (thanks @nss10!)#1421
lbeckman314 merged 7 commits into
developfrom
fix/service-account-deletion

Conversation

@lbeckman314

@lbeckman314 lbeckman314 commented May 20, 2026

Copy link
Copy Markdown
Contributor

Overview 🌀

This PR address Issue #71 Clean Orphaned Resources as Go routine + PVC Finalizers by adding improvements to how ServiceAccounts and related task resources are managed by Funnel.

Current Behavior ⚠️

In Funnel 2026-04-21, cleanOrphanedResources was moved to run at app startup. However, it was implemented as a blocking call, which causes health checks to fail when there are multiple tasks requiring cleanup. Additionally, after running for some time, the Kubernetes API becomes rate limited, causing the pod to crash.

Proposed fixes:

  1. Convert to a goroutine: — Run cleanOrphanedResources asynchronously so it no longer blocks startup and health checks can proceed normally.

  2. Prevent Kubernetes API rate limiting — Implement a batched deletion strategy with a sleep interval between batches to spread out API calls and stay within rate limits.

New Behavior ✔️

Breaking Changes? ❌

Caution

Yes! This update currently breaks the TTL config first introduced by PR #1357.

This feature is being re-introduced as part of PR #1410 to avoid breaking existing configurations.

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
@github-actions

Copy link
Copy Markdown

Gen3 Integration Tests

filepath passed skipped SUBTOTAL
tests/test_gen3_workflow.py 13 5 18
TOTAL 13 5 18

Comment thread worker/kubernetes.go Fixed
@lbeckman314
lbeckman314 force-pushed the fix/service-account-deletion branch from 589dab4 to d2e910b Compare May 21, 2026 22:13
- K8s fake client does not support cascading delete from Owner field

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
[skip ci]

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
@github-actions

Copy link
Copy Markdown

Gen3 Integration Tests

filepath passed skipped SUBTOTAL
tests/test_gen3_workflow.py 13 5 18
TOTAL 13 5 18

}

// Delete PVC
err = resources.DeletePVC(ctx, taskId, b.conf.Kubernetes.JobsNamespace, b.client, b.log)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lbeckman314 — Deferring the deletion of these resources to Kubernetes using ownerReferences sounds good. However, we should handle that transition in a separate PR.

It also just occurred to me: what happens to cleanOrphanedResources? If there are any pending resources left behind by older server runs or due to previous deletion errors, our logic might track them but never actually clean them up.

Kubernetes garbage collection will continuously retry deleting these resources until they are gone, so we don't need to worry about transient API errors or rate limiting.

Given this, we should do one of two things:

  1. Make these changes in a separate PR, and mark this change as a breaking change.
  2. Update the cleanOrphanedResources method so it no longer looks for these specific resources as "orphaned," since Kubernetes will handle the cleanup natively.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in PR #1423!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @lbeckman314 , I was suggesting #1423 to be a PR pointed to develop, such that it includes all changes related to resource cleanup. Keep #1421 only about handling Service Account deletion race condition. All logic regarding resource cleanup should be handled in #1423 only.

Comment thread compute/kubernetes/backend_test.go Outdated
if err == nil {
t.Error("expected ConfigMap to be deleted, but it still exists")
}
// ConfigMap deletion is handled by Kubernetes garbage collection via ownerReferences,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of Configmap, look for a resource which is actually being cleaned up by cleanResources -- say PV. But as I mentioned above, it has to be in a PR of its own.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated in PR #1423!

Move backend.go and backend_test.go resource cleanup changes to
review/service-account-deletion (PR #1423) per reviewer request.
PR #1421 should only contain the ServiceAccount deletion race condition fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]
@github-actions

Copy link
Copy Markdown

Gen3 Integration Tests

Test summary after running integration tests

filepath passed failed skipped SUBTOTAL
tests/test_gen3_workflow.py 12 1 5 18
TOTAL 12 1 5 18

Test summary after rerunning failed integration tests

filepath failed SUBTOTAL
tests/test_gen3_workflow.py 1 1
TOTAL 1 1

@github-actions

Copy link
Copy Markdown

Gen3 Integration Tests

filepath passed skipped SUBTOTAL
tests/test_gen3_workflow.py 13 5 18
TOTAL 13 5 18

@github-actions

github-actions Bot commented Jun 3, 2026

Copy link
Copy Markdown

Gen3 Integration Tests

filepath passed skipped SUBTOTAL
tests/test_gen3_workflow.py 25 4 29
TOTAL 25 4 29

@lbeckman314
lbeckman314 merged commit 89af0db into develop Jun 3, 2026
18 checks passed
@lbeckman314
lbeckman314 deleted the fix/service-account-deletion branch June 3, 2026 22:39
lbeckman314 added a commit that referenced this pull request Jun 9, 2026
* fix: Resolve ServiceAccount deletion race condition (thanks @nss10!)

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: K8s Unit Tests

- K8s fake client does not support cascading delete from Owner field

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Revert function name in serviceaccount.go

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Update comment

[skip ci]

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Remove unused parameter

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Restrict fix/service-account-deletion to SA race condition only

Move backend.go and backend_test.go resource cleanup changes to
review/service-account-deletion (PR #1423) per reviewer request.
PR #1421 should only contain the ServiceAccount deletion race condition fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* fix: Add support for cleaning orphaned Executor jobs

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: Test + lint errors

* fix: Linting errors

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

---------

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
lbeckman314 added a commit that referenced this pull request Jun 10, 2026
…ackoffLimit`) (#1413)

* fix: Ensure CreateContainerConfigError results in SYSTEM_ERROR

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: Minor linting error in kubernetes backend test

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* feat: Update ContainerConfigError status check in K8s

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: Correctly mark tasks as SYSTEM_ERROR when K8s Job creation fails

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: enrich Kubernetes error diagnostics with pod warning events and exit info

Surfaces human-readable detail from pod Warning events and container
termination status into TES system logs, so users can see messages like
'secret "foo" not found' without needing kubectl access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* fix: Resolve ServiceAccount deletion race condition (thanks @nss10!)

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: K8s Unit Tests

- K8s fake client does not support cascading delete from Owner field

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Revert function name in serviceaccount.go

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Update comment

[skip ci]

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Remove unused parameter

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* feat: Add state filter in ListTasks (Credit: @nss10)

- Original source: 7f60650

* feat: Add support for graceful FailedCreateEvents

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Combine `if` statements in kubernetes/backend.go

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: Add Executor Pod status check

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* fix: Correctly set K8sExecutorErr reason fallback

* feat: Add Executor-pod error events to SYSTEM_LOG

* chore: Restrict fix/service-account-deletion to SA race condition only

Move backend.go and backend_test.go resource cleanup changes to
review/service-account-deletion (PR #1423) per reviewer request.
PR #1421 should only contain the ServiceAccount deletion race condition fix.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* fix: Add ImagePullBackOff to terminal pod states (SYSTEM_ERROR)

* fix: Add remaining K8s terminal pod states (SYSTEM_ERROR)

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* chore: Remove debug fmt.Println statements from reconciler

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* refactor: Unify FetchPodWarningEvents as a shared exported function

Rename fetchExecutorPodWarningEvents (worker) and the backend method
fetchPodWarningEvents to a single exported FetchPodWarningEvents in the
compute/kubernetes package. worker/kubernetes.go now imports and calls
the shared function, eliminating duplicate logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* fix: Replace maxErrEventWrites with failedCreateThreshold + minFailureSpan

hasJobFailedCreateEvent now fetches all job events in one API call and
requires both a minimum occurrence count (failedCreateThreshold=5) and a
minimum time span between first and last failure (minFailureSpan=20s)
before treating FailedCreate events as a persistent error. This avoids
false-positives from rapid transient bursts early in a job's lifecycle.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* fix: Update ImagePullBackOff test to match current terminal waiting list

ImagePullBackOff was added to terminalWaitingReasons but the
corresponding test case was not updated. Correct the assertion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* fix: Minor linting fix

* fix: Add support for cleaning orphaned Executor jobs

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>

* refactor: Address remaining PR review comments

- Move terminalWaitingReasons inside hasTerminalContainerWaitingError (no longer needed at package scope)
- Remove InitContainers check (YAGNI — worker/executor pods don't use init containers)
- Use slices.Contains in hasTerminalContainerWaitingError
- Consolidate pod list fetch: fetch pods once per job and pass to both hasTerminalContainerWaitingError and FetchPodWarningEvents to avoid redundant K8s API calls
- Update FetchPodWarningEvents signature to accept *corev1.PodList instead of jobName
- Update all callers (reconciler, worker/kubernetes.go, tests)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Assisted-by: Claude Code:claude [Claude Code]

* Update backend.go

---------

Signed-off-by: Liam Beckman <lbeckman314@gmail.com>
Co-authored-by: Sai Shanmukha <nss10@outlook.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants