Skip to content

YARN-10423. Extend UIv2 to download application diagnostic bundle as a zip folder with each file name - #8766

Open
Hean-Chhinling wants to merge 2 commits into
apache:trunkfrom
Hean-Chhinling:trunk_YARN-10423-2
Open

Hean-Chhinling wants to merge 2 commits into
apache:trunkfrom
Hean-Chhinling:trunk_YARN-10423-2

Conversation

@Hean-Chhinling

@Hean-Chhinling Hean-Chhinling commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Contains content generated by Cursor.

Description of PR

This PR is part of the YARN-10323 [Umbrella] YARN Diagnostic collector.

Its aims is to add a button in the YARN UIv2 for calling the Application Diagnostic endpoint from #8762.

Below is the screenshot:

DiagnosticUIv2

It takes a couple mins to run and after a ZIP folder will be downloaded. Each of the diagnostic artifacts will be in its own separate file.

There is a ZIP utility written from scratch at diagnostic-archive.js to avoid installing new dependency.

This PR depends on the following PRs:

  1. YARN-10421: Adding controller for handling diagnostic python script #8762 - Controller service class for the new diagnostic endpoints
  2. YARN-11823: add new endpoints for getting jstacks of ApplicationId, NodeManager and ResourceManager process #8123 - new JStacks endpoints.
  3. YARN-10422: add python script for collecting diagnostic data #8742 - python diagnostic collector script

How was this patch tested?

Tested manually and wrote unit-test for utility function. They all work as expected.

For code changes:

  • Does the title of this PR start with the corresponding JIRA issue id (e.g. 'HADOOP-17799. Your PR title ...')?
  • Object storage: Have the integration tests been executed and the endpoint
    declared according to the connector-specific documentation? Note: Automated CI
    testing doesn't cover all cases so manual testing with cloud storage is still
    required.
  • If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?
  • If applicable, have you updated the LICENSE, LICENSE-binary, NOTICE-binary files?

AI Tooling

If an AI tool was used:

@hadoop-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 0m 23s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 codespell 0m 0s codespell was not available.
+0 🆗 detsecrets 0m 0s detect-secrets was not available.
+0 🆗 jshint 0m 0s jshint was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
_ trunk Compile Tests _
+1 💚 mvninstall 27m 59s trunk passed
+1 💚 shadedclient 43m 18s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+1 💚 mvninstall 0m 56s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 shadedclient 15m 3s patch has no errors when building and testing our client artifacts.
_ Other Tests _
+1 💚 asflicense 0m 35s The patch does not generate ASF License warnings.
61m 43s
Subsystem Report/Notes
Docker ClientAPI=1.56 ServerAPI=1.56 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8766/1/artifact/out/Dockerfile
GITHUB PR #8766
Optional Tests dupname asflicense shadedclient codespell detsecrets jshint
uname Linux 387a571b0205 5.15.0-190-generic #200-Ubuntu SMP Fri Aug 7 15:06:04 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / 6d8e904
Max. process+thread count 616 (vs. ulimit of 10000)
modules C: hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui U: hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8766/1/console
versions git=2.43.0 maven=3.9.15
Powered by Apache Yetus 0.14.1 https://yetus.apache.org

This message was automatically generated.

@joseluisll joseluisll 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.

Thanks for picking this back up, @Hean-Chhinling. A one-click diagnostic bundle in UI2 will be useful. Some general points first, then inline comments.

Dependencies. This calls /common-issues/collect from #8762, which runs the collector script from #8742 (YARN-10422). The script calls the NodeManager endpoints ws/v1/node/apps/{appId}/jstack/{n} and ws/v1/node/jstack/{n}, and the panel text refers to yarn.nodemanager.jstack-endpoints.enabled. I couldn't find either the endpoints or the property in trunk, #8762 or #8742. Which PR/JIRA adds them? Could you list all the dependencies in the description, so it's clear this can only go in after them?

Scope vs. the JIRA. YARN-10423 asks for a UI2 page that lists the diagnostic cases defined by the collector script (i.e. /common-issues/list). This PR hard-codes the application_diagnostic case on the app info page. That's a reasonable first step, but please either update the JIRA description or move this to its own sub-task of YARN-10323, so the JIRA matches what gets merged.

Where the archive is built. As #8762 stands, the RM reads every output file into a String (DiagnosticsService.collectIssueFilesContent) and returns everything in one JSON response. The browser then parses it and builds the ZIP in memory. With large aggregated logs (yarn logs -applicationId), that means several full copies on both the RM and the client. Have you considered having the endpoint stream application/zip directly (StreamingOutput + ZipOutputStream)? The UI would then be a plain download, and the hand-written ZIP/CRC32 code here would no longer be needed.

HTTP method. Collecting is expensive and has side effects (it runs yarn logs and takes jstacks on the NodeManager), but it's exposed as GET. RestCsrfPreventionFilter skips GET,OPTIONS,HEAD,TRACE by default, so when yarn.resourcemanager.webapp.rest-csrf.enabled is turned on this endpoint still isn't protected. RMWebServices already uses @POST for a comparable admin action (/scheduler-logs). I'd suggest POST in #8762 and in this PR.

Tests. Maven/Yetus don't run the Ember unit tests, so the +1 doesn't cover them. I ran tests/unit/utils/diagnostic-archive-test.js against app/utils/diagnostic-archive.js, and resolveEntryFileName adds txt extension for non-xml payload fails: it returns application_logs.log, but the test expects application_logs.txt. Details inline. Could you run ember test locally before the next push?

Housekeeping.

  • Hadoop's title convention is YARN-10423. <summary> (period, not colon).
  • The ASF generative-tooling checkbox ("My use of AI contributions follows the ASF legal policy") in the description is unchecked.

if (contentType.indexOf('json') >= 0 || content.startsWith('{') || content.startsWith('[')) {
return `${filename}.json`;
}
if (filename.indexOf('log') >= 0) {

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.

This substring check matches "log" anywhere in the name, which is why the PR's own test resolveEntryFileName adds txt extension for non-xml payload fails: application_logs → application_logs.log, expected .txt. It would also match names like catalog or dialog. The script names its log files resourcemanager_log and nodemanager_log, so matching the suffix would be enough:

  if (/(^|_)log$/.test(filename)) {

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.

Nice catch, I will update this in my next commit. I thought the unit-test was failing because I have not updated the unit-test since I changed the file extension before from .log to .txt.

fileEntry.contentType,
content,
);
let zipPath = `${folderName}/${relativeName.replace(/^\/+/, '')}`;

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.

Entry names aren't sanitised, but the folder name is (buildBundleFolderName, line 23). The collector script in #8742 writes the NodeManager jstack as nm_{amHostHttpAddress}_jstack, so the entry becomes e.g. …/nm_host1.example.com:8042_jstack.txt. Windows can't extract a file with : in its name. Could you apply the same /[^\w.-]+/g → '_' replacement to each path segment of relativeName? A unit test with a host:port filename would cover it.

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.

Nice catch, I will apply to /[^\w.-]+/g → '_' to each of the segment of relativeName.
When I was tested on Mac, it all works as expected. Thank you for your suggestions!

Accept: 'application/json'
},
success: (payload) => resolve(payload),
error: (jqXHR) => reject(this.parseAjaxError(jqXHR))

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.

