Skip to content

Use storageCapabilities config as authoritative signal for SPM availability#3587

Merged
yurishkuro merged 8 commits intojaegertracing:mainfrom
yurishkuro:fix-monitor
Mar 10, 2026
Merged

Use storageCapabilities config as authoritative signal for SPM availability#3587
yurishkuro merged 8 commits intojaegertracing:mainfrom
yurishkuro:fix-monitor

Conversation

@yurishkuro
Copy link
Copy Markdown
Member

@yurishkuro yurishkuro commented Mar 10, 2026

Problem

The Service Performance Monitoring (SPM) Monitor page had several interconnected issues:

  1. Page crash on loadMonitorATMEmptyState crashed with TypeError: Cannot read properties of undefined (reading 'mainTitle') because the embedded config provided a monitor key that completely overwrote defaultConfig.monitor (including emptyState), due to a shallow merge.

  2. Unnecessary API calls — When metrics storage is not configured, the Monitor page fired API requests on load and only showed the empty state after receiving a 501 Not Implemented response. The intent was always to use storageCapabilities.metricsStorage from config as the authoritative signal, not a runtime API probe.

  3. storageCapabilities in jaeger-ui.config.json was silently ignored — The Vite dev-server plugin injected the JSON config into getJaegerUiConfig() but never into getJaegerStorageCapabilities(). Since index.html always defines getJaegerStorageCapabilities (hardcoded to return metricsStorage: false), the config file value was always overwritten.

Changes

default-config.ts — add monitor to mergeFields

mergeFields controls which top-level config keys are shallow-merged (rather than wholesale replaced) when the embedded config is combined with defaults. monitor was missing, so a backend-provided monitor: { menuEnabled: true } silently dropped emptyState, causing the crash.

vite.config.mts — inject storageCapabilities into getJaegerStorageCapabilities()

The Go query-service injects storageCapabilities into index.html via a separate search-replace on JAEGER_STORAGE_CAPABILITIES. The Vite plugin was only replicating the JAEGER_CONFIG injection. It now also replaces JAEGER_STORAGE_CAPABILITIES when storageCapabilities is present in jaeger-ui.config.json, making local dev fully equivalent to production.

get-config.ts — add comments clarifying the two-source design

storageCapabilities is intentionally loaded from a separate source (getJaegerStorageCapabilities) and always overrides anything in the main UI config, keeping the backend authoritative. This was not documented; comments now explain the three-priority merge model.

Monitor/ServicesView/index.tsx — gate on config, not runtime API response

  • Reads storageCapabilities.metricsStorage from config at render time.
  • Returns <MonitorATMEmptyState /> immediately when it is false, with no API calls made.
  • Removed the isATMActivated !== false guard from fetchMetrics (it was checking a Redux flag that was only set after a 501 response).

reducers/metrics.ts / types/metrics.ts — remove isATMActivated

isATMActivated was a runtime approximation of storageCapabilities.metricsStorage: it started as null, then flipped to false upon a 501 response. Now that the config is the authoritative source (known at page load), this redundant state and the 501-detection logic have been removed entirely.

Tests

  • get-config.test.js — new storageCapabilities describe block covering: backend injection, fallback to defaults, and precedence of getJaegerStorageCapabilities over the UI config.
  • reducers/metrics.test.js — removed isATMActivated assertions and the dedicated "501 Not Implemented" test case.
  • Monitor/ServicesView/index.test.js — updated getConfigValue mock to return true for storageCapabilities.metricsStorage; empty-state test now toggles the config mock instead of setting isATMActivated: false.
  • Monitor/index.test.js — added getConfigValue mock; updated empty-state test accordingly.

How the config loading works (for reference)

jaeger-ui.config.json
        │
        ▼
[dev] Vite plugin injects into index.html:
  • JAEGER_CONFIG            → consumed by getJaegerUiConfig()
  • JAEGER_STORAGE_CAPABILITIES → consumed by getJaegerStorageCapabilities()   ← was missing

[prod] Go query-service does the same search-replace

        │
        ▼
get-config.ts  assembles the final Config from three sources (lowest → highest priority):
  1. defaultConfig                 (compile-time defaults)
  2. getJaegerUiConfig()           (full UI config)
  3. getJaegerStorageCapabilities() (always wins for storageCapabilities)

AI Usage in this PR (choose one)

See AI Usage Policy.

  • None: No AI tools were used in creating this PR
  • Light: AI provided minor assistance (formatting, simple suggestions)
  • Moderate: AI helped with code generation or debugging specific parts
  • Heavy: AI generated most or all of the code changes

Signed-off-by: Yuri Shkuro <github@ysh.us>
@yurishkuro yurishkuro requested a review from a team as a code owner March 10, 2026 17:59
Copilot AI review requested due to automatic review settings March 10, 2026 17:59
@yurishkuro yurishkuro added the changelog:refactoring Internal, non-functional code improvements label Mar 10, 2026
@yurishkuro yurishkuro changed the title fix Fix display of Monitor menu and disable loading if storage is turned off Mar 10, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates Jaeger UI Monitor configuration merging and gating so the Monitor menu is controlled by config while the Monitor page behavior depends on metrics-storage capability.

Changes:

  • Add monitor to the list of config fields that should be deep-merged with user config.
  • Gate metrics fetching and the Monitor empty state on storageCapabilities.metricsStorage.
  • Change TopNav visibility logic for the Monitor link to use monitor.menuEnabled (not storage capability).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
packages/jaeger-ui/src/constants/default-config.ts Ensures monitor config merges with user config instead of being overwritten.
packages/jaeger-ui/src/components/Monitor/ServicesView/index.tsx Prevents metrics fetch/render when metrics storage capability is disabled.
packages/jaeger-ui/src/components/App/TopNav.tsx Keeps Monitor nav item controlled by monitor.menuEnabled instead of storage capability.

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

You can also share your feedback on Copilot code review. Take the survey.

Signed-off-by: Yuri Shkuro <github@ysh.us>
Signed-off-by: Yuri Shkuro <github@ysh.us>
Copilot AI review requested due to automatic review settings March 10, 2026 19:49
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

packages/jaeger-ui/src/utils/config/get-config.ts:28

  • getJaegerStorageCapabilities() may return a partial object (or in tests it’s stubbed to {}), but getCapabilities() currently returns it verbatim as long as it’s non-nullish. That can drop default keys like metricsStorage/archiveStorage and cause getConfigValue('storageCapabilities.*') to be undefined. Consider normalizing by merging into defaults (and guarding for non-object returns), e.g. return { ...defaultConfig.storageCapabilities, ...(capabilities ?? {}) } so missing flags default to false.
function getCapabilities() {
  const getter = window.getJaegerStorageCapabilities;
  const capabilities = typeof getter === 'function' ? getter() : null;
  return capabilities ?? defaultConfig.storageCapabilities;
}

packages/jaeger-ui/src/components/App/TopNav.tsx:76

  • The Monitor nav link gating changed from storageCapabilities.metricsStorage to monitor.menuEnabled, but the existing TopNav.test.js mock still returns true for storageCapabilities.metricsStorage (and not for monitor.menuEnabled). This will cause the test that asserts the Monitor link renders to fail unless the mock is updated to include monitor.menuEnabled (or the test is adjusted to match the new condition).
// We keep the menu item for Monitor in the top nav visible if config says so.
// If the storage capability indicates that metrics storage is not supported,
// the page will show a landing page with instructions on how to set up SPM.
if (getConfigValue('monitor.menuEnabled')) {
  NAV_LINKS.push({
    to: monitorATMUrl.getUrl(),
    matches: monitorATMUrl.matches,
    text: 'Monitor',
  });
}

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

You can also share your feedback on Copilot code review. Take the survey.

@yurishkuro yurishkuro changed the title Fix display of Monitor menu and disable loading if storage is turned off fix(monitor): use storageCapabilities config as authoritative signal for SPM availability Mar 10, 2026
yurishkuro and others added 2 commits March 10, 2026 16:09
Signed-off-by: Yuri Shkuro <github@ysh.us>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Yuri Shkuro <yurishkuro@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 10, 2026 20:10
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.


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

You can also share your feedback on Copilot code review. Take the survey.

Signed-off-by: Yuri Shkuro <github@ysh.us>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated 4 comments.

Comments suppressed due to low confidence (1)

packages/jaeger-ui/src/utils/config/get-config.ts:28

  • getCapabilities() returns the injected object as-is, so a partial injection (e.g. dev jaeger-ui.config.json with { "metricsStorage": true }) will drop default keys like archiveStorage and can yield undefined values. To keep defaults stable, consider merging with defaultConfig.storageCapabilities when the getter returns an object (e.g. default + injected), rather than replacing the whole object.
// getCapabilities reads storage capabilities injected by the query-service backend
// via window.getJaegerStorageCapabilities (search-replaced into index.html).
// In development, the Vite plugin replicates this injection from jaeger-ui.config.json.
// Falls back to the default config when the function is not present.
function getCapabilities() {
  const getter = window.getJaegerStorageCapabilities;
  const capabilities = typeof getter === 'function' ? getter() : null;
  return capabilities ?? defaultConfig.storageCapabilities;
}

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

You can also share your feedback on Copilot code review. Take the survey.

Signed-off-by: Yuri Shkuro <github@ysh.us>
Signed-off-by: Yuri Shkuro <github@ysh.us>
Copilot AI review requested due to automatic review settings March 10, 2026 21:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 14 out of 14 changed files in this pull request and generated no new comments.


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

You can also share your feedback on Copilot code review. Take the survey.

@yurishkuro yurishkuro merged commit 372ba8e into jaegertracing:main Mar 10, 2026
15 checks passed
@yurishkuro yurishkuro deleted the fix-monitor branch March 10, 2026 21:39
@yurishkuro yurishkuro changed the title fix(monitor): use storageCapabilities config as authoritative signal for SPM availability Use storageCapabilities config as authoritative signal for SPM availability Mar 10, 2026
@codecov
Copy link
Copy Markdown

codecov bot commented Mar 11, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.14%. Comparing base (0127635) to head (a930902).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3587      +/-   ##
==========================================
- Coverage   89.15%   89.14%   -0.02%     
==========================================
  Files         304      304              
  Lines        9719     9716       -3     
  Branches     2586     2588       +2     
==========================================
- Hits         8665     8661       -4     
- Misses       1051     1052       +1     
  Partials        3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Labels

changelog:refactoring Internal, non-functional code improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[refactor]: Simplify ATM configuration

2 participants