Skip to content

[main] NETOBSERV-2608: TLS, metrics and charts#1530

Merged
jotak merged 4 commits into
netobserv:mainfrom
openshift-cherrypick-robot:cherry-pick-1478-to-main
May 18, 2026
Merged

[main] NETOBSERV-2608: TLS, metrics and charts#1530
jotak merged 4 commits into
netobserv:mainfrom
openshift-cherrypick-robot:cherry-pick-1478-to-main

Conversation

@openshift-cherrypick-robot
Copy link
Copy Markdown
Contributor

@openshift-cherrypick-robot openshift-cherrypick-robot commented May 15, 2026

This is an automated cherry-pick of #1478

/assign Amoghrd

Summary by CodeRabbit

  • New Features

    • TLS tracking and metrics added: TLS usage (global, by version, group, cipher) and TLS flow rates; new TLS overview panels and filters/columns in flow views.
    • Export and flow queries now use a structured filter format for more robust filtering.
  • Improvements

    • Metrics visuals: responsive legend behavior and donut title/subtitle enhancements.
  • Bug Fixes

    • Removed extraneous console logging in error/status components.
  • Tests

    • Expanded test coverage for TLS metrics and structured queries.

Review Change Stack

jotak added 3 commits May 15, 2026 16:15
- Add 4 new overview panels related to TLS:
  - TLS global usage (traffic with/without TLS)
  - breakdown by version
  - breakdown by group
  - breakdown by cipher suite
- Add new column filter "tls" in manage columns panel
- Fix issues in logQL when querying on arrays: handle looking for field
  presence/absence
- Refactoring: introduce StructuredFlowQuery, an intermediate data structure used before the flow query is entirely serialized for HTTP.
  It allows to keep and manipulate structured filters for complex queries.
- Simplify TLS usage donut to show just TLS vs Other (it's
      easier to achieve with prom metrics)
- Show % of TLS rather than total Pps
- Fix a couple of JS warnings
Donut legend will not be dispayed anymore when rendered on small
screens. It's still available as a tooltip though.
@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented May 15, 2026

@openshift-cherrypick-robot: Ignoring requests to cherry-pick non-bug issues: NETOBSERV-2608

Details

In response to this:

This is an automated cherry-pick of #1478

/assign Amoghrd

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 15, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign oliviercazade for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci
Copy link
Copy Markdown

openshift-ci Bot commented May 15, 2026

Hi @openshift-cherrypick-robot. Thanks for your PR.

I'm waiting for a netobserv member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Tip

We noticed you've done this a few times! Consider joining the org to skip this step and gain /lgtm and other bot rights. We recommend asking approvers on your previous PRs to sponsor you.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 33056cdf-cc6b-4293-833b-ce7c8d6f4881

📥 Commits

Reviewing files that changed from the base of the PR and between b929627 and 21a1aa3.

📒 Files selected for processing (3)
  • web/locales/en/plugin__netobserv-plugin.json
  • web/src/components/tabs/netflow-overview/netflow-overview.tsx
  • web/src/utils/back-and-forth.ts
✅ Files skipped from review due to trivial changes (1)
  • web/locales/en/plugin__netobserv-plugin.json
🚧 Files skipped from review as they are similar to previous changes (2)
  • web/src/utils/back-and-forth.ts
  • web/src/components/tabs/netflow-overview/netflow-overview.tsx

📝 Walkthrough

Walkthrough

Adds TLS tracking and TLS-specific overview panels, migrates flow queries from string-encoded filters to a typed StructuredFlowQuery, refactors back-and-forth fetch and LogQL builder/filter logic, and wires TLS metrics and UI rendering throughout frontend and backend pieces.

Changes

TLS Tracking and Structured Query Model

