Skip to content

feat(site): warn on provisioner health during builds #15589

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Finalise Provisioner Warnings for Templates and template versions
  • Loading branch information
SasSwart committed Nov 22, 2024
commit 3cf53ef367a097226f4698c8785532a79f95d00c
9 changes: 5 additions & 4 deletions site/src/api/queries/organizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,17 @@ export const organizations = () => {
};
};

export const getProvisionerDaemonsKey = (organization: string) => [
export const getProvisionerDaemonsKey = (organization: string, tags?: Record<string, string>) => [
"organization",
organization,
tags,
"provisionerDaemons",
];

export const provisionerDaemons = (organization: string) => {
export const provisionerDaemons = (organization: string, tags?: Record<string, string>) => {
return {
queryKey: getProvisionerDaemonsKey(organization),
queryFn: () => API.getProvisionerDaemonsByOrganization(organization),
queryKey: getProvisionerDaemonsKey(organization, tags),
queryFn: () => API.getProvisionerDaemonsByOrganization(organization, tags),
};
};

Expand Down
30 changes: 30 additions & 0 deletions site/src/modules/provisioners/ProvisionerAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Alert, AlertColor, AlertTitle } from "@mui/material";
import { AlertDetail } from "components/Alert/Alert";
import { type FC } from "react";

type ProvisionerAlertProps = {
title: string,
detail: string,
severity: AlertColor,
}

export const ProvisionerAlert : FC<ProvisionerAlertProps> = ({
title,
detail,
severity,
}) => {
return (
<Alert
severity={severity}
css={(theme) => ({
borderRadius: 0,
border: 0,
borderBottom: `1px solid ${theme.palette.divider}`,
borderLeft: `2px solid ${theme.palette.error.main}`,
})}
>
<AlertTitle>{title}</AlertTitle>
<AlertDetail>{detail}</AlertDetail>
</Alert>
);
};
28 changes: 0 additions & 28 deletions site/src/modules/provisioners/useCompatibleProvisioners.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,4 @@
import { API } from "api/api";
import { ProvisionerDaemon } from "api/typesGenerated";
import { useEffect, useState } from "react";

export const useCompatibleProvisioners = (organization: string | undefined, tags: Record<string, string> | undefined) => {
const [compatibleProvisioners, setCompatibleProvisioners] = useState<ProvisionerDaemon[]>([])

useEffect(() => {
(async () => {
if (!organization) {
setCompatibleProvisioners([])
return
}

try {
const provisioners = await API.getProvisionerDaemonsByOrganization(
organization,
tags,
);

setCompatibleProvisioners(provisioners);
} catch (error) {
setCompatibleProvisioners([])
}
})();
}, [organization, tags])

return compatibleProvisioners
}

export const provisionersUnhealthy = (provisioners : ProvisionerDaemon[]) => {
return provisioners.reduce((allUnhealthy, provisioner) => {
Expand Down
47 changes: 46 additions & 1 deletion site/src/pages/CreateTemplatePage/BuildLogsDrawer.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
MockTemplateVersion,
MockWorkspaceBuildLogs,
} from "testHelpers/entities";
import { withWebSocket } from "testHelpers/storybook";
import { withProvisioners, withWebSocket } from "testHelpers/storybook";
import { BuildLogsDrawer } from "./BuildLogsDrawer";

const meta: Meta<typeof BuildLogsDrawer> = {
Expand Down Expand Up @@ -34,6 +34,51 @@ export const MissingVariables: Story = {
},
};

export const NoProvisioners: Story = {
args: {
templateVersion: {...MockTemplateVersion, organization_id: "org-id"},
},
decorators: [withProvisioners],
parameters: {
organization_id: "org-id",
tags: MockTemplateVersion.job.tags,
provisioners: [],
}
};

export const ProvisionersUnhealthy: Story = {
args: {
templateVersion: {...MockTemplateVersion, organization_id: "org-id"},
},
decorators: [withProvisioners],
parameters: {
organization_id: "org-id",
tags: MockTemplateVersion.job.tags,
provisioners: [
{
last_seen_at: new Date(new Date().getTime() - 5 * 60 * 1000).toISOString()
},
],
}
};

export const ProvisionersHealthy: Story = {
args: {
templateVersion: {...MockTemplateVersion, organization_id: "org-id"},
},
decorators: [withProvisioners],
parameters: {
organization_id: "org-id",
tags: MockTemplateVersion.job.tags,
provisioners: [
{
last_seen_at: new Date()
},
],
}
};


export const Logs: Story = {
args: {
templateVersion: {
Expand Down
70 changes: 33 additions & 37 deletions site/src/pages/CreateTemplatePage/BuildLogsDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Interpolation, Theme } from "@emotion/react";
import { type Interpolation, type Theme } from "@emotion/react";
import Close from "@mui/icons-material/Close";
import WarningOutlined from "@mui/icons-material/WarningOutlined";
import Button from "@mui/material/Button";
Expand All @@ -12,9 +12,10 @@ import { useWatchVersionLogs } from "modules/templates/useWatchVersionLogs";
import { WorkspaceBuildLogs } from "modules/workspaces/WorkspaceBuildLogs/WorkspaceBuildLogs";
import { type FC, useLayoutEffect, useRef } from "react";
import { navHeight } from "theme/constants";
import { provisionersUnhealthy, useCompatibleProvisioners } from "modules/provisioners/useCompatibleProvisioners";
import { Alert, AlertTitle } from "@mui/material";
import { AlertDetail } from "components/Alert/Alert";
import { provisionersUnhealthy } from "modules/provisioners/useCompatibleProvisioners";
import { useQuery } from "react-query";
import { provisionerDaemons } from "api/queries/organizations";
import { ProvisionerAlert } from "modules/provisioners/ProvisionerAlert";

type BuildLogsDrawerProps = {
error: unknown;
Expand All @@ -30,11 +31,15 @@ export const BuildLogsDrawer: FC<BuildLogsDrawerProps> = ({
variablesSectionRef,
...drawerProps
}) => {
const compatibleProvisioners = useCompatibleProvisioners(
templateVersion?.organization_id,
templateVersion?.job.tags
const org = templateVersion?.organization_id
const {
data: compatibleProvisioners,
isLoading: provisionerDaemonsLoading,
isError: couldntGetProvisioners,
} = useQuery(
org ? provisionerDaemons(org, templateVersion?.job.tags) : { enabled: false}
);
const compatibleProvisionersUnhealthy = provisionersUnhealthy(compatibleProvisioners);
const compatibleProvisionersUnhealthy = !compatibleProvisioners || provisionersUnhealthy(compatibleProvisioners);

const logs = useWatchVersionLogs(templateVersion);
const logsContainer = useRef<HTMLDivElement>(null);
Expand Down Expand Up @@ -74,35 +79,26 @@ export const BuildLogsDrawer: FC<BuildLogsDrawerProps> = ({
</IconButton>
</header>

{ !compatibleProvisioners && !logs ? (
// If there are no compatible provisioners, warn that this job may be stuck
<Alert
severity="warning"
css={{
borderRadius: 0,
border: 0,
// borderBottom: `1px solid ${theme.palette.divider}`,
// borderLeft: `2px solid ${theme.palette.error.main}`,
}}
>
<AlertTitle>Build stuck</AlertTitle>
<AlertDetail>No Compatible Provisioner Daemons have been configured</AlertDetail>
</Alert>
) : compatibleProvisionersUnhealthy && !logs && (
// If there are compatible provisioners in the db, but they have not reported recent health checks,
// warn that the job might be stuck
<Alert
severity="warning"
css={{
borderRadius: 0,
border: 0,
// borderBottom: `1px solid ${theme.palette.divider}`,
// borderLeft: `2px solid ${theme.palette.error.main}`,
}}
>
<AlertTitle>Build may be delayed</AlertTitle>
<AlertDetail>Compatible Provisioner Daemons have been silent for a while. This may result in a delayed build</AlertDetail>
</Alert>
{ !logs && !provisionerDaemonsLoading && (
couldntGetProvisioners ? (
<ProvisionerAlert
severity="warning"
title="Something went wrong"
detail="Could not determine provisioner status. Your template build may fail. If your template does not build, please contact your administrator"
/>
) : (!compatibleProvisioners || compatibleProvisioners.length == 0) ? (
<ProvisionerAlert
severity="warning"
title="Template Creation Stuck"
detail="This organization does not have any provisioners to process this template. Configure a provisioner."
/>
) : compatibleProvisionersUnhealthy && (
<ProvisionerAlert
severity="warning"
title="Template Creation Delayed"
detail="Provisioners are currently unresponsive. This may delay your template creation. Please contact your administrator for support."
/>
)
)}

{isMissingVariables ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
MockWorkspaceResourceSensitive,
MockWorkspaceVolumeResource,
} from "testHelpers/entities";
import { withDashboardProvider } from "testHelpers/storybook";
import { withDashboardProvider, withProvisioners } from "testHelpers/storybook";
import { TemplateVersionEditor } from "./TemplateVersionEditor";

const meta: Meta<typeof TemplateVersionEditor> = {
Expand Down Expand Up @@ -49,6 +49,101 @@ type Story = StoryObj<typeof TemplateVersionEditor>;

export const Example: Story = {};

export const UndefinedLogs: Story = {
Copy link
Member

Choose a reason for hiding this comment

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

👍

args: {
defaultTab: "logs",
buildLogs: undefined,
templateVersion: {
...MockTemplateVersion,
job: MockRunningProvisionerJob,
},
},
};

export const EmptyLogs: Story = {
args: {
defaultTab: "logs",
buildLogs: [],
templateVersion: {
...MockTemplateVersion,
job: MockRunningProvisionerJob,
},
},
};

export const CouldntGetProvisioners: Story = {
args: {
defaultTab: "logs",
buildLogs: [],
templateVersion: {
...MockTemplateVersion,
job: MockRunningProvisionerJob,
},
},
};

export const NoProvisioners: Story = {
args: {
defaultTab: "logs",
buildLogs: [],
templateVersion: {
...MockTemplateVersion,
job: MockRunningProvisionerJob,
organization_id: "org-id",
},
},
decorators: [withProvisioners],
parameters: {
organization_id: "org-id",
tags: MockRunningProvisionerJob.tags,
provisioners: [],
}
};

export const UnhealthyProvisioners: Story = {
args: {
defaultTab: "logs",
buildLogs: [],
templateVersion: {
...MockTemplateVersion,
job: MockRunningProvisionerJob,
organization_id: "org-id"
},
},
decorators: [withProvisioners],
parameters: {
organization_id: "org-id",
tags: MockRunningProvisionerJob.tags,
provisioners: [
{
last_seen_at: new Date(new Date().getTime() - 5 * 60 * 1000).toISOString()
},
],
}
};

export const HealthyProvisioners: Story = {
args: {
defaultTab: "logs",
buildLogs: [],
templateVersion: {
...MockTemplateVersion,
job: MockRunningProvisionerJob,
organization_id: "org-id"
},
},
decorators: [withProvisioners],
parameters: {
organization_id: "org-id",
tags: MockRunningProvisionerJob.tags,
provisioners: [
{
last_seen_at: new Date(),
},
],
}
};

export const Logs: Story = {
args: {
defaultTab: "logs",
Expand Down
Loading
Loading