Skip to content

🐛 Fix 5 bugs: Falco demo, screenshot preview, deployment mismatch, ArgoCD demo, cluster reachability#4204

Merged
clubanderson merged 1 commit intomainfrom
fix/bugs-batch
Apr 1, 2026
Merged

🐛 Fix 5 bugs: Falco demo, screenshot preview, deployment mismatch, ArgoCD demo, cluster reachability#4204
clubanderson merged 1 commit intomainfrom
fix/bugs-batch

Conversation

@clubanderson
Copy link
Copy Markdown
Collaborator

Summary

Fixes 5 bug issues in a single batch:

  • Falco Alerts shows demo data even when local agent is running #4198 - Falco Alerts shows demo data even when local agent is running: FalcoAlerts now gates demo content on isDemoMode instead of always showing hardcoded demo alerts with isDemoData: true. When not in demo mode, shows install prompt and empty state.

  • Add image preview button to feedback screenshot thumbnails #4199 - Add image preview button to feedback screenshot thumbnails: Added Eye/preview button to screenshot thumbnails in the feedback modal. Clicking opens a fullscreen image overlay with the screenshot at full resolution. Overlay is dismissible via Escape key, close button, or backdrop click. Reuses existing Maximize2 and Eye icons already imported.

  • Deployment status mismatch: list card shows degraded/failed while drilldown shows healthy #4200 - Deployment status mismatch between list and drilldown: DeploymentDrillDown now reconciles reason/message with live kubectl data after fetching. When the live fetch shows the deployment is healthy (readyReplicas === replicas), stale failure reason/message from the click payload is cleared. When still unhealthy, reason is derived from the deployment's actual conditions.

  • ArgoCD cards show Demo mode even when ArgoCD is deployed and running #4201 - ArgoCD cards show Demo mode even when ArgoCD is deployed: ArgoCD hooks (useArgoCDApplications, useArgoCDHealth, useArgoCDSyncStatus) no longer force mock fallback when the API returns empty items or zero totals with isDemoData=false. Previously, an empty result from a running ArgoCD was treated as "no ArgoCD" and triggered demo data. Integration banner in all three ArgoCD cards is now conditional on isDemoData.

  • 🐛 Console shows cluster as reachable when API server is externally unreachable #4202 - Console shows cluster as reachable when API server is externally unreachable: Added external TCP probe (probeAPIServer) in the Go backend's GetClusterHealth. New externallyReachable field on ClusterHealth distinguishes internal vs external reachability. The ClusterHealth card shows a yellow "ext. unreachable" warning badge when the cluster is internally healthy but the API server TCP probe fails.

Files changed

  • pkg/k8s/client.go - External reachability TCP probe + new field
  • web/src/hooks/useArgoCD.ts - Fix aggressive mock fallback
  • web/src/hooks/mcp/types.ts - Add externallyReachable to types
  • web/src/hooks/mcp/shared.ts - Pass through externallyReachable
  • web/src/components/cards/ComplianceCards.tsx - FalcoAlerts demo gating
  • web/src/components/cards/ArgoCDApplications.tsx - Conditional banner
  • web/src/components/cards/ArgoCDHealth.tsx - Conditional banner
  • web/src/components/cards/ArgoCDSyncStatus.tsx - Conditional banner
  • web/src/components/cards/ClusterHealth.tsx - External unreachable badge
  • web/src/components/drilldown/views/DeploymentDrillDown.tsx - Status reconciliation
  • web/src/components/feedback/FeatureRequestModal.tsx - Screenshot preview

Test plan

  • Verify FalcoAlerts shows demo data only in demo mode, shows empty state otherwise
  • Verify screenshot preview button appears on thumbnails in feedback modal
  • Verify screenshot preview overlay opens/closes correctly (Escape, close button, backdrop)
  • Verify deployment drilldown clears stale failure reason when live data shows healthy
  • Verify ArgoCD cards show live data (not demo) when ArgoCD API returns isDemoData: false
  • Verify ArgoCD integration banner is hidden when live data is available
  • Verify ClusterHealth shows ext. unreachable badge when API server probe fails
  • TypeScript builds cleanly (npx tsc --noEmit)
  • Go builds cleanly (go build ./...)

Closes #4198, closes #4199, closes #4200, closes #4201, closes #4202

…mismatch, ArgoCD demo mode, cluster reachability

- #4198: FalcoAlerts now gates demo content on isDemoMode instead of
  always showing hardcoded demo alerts. When not in demo mode, shows
  install prompt and empty state.

- #4199: Add Eye/preview button to screenshot thumbnails in the feedback
  modal. Clicking opens a fullscreen image overlay (dismissible via
  Escape, close button, or backdrop click).

- #4200: DeploymentDrillDown now reconciles reason/message with live
  kubectl data. When live fetch shows healthy (readyReplicas === replicas),
  stale failure reason/message is cleared. When still unhealthy, reason
  is derived from deployment conditions.

- #4201: ArgoCD hooks no longer force mock fallback when API returns
  empty items or zero totals with isDemoData=false. Integration banner
  in all three ArgoCD cards is now conditional on isDemoData.

- #4202: Add external API server TCP probe in Go backend's
  GetClusterHealth. New externallyReachable field distinguishes internal
  vs external reachability. ClusterHealth card shows warning badge when
  cluster is internally healthy but externally unreachable.

Closes #4198, closes #4199, closes #4200, closes #4201, closes #4202

Signed-off-by: Andrew Anderson <andy@clubanderson.com>
Copilot AI review requested due to automatic review settings April 1, 2026 23:47
@kubestellar-prow kubestellar-prow bot added the dco-signoff: yes Indicates the PR's author has signed the DCO. label Apr 1, 2026
@kubestellar-prow
Copy link
Copy Markdown
Contributor

[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 clubanderson 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

@netlify
Copy link
Copy Markdown

netlify bot commented Apr 1, 2026

Deploy Preview for kubestellarconsole ready!

Name Link
🔨 Latest commit c9b01c3
🔍 Latest deploy log https://app.netlify.com/projects/kubestellarconsole/deploys/69cdae855081ba000832daeb
😎 Deploy Preview https://deploy-preview-4204.console-deploy-preview.kubestellar.io
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@clubanderson clubanderson merged commit be76cee into main Apr 1, 2026
19 of 20 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

👋 Hey @clubanderson — thanks for opening this PR!

🤖 This project is developed exclusively using AI coding assistants.

Please do not attempt to code anything for this project manually.
All contributions should be authored using an AI coding tool such as:

This ensures consistency in code style, architecture patterns, test coverage,
and commit quality across the entire codebase.


This is an automated message.

@kubestellar-prow kubestellar-prow bot deleted the fix/bugs-batch branch April 1, 2026 23:47
@kubestellar-prow kubestellar-prow bot added the size/L Denotes a PR that changes 100-499 lines, ignoring generated files. label Apr 1, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 1, 2026

Thank you for your contribution! Your PR has been merged.

Check out what's new:

Stay connected: Slack #kubestellar-dev | Multi-Cluster Survey

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

This PR batches fixes for five console bugs across GitOps (ArgoCD), compliance (Falco demo gating), feedback UX (screenshot preview), deployment drilldown status reconciliation, and cluster reachability (external TCP probe surfaced to UI).

Changes:

  • Adjust ArgoCD hooks/cards to avoid incorrect demo fallback and hide integration banners when live (non-demo) data is in use.
  • Add a screenshot preview action in the FeatureRequest modal and reconcile stale deployment failure reason/message in the drilldown after live refresh.
  • Introduce an external API server TCP probe in the Go backend, plumb externallyReachable through MCP types, and show an “ext. unreachable” warning in ClusterHealth.

Reviewed changes

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

Show a summary per file
File Description
pkg/k8s/client.go Adds external TCP probe (ExternallyReachable) during cluster health and appends an issue when probe fails.
web/src/hooks/useArgoCD.ts Treats empty/zero live responses as valid live data (no forced mock fallback).
web/src/hooks/mcp/types.ts Extends MCP cluster types with externallyReachable.
web/src/hooks/mcp/shared.ts Passes externallyReachable from backend health into cached cluster state.
web/src/components/cards/ComplianceCards.tsx Gates Falco demo alerts behind isDemoMode, shows install/empty state otherwise.
web/src/components/cards/ArgoCDApplications.tsx Shows ArgoCD integration notice only in demo/fallback mode.
web/src/components/cards/ArgoCDHealth.tsx Shows ArgoCD integration notice only in demo/fallback mode.
web/src/components/cards/ArgoCDSyncStatus.tsx Shows ArgoCD integration notice only in demo/fallback mode.
web/src/components/cards/ClusterHealth.tsx Adds “ext. unreachable” warning badge when external probe fails.
web/src/components/drilldown/views/DeploymentDrillDown.tsx Reconciles/clears stale reason/message based on live kubectl deployment JSON.
web/src/components/feedback/FeatureRequestModal.tsx Adds screenshot thumbnail preview button and fullscreen image overlay.

Comment on lines +1644 to +1656
<div
ref={screenshotPreviewRef}
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm"
onClick={(e) => {
if (e.target === screenshotPreviewRef.current) {
setPreviewImageSrc(null)
}
}}
onKeyDown={(e) => {
if (e.key === 'Escape') {
setPreviewImageSrc(null)
}
}}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The screenshot preview overlay’s Escape handling likely won’t work: the div isn’t focusable (so onKeyDown won’t fire reliably), and BaseModal’s global Escape handler will close the entire modal instead of just the preview. Consider adding a document-level keydown listener while previewImageSrc is set (like the existing isPreviewFullscreen logic) and/or disabling BaseModal closeOnEscape while the preview is open so Escape closes the overlay first.

Copilot uses AI. Check for mistakes.
Comment on lines +1460 to +1468
<button
type="button"
onClick={e => { e.stopPropagation(); setPreviewImageSrc(s.preview) }}
className="p-1.5 rounded-md bg-secondary/80 text-foreground hover:bg-secondary transition-colors"
title="Preview screenshot"
aria-label="Preview screenshot"
>
<Eye className="w-3.5 h-3.5" />
</button>
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

New screenshot preview behavior isn’t covered by tests. web/src/components/feedback/FeatureRequestModal.test.tsx currently only asserts the export; please add RTL/Vitest tests for showing the preview button per thumbnail and opening/closing the overlay (backdrop, close button, Escape) to prevent regressions.

Copilot uses AI. Check for mistakes.
Comment on lines +355 to +363
{/* Warn when cluster is internally reachable but API server is externally unreachable (#4202) */}
{!clusterUnreachable && !clusterLoading && cluster.externallyReachable === false && (
<span
className="flex items-center gap-0.5 text-2xs px-1.5 py-0.5 rounded bg-yellow-500/15 text-yellow-400 border border-yellow-500/20 shrink-0"
title="API server externally unreachable — cluster healthy internally but external access may be blocked"
>
<WifiOff className="w-3 h-3" />
<span className="hidden sm:inline">ext. unreachable</span>
</span>
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

This externally-reachable warning badge can become icon-only on small screens (the text is hidden). A title tooltip isn’t announced by screen readers; add an aria-label or an sr-only text node so the warning is accessible when only the icon is visible.

Copilot uses AI. Check for mistakes.
Comment on lines +106 to +109
// Falco has no live data hook yet so isDemoData is always true.
// However, we still gate the demo content on isDemoMode so the card
// correctly shows an empty / install-prompt state when a real agent
// is connected but Falco is not installed.
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The comment says Falco “isDemoData is always true”, but the code reports isDemoData: showDemo (false when not in demo mode). Please update the comment to match the actual behavior (demo alerts gated by isDemoMode; otherwise not demo).

Suggested change
// Falco has no live data hook yet so isDemoData is always true.
// However, we still gate the demo content on isDemoMode so the card
// correctly shows an empty / install-prompt state when a real agent
// is connected but Falco is not installed.
// Falco has no live data hook yet, so this card only shows demo alerts.
// Demo content (and isDemoData) is gated by isDemoMode; when not in
// demo mode the card shows an empty / install-prompt state so it
// behaves correctly when a real agent is connected but Falco itself
// is not installed.

Copilot uses AI. Check for mistakes.
Comment on lines +179 to +197
// Reconcile reason/message with live state (#4200):
// If live data shows healthy, clear the stale failure reason/message.
// If still unhealthy, derive reason from deployment conditions.
if (liveReady === liveReplicas && liveReplicas > 0) {
setLiveReason(undefined)
setLiveMessage(undefined)
} else {
// Extract current condition from the deployment status
const conditions = (deploy.status?.conditions || []) as Array<{ type: string; status: string; reason?: string; message?: string }>
const failedCondition = conditions.find(
(c: { type: string; status: string }) =>
(c.type === 'Available' && c.status === 'False') ||
(c.type === 'Progressing' && c.status === 'False') ||
(c.type === 'ReplicaFailure' && c.status === 'True')
)
if (failedCondition) {
setLiveReason(failedCondition.reason || liveReason)
setLiveMessage(failedCondition.message || liveMessage)
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

In fetchData(), setLiveReason/setLiveMessage use the captured liveReason/liveMessage values, which can be stale by the time the async kubectl call resolves. Prefer functional state updates (prev => ...) or fall back to the initial payload values. Also, when liveReplicas/liveReady are both 0 (scaled to zero), the code won’t clear a stale failure reason/message, which can still show “Degraded” with an old failure reason.

Copilot uses AI. Check for mistakes.
Comment on lines +371 to 382
if (!result.isDemoData) {
// Real data from ArgoCD — use it even if empty (ArgoCD installed
// but no applications deployed yet). Previously an empty list
// was treated as "no ArgoCD" which triggered mock fallback (#4201).
setApplications(result.items || [])
setIsDemoData(false)
setError(null)
setConsecutiveFailures(0)
setLastRefresh(Date.now())
initialLoadDone.current = true
saveToCache(APPS_CACHE_KEY, result.items, false)
saveToCache(APPS_CACHE_KEY, result.items || [], false)
return
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Hook semantics changed to treat an empty applications list as live data (isDemoData=false). Please extend web/src/hooks/tests/useArgoCD.test.ts to cover the new behavior (API returns isDemoData=false with items=[] should not fall back to mock/demo) so #4201 stays fixed.

Copilot uses AI. Check for mistakes.
Comment on lines +96 to +110
{/* Integration notice — only shown in demo/fallback mode (#4201) */}
{isDemoData && (
<div className="flex items-start gap-2 p-2 mb-3 rounded-lg bg-orange-500/10 border border-orange-500/20 text-xs">
<AlertCircle className="w-4 h-4 text-orange-400 flex-shrink-0 mt-0.5" />
<div>
<p className="text-orange-400 font-medium">{t('argoCDHealth.argocdIntegration')}</p>
<p className="text-muted-foreground">
{t('argoCDHealth.installArgoCD')}{' '}
<a href="https://argo-cd.readthedocs.io/en/stable/getting_started/" target="_blank" rel="noopener noreferrer" className="text-purple-400 hover:underline inline-block py-2">
{t('argoCDHealth.installGuide')}
</a>
</p>
</div>
</div>
</div>
)}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

ArgoCDHealth now shows the integration banner only when isDemoData is true, but the existing unit test currently asserts the banner is rendered in the live-data scenario. Update the tests (web/src/components/cards/tests/ArgoCDHealth.test.tsx) to match the new conditional behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +115 to +129
{/* Integration notice — only shown in demo/fallback mode (#4201) */}
{isDemoData && (
<div className="flex items-start gap-2 p-2 mb-3 rounded-lg bg-orange-500/10 border border-orange-500/20 text-xs">
<AlertCircle className="w-4 h-4 text-orange-400 flex-shrink-0 mt-0.5" />
<div>
<p className="text-orange-400 font-medium">{t('argoCDSyncStatus.argocdIntegration')}</p>
<p className="text-muted-foreground">
{t('argoCDSyncStatus.installArgoCD')}{' '}
<a href="https://argo-cd.readthedocs.io/en/stable/getting_started/" target="_blank" rel="noopener noreferrer" className="text-purple-400 hover:underline inline-block py-2">
{t('argoCDSyncStatus.installGuide')}
</a>
</p>
</div>
</div>
</div>
)}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

ArgoCDSyncStatus now gates the integration banner behind isDemoData, but the existing unit test asserts the banner in the live-data scenario. Update web/src/components/cards/tests/ArgoCDSyncStatus.test.tsx accordingly so CI doesn’t fail.

Copilot uses AI. Check for mistakes.
Comment on lines +1544 to +1556
// Populate the API server URL from the REST config for the frontend to display.
// Also run an external TCP probe to distinguish internal-only vs external reachability (#4202).
if health.Reachable {
m.mu.RLock()
cfg := m.configs[contextName]
m.mu.RUnlock()
if cfg != nil && cfg.Host != "" {
health.APIServer = cfg.Host
reachable := probeAPIServer(cfg.Host)
health.ExternallyReachable = &reachable
if !reachable {
health.Issues = append(health.Issues, "API server externally unreachable (TCP probe failed)")
}
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

New externallyReachable behavior isn’t unit-tested. pkg/k8s/client_test.go has GetClusterHealth coverage; consider adding a test that sets m.configs[contextName].Host to a local listener address and asserts ExternallyReachable is set true/false and that the “API server externally unreachable” issue is appended when the TCP probe fails.

Copilot uses AI. Check for mistakes.
Comment on lines +498 to +509
// Real data from ArgoCD — use it even if totals are zero
// (ArgoCD installed but no applications yet). Previously a
// zero total was treated as "no ArgoCD" which triggered mock
// fallback even when ArgoCD was running (#4201).
setStats(result.stats)
setIsDemoData(false)
setError(null)
setConsecutiveFailures(0)
setLastRefresh(Date.now())
initialLoadDone.current = true
saveToCache(HEALTH_CACHE_KEY, result.stats, false)
return
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

Health/sync hooks now treat zero-total stats as live data (isDemoData=false) instead of forcing mock fallback. Please add/adjust hook tests to cover the “isDemoData=false with all-zero stats” case for useArgoCDHealth and useArgoCDSyncStatus so this regression doesn’t reappear.

Copilot uses AI. Check for mistakes.
@clubanderson
Copy link
Copy Markdown
Collaborator Author

🔄 Auto-Applying Copilot Code Review

Copilot code review found 1 code suggestion(s) and 10 general comment(s).

@copilot Please apply all of the following code review suggestions:

  • web/src/components/cards/ComplianceCards.tsx (line 109): // Falco has no live data hook yet, so this card only shows demo alerts. // De...

Also address these general comments:

  • web/src/components/feedback/FeatureRequestModal.tsx (line 1656): The screenshot preview overlay’s Escape handling likely won’t work: the div isn’t focusable (so onKeyDown won’t fire rel
  • web/src/components/feedback/FeatureRequestModal.tsx (line 1468): New screenshot preview behavior isn’t covered by tests. web/src/components/feedback/FeatureRequestModal.test.tsx current
  • web/src/components/cards/ClusterHealth.tsx (line 363): This externally-reachable warning badge can become icon-only on small screens (the text is hidden). A title tooltip isn’
  • web/src/components/drilldown/views/DeploymentDrillDown.tsx (line 197): In fetchData(), setLiveReason/setLiveMessage use the captured liveReason/liveMessage values, which can be stale by the t
  • web/src/components/drilldown/views/DeploymentDrillDown.tsx (line 121): The new live reason/message reconciliation in DeploymentDrillDown isn’t covered by tests. There is an existing smoke tes
  • web/src/hooks/useArgoCD.ts (line 382): Hook semantics changed to treat an empty applications list as live data (isDemoData=false). Please extend web/src/hooks/
  • web/src/components/cards/ArgoCDHealth.tsx (line 110): ArgoCDHealth now shows the integration banner only when isDemoData is true, but the existing unit test currently asserts
  • web/src/components/cards/ArgoCDSyncStatus.tsx (line 129): ArgoCDSyncStatus now gates the integration banner behind isDemoData, but the existing unit test asserts the banner in th
  • pkg/k8s/client.go (line 1556): New externallyReachable behavior isn’t unit-tested. pkg/k8s/client_test.go has GetClusterHealth coverage; consider addin
  • web/src/hooks/useArgoCD.ts (line 509): Health/sync hooks now treat zero-total stats as live data (isDemoData=false) instead of forcing mock fallback. Please ad

Push all fixes in a single commit. Run cd web && npm run build && npm run lint before committing.


Auto-generated by copilot-review-apply workflow.

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

Labels

dco-signoff: yes Indicates the PR's author has signed the DCO. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

3 participants