Skip to content

[RAPTOR-19727] Don't suggest logs command that won't work - #852

Open
taras-pokornyy wants to merge 2 commits into
datarobot-oss:mainfrom
taras-pokornyy:RAPTOR-19727_fix_logs_command_sugestion
Open

[RAPTOR-19727] Don't suggest logs command that won't work#852
taras-pokornyy wants to merge 2 commits into
datarobot-oss:mainfrom
taras-pokornyy:RAPTOR-19727_fix_logs_command_sugestion

Conversation

@taras-pokornyy

@taras-pokornyy taras-pokornyy commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

RATIONALE

RAPTOR-19727: when a build fails or is
cancelled before the builder produces any output, the CLI unconditionally told the user to run
dr artifact build logs <artifact> <build> — a command that either 404s or succeeds while
printing nothing, since there's genuinely no log data to show. The suggestion was built purely off
the build's terminal status, with no check that logs actually existed, and was duplicated
independently across three call sites (with inconsistent wording, and one missing the artifact ID
entirely, so the command it "helpfully" printed couldn't even be copy-pasted as-is).

dr artifact build logs itself also had no real empty case: text mode printed nothing (a silent
no-op) and -o json printed the literal string null instead of [].

CHANGES

  • Added workload.BuildLogsAvailable(artifactID, buildID) and
    workload.BuildFailureMessage(artifactID, buildID, status, logsAvailable) as the single shared
    way to decide whether a logs hint is worth showing and to word it — replacing three independent,
    drifted copies of the same message.
  • WaitForBuild no longer guesses at a logs suggestion (it doesn't have the artifact ID cheaply);
    it returns a plain status error and lets callers that already have log info build the final
    message via BuildFailureMessage.
  • cmd/artifact/build/get and cmd/artifact/build/create now reuse the log tail BuildSummaryFor
    already fetches to decide whether to show the hint, at no extra API cost; dr workload up's
    buildImage is the one call site with no pre-fetched log info, so it does the one extra precheck
    fetch.
  • RenderBuildLogs now matches RenderWorkloadLogs's established empty-output convention: JSON is
    always an array (never null), and text mode prints "No logs found." to stderr so stdout stays
    clean for piping/grepping.
  • Merged main to pick up the concurrent OTEL-based build-log streaming rewrite
    (internal/workload/build_logs.go, live --wait log tailing in artifact build create) and
    resolved the two real conflicts (internal/workload/build.go, cmd/artifact/build/create/cmd.go)
    by layering this change's log-availability precheck on top of main's new streaming
    implementation. Updated two tests that asserted the old raw-HTTP-404 behavior of
    GetArtifactBuildLogs, since logs are now read from the OTEL stream, where "no logs for this
    build" is a normal empty page rather than a 404.

PR Automation

Comment-Commands: Trigger CI by commenting on the PR:

  • /trigger-smoke-test or /trigger-test-smoke - Run smoke tests
  • /trigger-install-test or /trigger-test-install - Run installation tests

Labels: Apply labels to trigger workflows:

  • run-smoke-tests or go - Run smoke tests on demand (only works for non-forked PRs)

Important

For Forked PRs: The run-smoke-tests label won't work. A required Smoke Tests check will block merge until a maintainer acts:

  • A maintainer uses /approve-smoke-tests to run smoke tests (results will set the check)
  • A maintainer uses /skip-smoke-tests to bypass the check without running tests

Please comment requesting a maintainer review if you need smoke tests to run.


Note

Low Risk
CLI error messaging and log rendering only; no changes to auth, deploy logic, or API contracts beyond clearer exit errors.

Overview
Failed or cancelled builds no longer always tell users to run dr artifact build logs when there is nothing to show. BuildLogsAvailable and BuildFailureMessage centralize that decision: suggest dr artifact build logs <artifact> <build> only when logs exist, otherwise say no logs were captured.

WaitForBuild now returns a plain terminal-status error (no logs hint). dr artifact build get and create --wait build the user-facing error via BuildFailureMessage, using summary.LogTail from BuildSummaryFor so there is no extra fetch. dr workload up is the one path that still calls BuildLogsAvailable before wording the failure.

RenderBuildLogs matches workload logs behavior: JSON always emits [] (not null); empty text mode prints "No logs found." on stderr so stdout stays pipe-safe.

Reviewed by Cursor Bugbot for commit 5c756c6. Configure here.

@datarobot-pr-review-router

Copy link
Copy Markdown

🎫 Jira: RAPTOR-19727 — CLI must not point users at build logs that don't exist

@taras-pokornyy

Copy link
Copy Markdown
Contributor Author

/approve-smoke-tests

@github-actions

Copy link
Copy Markdown
Contributor

🔐 Fork PR smoke tests triggered by @taras-pokornyy

⚠️ Security Notice: This will run tests with access to repository secrets.

What happens next:

  1. Security scans will run automatically (Trivy, gosec)
  2. If security scans pass, smoke tests will run
  3. Results will be posted as PR comments

⚠️ Important: Review the PR code carefully before approving!

@github-actions

Copy link
Copy Markdown
Contributor

🔐 Fork smoke tests started by maintainer

⏳ Security scans passed. Running smoke tests...

Commit: 5c756c67ae5af6d0011d4ecf9a9a89a073279cb3
View run

@github-actions

Copy link
Copy Markdown
Contributor

All smoke tests passed! (Fork PR)

✅ Security Scan: success
✅ Linux: success
✅ Windows: success

View run details

@taras-pokornyy
taras-pokornyy marked this pull request as ready for review August 27, 2026 14:11
@taras-pokornyy
taras-pokornyy requested a review from a team as a code owner August 27, 2026 14:11

@cursor cursor Bot 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.

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 5c756c6. Configure here.

// logsAvailable is true.
func BuildFailureMessage(artifactID, buildID, status string, logsAvailable bool) error {
if !logsAvailable {
return fmt.Errorf("build %s ended with status %s; no logs were captured for this build", buildID, status)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fetch errors claimed as missing logs

Medium Severity

BuildFailureMessage states that no logs were captured whenever logsAvailable is false, but callers treat a failed logs fetch the same as a successful empty result. BuildLogsAvailable returns false on any error, and create/get infer availability from LogTail after BuildSummaryFor swallows fetch failures. A 5xx, 403, or transient outage then tells the user logs never existed, so they will not retry dr artifact build logs even though the stream may have just printed lines or the command would succeed moments later.

Additional Locations (2)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5c756c6. Configure here.

@wojtekwdr wojtekwdr left a comment

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.

LGTM in general, one medium finding

// Precheck log availability before suggesting the logs command --
// this is the only call site with no already-fetched log info, so
// it's the one place that needs its own extra fetch.
return built.ID, workload.BuildFailureMessage(artifactID, built.ID, built.Status, hasLogsFn(artifactID, built.ID))

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.

[medium] Build log ingestion trails the builder by 20-40s per the buildLogLagAllowance comment, and this fires right after tail.Finish(), which is a single catch-up poll. A build that fails fast gets told no logs were captured, then the lines land a second later. Could the tail track whether it ever emitted a line instead?

return fmt.Errorf("build %s ended with status %s; no logs were captured for this build", buildID, status)
}

return fmt.Errorf("build %s ended with status %s; see 'dr artifact build logs %s %s'", buildID, status, artifactID, buildID)

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.

[low] question: does this hint hold up for a build that only logged at DEBUG? dr artifact build logs defaults to --level info and filters, but both availability checks count unfiltered entries, so that build gets the hint and then prints "No logs found."

fmt.Errorf("build %s ended with status %s", id, workload.BuildStatusFailed)
}
// Logs exist for this build, so the failure message should point at them.
f.hasLogs = func(string, string) bool { return true }

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.

[low] Both tests that reach this path force hasLogs true, so nothing asserts the "no logs were captured" wording through buildImage. Hardcoding true at build.go:487 would still pass the suite.

@ajalon1 ajalon1 left a comment

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.

Additional review findings (on top of the existing threads on ingestion lag, unfiltered availability counting, and fetch-errors-as-no-logs). Both verified against the code; anchors are on added lines. Defer to Woj's approval

// build. Used to decide whether it's worth pointing the user at
// 'dr artifact build logs' -- suggesting a command that 404s or prints
// nothing is worse than not suggesting anything.
func BuildLogsAvailable(artifactID, buildID string) bool {

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.

[P2] BuildLogsAvailable drains every log page to answer a boolean

BuildLogsAvailable calls GetArtifactBuildLogs, which hardcodes maxEntries = 0 (build.go:337). In drainLogPages, maxEntries <= 0 means "drain every page" — it follows every next link to the end with no early exit. For a failed build with a verbose Docker log stream, that's potentially dozens of HTTP round-trips just to evaluate len(entries) > 0.

The trigger is this PR's only "extra precheck fetch" call site: buildImage in internal/workload/up/build.go calls hasLogsFn on every failed dr workload up, right after BuildLogTail already fetched (much of) the same data during the wait.

The fix is cheap because the seam already exists: call fetchArtifactBuildLogs(artifactID, buildID, 1, "", "", "") directly — limit=1 short-circuits after the first page — or give GetArtifactBuildLogs a limit parameter. (Related to but distinct from the ingestion-lag concern raised in the other thread: even with correct timing, this check is far more expensive than it needs to be.)

Suggested change
func BuildLogsAvailable(artifactID, buildID string) bool {
func BuildLogsAvailable(artifactID, buildID string) bool {
// limit=1: we only need to know whether at least one entry exists;
// maxEntries=0 would drain every page of a verbose build's log stream.
otel, err := fetchArtifactBuildLogs(artifactID, buildID, 1, "", "", "build logs")
return err == nil && len(otel) > 0
}

}

if len(entries) == 0 {
fmt.Fprintln(os.Stderr, "No logs found.")

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.

[P3] "No logs found." is wrong for logs filtered out by the default level

Verified end-to-end: cmd/artifact/build/logs/cmd.go calls GetArtifactBuildLogs (unfiltered), then FilterLogsByLevel(entries, "info") (the flag's default), then RenderBuildLogs. A DEBUG-only build therefore delivers an empty slice here, and this now prints No logs found. to stderr — factually wrong, the logs exist. Before this PR, empty text output printed nothing, so this message is newly introduced for the filtered-empty case, and it nudges the user away from the actual remedy (--level debug).

Render-side sibling of the unfiltered-availability comment on build.go: since filtering happens in the caller, RenderBuildLogs can't distinguish "no logs" from "all filtered out". Options: pass the pre-filter count into the render call, or have the cmd emit a level-aware message like No logs at this level (try --level debug).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants