Skip to content

Surface kubectl job failures instead of masking them#598

Open
floatingman wants to merge 3 commits into
rancher:mainfrom
floatingman:fix/kubectl-command-masked-error
Open

Surface kubectl job failures instead of masking them#598
floatingman wants to merge 3 commits into
rancher:mainfrom
floatingman:fix/kubectl-command-masked-error

Conversation

@floatingman

Copy link
Copy Markdown
Contributor

Problem

kubectl.Command() masked the real cause of helper-Job failures. It only re-surfaced non-timeout net.Errors from CreateJobAndRunKubectlCommands; the watch timeout from wait.WatchWait (a plain error) and other non-net.Error failures were silently discarded. It then proceeded to fetch the job pod's logs, and when no pod matched, GetPodLogs("") failed with a misleading resource name may not be empty — hiding the actual root cause (e.g. an unpullable shell-image in an airgap cluster).

Fix

  • Capture the job error as jobErr instead of swallowing it, while preserving the best-effort log fetch (pod logs usually explain the failure).
  • Guard the empty-podName case with a clear error that includes jobErr.
  • Augment the log-stream failure with jobErr when the job also failed.
  • Behavior is unchanged when logs are retrievable — still returns (logs, nil).

Verification

Reproduced the failure condition (unpullable shell-image) against a live cluster via rancher/tests:

  • Before: error streaming pod logs for pod kube-system/: resource name may not be empty
  • After: kubectl job kubectl-hrglgn failed (job error: error with watch connection…); … container … is waiting to start: image can't be pulled

gofmt clean; go vet / go build ./extensions/kubectl/ pass.

Command() discarded any non-net.Error failure from
CreateJobAndRunKubectlCommands, including the watch timeout returned by
wait.WatchWait. It then proceeded to fetch the job pod's logs, and when no
pod could be found GetPodLogs failed with a misleading
"resource name may not be empty", hiding the real cause (e.g. an
unpullable shell-image in an airgap cluster).

Capture the job error while still attempting the best-effort log fetch,
since the pod logs usually explain the failure. When the logs cannot be
retrieved, include the job error in the returned error so the root cause
is visible, and guard the empty-pod case with a clear message.

Verified against an airgap-style failure (unpullable shell-image): the
error now reports the job failure and "image can't be pulled" instead of
"resource name may not be empty".
@floatingman

Copy link
Copy Markdown
Contributor Author

🔗 Related PRs — full fix chain for the airgap resource name may not be empty failure

This PR (shepherd) is the error-reporting improvement — it is independent and not strictly required for the fix, but it stops masking the real cause.

WorkloadTestSuite/TestDeployments/WorkloadUpgradeTest (and any other path using kubectl.Command) fails in airgap because the rancher-shell helper Job can't pull its image. Three changes address different layers:

Dependency: tests#771 sets the variable; qa-infra-automation#123 makes the role consume it. qa-infra-automation#123 is the root-cause fix; tests#771 supplies the value. This shepherd PR is independent.

Also required (ops): mirror rancher/shell:<tag> to <registry>/rancher/shell:<tag> — the containerd proxycache/quaycache mirrors do not cover this verbatim reference.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR updates extensions/kubectl.Command() to preserve and surface underlying Kubernetes Job failures instead of masking them behind secondary log-streaming errors, while keeping the existing “best-effort fetch logs” behavior.

Changes:

  • Stop discarding non-net.Error Job failures by capturing the Job error (jobErr) and using it when log retrieval/pod discovery fails.
  • Add an explicit guard for the empty-pod-name case with a clearer, job-error-aware message.
  • Improve function-level documentation to better describe the behavior and error-handling semantics.
Comments suppressed due to low confidence (1)

extensions/kubectl/command.go:110

  • When ProxyDownstream/List pods fails and the job itself already errored (jobErr != nil), the returned error drops jobErr entirely. That can still mask the underlying job failure in exactly the scenarios this PR is trying to surface (e.g., watch timeout, create failures) because logs can't be retrieved if proxying/listing fails.
	jobErr := CreateJobAndRunKubectlCommands(clusterID, jobName, jobTemplate, client)

	steveClient, err := client.Steve.ProxyDownstream(clusterID)
	if err != nil {
		return "", err

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

podLogs, err := kubeconfig.GetPodLogs(client, clusterID, podName, Namespace, logBufferSize)
if err != nil {
if jobErr != nil {
return "", fmt.Errorf("kubectl job %s failed (job error: %w); failed to stream logs for pod %s/%s: %v", jobName, jobErr, Namespace, podName, err)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants