Skip to content

feat: v4 multi-cloud estate, global omni-search, and topology UX refinements - #206

Open
JLCode-tech wants to merge 51 commits into
stagingfrom
feat/v4-multi-cloud-ux-enhancements
Open

feat: v4 multi-cloud estate, global omni-search, and topology UX refinements#206
JLCode-tech wants to merge 51 commits into
stagingfrom
feat/v4-multi-cloud-ux-enhancements

Conversation

@JLCode-tech

@JLCode-tech JLCode-tech commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

This pull request brings together multi-cloud Azure / GCP / AWS cluster and auth support, global Omni-Search, unified CRD exploration, direct F5 BNK topology views, multi-cluster LLM Observability analytics, and UX polish across the v4 platform.

Key Highlights

1. Multi-Cloud & Hybrid Estate

  • Added Microsoft Azure Service Principal and Entra ID SSO credential templates with automatic provider validation.
  • Implemented pure-Python GCP and Azure OAuth token generation with corporate SSL proxy interception resilience.
  • Added Multi-Cloud Estate overview widget to Dashboard with provider breakdown chips and interactive cluster filtering.
  • Enhanced consumption and cluster tables with cloud provider badges and region selectors.

2. Global Omni-Search

  • Added backend search endpoint (/api/v1/k8s/search) scanning clusters, projects, stacks, blueprints, and workloads.
  • Added HeroOmniSearch with keyboard shortcuts (Cmd+K / Ctrl+K), type categorizations, and instant deep links.

3. LLM Observability & Multi-Cluster AI Gateway

  • Added multi-cluster Latency breakdown on the AI Gateway Dashboard Overview, rendering per-cluster latency trends when "All Clusters" is selected.
  • Added exact arithmetic mean latency computation across clusters in Provider Usage metrics.
  • Added fleet-level multi-cluster log collection, newest-first unified sorting, cursor-based pagination, and cluster attribution badges in log tables and the inspection drawer.

4. F5 BNK Topology & Diagnostics Polish

  • Streamlined F5 BNK page to direct vertical sub-views (Object Topology Graph, Traffic Flow Pipeline, Policy & Security Matrix) removing confusing nested tab hierarchies.
  • Added per-listener connection counters and live TMM traffic telemetry to Traffic Flow views.
  • Optimized diagnostics data caching to eliminate redundant queries during tab switching.

5. CNF & Kubernetes CRD Hub Consolidation

  • Streamlined CNF into a dedicated Telco Infrastructure hub with interactive namespace topology chips and a direct shortcut to the Kubernetes CRD Explorer.
  • Unified CRD browsing into K8sCrdExplorerPanel on the Kubernetes page.

6. Fleet & Benchmarks Polish

  • Removed redundant setup progress stepper banner on Benchmarks page while preserving the top-right Setup Guide.
  • Unified provider filter chips and group guidance across Fleet tabs.
  • Optimized LLM Observability filters and debounce hooks.

Verification

  • Automated Frontend Tests: 301 test suites passing (including LlmDashboard.test.tsx and LlmLogs.test.tsx).
  • Automated Backend Tests: Component, search, and LLM observability test suites passing (27 tests in test_llm_observability_service.py and routes).
  • Build: Vite production build succeeded with zero errors.
  • E2E / Stack: Verified healthy against live Docker Compose deployment at https://localhost.

@JLCode-tech
JLCode-tech force-pushed the feat/v4-multi-cloud-ux-enhancements branch from 9f75326 to 42ee999 Compare September 7, 2026 00:05
@JLCode-tech
JLCode-tech changed the base branch from main to staging September 7, 2026 02:24
jgruberf5 pushed a commit that referenced this pull request Sep 8, 2026
…core

Self-review findings on feat/v4-multi-cloud-ux-enhancements (tractable
minors + a real coverage gap; no blockers).

MINOR 1 — llm_observability_service.stats fleet branch: per-cluster
`models` is already a COUNT of distinct models, so max() silently
understated the fleet whenever clusters ran disjoint model sets. Switch
to sum() as an upper bound of "models in use across the fleet" (matching
the generic "Models" stat-tile), with a comment stating the true union
is not computable from these inputs. Pin the semantics with a fleet
`stats` test (previously untested).

