feat: show workspace health error alert above agents in WorkspacePage (#19400)

closes #19338 

<img width="1840" height="1191" alt="image"
src="https://github.com/user-attachments/assets/eeefda13-88d1-4a26-ba57-9749abda3f17"
/>
This commit is contained in:
Andrew Aquino
2025-08-21 17:22:25 -07:00
committed by GitHub
parent ad5e6785f4
commit 9a872f903e
5 changed files with 82 additions and 2 deletions
@@ -9,6 +9,7 @@ import type { ProvisionerJobLog } from "api/typesGenerated";
import { action } from "storybook/actions";
import type { WorkspacePermissions } from "../../modules/workspaces/permissions";
import { Workspace } from "./Workspace";
import { defaultPermissions } from "./WorkspaceNotifications/WorkspaceNotifications.stories";
// Helper function to create timestamps easily - Copied from AppStatuses.stories.tsx
const createTimestamp = (
@@ -349,6 +350,23 @@ export const Stopping: Story = {
},
};
export const Unhealthy: Story = {
args: {
...Running.args,
workspace: Mocks.MockUnhealthyWorkspace,
},
};
export const UnhealthyWithoutUpdatePermission: Story = {
args: {
...Unhealthy.args,
permissions: {
...defaultPermissions,
updateWorkspace: false,
},
},
};
export const FailedWithLogs: Story = {
args: {
...Running.args,
@@ -21,6 +21,8 @@ import {
WorkspaceBuildProgress,
} from "./WorkspaceBuildProgress";
import { WorkspaceDeletedBanner } from "./WorkspaceDeletedBanner";
import { NotificationActionButton } from "./WorkspaceNotifications/Notifications";
import { findTroubleshootingURL } from "./WorkspaceNotifications/WorkspaceNotifications";
import { WorkspaceTopbar } from "./WorkspaceTopbar";
interface WorkspaceProps {
@@ -97,6 +99,8 @@ export const Workspace: FC<WorkspaceProps> = ({
(workspace.latest_build.matched_provisioners?.available ?? 1) > 0;
const shouldShowProvisionerAlert =
workspacePending && !haveBuildLogs && !provisionersHealthy && !isRestarting;
const troubleshootingURL = findTroubleshootingURL(workspace.latest_build);
const hasActions = permissions.updateWorkspace || troubleshootingURL;
return (
<div className="flex flex-col flex-1 min-h-0">
@@ -194,6 +198,41 @@ export const Workspace: FC<WorkspaceProps> = ({
</Alert>
)}
{!workspace.health.healthy && (
<Alert severity="warning">
<AlertTitle>Workspace is unhealthy</AlertTitle>
<AlertDetail>
<p>
Your workspace is running but{" "}
{workspace.health.failing_agents.length > 1
? `${workspace.health.failing_agents.length} agents are unhealthy`
: "1 agent is unhealthy"}
.
</p>
{hasActions && (
<div className="flex items-center gap-2">
{permissions.updateWorkspace && (
<NotificationActionButton
onClick={() => handleRestart()}
>
Restart
</NotificationActionButton>
)}
{troubleshootingURL && (
<NotificationActionButton
onClick={() =>
window.open(troubleshootingURL, "_blank")
}
>
Troubleshooting
</NotificationActionButton>
)}
</div>
)}
</AlertDetail>
</Alert>
)}
{transitionStats !== undefined && (
<WorkspaceBuildProgress
workspace={workspace}
@@ -12,7 +12,7 @@ import type { WorkspacePermissions } from "modules/workspaces/permissions";
import { expect, userEvent, waitFor, within } from "storybook/test";
import { WorkspaceNotifications } from "./WorkspaceNotifications";
const defaultPermissions: WorkspacePermissions = {
export const defaultPermissions: WorkspacePermissions = {
readWorkspace: true,
updateWorkspaceVersion: true,
updateWorkspace: true,
@@ -275,7 +275,7 @@ const styles = {
},
} satisfies Record<string, Interpolation<Theme>>;
const findTroubleshootingURL = (
export const findTroubleshootingURL = (
workspaceBuild: WorkspaceBuild,
): string | undefined => {
for (const resource of workspaceBuild.resources) {
+23
View File
@@ -994,6 +994,15 @@ export const MockWorkspaceSubAgent: TypesGen.WorkspaceAgent = {
],
};
const MockWorkspaceUnhealthyAgent: TypesGen.WorkspaceAgent = {
...MockWorkspaceAgent,
id: "test-workspace-unhealthy-agent",
name: "a-workspace-unhealthy-agent",
status: "timeout",
lifecycle_state: "start_error",
health: { healthy: false },
};
export const MockWorkspaceAppStatus: TypesGen.WorkspaceAppStatus = {
id: "test-app-status",
created_at: "2022-05-17T17:39:01.382927298Z",
@@ -1445,6 +1454,20 @@ export const MockStoppingWorkspace: TypesGen.Workspace = {
status: "stopping",
},
};
export const MockUnhealthyWorkspace: TypesGen.Workspace = {
...MockWorkspace,
id: "test-unhealthy-workspace",
health: {
healthy: false,
failing_agents: [MockWorkspaceUnhealthyAgent.id],
},
latest_build: {
...MockWorkspace.latest_build,
resources: [
{ ...MockWorkspaceResource, agents: [MockWorkspaceUnhealthyAgent] },
],
},
};
export const MockStartingWorkspace: TypesGen.Workspace = {
...MockWorkspace,
id: "test-starting-workspace",