Layer / File(s) Summary
Data types and constants
pkg/utils/constants/constants.go, config/sample-config.yaml, web/src/model/config.ts, web/src/model/filters.ts, web/src/model/flow-query.ts
Adds MetricTypeTLSFlows, renames TLSCurve→TLSGroup in sample config, introduces StructuredFlowQuery and structuredToRawQuery, extends filter/id unions and Feature with tlsTracking.
Prometheus metric classification
pkg/prometheus/inventory.go, pkg/prometheus/inventory_test.go
Classifies metrics with _tls_flows/_flows into TLSFlows/Flows ValueField and updates tests to assert TLS metric discovery.
Backend query builders and filters
pkg/loki/flow_query.go, pkg/loki/topology_query.go, pkg/model/filters/logql.go
Refactors addLineFilters to switch-based dispatch, adds appendTLSFilter, exposes TopologyInput.GetActualDataField(), and changes ArrayLineFilter to return (LineFilter,bool) for empty-array match handling.
API types and routes
web/src/api/loki.ts, web/src/api/routes.ts
Converts exported result classes to interfaces, adds TLS fields to NetflowMetrics, routes accept StructuredFlowQuery and use structuredToRawQuery internally.
Filter encoding & back-and-forth
web/src/utils/back-and-forth.ts, web/src/utils/__tests__/back-and-forth.spec.ts
Refactors filter encoding to map/join, rewrites getFetchFunctions/getFlowsBNF/getMetricsBNF to accept StructuredFlowQuery and derive match/bidirectional behavior; updates tests to use structured filters.
Capabilities and panels
web/src/utils/netflow-capabilities-hook.ts, web/src/utils/overview-panels.ts, web/src/model/netflow-context.ts
Adds isTLSTracking capability, tlsIdMatcher, new TLS overview panel IDs, includes TLS panels in defaults, and updates panel metadata and custom metric parsing.
Metrics components & utils
web/src/components/metrics/metrics-donut.tsx, web/src/components/metrics/metrics-graph.tsx, web/src/utils/metrics.ts
Makes MetricsDonut more flexible (optional totalMetric, internalText/internalSubtitle, responsive legend), fixes legend naming/padding; improves MetricsGraph tooltip fallback; adds Flows formatting.
NetflowOverview TLS fetching & rendering
web/src/components/tabs/netflow-overview/netflow-overview.tsx, web/src/components/tabs/netflow-overview/__tests__/netflow-overview.spec.tsx
Adds filterDefinitions prop; refactors queries to StructuredFlowQuery; fetches TLS total and breakdowns; computes pctTLS and adds TLS panel rendering for global/version/group/cipher with donut/graph variants.
Integration wiring & tests
web/src/components/drawer/netflow-traffic-drawer.tsx, web/src/components/modals/export-modal.tsx, web/src/components/modals/columns-modal.tsx, web/src/components/slider/scope-slider.tsx, web/src/components/modals/__tests__/export-modal.spec.tsx
Passes filterDefinitions into NetflowOverview; export modal uses StructuredFlowQuery; columns modal adds tls key; scope slider adds titleId; tests updated to structuredFilters.
Locales & cleanup
web/locales/en/plugin__netobserv-plugin.json, web/src/components/messages/empty.tsx, web/src/components/messages/error.tsx
Adds translations for flows/fps and TLS metric labels; removes debug console logging from status handlers.

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs:

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is minimal—just a single-line automation note and an assignment. It does not follow the provided template, which requires a proper description, dependency listing, and checklist completion for configuration, testing, and QE requirements. Expand the description to match the template: add details about the TLS feature, list any dependencies, and complete all checklist items (configuration, unit tests, and QE validation).
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly references NETOBSERV-2608 and describes the main change: adding TLS metrics and charts support with a Patternfly 5 update (pf5). It directly reflects the substantial changes across configuration, backend queries, and frontend components.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Linked repositories: Your configuration references 2 linked repositories, but your current plan allows 1. Analyzed netobserv/netobserv-operator, skipped netobserv/flowlogs-pipeline.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Amoghrd
Copy link
Copy Markdown
Member

Amoghrd commented May 15, 2026

/ok-to-test

@openshift-ci openshift-ci Bot added ok-to-test To set manually when a PR is safe to test. Triggers image build on PR. and removed needs-ok-to-test labels May 15, 2026
@github-actions
Copy link
Copy Markdown

New image:

quay.io/netobserv/network-observability-console-plugin:3f2900e

It will expire in two weeks.

To deploy this build, run from the operator repo, assuming the operator is running:

USER=netobserv VERSION=3f2900e make set-plugin-image

@openshift-ci-robot
Copy link
Copy Markdown
Collaborator

openshift-ci-robot commented May 15, 2026

@openshift-cherrypick-robot: This pull request references NETOBSERV-2608 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

This is an automated cherry-pick of #1478

/assign Amoghrd

Summary by CodeRabbit