When the 5-minute timeout fires, jQuery 3.6 calls jqXHR.abort("timeout"), which sets jqXHR.status to 0. parseAjaxError then reaches the status === 0 branch (line 82) and shows "Not able to connect to the Resource Manager.". That's misleading for an operation the panel says may take several minutes. The error callback also receives textStatus, so a timeout can be reported separately:

        error: (jqXHR, textStatus) => reject(this.parseAjaxError(jqXHR, textStatus))
  parseAjaxError(jqXHR, textStatus) {
    if (textStatus === 'timeout') {
      return new Error('Diagnostic collection did not finish within 5 minutes. ' +
        'It may still be running on the Resource Manager; check the RM logs.');
    }
    ...

Please add a unit test for that case next to the existing parseAjaxError ones.

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.

You have a very good eye, @joseluisll!
I will update this in my next commit along with a unit-test to make sure the timeout shows different error.

Comment on lines +28 to +29
isCollectingApplicationDiagnostic: false,
diagnosticCollectError: null,

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.

Controllers are singletons in Ember 2.8, and neither routes/yarn-app/info.js nor routes/abstract.js resets these properties. So if you start a collection for app A and open app B's info page, B shows A's spinner with the button disabled, and an error from A stays visible on B. Resetting them in the route's setupController (or storing the state per appId) would fix it:

  setupController(controller, model) {
    this._super(controller, model);
    controller.setProperties({
      isCollectingApplicationDiagnostic: false,
      diagnosticCollectError: null
    });
  },

(The in-flight request's finally still clears the flag later, which is harmless.)

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.

Thank you for the suggestion! It seems like the in-flight request's finally could cause a problem.
If a collection on app B has started while A is still going. Then A's finally will clear B's spinner and re-enabled the app's B button while it is still collecting. This is also appA error handler will show on appB page.
I will fix on of this in my next commit with by giving id to the app request and only update the page if the id is the latest.

</div>
{{/if}}

<div class="col-md-12 margin-bottom-20">

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.

A few things about the panel:

  • Permissions. YARN-10421: Adding controller for handling diagnostic python script #8762 calls initForWritableEndpoints(callerUGI, true), so with yarn.acl.enable=true only YARN admins can run this. Everyone else gets a 403 after clicking. Could the panel say "Requires YARN admin privileges when ACLs are enabled", and could a 403 be shown with that message?
  • Placement. The panel goes above the app's own Diagnostics block, which pushes the failure diagnostics users usually look for further down. Putting it below that block, or making it collapsible, would avoid that.
  • Hard-coded description. The bullet list and the yarn.nodemanager.jstack-endpoints.enabled instructions (lines 44-61) describe what the script happens to do today, and will drift from it (the property doesn't exist in trunk yet either). A short general sentence plus a link to the docs (YARN-10461) would be easier to maintain.
  • Styles. margin-left-15 (line 75) isn't defined in app/styles (only margin-bottom-20 is). The inline style="margin-bottom: …" attributes could be classes too.

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.

Thank you for the suggestion, @joseluisll!
These are very good points. My bad that I miss the important permission check here.
I will make these necessary changes in my next commit.

/**
* Build a ZIP archive (stored, no compression) containing one top-level folder.
*/
export function buildDiagnosticZipBlob(folderName, entries) {

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.

I opened the output with unzip -t and Python's zipfile. The archive is valid, but a few header fields are worth fixing:

  • The modification date and time are 0, so every entry is listed as 1980-00-00 00:00, which is not a valid DOS date. Please write the DOS date/time from generatedAt: local header offsets 10 and 12, central directory offsets 12 and 14.
  • Names are encoded as UTF-8, but general-purpose bit 11 (0x0800) isn't set (local header offset 6, central directory offset 8). Readers that follow the spec decode non-ASCII names as CP437 (Python shows ünïcode as ├╝n├»code). The script's names are ASCII today, so this is minor, but it's a one-line change.
  • The folderName parameter isn't used, and the doc comment says "containing one top-level folder" although no directory entry is written.

The only test today is blob.size > 0. Could you add one that checks the bytes? For example: CRC-32 of "hello" is 0x3610a686, and the end-of-central-directory entry count and central directory offset are easy to assert. Please also include a non-ASCII case: PhantomJS (the testem.json launcher) has no TextEncoder, so the tests exercise utf8EncodeFallback.

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.

Thank you for your detailed feedback, I really appreciate it.
My bad that I missed these points. I will fix the code in my next commit along with the unit-tests.

* limitations under the License.
*/

const ISSUE_ID = 'application_diagnostic';

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.

Nit: this duplicates ISSUE_ID in app/services/application-diagnostic.js (line 26). The service already passes it to buildBundleFolderName, so the util could drop its own copy (or export one that the service imports).

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.

Thanks, I will centralise this ISSUE_ID and pass it to application-diagnostic.js.

@Hean-Chhinling

Hean-Chhinling commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

Thank you so much @joseluisll for your detailed feedback! I really appreciate it.

Let me address your overall review below and then I will answer in each of the review thread:

Dependencies. This calls /common-issues/collect from #8762, which runs the collector script from #8742 (YARN-10422). The script calls the NodeManager endpoints ws/v1/node/apps/{appId}/jstack/{n} and ws/v1/node/jstack/{n}, and the panel text refers to yarn.nodemanager.jstack-endpoints.enabled. I couldn't find either the endpoints or the property in trunk, #8762 or #8742. Which PR/JIRA adds them? Could you list all the dependencies in the description, so it's clear this can only go in after them?

Exactly, that is how this new feature will work. The new jstack endpoint is on this PR #8123. I had already added it in the PR description that it must go in before the UI changes.

Scope vs. the JIRA. YARN-10423 asks for a UI2 page that lists the diagnostic cases defined by the collector script (i.e. /common-issues/list). This PR hard-codes the application_diagnostic case on the app info page. That's a reasonable first step, but please either update the JIRA description or move this to its own sub-task of YARN-10323, so the JIRA matches what gets merged.

Good catch! When the doc was prepared there were 4 endpoints for diagnostic collector. However, after several round of discussions I narrowed down into only application_diagnostic option that is needed to be called from UIv2. I will change the jira description and probably remove the /common-issues/list as it is not needed.

Where the archive is built. As #8762 stands, the RM reads every output file into a String (DiagnosticsService.collectIssueFilesContent) and returns everything in one JSON response. The browser then parses it and builds the ZIP in memory. With large aggregated logs (yarn logs -applicationId), that means several full copies on both the RM and the client. Have you considered having the endpoint stream application/zip directly (StreamingOutput + ZipOutputStream)? The UI would then be a plain download, and the hand-written ZIP/CRC32 code here would no longer be needed.

Yep, I have thought about that duplication but have not found anything useful yet. Thank you so much for suggestion, I will research more about this. Having the backend endpoint send as a zip would be significantly easier than the UI doing it.

HTTP method. Collecting is expensive and has side effects (it runs yarn logs and takes jstacks on the NodeManager), but it's exposed as GET. RestCsrfPreventionFilter skips GET,OPTIONS,HEAD,TRACE by default, so when yarn.resourcemanager.webapp.rest-csrf.enabled is turned on this endpoint still isn't protected. RMWebServices already uses @post for a comparable admin action (/scheduler-logs). I'd suggest POST in #8762 and in this PR.

Thank you so much for the suggestion! I will change the collect endpoint to POST method.

Tests. Maven/Yetus don't run the Ember unit tests, so the +1 doesn't cover them. I ran tests/unit/utils/diagnostic-archive-test.js against app/utils/diagnostic-archive.js, and resolveEntryFileName adds txt extension for non-xml payload fails: it returns application_logs.log, but the test expects application_logs.txt. Details inline. Could you run ember test locally before the next push?

Nice catch, I will make sure to run Ember unit-tests locally next time before the push. Regarding this failure application_logs.log expect application_logs.txt was from my recent changes that I have not update the test code. I will update this in my next commit.

Housekeeping.
Hadoop's title convention is YARN-10423.

(period, not colon).
The ASF generative-tooling checkbox ("My use of AI contributions follows the ASF legal policy") in the description is unchecked.

And thank you so much for letting me know this housekeeping rules. I was not familiar about this before.

Please let me know if something is not clear to you. I would be more than happy to address the issue further :)

@Hean-Chhinling Hean-Chhinling changed the title YARN-10423: Extend UIv2 to download application diagnostic bundle as a zip folder with each file name YARN-10423. Extend UIv2 to download application diagnostic bundle as a zip folder with each file name Oct 21, 2026
@hadoop-yetus

Copy link
Copy Markdown

🎊 +1 overall

Vote Subsystem Runtime Logfile Comment
+0 🆗 reexec 20m 24s Docker mode activated.
_ Prechecks _
+1 💚 dupname 0m 0s No case conflicting files found.
+0 🆗 codespell 0m 0s codespell was not available.
+0 🆗 detsecrets 0m 0s detect-secrets was not available.
+0 🆗 jshint 0m 0s jshint was not available.
+1 💚 @author 0m 0s The patch does not contain any @author tags.
_ trunk Compile Tests _
+1 💚 mvninstall 50m 10s trunk passed
+1 💚 shadedclient 85m 29s branch has no errors when building and testing our client artifacts.
_ Patch Compile Tests _
+1 💚 mvninstall 2m 5s the patch passed
+1 💚 blanks 0m 0s The patch has no blanks issues.
+1 💚 shadedclient 34m 5s patch has no errors when building and testing our client artifacts.
_ Other Tests _
+1 💚 asflicense 0m 53s The patch does not generate ASF License warnings.
144m 57s
Subsystem Report/Notes
Docker ClientAPI=1.56 ServerAPI=1.56 base: https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8766/2/artifact/out/Dockerfile
GITHUB PR #8766
Optional Tests dupname asflicense shadedclient codespell detsecrets jshint
uname Linux 8d649b4f2cab 5.15.0-186-generic #196-Ubuntu SMP Sat Jun 20 16:09:34 UTC 2026 x86_64 x86_64 x86_64 GNU/Linux
Build tool maven
Personality dev-support/bin/hadoop.sh
git revision trunk / 260d4e4
Max. process+thread count 575 (vs. ulimit of 10000)
modules C: hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui U: hadoop-yarn-project/hadoop-yarn/hadoop-yarn-ui
Console output https://ci-hadoop.apache.org/job/hadoop-multibranch/job/PR-8766/2/console
versions git=2.43.0 maven=3.9.15
Powered by Apache Yetus 0.14.1 https://yetus.apache.org

This message was automatically generated.

This branch has not been deployed

No deployments
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.

3 participants