Coverage gap — routes/k8s/search._scan_cluster_for_query (the ~260-line
live-scan core) was FULLY MOCKED in both integration tests. Add unit
tests that mock the k8s client RESPONSES (not the scan function) and
exercise Ingress/HTTPRoute/VirtualServer/Egress/Gateway/Service parsing,
LB-IP harvesting, CRD-absent tolerance, and the ThreadPoolExecutor
timeout harvest (partial results, no hang) + cross-cluster dedup. Add a
request-based negative-authZ test asserting the route requires
require_viewer (was only asserted by inspection).

Did NOT touch credentials_service.py (reconciled with #208 at merge).

Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
@jgruberf5

Copy link
Copy Markdown
Collaborator

Self-review (cold, adversarial) + fixes applied

Independent cold audit of the highest-risk surfaces. No blockers. The top-flagged risk — an omni-search cross-tenant leak — was investigated and refuted: search.py issues unfiltered .all() queries, but the app has no read-level tenancy (existing list_all_clusters/list_projects/get_project all return all rows to any require_viewer); the search route is require_viewer-consistent with that model, and matching is Python substring (no injection). LogQL is escaped + tested; no migrations.

Findings (minor) — fixed @ 3cccc07f:

  • Fleet distinct-model count (llm_observability_service.py): max() across clusters silently understated the fleet when clusters ran disjoint model sets. Switched to sum() (an honest upper-bound matching the generic "Models" tile label; a true distinct union is uncomputable from per-cluster counts) + a comment noting the limitation. New fleet-stats test (c1=3, c2=4 → 7, proving not max()); mutation-verified.
  • Search-scan core coverage gap: _scan_cluster_for_query (~260 lines) was fully mocked out in both integration tests. Added test_routes_k8s_search_scan.py mocking the k8s client RESPONSES (not the function): parses every resource kind (Ingress host/TLS/dedup, HTTPRoute, VirtualServer, Egress dnsNat46, BNKGateway, Service LB-IP match), the ThreadPoolExecutor timeout→partial-results-no-hang-no-dup path, and dedup-key spanning kind/cluster/ns/name. Added a negative-authZ integration test (unauthenticated → 401, scan never called). 3 mutants killed.

Merge-coordination note: the credential-fallback + dead-Azure-resolver findings in credentials_service.py were left untouched here on purpose — PRs #207/#208 are concurrently fixing that same file; those findings are being reconciled on #208 (fail-closed) to keep the three PRs mutually mergeable.

Verified: 41 target tests pass, ruff clean, --check up to date (no contract change). Ready for review.

@bonnyr-f5

Copy link
Copy Markdown
Collaborator

Review — review-discipline pipeline (structural / invariant sweep)

Cold structural sweep at head 3cccc07f — invariants across the surface + an end-to-end trace of the credential path, not a line-by-line read of the ~7.3k-line diff (see "not covered" below).

Verdict: REVISE. The feared omni-search/estate cross-tenant BLOCK did not materialize; the blocker is a headline feature broken on-branch by an undeclared schema dependency.

Major

Major-1 · Azure credential env path references columns that don't exist on this branch. credentials_service.get_cloud_credentials_env (~L288-292) reads template.azure_client_id and template.azure_client_secret_encrypted. Neither column exists on CloudCredentialTemplate in #206's own model (models/system.py on-branch has only azure_subscription_id/azure_tenant_id/azure_credentials_encrypted). Those two columns + their migration v2_156_add_azure_credential_template_fields.py exist only in sibling PR #207, which #206 neither includes nor declares a dependency on.

Minor

  • Minor-1 · Credential resolution silently widens beyond per-project templates (PLAUSIBLE intended). get_gcp_service_account_info/get_azure_service_principal_info now accept project=None, fall back to the instance is_default=True template, then to process env (GCP_SERVICE_ACCOUNT_KEY / GOOGLE_APPLICATION_CREDENTIALS_JSON). A project that previously had no cloud creds (returned None) now silently inherits the instance-default/shared credential. Consistent with the documented instance-wide role model, but it changes tenant-isolation semantics for cloud auth — a maintainer should confirm the shared-default is intended (the env-var fallback bypasses per-project templates entirely). Same shape flagged on feat(k8s): unified cloud OAuth token generation for GKE and AKS #208.
  • Minor-2 — PR body advertises /api/v1/k8s/search; the mounted route is /api/k8s/search. Cosmetic; wiring is consistent.

Nits

  • Omni-search live scan: 6s aggregate as_completed timeout, 3s per-call, up to 10 threads — under many active clusters partial results are returned silently (logged at debug). Fine for a search affordance; completeness is non-deterministic.

Verified clean (the feared BLOCK did not materialize)

Not covered (structural honesty)

  • Line-by-line of the ~7.3k-line frontend (topology/CNF/benchmarks/LLM dashboard UX) — swept only the security-relevant components (search, estate, credentials, LLM-obs) and guard patterns.
  • Exhaustive route-collision diff against every open PR's full router (confirmed feat: v4 multi-cloud estate, global omni-search, and topology UX refinements #206's distinctive paths only).
  • Runtime execution of the AttributeError — confirmed by static trace through the model/service field sets + the except, not by running an Azure resolution.

Review Assessment

  • Verdict: REVISE
  • Audit SHA: 3cccc07f124ab085153a8793cf8502f141e533c3
  • Cold Audit Performed: Yes — structural/invariant sweep, independent context
  • Invariants Verified: INV-1 (held by documented instance-wide model, no new leak — the omni-search BLOCK did not materialize); INV-4 (routes distinct; Major-1 is an undeclared schema dep on feat(auth): add Microsoft Azure Service Principal and Entra ID SSO credential templates #207); INV-6 (read-only display/filter guards, length-gated enabled); INV-7 (no migrations); secrets (clean)
  • Git & Harness Cleanliness: Clean

Findings & Action Items

🤖 Generated with Claude Code

- Convert AWS/IBM region validators from hardcoded-list rejection to
  pattern-based acceptance so new or private regions are selectable.
- Add Azure and GCP region validators using the same pattern-based
  approach; wire them into project, credential-template, and cluster
  schemas/routes.
- Update frontend region selectors (AWS, Cloud, SystemDefaults) to
  free-form inputs with datalist suggestions instead of restrictive
  dropdowns.
- Add KubernetesCluster.account_id and discovery_status columns plus
  fleet-health response fields for cloud context.
- Update unit tests for validators, project schemas, k8s schemas, and
  frontend selectors.
…ctor

- Extract shared is_operator_live_connected() helper and use it in the
  operator list, fleet health, and BNK health context.
- Reuse services.scanner.nodes.parse_node() in BNK fetch instead of
  duplicating the zone/instance-type label fallback logic.
- Add an optional label prop to CloudRegionSelector and reuse it in
  SystemDefaults to remove four near-identical region input blocks.
- Add connectivity and integration sections to BnkHealthResponse.
- Reuse the cluster's persisted status for connectivity and the shared
  operator live-connection helper for integration.
- Display ConnectivityBadge and IntegrationBadge in the dashboard banner.
- Add backend unit tests and frontend dashboard tests for the new fields.
Add /detect-credentials endpoint that discovers existing Kubernetes
clusters from a project's credential template for AWS, IBM Cloud,
Azure, and GCP. Each provider lists accessible clusters, builds a
kubeconfig from the template credentials, and registers the cluster
in BNK-Forge.

- New ClusterDiscoveryService orchestrates detection and registration.
- Provider helpers: EKS, ROKS, AKS, GKE.
- Frontend auto-detect switched to api.detectClustersFromCredentials().
- Backend + frontend tests updated; openapi.json and api-generated.ts
  regenerated.
- Move BNK Resources tab from System page to Fleet page
- Make GET /api/system/bnk-consumption viewer-accessible
- Move MCP Server from standalone sidebar page to System page tab
- Move Benchmarks sidebar item from OPERATE to OBSERVE section
- Update affected tests and regenerate OpenAPI types
- Add services/bnk/traffic_stats.py with analyze_traffic_stats() and
  fetch_tmm_traffic_stats() wrapping existing TMM debug helpers.
- Add Pydantic schemas for listener/egress/firewall-rule traffic stats.
- Wire trafficStats into the unified /f5bnk/data response.
- Surface hit/connection badges on F5BNKTopologyViewer listener/egress nodes.
- Add hits column to F5BNKPolicyViewer firewall-rule tables.
- Add total-connections summary chips in TrafficFlowOverview.
- Regenerate openapi.json and TypeScript generated types.
- Add backend unit tests and frontend component/hook tests.
- Enrich BNK topology with gateway/listener/route accepted/programmed conditions
- Add policy resolved/programmed status to topology and policy associations
- Add response models for gateway topology and policy associations endpoints
- Surface inline status badges in topology, traffic flow, and policy views
- Visualize cross-namespace ReferenceGrants in topology and traffic flow
- Extract shared ConditionsList component for Gateway/HTTPRoute/Service details
- Add lightweight Service detail fallback and register it in resource registry
- Regenerate OpenAPI spec and TypeScript generated types
…urce with settings

Module Library sync failed for official-bnk-forge-modules because the
clone used source.branch and then tried git checkout <git_ref>. A shallow
branch clone does not fetch tags, so checking out a tag ref (v2.2.0) failed
with 'pathspec did not match any file(s) known to git'.

Use source.git_ref (falling back to branch) directly in git clone --branch,
which accepts branch and tag names and already checks out the requested ref.

Also reconcile the canonical official module source with the current
module_library.git_* settings before a direct source sync, so a stale
branch/git_ref on the source row does not override the configured ref.

Validated: /api/module-sources/3/sync now succeeds, discovers 24 pack
modules, and updates the source row to branch=git_ref=release/2.2.
…d CNE available state

- Update has_condition() and get_condition_message() to inspect direct conditions arrays on parent_status dicts as well as standard K8s status.conditions.
- Add get_policy_operational_status() to evaluate status.ancestors and status.descendants condition refs for BNKNetPolicy and BNKSecPolicy in BNK 2.3.
- Update _build_cne_instance() to recognize Available/Reconciled condition states and populate default phase when healthy.
- Update _match_routes_to_listener() to check parent_status condition acceptance.
Stop per-request ThreadPoolExecutors from spawning 20 workers each, which
exploded backend PID count to 100+ under concurrent BNK page loads. Use
module-level shared executors with small caps for BNK CRD fetches and TMM
configview probes.

Add Redis-backed short-term caches for:
- EKS/GCP bearer tokens (10 min TTL)
- fetch_all_bnk_data results (30 s TTL)
- TMM traffic stats + configview uuid mappings (30 s / 5 min TTL)
- CWC license status (30 s) and report (60 s)

Each cache supports force=true to bypass when the UI explicitly refreshes.
License activation invalidates the cached status/report so the new state is
reflected immediately.
Add account_id, discovery_status, connectivity_status, integration_status,
zones, access_method, and node_count to the KubernetesCluster model, cluster
response schemas, serializers, and detail endpoints. Populate account_id from
credential-template discovery paths (AWS account, Azure subscription, GCP
project) and persist version/node_count/zones/last_synced_at from the scanner.

Includes migration v2_157 and a new GET /api/projects/{project_id}/connectivity
route backed by probe_project_clusters.
…uster

The _build_bnk_context helper added in the health refactor queries
ConnectedOperator by cluster.id. Tests that patched KubernetesService
returned a MagicMock cluster, causing a SQLite bind error. Configure the
mock to return the real test cluster so the endpoint can build its
connectivity/integration context.
JLCode-tech and others added 28 commits September 11, 2026 14:33
…zure api_server (M3)

M1 (coverage gap): the discovered-cluster kubeconfig is encrypted at rest via
encrypt_value(), but no test asserted it — a mutation to plaintext passed all 8
tests. Add test_persisted_kubeconfig_is_encrypted_at_rest, which reads the
persisted kubeconfig_encrypted column and asserts it is NOT plaintext
(no "apiVersion" in the stored value; stored != decrypt_value(stored)) yet
decrypts back to the real kubeconfig. Reverting encrypt_value to plaintext reds
this test (decrypt raises / stored equals its plaintext).

M3 (defensive): _detect_azure_clusters composed api_server as
f"https://{creds['server']}:443", which double-prefixes into a malformed URL if
creds['server'] ever carries a scheme or an explicit port. Add
_normalize_api_server_host() to strip a leading http(s):// scheme and a trailing
:port so a bare host, scheme-prefixed host, or host:port all yield one correct
https://host:443, with a 3-shape parametrized test.

Scoped to M1 + M3; region validators (M2) untouched. No API/model change.

Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
…O routes, doc SSO/terraform split

F1: normalize naive azure_sso_token_expiry to UTC before comparing in
_test_azure_template — matches get_sso_status / credential_refresh_service
guards; fixes TypeError on SQLite/dev naive round-trip. Adds SQLite
regression test (mutation-verified: fails with the exact TypeError without
the guard).

F2: remove the unwired standalone Azure SSO routes (/azure/sso/initiate,
/azure/sso/poll, /azure/subscriptions), their request models, and the
unused client methods (initiateAzureSSO/pollAzureSSO/listAzureSubscriptions).
The frontend uses the server-side template flow (authenticate-sso/poll-sso,
returns only has_credentials); these paths leaked long-lived access/refresh
tokens in the response body. Regenerated openapi.json + api-generated.ts.

F4: document at the terraform credential-injection site that SSO Azure
templates deliberately inject no credential (SSO is validation/console;
terraform provisioning uses the service-principal secret).

Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
- Rebase onto work/v4-localhost (incorporating staging and #205)
- Renumber Alembic migration to v2_158 with down_revision v2_157
- Restrict azure_auth_method typing with Literal["service_principal", "sso"]
- Persist refreshed Azure SSO tokens on test connection by committing DB session
- Ensure SSO action menu and config completeness checks gate strictly on azure_auth_method == 'sso'
- Add regression component tests for refresh token DB persistence and config validation
…topology views

- Add global Omni-Search endpoint (/api/v1/k8s/search) with client hook and Hero search bar on Dashboard
- Add MultiCloudEstate overview card with provider breakdowns and interactive filtering
- Streamline F5 BNK page to direct vertical topology views (Object Topology, Traffic Flow Pipeline, Policy Matrix)
- Consolidate CRD exploration into a unified Kubernetes Explorer Hub and streamline CNF infrastructure view
- Polish Benchmarks page setup headers and remove redundant stepper progress banner
- Clean up Fleet Management provider filter chips and guidance
- Optimize LLM Observability filters and debounce hooks with full test coverage
…core

Self-review findings on feat/v4-multi-cloud-ux-enhancements (tractable
minors + a real coverage gap; no blockers).

MINOR 1 — llm_observability_service.stats fleet branch: per-cluster
`models` is already a COUNT of distinct models, so max() silently
understated the fleet whenever clusters ran disjoint model sets. Switch
to sum() as an upper bound of "models in use across the fleet" (matching
the generic "Models" stat-tile), with a comment stating the true union
is not computable from these inputs. Pin the semantics with a fleet
`stats` test (previously untested).

Coverage gap — routes/k8s/search._scan_cluster_for_query (the ~260-line
live-scan core) was FULLY MOCKED in both integration tests. Add unit
tests that mock the k8s client RESPONSES (not the scan function) and
exercise Ingress/HTTPRoute/VirtualServer/Egress/Gateway/Service parsing,
LB-IP harvesting, CRD-absent tolerance, and the ThreadPoolExecutor
timeout harvest (partial results, no hang) + cross-cluster dedup. Add a
request-based negative-authZ test asserting the route requires
require_viewer (was only asserted by inspection).

Did NOT touch credentials_service.py (reconciled with #208 at merge).

Claude-Session: https://claude.ai/code/session_01UCsZXDxBsWV2s4kT47DwDW
@JLCode-tech
JLCode-tech force-pushed the feat/v4-multi-cloud-ux-enhancements branch from 3cccc07 to 7294fe9 Compare September 11, 2026 06:44
@JLCode-tech

Copy link
Copy Markdown
Collaborator Author

Re-Review Requested: All Review Feedback Addressed & Rebased onto #207

We have rebased feat/v4-multi-cloud-ux-enhancements onto origin/feat/azure-auth-templates (PR #207) and addressed all items raised in the review audit:

  1. Major 1 (Azure Credential Template Columns & Migration):

  2. Minor 1 (Shared Instance-Default Fallback):

    • Confirmed and verified that explicit project credential templates fail-closed, while un-scoped requests utilize the ambient/default instance template according to the tenancy architecture.
  3. Minor 2 (Route Discovery & Integration Coverage):

    • Confirmed /api/k8s/search route resolution and added comprehensive integration tests in test_routes_k8s_search.py.
  4. Testing & Code Hygiene:

    • Regenerated backend/openapi.json and frontend-v2/src/types/api-generated.ts.
    • Frontend tests: 299/299 test suites passing (2,647 tests passed).
    • Backend tests: 100% passing for all search, credentials, and cloud provider normalization suites.
    • scripts/lint-commit-markers.sh: OK.

@bonnyr-f5 — ready for final re-review!

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.

4 participants