Release Notes

  • New Features

  • Added TLS tracking capability with new metrics: TLS usage (global, by version, by group, by cipher suite), and TLS flows rate

  • Added TLS-related filters and columns to flow data visualization

  • Improved metrics visualization with responsive legend design

  • Bug Fixes

  • Removed unnecessary console logging in error components

  • Tests

  • Enhanced test coverage for TLS metrics and structured query handling

Review Change Stack

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/src/utils/back-and-forth.ts (1)

56-82: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Type mismatch: mixing StructuredFlowQuery with FlowQuery contract.

Lines 68, 69, and 71-75 spread initialQuery: StructuredFlowQuery and add filters strings. getFlowMetrics expects FlowQuery. Convert first:

 const getMetricsBNF = (
   initialQuery: StructuredFlowQuery,
   range: number | TimeRange,
   orig: Filter[],
   swapped: Filter[]
 ): Promise<FlowMetricsResult> => {
-  // When bnf is on, this replaces the usual getMetrics with a function with same arguments that runs 3 queries and merge their results
-  // in order to get the ORIGINAL + SWAPPED - OVERLAP
-  // OVERLAP being ORIGINAL AND SWAPPED.
-  // E.g: if ORIGINAL is "SrcNs=foo", SWAPPED is "DstNs=foo" and OVERLAP is "SrcNs=foo AND DstNs=foo"
   const matchAny = initialQuery.structuredFilters.match === 'any';
   const overlapFilters = matchAny ? undefined : [...orig, ...swapped];
-  const promOrig = getFlowMetrics({ ...initialQuery, filters: filtersToString(orig, matchAny) }, range);
-  const promSwapped = getFlowMetrics({ ...initialQuery, filters: filtersToString(swapped, matchAny) }, range);
+  const rawQuery = structuredToRawQuery(initialQuery);
+  const promOrig = getFlowMetrics({ ...rawQuery, filters: filtersToString(orig, matchAny) }, range);
+  const promSwapped = getFlowMetrics({ ...rawQuery, filters: filtersToString(swapped, matchAny) }, range);
   const promOverlap = overlapFilters
     ? getFlowMetrics(
         {
-          ...initialQuery,
+          ...rawQuery,
           filters: filtersToString(overlapFilters, matchAny)
         },
         range
       )
     : Promise.resolve(undefined);
   return Promise.all([promOrig, promSwapped, promOverlap]).then(([rsOrig, rsSwapped, rsOverlap]) =>
     mergeMetricsBNF(range, rsOrig, rsSwapped, rsOverlap)
   );
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/utils/back-and-forth.ts` around lines 56 - 82, getMetricsBNF is
passing initialQuery (a StructuredFlowQuery) directly into getFlowMetrics calls
while overwriting filters with a string; getFlowMetrics expects a FlowQuery—fix
by converting initialQuery into a FlowQuery before spreading into the calls
(e.g., build a FlowQuery object from initialQuery fields, or call a converter
function) and then pass that converted FlowQuery plus the filters produced by
filtersToString for orig, swapped, and overlap; update the three places where
getFlowMetrics is invoked (promOrig, promSwapped, promOverlap) to use the
converted FlowQuery instead of initialQuery so the types align (referencing
getMetricsBNF, initialQuery, getFlowMetrics, StructuredFlowQuery, FlowQuery,
filtersToString).
web/src/components/tabs/netflow-overview/netflow-overview.tsx (1)

80-97: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove the unused filterDefinitions prop from NetflowOverviewProps.

The prop is declared at line 87 but never referenced anywhere in the component. Remove it from the interface unless it's needed for a downstream consumer.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/tabs/netflow-overview/netflow-overview.tsx` around lines
80 - 97, NetflowOverviewProps declares an unused prop filterDefinitions; remove
the filterDefinitions property from the NetflowOverviewProps interface (and any
associated optional typing) to eliminate the dead prop declaration — update the
NetflowOverviewProps interface definition (the type that includes limit, panels,
recordType, metrics, etc.) by deleting the filterDefinitions: FilterDefinition[]
entry and adjust any imports or downstream consumers only if they actually
reference filterDefinitions elsewhere.
🧹 Nitpick comments (1)
web/src/model/flow-query.ts (1)

47-55: 💤 Low value

Remove redundant undefined assignment.

Line 51 sets structuredFilters: undefined but then line 53 deletes it. The undefined assignment is redundant.

♻️ Simplify
 export const structuredToRawQuery = (q: StructuredFlowQuery): FlowQuery => {
   const raw = {
     ...q,
-    filters: filtersToString(q.structuredFilters.list, q.structuredFilters.match === 'any'),
-    structuredFilters: undefined
+    filters: filtersToString(q.structuredFilters.list, q.structuredFilters.match === 'any')
   };
   delete raw.structuredFilters;
   return raw;
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/model/flow-query.ts` around lines 47 - 55, The function
structuredToRawQuery assigns structuredFilters: undefined in the constructed raw
object and then immediately deletes it, which is redundant; update
structuredToRawQuery to omit structuredFilters when building raw (remove the
structuredFilters: undefined property and the subsequent delete) so the function
simply spreads q and sets filters via filtersToString using
q.structuredFilters.list and q.structuredFilters.match, leaving
structuredFilters out of the returned FlowQuery.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/model/filters/logql.go`:
- Around line 276-289: The code currently treats the empty sentinel only when
it's the sole value, which causes mixed inputs (e.g. values contains "" and "x")
to be converted into an over-broad regex; change the logic to detect any empty
sentinel in values and handle it separately: when values contains "" and other
values, remove the empty sentinel from the slice that becomes LineFilter.values
(avoid appending a lineMatch with typeRegexArrayContains for the empty), and
instead combine the missing-key condition using NotContainsKeyLineFilter(key)
for the positive case (or the appropriate negated equivalent when not==true) so
the final behavior is "key is missing OR array contains any of the remaining
values"; update the code around LineFilter, the loop that appends lineMatch
entries, and the return path that currently uses RegexMatchLineFilter and
NotContainsKeyLineFilter to implement this combined semantics.

In `@web/src/components/metrics/metrics-donut.tsx`:
- Around line 150-151: The radius and innerRadius props use showLegend but the
component determines visibility with showLegendResponsive, causing wrong sizing
when the legend is hidden responsively; update the radius and innerRadius
calculations to use showLegendResponsive (instead of showLegend) so that
radius={showLegendResponsive ? dimensions.height / 3 : undefined} and
innerRadius={showLegendResponsive ? dimensions.height / 4 : undefined}, ensuring
the donut sizing matches the actual legend visibility (refer to the
radius/innerRadius props and the showLegendResponsive variable in
metrics-donut.tsx).

In `@web/src/components/tabs/netflow-overview/netflow-overview.tsx`:
- Around line 1145-1204: The tls_usage_global branch wrongly treats pctTLS==0 as
falsy and hardcodes 'n/a', and it force-asserts total with total! even though
total may be undefined; update the pctTLS display to check for a defined numeric
value (e.g. typeof pctTLS === 'number' or pctTLS !== undefined) and pass its
formatted value or t('n/a') into MetricsDonut (internalText), and change the
MetricsGraph rendering to only pass the total when it is actually defined (avoid
total!): either render emptyGraph or the donut when total is missing, or build
the metrics array conditionally (e.g. metrics and total ? [metrics, total] :
[metrics]) so you never assert a non-null total; reference symbols: pctTLS,
MetricsDonut, MetricsGraph, props.metrics.totalFlowRate, and panelError.
- Around line 410-435: The metric legend labels are hardcoded in the Result
handlers for getFlowGenericMetrics — replace the literal strings 'Total flows'
and 'TLS flows' with translated strings using the react-i18next t(...) function
(i.e. set totalFlowRate to use name: t('Total flows') and tlsFlowRate to use
name: t('TLS flows')), ensure the component has access to t (via useTranslation)
and add t to the fetch useCallback dependency array so setMetrics/currentMetrics
updates use the localized labels correctly.

In `@web/src/utils/back-and-forth.ts`:
- Around line 49-54: getFlowsBNF is currently spreading a StructuredFlowQuery
(initialQuery) and injecting a string filters field, creating a type hybrid that
violates the FlowQuery contract expected by getFlowRecords. Fix by building a
proper FlowQuery object: extract the FlowQuery-compatible properties from
initialQuery (e.g., time range, pagination, showDuplicates, any other FlowQuery
fields) and set filters to the combined string from filtersToString, instead of
spreading initialQuery directly; then call getFlowRecords with that constructed
FlowQuery. Reference: getFlowsBNF, StructuredFlowQuery, FlowQuery,
getFlowRecords, filtersToString.

---

Outside diff comments:
In `@web/src/components/tabs/netflow-overview/netflow-overview.tsx`:
- Around line 80-97: NetflowOverviewProps declares an unused prop
filterDefinitions; remove the filterDefinitions property from the
NetflowOverviewProps interface (and any associated optional typing) to eliminate
the dead prop declaration — update the NetflowOverviewProps interface definition
(the type that includes limit, panels, recordType, metrics, etc.) by deleting
the filterDefinitions: FilterDefinition[] entry and adjust any imports or
downstream consumers only if they actually reference filterDefinitions
elsewhere.

In `@web/src/utils/back-and-forth.ts`:
- Around line 56-82: getMetricsBNF is passing initialQuery (a
StructuredFlowQuery) directly into getFlowMetrics calls while overwriting
filters with a string; getFlowMetrics expects a FlowQuery—fix by converting
initialQuery into a FlowQuery before spreading into the calls (e.g., build a
FlowQuery object from initialQuery fields, or call a converter function) and
then pass that converted FlowQuery plus the filters produced by filtersToString
for orig, swapped, and overlap; update the three places where getFlowMetrics is
invoked (promOrig, promSwapped, promOverlap) to use the converted FlowQuery
instead of initialQuery so the types align (referencing getMetricsBNF,
initialQuery, getFlowMetrics, StructuredFlowQuery, FlowQuery, filtersToString).

---

Nitpick comments:
In `@web/src/model/flow-query.ts`:
- Around line 47-55: The function structuredToRawQuery assigns
structuredFilters: undefined in the constructed raw object and then immediately
deletes it, which is redundant; update structuredToRawQuery to omit
structuredFilters when building raw (remove the structuredFilters: undefined
property and the subsequent delete) so the function simply spreads q and sets
filters via filtersToString using q.structuredFilters.list and
q.structuredFilters.match, leaving structuredFilters out of the returned
FlowQuery.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 85139e97-7e7f-4368-a863-107cabcbce3e

📥 Commits

Reviewing files that changed from the base of the PR and between ce8ffab and b929627.

⛔ Files ignored due to path filters (1)
  • web/src/globals.d.ts is excluded by !**/*.d.ts
📒 Files selected for processing (30)
  • config/sample-config.yaml
  • pkg/loki/flow_query.go
  • pkg/loki/topology_query.go
  • pkg/model/filters/logql.go
  • pkg/prometheus/inventory.go
  • pkg/prometheus/inventory_test.go
  • pkg/utils/constants/constants.go
  • web/locales/en/plugin__netobserv-plugin.json
  • web/src/api/loki.ts
  • web/src/api/routes.ts
  • web/src/components/drawer/netflow-traffic-drawer.tsx
  • web/src/components/messages/empty.tsx
  • web/src/components/messages/error.tsx
  • web/src/components/metrics/metrics-donut.tsx
  • web/src/components/metrics/metrics-graph.tsx
  • web/src/components/modals/__tests__/export-modal.spec.tsx
  • web/src/components/modals/columns-modal.tsx
  • web/src/components/modals/export-modal.tsx
  • web/src/components/slider/scope-slider.tsx
  • web/src/components/tabs/netflow-overview/__tests__/netflow-overview.spec.tsx
  • web/src/components/tabs/netflow-overview/netflow-overview.tsx
  • web/src/model/config.ts
  • web/src/model/filters.ts
  • web/src/model/flow-query.ts
  • web/src/model/netflow-context.ts
  • web/src/utils/__tests__/back-and-forth.spec.ts
  • web/src/utils/back-and-forth.ts
  • web/src/utils/metrics.ts
  • web/src/utils/netflow-capabilities-hook.ts
  • web/src/utils/overview-panels.ts
💤 Files with no reviewable changes (2)
  • web/src/components/messages/error.tsx
  • web/src/components/messages/empty.tsx

Comment on lines +276 to +289
if len(values) == 1 && (values[0] == "" || values[0] == `""`) {
// Special case, filter for n/a; note that logQL json doesn't support arrays, so we need a different solution
if not {
// key!=n/a
return RegexMatchLineFilter(key, true, ".*"), false
}
// key=n/a
return NotContainsKeyLineFilter(key), false
}
lf := LineFilter{key: key, not: not}
for _, value := range values {
lf.values = append(lf.values, lineMatch{valueType: typeRegexArrayContains, value: value})
}
return lf
return lf, false
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle empty sentinel even when mixed with other array values.

Line 276 only treats empty as special when it is the sole value. With mixed inputs (e.g. ["", "x"]), the empty value becomes a regex that matches any array, which over-broadens results.

Proposed fix
 func ArrayLineFilter(key string, values []string, not bool) (LineFilter, bool) {
-	if len(values) == 1 && (values[0] == "" || values[0] == `""`) {
+	hasEmptyMatch := false
+	nonEmpty := make([]string, 0, len(values))
+	for _, v := range values {
+		if v == "" || v == `""` {
+			hasEmptyMatch = true
+			continue
+		}
+		nonEmpty = append(nonEmpty, v)
+	}
+
+	if hasEmptyMatch && len(nonEmpty) == 0 {
 		// Special case, filter for n/a; note that logQL json doesn't support arrays, so we need a different solution
 		if not {
 			// key!=n/a
 			return RegexMatchLineFilter(key, true, ".*"), false
 		}
 		// key=n/a
 		return NotContainsKeyLineFilter(key), false
 	}
-	lf := LineFilter{key: key, not: not}
-	for _, value := range values {
+	lf := LineFilter{key: key, not: not}
+	for _, value := range nonEmpty {
 		lf.values = append(lf.values, lineMatch{valueType: typeRegexArrayContains, value: value})
 	}
-	return lf, false
+	return lf, hasEmptyMatch
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/model/filters/logql.go` around lines 276 - 289, The code currently treats
the empty sentinel only when it's the sole value, which causes mixed inputs
(e.g. values contains "" and "x") to be converted into an over-broad regex;
change the logic to detect any empty sentinel in values and handle it
separately: when values contains "" and other values, remove the empty sentinel
from the slice that becomes LineFilter.values (avoid appending a lineMatch with
typeRegexArrayContains for the empty), and instead combine the missing-key
condition using NotContainsKeyLineFilter(key) for the positive case (or the
appropriate negated equivalent when not==true) so the final behavior is "key is
missing OR array contains any of the remaining values"; update the code around
LineFilter, the loop that appends lineMatch entries, and the return path that
currently uses RegexMatchLineFilter and NotContainsKeyLineFilter to implement
this combined semantics.

Comment on lines 150 to 151
radius={showLegend ? dimensions.height / 3 : undefined}
innerRadius={showLegend ? dimensions.height / 4 : undefined}
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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Inconsistent radius calculation when legend is hidden responsively.

Lines 150-151 use showLegend to calculate radius, but legend rendering uses showLegendResponsive (lines 145, 149, 161). When showLegend={true} but screen width < 550px, the legend is hidden yet the donut remains small, wasting space.

Use showLegendResponsive for consistent sizing:

-        radius={showLegend ? dimensions.height / 3 : undefined}
-        innerRadius={showLegend ? dimensions.height / 4 : undefined}
+        radius={showLegendResponsive ? dimensions.height / 3 : undefined}
+        innerRadius={showLegendResponsive ? dimensions.height / 4 : undefined}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
radius={showLegend ? dimensions.height / 3 : undefined}
innerRadius={showLegend ? dimensions.height / 4 : undefined}
radius={showLegendResponsive ? dimensions.height / 3 : undefined}
innerRadius={showLegendResponsive ? dimensions.height / 4 : undefined}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/metrics/metrics-donut.tsx` around lines 150 - 151, The
radius and innerRadius props use showLegend but the component determines
visibility with showLegendResponsive, causing wrong sizing when the legend is
hidden responsively; update the radius and innerRadius calculations to use
showLegendResponsive (instead of showLegend) so that
radius={showLegendResponsive ? dimensions.height / 3 : undefined} and
innerRadius={showLegendResponsive ? dimensions.height / 4 : undefined}, ensuring
the donut sizing matches the actual legend visibility (refer to the
radius/innerRadius props and the showLegendResponsive variable in
metrics-donut.tsx).

Comment on lines +410 to +435
promises.push(
Result.fromPromise(getFlowGenericMetrics(fqTotal, range)).then(res => {
const totalFlowRate = res
.map(success => (success.metrics.length > 0 ? { ...success.metrics[0], name: 'Total flows' } : undefined))
.mapError(err => getStructuredHTTPError(err, `Total flow rate`));
currentMetrics = { ...currentMetrics, totalFlowRate };
setMetrics(currentMetrics);
return res.map(r => r.stats).or(emptyStats);
})
);
const fqTLS: StructuredFlowQuery = {
...baseQuery,
function: 'rate',
type: 'TlsFlows',
aggregateBy: 'app'
};
promises.push(
Result.fromPromise(getFlowGenericMetrics(fqTLS, range)).then(res => {
const tlsFlowRate = res
.map(success => (success.metrics.length > 0 ? { ...success.metrics[0], name: 'TLS flows' } : undefined))
.mapError(err => getStructuredHTTPError(err, `TLS flow rate`));
currentMetrics = { ...currentMetrics, tlsFlowRate };
setMetrics(currentMetrics);
return res.map(r => r.stats).or(emptyStats);
})
);
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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Hardcoded English strings in metric names.

'Total flows' (line 413) and 'TLS flows' (line 429) are user-facing — they surface as legend labels in the donut/graph. Wrap with t(...).

🌐 Proposed fix
-            .map(success => (success.metrics.length > 0 ? { ...success.metrics[0], name: 'Total flows' } : undefined))
+            .map(success =>
+              success.metrics.length > 0 ? { ...success.metrics[0], name: t('Total flows') } : undefined
+            )
@@
-            .map(success => (success.metrics.length > 0 ? { ...success.metrics[0], name: 'TLS flows' } : undefined))
+            .map(success =>
+              success.metrics.length > 0 ? { ...success.metrics[0], name: t('TLS flows') } : undefined
+            )

Note: t must then be included in the fetch useCallback deps.

As per coding guidelines: "All user-facing strings must use react-i18next for internationalization".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
promises.push(
Result.fromPromise(getFlowGenericMetrics(fqTotal, range)).then(res => {
const totalFlowRate = res
.map(success => (success.metrics.length > 0 ? { ...success.metrics[0], name: 'Total flows' } : undefined))
.mapError(err => getStructuredHTTPError(err, `Total flow rate`));
currentMetrics = { ...currentMetrics, totalFlowRate };
setMetrics(currentMetrics);
return res.map(r => r.stats).or(emptyStats);
})
);
const fqTLS: StructuredFlowQuery = {
...baseQuery,
function: 'rate',
type: 'TlsFlows',
aggregateBy: 'app'
};
promises.push(
Result.fromPromise(getFlowGenericMetrics(fqTLS, range)).then(res => {
const tlsFlowRate = res
.map(success => (success.metrics.length > 0 ? { ...success.metrics[0], name: 'TLS flows' } : undefined))
.mapError(err => getStructuredHTTPError(err, `TLS flow rate`));
currentMetrics = { ...currentMetrics, tlsFlowRate };
setMetrics(currentMetrics);
return res.map(r => r.stats).or(emptyStats);
})
);
promises.push(
Result.fromPromise(getFlowGenericMetrics(fqTotal, range)).then(res => {
const totalFlowRate = res
.map(success =>
success.metrics.length > 0 ? { ...success.metrics[0], name: t('Total flows') } : undefined
)
.mapError(err => getStructuredHTTPError(err, `Total flow rate`));
currentMetrics = { ...currentMetrics, totalFlowRate };
setMetrics(currentMetrics);
return res.map(r => r.stats).or(emptyStats);
})
);
const fqTLS: StructuredFlowQuery = {
...baseQuery,
function: 'rate',
type: 'TlsFlows',
aggregateBy: 'app'
};
promises.push(
Result.fromPromise(getFlowGenericMetrics(fqTLS, range)).then(res => {
const tlsFlowRate = res
.map(success =>
success.metrics.length > 0 ? { ...success.metrics[0], name: t('TLS flows') } : undefined
)
.mapError(err => getStructuredHTTPError(err, `TLS flow rate`));
currentMetrics = { ...currentMetrics, tlsFlowRate };
setMetrics(currentMetrics);
return res.map(r => r.stats).or(emptyStats);
})
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@web/src/components/tabs/netflow-overview/netflow-overview.tsx` around lines
410 - 435, The metric legend labels are hardcoded in the Result handlers for
getFlowGenericMetrics — replace the literal strings 'Total flows' and 'TLS
flows' with translated strings using the react-i18next t(...) function (i.e. set
totalFlowRate to use name: t('Total flows') and tlsFlowRate to use name: t('TLS
flows')), ensure the component has access to t (via useTranslation) and add t to
the fetch useCallback dependency array so setMetrics/currentMetrics updates use
the localized labels correctly.

Comment thread web/src/components/tabs/netflow-overview/netflow-overview.tsx
Comment thread web/src/utils/back-and-forth.ts
@Amoghrd
Copy link
Copy Markdown
Member

Amoghrd commented May 15, 2026

/label qe-approved
Verified using PR #1528

@openshift-ci openshift-ci Bot added the qe-approved QE has approved this pull request label May 15, 2026
@Amoghrd
Copy link
Copy Markdown
Member

Amoghrd commented May 15, 2026

@jotak There are some coderabbit review comments, do you want to fix them before merging?

@Amoghrd Amoghrd requested review from jotak and jpinsonneau May 15, 2026 17:14
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 50.27322% with 91 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.22%. Comparing base (ce8ffab) to head (b929627).

Files with missing lines Patch % Lines
...ponents/tabs/netflow-overview/netflow-overview.tsx 34.83% 53 Missing and 5 partials ⚠️
web/src/utils/back-and-forth.ts 71.42% 6 Missing and 2 partials ⚠️
pkg/model/filters/logql.go 0.00% 6 Missing ⚠️
pkg/loki/topology_query.go 61.53% 5 Missing ⚠️
pkg/loki/flow_query.go 75.00% 4 Missing ⚠️
web/src/utils/metrics.ts 0.00% 3 Missing ⚠️
pkg/prometheus/inventory.go 50.00% 1 Missing and 1 partial ⚠️
web/src/components/metrics/metrics-donut.tsx 33.33% 1 Missing and 1 partial ⚠️
web/src/utils/overview-panels.ts 66.66% 2 Missing ⚠️
web/src/model/filters.ts 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1530      +/-   ##
==========================================
- Coverage   52.42%   52.22%   -0.20%     
==========================================
  Files         233      233              
  Lines       12452    12551      +99     
  Branches     1564     1575      +11     
==========================================
+ Hits         6528     6555      +27     
- Misses       5310     5378      +68     
- Partials      614      618       +4     
Flag Coverage Δ
uitests 56.26% <48.61%> (-0.28%) ⬇️
unittests 40.92% <56.41%> (-0.05%) ⬇️

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

Files with missing lines Coverage Δ
web/src/api/loki.ts 84.61% <ø> (-1.60%) ⬇️
...b/src/components/drawer/netflow-traffic-drawer.tsx 47.70% <ø> (ø)
web/src/components/messages/empty.tsx 51.35% <ø> (+1.35%) ⬆️
web/src/components/messages/error.tsx 23.68% <ø> (+0.30%) ⬆️
web/src/components/metrics/metrics-graph.tsx 90.00% <ø> (ø)
web/src/components/modals/columns-modal.tsx 64.77% <100.00%> (ø)
web/src/components/modals/export-modal.tsx 62.85% <ø> (ø)
web/src/components/slider/scope-slider.tsx 90.00% <ø> (ø)
web/src/model/config.ts 100.00% <ø> (ø)
web/src/model/flow-query.ts 91.66% <100.00%> (+1.66%) ⬆️
... and 12 more

... and 1 file with indirect coverage changes

🚀 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.

@github-actions github-actions Bot removed the ok-to-test To set manually when a PR is safe to test. Triggers image build on PR. label May 18, 2026
@jotak
Copy link
Copy Markdown
Member

jotak commented May 18, 2026

@Amoghrd it's nothing big but yes i've addressed some of the feedback

@jotak jotak added the ok-to-test To set manually when a PR is safe to test. Triggers image build on PR. label May 18, 2026
Copy link
Copy Markdown
Member

@jotak jotak left a comment

Choose a reason for hiding this comment

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

/lgtm

@openshift-ci openshift-ci Bot added the lgtm label May 18, 2026
@github-actions
Copy link
Copy Markdown

New image:

quay.io/netobserv/network-observability-console-plugin:65df4bc

It will expire in two weeks.

To deploy this build, run from the operator repo, assuming the operator is running:

USER=netobserv VERSION=65df4bc make set-plugin-image

@jotak jotak merged commit b9947b2 into netobserv:main May 18, 2026
9 of 12 checks passed
@jotak jotak changed the title [main] NETOBSERV-2608: TLS, metrics and charts [pf5] [main] NETOBSERV-2608: TLS, metrics and charts May 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

jira/valid-reference lgtm ok-to-test To set manually when a PR is safe to test. Triggers image build on PR. qe-approved QE has approved this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants