Skip to content

fix(#975): add diagnostic context to provisioning error logs - #976

Draft
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/975-improve-error-logging
Draft

fix(#975): add diagnostic context to provisioning error logs#976
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/975-improve-error-logging

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown

Replace generic "failed to provision host" messages with errors that include instance IDs, instance tags, platform, addresses, and failed host lists. This enables Splunk queries to distinguish AWS capacity errors from SSH failures, network issues, and timeouts.

Changes:

  • dynamic.go: Include failed hosts list and instance tag when
    all provisioning attempts are exhausted. Add instance ID and
    timeout duration to timeout errors. Add structured log fields
    (instanceId, instanceTag, address) to all error log calls.
  • hostpool.go: Add host name, address, and platform to the
    provisioning task launch failure log. Use %w instead of %v
    for proper error chain preservation.
  • dynamicpool.go: Add error log with instanceTag and platform
    when LaunchInstance fails (previously silent). Add structured
    fields to pool allocation failure log.
  • Tests: Add diagnostic context verification tests for dynamic,
    static, and dynamic pool provisioning to assert error messages
    contain instance IDs, host names, and descriptive context.

Closes #975

Post-script verification

  • Branch is not main/master (agent/975-improve-error-logging)
  • Secret scan passed (gitleaks — 8ae7a0c24d48998f9ddc19c7d68db4b4b25fdc85..HEAD)
  • Pre-commit hooks passed (authoritative run on runner)
  • Tests ran inside sandbox

Replace generic "failed to provision host" messages with errors
that include instance IDs, instance tags, platform, addresses,
and failed host lists. This enables Splunk queries to distinguish
AWS capacity errors from SSH failures, network issues, and
timeouts.

Changes:
- dynamic.go: Include failed hosts list and instance tag when
  all provisioning attempts are exhausted. Add instance ID and
  timeout duration to timeout errors. Add structured log fields
  (instanceId, instanceTag, address) to all error log calls.
- hostpool.go: Add host name, address, and platform to the
  provisioning task launch failure log. Use %w instead of %v
  for proper error chain preservation.
- dynamicpool.go: Add error log with instanceTag and platform
  when LaunchInstance fails (previously silent). Add structured
  fields to pool allocation failure log.
- Tests: Add diagnostic context verification tests for dynamic,
  static, and dynamic pool provisioning to assert error messages
  contain instance IDs, host names, and descriptive context.

Closes #975
@fullsend-ai-review

fullsend-ai-review Bot commented Jul 20, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 9:40 PM UTC · Completed 9:47 PM UTC
Commit: 37b10e4 · View workflow run →

@fullsend-ai-review

Copy link
Copy Markdown

Review — approve

PR: #976 — fix(#975): add diagnostic context to provisioning error logs
Author: fullsend-ai-coder[bot]
Scope: 3 production files, 3 test files (142 additions, 12 deletions)

Summary

This PR enriches provisioning error messages and structured log fields across the three allocation strategies (dynamic, dynamic pool, static host pool) so that Splunk queries can distinguish AWS capacity errors from SSH failures, network issues, and timeouts. The changes directly address #975, which documented complete loss of diagnostic visibility in MPC error logs.

Dimension Analysis

Correctness ✅

  • Error wrapping: %v%w in hostpool.go correctly preserves the error chain, enabling errors.Is/errors.As for callers.
  • Import cleanup: Removing the "errors" import from dynamic.go is valid — both errors.New call sites are replaced with fmt.Errorf.
  • Error propagation: All enriched error messages correctly propagate through handleHostAllocationcreateErrorSecret, so diagnostic context appears both in logs (structured fields) and in the error secret (message string).
  • New log.Error in dynamicpool.go: The LaunchInstance failure path was previously silent (no log, raw error return). Adding the log and wrapping the error is the correct fix.
  • Tests: Three new When("error messages contain diagnostic context") blocks cover the key paths — exhausted attempts, timeout with instance ID, and launch failure in dynamic pool. The static test also validates error secret content. Assertions target the actual enriched strings.

Security ✅

No concerns. Logged data (instance IDs, tags, addresses, platform strings) is operational infrastructure metadata, not user data or credentials. No new external inputs are trusted.

Intent & Coherence ✅

The change is well-scoped to the issue's acceptance criteria: enriching error messages to enable log-based diagnosis. No unrelated behavioral changes. The PR description accurately reflects the diff.

Style & Conventions ✅

  • Structured log fields follow existing codebase patterns ("instanceId", "instanceTag", etc.).
  • Test structure uses When()/It()/Should() per project standards.
  • time.Sleep in the timeout test follows the pre-existing pattern (see provision_dynamic_test.go lines 128–131).

Documentation ✅

No in-repo documentation is affected by this logging-only change.

Cross-Repo Contracts ✅

Error messages are consumed internally (error secrets, controller logs). No external API surface changed.

Low-Severity Observations

  1. log.Error(nil, "all provisioning attempts exhausted", ...) in dynamic.go: Passing nil as the error to log.Error is valid in logr but slightly unusual. Most callers in this codebase pass a non-nil error. Consider creating the fmt.Errorf first and passing it to both the log and the return, as done in the timeout path. Not blocking.

  2. time.Sleep(time.Second * 3) in the timeout test: Inherently timing-sensitive. If CI is slow, the 3-second sleep may not exceed the 2-second allocation-timeout reliably. This matches the existing test pattern, so it's not a regression, but a future Eventually refactor would improve robustness.

Verdict

The changes are correct, well-tested, and directly address the operational issue described in #975. Approving.


Labels: PR fixes a bug in error logging with low-risk enrichment of error messages

log := logr.FromContextOrDiscard(ctx)
if tr.Annotations[FailedHosts] != "" {
return reconcile.Result{}, errors.New("failed to provision host")
log.Error(nil, "all provisioning attempts exhausted",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] style

log.Error(nil, ...) passes nil as the error argument. While valid in logr, this is unusual in this codebase where most callers pass a non-nil error. Consider creating the fmt.Errorf first and passing it to both the log and the return, as done in the timeout path a few lines below.

Suggested fix: Create the error with fmt.Errorf first, then pass it to log.Error and return it.


// Verify instance was created and has an ID
tr := getUserTaskRun(ctx, client, "test-timeout-ctx")
Expect(tr.Annotations[CloudInstanceId]).ShouldNot(BeEmpty())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] testing

time.Sleep(time.Second * 3) is timing-sensitive. The 3-second sleep must exceed the 2-second allocation-timeout, but slow CI environments could make this flaky. This follows the pre-existing pattern in the file.

Suggested fix: Consider increasing the sleep margin or refactoring to use Eventually with a polling interval in a future cleanup.

@fullsend-ai-review fullsend-ai-review Bot added ready-for-merge All reviewers approved — ready to merge bug Something isn't working risk/low AI-assessed low risk dependency update labels Jul 20, 2026
@codecov-commenter

codecov-commenter commented Jul 20, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 58.53659% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 75.86%. Comparing base (8ae7a0c) to head (8da1cfb).

Files with missing lines Patch % Lines
pkg/reconciler/taskrun/dynamic.go 76.00% 6 Missing ⚠️
pkg/reconciler/taskrun/hostpool.go 0.00% 6 Missing ⚠️
pkg/reconciler/taskrun/dynamicpool.go 50.00% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #976      +/-   ##
==========================================
- Coverage   76.92%   75.86%   -1.06%     
==========================================
  Files          26       26              
  Lines        2817     2847      +30     
==========================================
- Hits         2167     2160       -7     
- Misses        452      492      +40     
+ Partials      198      195       -3     
Flag Coverage Δ
e2e-tests 31.19% <12.19%> (+0.91%) ⬆️
unit-tests 73.97% <58.53%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/reconciler/taskrun/dynamicpool.go 83.87% <50.00%> (-0.84%) ⬇️
pkg/reconciler/taskrun/dynamic.go 67.04% <76.00%> (+2.48%) ⬆️
pkg/reconciler/taskrun/hostpool.go 69.56% <0.00%> (-2.51%) ⬇️

... and 3 files with indirect coverage changes


Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 8ae7a0c...8da1cfb. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@meyrevived

Copy link
Copy Markdown
Contributor

/agentic_review

@qodo-app-for-konflux-ci

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 2 rules

Grey Divider


Action required

1. Instance ID lost in error 🐞 Bug ≡ Correctness
Description
When launchProvisioningTask fails, DynamicResolver.Allocate deletes CloudInstanceId from
tr.Annotations before constructing the new structured log fields and wrapped error that read
tr.Annotations[CloudInstanceId]. If termination succeeds, the returned error/logs can show an
empty instance ID, defeating the added diagnostic context and affecting user-visible error secrets.
Code

pkg/reconciler/taskrun/dynamic.go[R137-143]

+					log.Error(err, "failed to provision cloud host",
+						"instanceId", tr.Annotations[CloudInstanceId],
+						"instanceTag", r.instanceTag,
+						"address", address,
+					)
				}
-				return reconcile.Result{}, err
+				return reconcile.Result{}, fmt.Errorf("failed to provision cloud host (instance: %s, address: %s): %w", tr.Annotations[CloudInstanceId], address, err)
Relevance

⭐⭐⭐ High

Similar bug fixed: PR886 stopped deleting CloudInstanceId early to avoid empty IDs in
termination/logs.

PR-#886

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code deletes the annotation and then uses the same annotation key for the new log fields and
wrapped error. Allocation errors are later used to build the user-facing error secret text, so the
missing ID is externally visible.

pkg/reconciler/taskrun/dynamic.go[123-144]
pkg/reconciler/taskrun/taskrun.go[463-469]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
In the provisioning-task launch failure path, the code deletes `tr.Annotations[CloudInstanceId]` and then immediately uses `tr.Annotations[CloudInstanceId]` to populate new log fields and the returned wrapped error string. This can erase the instance ID from the very diagnostics this PR adds.

### Issue Context
Errors returned from host allocation are surfaced to users via `createErrorSecret` as part of `Error allocating host: ...`, so losing the instance ID reduces debuggability.

### Fix Focus Areas
- pkg/reconciler/taskrun/dynamic.go[123-144]
- pkg/reconciler/taskrun/taskrun.go[463-469]

### Suggested fix
Store `instanceID := tr.Annotations[CloudInstanceId]` in a local variable before calling `TerminateInstance` / deleting the annotation. Use `instanceID` for:
- the structured log field `instanceId`
- the returned `fmt.Errorf(... instance: %s ...)`
Optionally, also use it in the terminate call for clarity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Sleep-based timeout test 🐞 Bug ☼ Reliability
Description
The new dynamic provisioning test uses time.Sleep(3s) to trigger the allocation timeout, which
slows the test suite and can be timing-flaky in loaded CI environments. The timeout logic is purely
computed from AllocationStartTimeAnnotation and r.timeout, so the test can simulate timeout
deterministically by backdating the annotation instead of sleeping.
Code

pkg/reconciler/taskrun/provision_dynamic_test.go[R315-317]

+			// Wait for timeout (allocation-timeout is 2 seconds in test config)
+			time.Sleep(time.Second * 3)
+
Relevance

⭐⭐⭐ High

Repo has acted on flaky/slow test patterns before (sync/Eventually instead of timing races); likely
prefers deterministic timeout simulation.

PR-#458

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The test explicitly sleeps to exceed the timeout. The reconciler’s timeout logic is based on an
annotation timestamp plus the configured timeout, so the same condition can be reached by directly
setting the annotation to a past value.

pkg/reconciler/taskrun/provision_dynamic_test.go[286-323]
pkg/reconciler/taskrun/dynamic.go[60-85]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
A test waits on wall-clock time (`time.Sleep`) to exceed the allocation timeout, increasing runtime and introducing timing sensitivity.

### Issue Context
The production timeout check compares `AllocationStartTimeAnnotation + r.timeout` against `time.Now().Unix()`.

### Fix Focus Areas
- pkg/reconciler/taskrun/provision_dynamic_test.go[301-323]
- pkg/reconciler/taskrun/dynamic.go[60-85]

### Suggested fix
Replace `time.Sleep(...)` by updating the TaskRun’s `AllocationStartTimeAnnotation` to an older timestamp (e.g., `time.Now().Add(-5*time.Second).Unix()`) and `client.Update(...)` the TaskRun before the second reconcile. This keeps coverage while making the test fast and deterministic.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Dynamic provision error untested 📘 Rule violation ▣ Testability
Description
The modified provisioning-task failure path in DynamicResolver.Allocate adds new executable
error/logging lines, but there is no test scenario that can reach it given the current test harness
always provides the SSH secret and the fake client create path does not fail. This violates the
requirement that every new/modified executable line in the patch is exercised by automated tests (or
has an explicit coverage justification).
Code

pkg/reconciler/taskrun/dynamic.go[R137-143]

+					log.Error(err, "failed to provision cloud host",
+						"instanceId", tr.Annotations[CloudInstanceId],
+						"instanceTag", r.instanceTag,
+						"address", address,
+					)
				}
-				return reconcile.Result{}, err
+				return reconcile.Result{}, fmt.Errorf("failed to provision cloud host (instance: %s, address: %s): %w", tr.Annotations[CloudInstanceId], address, err)
Relevance

⭐⭐ Medium

Team values more coverage (added many sad-path tests), but no consistent enforcement of “every new
line” rule found.

PR-#872

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2363 requires automated tests to execute every new/modified executable line. The
changed block in dynamic.go only runs when launchProvisioningTask(...) returns an error, and
launchProvisioningTask(...) errors when the SSH secret is missing or create fails; however, the
dynamic test harness always creates the SSH secret (awskeys), making this branch unexercised by
existing tests.

Rule 2363: Require tests exercising every new or modified executable line in a patch
pkg/reconciler/taskrun/dynamic.go[123-144]
pkg/reconciler/taskrun/taskrun.go[971-983]
pkg/reconciler/taskrun/taskrun_helpers_test.go[303-327]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR modifies the `launchProvisioningTask(...)` error-handling block in `DynamicResolver.Allocate`, but the existing tests do not exercise this branch. Per compliance, each new/changed executable line must be executed by at least one automated test (or explicitly justified).

## Issue Context
`launchProvisioningTask(...)` returns an error primarily when the SSH secret is missing or when the TaskRun create call fails. The current dynamic test setup always creates the `awskeys` secret, so the new wrapped error/log lines in this branch are unlikely to be executed.

## Fix Focus Areas
- pkg/reconciler/taskrun/dynamic.go[123-144]
- pkg/reconciler/taskrun/taskrun.go[971-983]
- pkg/reconciler/taskrun/taskrun_helpers_test.go[303-327]
- pkg/reconciler/taskrun/provision_dynamic_test.go[90-170]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


4. HostPool provision error untested 📘 Rule violation ▣ Testability
Description
The modified HostPool.Allocate provisioning-task launch failure path adds/changes executable
error/logging lines, but the test harness always provides the awskeys secret and does not
introduce a failing create path to execute this branch. This violates the requirement that every
new/modified executable line in the patch is exercised by automated tests (or has an explicit
coverage justification).
Code

pkg/reconciler/taskrun/hostpool.go[R138-150]

+		log.Error(err, "failed to launch provisioning task, unassigning host",
+			"host", selected.Name,
+			"address", selected.Address,
+			"platform", hp.targetPlatform,
+		)
		delete(tr.Labels, constant.AssignedHost)
		controllerutil.RemoveFinalizer(tr, PipelineFinalizer)
		updateErr := UpdateTaskRunWithRetry(ctx, r.client, r.apiReader, tr)
		if updateErr != nil {
			log.Error(updateErr, "Could not unassign task after provisioning failure")
			return reconcile.Result{}, err
		}
-		return reconcile.Result{}, fmt.Errorf("failed to provision host: %v", err)
+		return reconcile.Result{}, fmt.Errorf("failed to provision host %s (%s): %w", selected.Name, selected.Address, err)
Relevance

⭐⭐ Medium

History shows adding failure-path tests in taskrun reconciler, but no explicit prior requirement for
this exact branch.

PR-#872

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
PR Compliance ID 2363 requires tests to execute all new/modified executable lines. The changed
HostPool.Allocate branch is only executed when launchProvisioningTask(...) returns an error;
launchProvisioningTask(...) errors when the SSH secret is missing (or create fails), but the
static test setup always creates the awskeys secret, so this branch is not exercised by current
tests.

Rule 2363: Require tests exercising every new or modified executable line in a patch
pkg/reconciler/taskrun/hostpool.go[134-151]
pkg/reconciler/taskrun/taskrun.go[971-983]
pkg/reconciler/taskrun/taskrun_helpers_test.go[270-279]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR changes executable lines in `HostPool.Allocate` in the error path when `launchProvisioningTask(...)` fails (adds structured fields and changes returned error formatting/wrapping). There is no test that forces `launchProvisioningTask(...)` to fail, so these modified lines are not exercised.

## Issue Context
`launchProvisioningTask(...)` fails when it cannot find the SSH secret or cannot create the provisioning TaskRun. The static test setup always includes the `awskeys` secret, so this path is not covered.

## Fix Focus Areas
- pkg/reconciler/taskrun/hostpool.go[134-151]
- pkg/reconciler/taskrun/taskrun.go[971-983]
- pkg/reconciler/taskrun/taskrun_helpers_test.go[270-279]
- pkg/reconciler/taskrun/provision_static_test.go[22-120]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
5. Nil error in Error() 🐞 Bug ◔ Observability
Description
DynamicResolver.Allocate calls log.Error(nil, ...) when provisioning attempts are exhausted,
emitting an error-level log without an error value and diverging from the repo’s normal logging
pattern. This reduces log consistency/utility and makes the returned error (created afterward)
unavailable to the logger.
Code

pkg/reconciler/taskrun/dynamic.go[R50-54]

+		log.Error(nil, "all provisioning attempts exhausted",
+			"failedHosts", tr.Annotations[FailedHosts],
+			"instanceTag", r.instanceTag,
+		)
+		return reconcile.Result{}, fmt.Errorf("failed to provision host, all attempts exhausted for instance tag %s (previously failed hosts: %s)", r.instanceTag, tr.Annotations[FailedHosts])
Relevance

⭐⭐ Medium

No clear precedent on log.Error(nil); repo improves structured logging but nil-error usage not
historically addressed.

PR-#559

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The exhausted-attempts branch explicitly calls log.Error(nil, ...). In contrast, other error logs
in the reconciler pass a non-nil error value to log.Error, even when it’s a synthesized error.

pkg/reconciler/taskrun/dynamic.go[47-55]
pkg/reconciler/taskrun/taskrun.go[283-290]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`DynamicResolver.Allocate` logs an error with `log.Error(nil, ...)` in the “attempts exhausted” path, so the error-level log does not carry an error value.

### Issue Context
Elsewhere in this repo, error-level logs consistently include a non-nil error (even if synthetic) to provide consistent metadata.

### Fix Focus Areas
- pkg/reconciler/taskrun/dynamic.go[47-55]

### Suggested fix
Create the error first (same one you return) and pass it to `log.Error(err, ...)` (or switch this log to `log.Info(...)` if you intentionally don’t want an error object).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Qodo Logo

log.Error(unassignErr, "failed to unassign instance from task after provisioning failure")
} else {
log.Error(err, "failed to provision cloud host")
log.Error(err, "failed to provision cloud host",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. Dynamic provision error untested 📘 Rule violation ▣ Testability

The modified provisioning-task failure path in DynamicResolver.Allocate adds new executable
error/logging lines, but there is no test scenario that can reach it given the current test harness
always provides the SSH secret and the fake client create path does not fail. This violates the
requirement that every new/modified executable line in the patch is exercised by automated tests (or
has an explicit coverage justification).
Agent Prompt
## Issue description
The PR modifies the `launchProvisioningTask(...)` error-handling block in `DynamicResolver.Allocate`, but the existing tests do not exercise this branch. Per compliance, each new/changed executable line must be executed by at least one automated test (or explicitly justified).

## Issue Context
`launchProvisioningTask(...)` returns an error primarily when the SSH secret is missing or when the TaskRun create call fails. The current dynamic test setup always creates the `awskeys` secret, so the new wrapped error/log lines in this branch are unlikely to be executed.

## Fix Focus Areas
- pkg/reconciler/taskrun/dynamic.go[123-144]
- pkg/reconciler/taskrun/taskrun.go[971-983]
- pkg/reconciler/taskrun/taskrun_helpers_test.go[303-327]
- pkg/reconciler/taskrun/provision_dynamic_test.go[90-170]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

if err != nil {
//ugh, try and unassign
log.Error(err, "failed to launch provisioning task, unassigning host")
log.Error(err, "failed to launch provisioning task, unassigning host",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Hostpool provision error untested 📘 Rule violation ▣ Testability

The modified HostPool.Allocate provisioning-task launch failure path adds/changes executable
error/logging lines, but the test harness always provides the awskeys secret and does not
introduce a failing create path to execute this branch. This violates the requirement that every
new/modified executable line in the patch is exercised by automated tests (or has an explicit
coverage justification).
Agent Prompt
## Issue description
The PR changes executable lines in `HostPool.Allocate` in the error path when `launchProvisioningTask(...)` fails (adds structured fields and changes returned error formatting/wrapping). There is no test that forces `launchProvisioningTask(...)` to fail, so these modified lines are not exercised.

## Issue Context
`launchProvisioningTask(...)` fails when it cannot find the SSH secret or cannot create the provisioning TaskRun. The static test setup always includes the `awskeys` secret, so this path is not covered.

## Fix Focus Areas
- pkg/reconciler/taskrun/hostpool.go[134-151]
- pkg/reconciler/taskrun/taskrun.go[971-983]
- pkg/reconciler/taskrun/taskrun_helpers_test.go[270-279]
- pkg/reconciler/taskrun/provision_static_test.go[22-120]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

log := logr.FromContextOrDiscard(ctx)
if tr.Annotations[FailedHosts] != "" {
return reconcile.Result{}, errors.New("failed to provision host")
log.Error(nil, "all provisioning attempts exhausted",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

3. Nil error in error() 🐞 Bug ◔ Observability

DynamicResolver.Allocate calls log.Error(nil, ...) when provisioning attempts are exhausted,
emitting an error-level log without an error value and diverging from the repo’s normal logging
pattern. This reduces log consistency/utility and makes the returned error (created afterward)
unavailable to the logger.
Agent Prompt
### Issue description
`DynamicResolver.Allocate` logs an error with `log.Error(nil, ...)` in the “attempts exhausted” path, so the error-level log does not carry an error value.

### Issue Context
Elsewhere in this repo, error-level logs consistently include a non-nil error (even if synthetic) to provide consistent metadata.

### Fix Focus Areas
- pkg/reconciler/taskrun/dynamic.go[47-55]

### Suggested fix
Create the error first (same one you return) and pass it to `log.Error(err, ...)` (or switch this log to `log.Info(...)` if you intentionally don’t want an error object).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

log.Error(unassignErr, "failed to unassign instance from task after provisioning failure")
} else {
log.Error(err, "failed to provision cloud host")
log.Error(err, "failed to provision cloud host",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

4. Instance id lost in error 🐞 Bug ≡ Correctness

When launchProvisioningTask fails, DynamicResolver.Allocate deletes CloudInstanceId from
tr.Annotations before constructing the new structured log fields and wrapped error that read
tr.Annotations[CloudInstanceId]. If termination succeeds, the returned error/logs can show an
empty instance ID, defeating the added diagnostic context and affecting user-visible error secrets.
Agent Prompt
### Issue description
In the provisioning-task launch failure path, the code deletes `tr.Annotations[CloudInstanceId]` and then immediately uses `tr.Annotations[CloudInstanceId]` to populate new log fields and the returned wrapped error string. This can erase the instance ID from the very diagnostics this PR adds.

### Issue Context
Errors returned from host allocation are surfaced to users via `createErrorSecret` as part of `Error allocating host: ...`, so losing the instance ID reduces debuggability.

### Fix Focus Areas
- pkg/reconciler/taskrun/dynamic.go[123-144]
- pkg/reconciler/taskrun/taskrun.go[463-469]

### Suggested fix
Store `instanceID := tr.Annotations[CloudInstanceId]` in a local variable before calling `TerminateInstance` / deleting the annotation. Use `instanceID` for:
- the structured log field `instanceId`
- the returned `fmt.Errorf(... instance: %s ...)`
Optionally, also use it in the terminate call for clarity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Expect(tr.Annotations[CloudInstanceId]).ShouldNot(BeEmpty())
instanceID := tr.Annotations[CloudInstanceId]

// Wait for timeout (allocation-timeout is 2 seconds in test config)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

5. Sleep-based timeout test 🐞 Bug ☼ Reliability

The new dynamic provisioning test uses time.Sleep(3s) to trigger the allocation timeout, which
slows the test suite and can be timing-flaky in loaded CI environments. The timeout logic is purely
computed from AllocationStartTimeAnnotation and r.timeout, so the test can simulate timeout
deterministically by backdating the annotation instead of sleeping.
Agent Prompt
### Issue description
A test waits on wall-clock time (`time.Sleep`) to exceed the allocation timeout, increasing runtime and introducing timing sensitivity.

### Issue Context
The production timeout check compares `AllocationStartTimeAnnotation + r.timeout` against `time.Now().Unix()`.

### Fix Focus Areas
- pkg/reconciler/taskrun/provision_dynamic_test.go[301-323]
- pkg/reconciler/taskrun/dynamic.go[60-85]

### Suggested fix
Replace `time.Sleep(...)` by updating the TaskRun’s `AllocationStartTimeAnnotation` to an older timestamp (e.g., `time.Now().Add(-5*time.Second).Unix()`) and `client.Update(...)` the TaskRun before the second reconcile. This keeps coverage while making the test fast and deterministic.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@meyrevived
meyrevived marked this pull request as draft July 22, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ready-for-merge All reviewers approved — ready to merge risk/low AI-assessed low risk dependency update

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants