mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(site): Display workspace build error + option to retry in debug mode (#6903)
This commit is contained in:
+18
-4
@@ -489,15 +489,29 @@ export const postWorkspaceBuild = async (
|
||||
export const startWorkspace = (
|
||||
workspaceId: string,
|
||||
templateVersionID: string,
|
||||
logLevel?: TypesGen.CreateWorkspaceBuildRequest["log_level"],
|
||||
) =>
|
||||
postWorkspaceBuild(workspaceId, {
|
||||
transition: "start",
|
||||
template_version_id: templateVersionID,
|
||||
log_level: logLevel,
|
||||
})
|
||||
export const stopWorkspace = (
|
||||
workspaceId: string,
|
||||
logLevel?: TypesGen.CreateWorkspaceBuildRequest["log_level"],
|
||||
) =>
|
||||
postWorkspaceBuild(workspaceId, {
|
||||
transition: "stop",
|
||||
log_level: logLevel,
|
||||
})
|
||||
export const deleteWorkspace = (
|
||||
workspaceId: string,
|
||||
logLevel?: TypesGen.CreateWorkspaceBuildRequest["log_level"],
|
||||
) =>
|
||||
postWorkspaceBuild(workspaceId, {
|
||||
transition: "delete",
|
||||
log_level: logLevel,
|
||||
})
|
||||
export const stopWorkspace = (workspaceId: string) =>
|
||||
postWorkspaceBuild(workspaceId, { transition: "stop" })
|
||||
export const deleteWorkspace = (workspaceId: string) =>
|
||||
postWorkspaceBuild(workspaceId, { transition: "delete" })
|
||||
|
||||
export const cancelWorkspaceBuild = async (
|
||||
workspaceBuildId: TypesGen.WorkspaceBuild["id"],
|
||||
|
||||
@@ -60,9 +60,14 @@ export const AlertBanner: FC<React.PropsWithChildren<AlertBannerProps>> = ({
|
||||
spacing={0}
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack direction="row" alignItems="center" spacing={1}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={2}
|
||||
className={classes.fullWidth}
|
||||
>
|
||||
{severityConstants[severity].icon}
|
||||
<Stack spacing={0}>
|
||||
<Stack spacing={0} className={classes.fullWidth}>
|
||||
{children}
|
||||
{alertMessage}
|
||||
{detail && (
|
||||
@@ -94,11 +99,11 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
|
||||
borderColor: severityConstants[props.severity].color,
|
||||
border: `1px solid ${colors.orange[7]}`,
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: `${theme.spacing(1)}px ${theme.spacing(2)}px`,
|
||||
padding: theme.spacing(2),
|
||||
backgroundColor: `${colors.gray[16]}`,
|
||||
textAlign: "left",
|
||||
|
||||
"& span": {
|
||||
"& > span": {
|
||||
paddingTop: `${theme.spacing(0.25)}px`,
|
||||
},
|
||||
|
||||
@@ -108,4 +113,8 @@ const useStyles = makeStyles<Theme, StyleProps>((theme) => ({
|
||||
marginRight: `${theme.spacing(1)}px`,
|
||||
},
|
||||
}),
|
||||
|
||||
fullWidth: {
|
||||
width: "100%",
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -13,8 +13,7 @@ export const severityConstants: Record<
|
||||
color: colors.orange[7],
|
||||
icon: (
|
||||
<ReportProblemOutlinedIcon
|
||||
fontSize="small"
|
||||
style={{ color: colors.orange[7] }}
|
||||
style={{ color: colors.orange[7], fontSize: 16 }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
@@ -22,15 +21,12 @@ export const severityConstants: Record<
|
||||
color: colors.red[7],
|
||||
icon: (
|
||||
<ErrorOutlineOutlinedIcon
|
||||
fontSize="small"
|
||||
style={{ color: colors.red[7] }}
|
||||
style={{ color: colors.red[7], fontSize: 16 }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
info: {
|
||||
color: colors.blue[7],
|
||||
icon: (
|
||||
<InfoOutlinedIcon fontSize="small" style={{ color: colors.blue[7] }} />
|
||||
),
|
||||
icon: <InfoOutlinedIcon style={{ color: colors.blue[7], fontSize: 16 }} />,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ const useStyles = makeStyles<
|
||||
background: theme.palette.background.default,
|
||||
},
|
||||
scrollWrapper: {
|
||||
width: "fit-content",
|
||||
minWidth: "fit-content",
|
||||
},
|
||||
line: {
|
||||
wordBreak: "break-all",
|
||||
@@ -109,7 +109,7 @@ const useStyles = makeStyles<
|
||||
},
|
||||
|
||||
"&.debug": {
|
||||
backgroundColor: theme.palette.grey[900],
|
||||
backgroundColor: theme.palette.background.paperLight,
|
||||
},
|
||||
|
||||
"&.warn": {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { action } from "@storybook/addon-actions"
|
||||
import { Story } from "@storybook/react"
|
||||
import { ProvisionerJobLog } from "api/typesGenerated"
|
||||
import * as Mocks from "../../testHelpers/entities"
|
||||
import { Workspace, WorkspaceErrors, WorkspaceProps } from "./Workspace"
|
||||
|
||||
@@ -73,6 +74,23 @@ Failed.args = {
|
||||
},
|
||||
}
|
||||
|
||||
export const FailedWithLogs = Template.bind({})
|
||||
FailedWithLogs.args = {
|
||||
...Running.args,
|
||||
workspace: {
|
||||
...Mocks.MockFailedWorkspace,
|
||||
latest_build: {
|
||||
...Mocks.MockFailedWorkspace.latest_build,
|
||||
job: {
|
||||
...Mocks.MockFailedWorkspace.latest_build.job,
|
||||
error:
|
||||
"recv workspace provision: plan terraform: terraform plan: exit status 1",
|
||||
},
|
||||
},
|
||||
},
|
||||
failedBuildLogs: makeFailedBuildLogs(),
|
||||
}
|
||||
|
||||
export const Deleting = Template.bind({})
|
||||
Deleting.args = {
|
||||
...Running.args,
|
||||
@@ -122,3 +140,494 @@ CancellationError.args = {
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
function makeFailedBuildLogs(): ProvisionerJobLog[] {
|
||||
return [
|
||||
{
|
||||
id: 2362,
|
||||
created_at: "2023-03-21T15:57:42.637Z",
|
||||
log_source: "provisioner_daemon",
|
||||
log_level: "info",
|
||||
stage: "Setting up",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2363,
|
||||
created_at: "2023-03-21T15:57:42.674Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2364,
|
||||
created_at: "2023-03-21T15:57:42.674Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "Initializing the backend...",
|
||||
},
|
||||
{
|
||||
id: 2365,
|
||||
created_at: "2023-03-21T15:57:42.674Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2366,
|
||||
created_at: "2023-03-21T15:57:42.674Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "Initializing provider plugins...",
|
||||
},
|
||||
{
|
||||
id: 2367,
|
||||
created_at: "2023-03-21T15:57:42.674Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: '- Finding coder/coder versions matching "~\u003e 0.6.17"...',
|
||||
},
|
||||
{
|
||||
id: 2368,
|
||||
created_at: "2023-03-21T15:57:42.84Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
'- Finding kreuzwerker/docker versions matching "~\u003e 3.0.1"...',
|
||||
},
|
||||
{
|
||||
id: 2369,
|
||||
created_at: "2023-03-21T15:57:42.986Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"- Using kreuzwerker/docker v3.0.2 from the shared cache directory",
|
||||
},
|
||||
{
|
||||
id: 2370,
|
||||
created_at: "2023-03-21T15:57:43.03Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "- Using coder/coder v0.6.20 from the shared cache directory",
|
||||
},
|
||||
{
|
||||
id: 2371,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2372,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"Terraform has created a lock file .terraform.lock.hcl to record the provider",
|
||||
},
|
||||
{
|
||||
id: 2373,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"selections it made above. Include this file in your version control repository",
|
||||
},
|
||||
{
|
||||
id: 2374,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"so that Terraform can guarantee to make the same selections by default when",
|
||||
},
|
||||
{
|
||||
id: 2375,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: 'you run "terraform init" in the future.',
|
||||
},
|
||||
{
|
||||
id: 2376,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2377,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2378,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "Warning: Incomplete lock file information for providers",
|
||||
},
|
||||
{
|
||||
id: 2379,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2380,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"Due to your customized provider installation methods, Terraform was forced to",
|
||||
},
|
||||
{
|
||||
id: 2381,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"calculate lock file checksums locally for the following providers:",
|
||||
},
|
||||
{
|
||||
id: 2382,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: " - coder/coder",
|
||||
},
|
||||
{
|
||||
id: 2383,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: " - kreuzwerker/docker",
|
||||
},
|
||||
{
|
||||
id: 2384,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2385,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"The current .terraform.lock.hcl file only includes checksums for linux_amd64,",
|
||||
},
|
||||
{
|
||||
id: 2386,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"so Terraform running on another platform will fail to install these",
|
||||
},
|
||||
{
|
||||
id: 2387,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "providers.",
|
||||
},
|
||||
{
|
||||
id: 2388,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2389,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "To calculate additional checksums for another platform, run:",
|
||||
},
|
||||
{
|
||||
id: 2390,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: " terraform providers lock -platform=linux_amd64",
|
||||
},
|
||||
{
|
||||
id: 2391,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "(where linux_amd64 is the platform to generate)",
|
||||
},
|
||||
{
|
||||
id: 2392,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2393,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "Terraform has been successfully initialized!",
|
||||
},
|
||||
{
|
||||
id: 2394,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2395,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
'You may now begin working with Terraform. Try running "terraform plan" to see',
|
||||
},
|
||||
{
|
||||
id: 2396,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"any changes that are required for your infrastructure. All Terraform commands",
|
||||
},
|
||||
{
|
||||
id: 2397,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "should now work.",
|
||||
},
|
||||
{
|
||||
id: 2398,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2399,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"If you ever set or change modules or backend configuration for Terraform,",
|
||||
},
|
||||
{
|
||||
id: 2400,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"rerun this command to reinitialize your working directory. If you forget, other",
|
||||
},
|
||||
{
|
||||
id: 2401,
|
||||
created_at: "2023-03-21T15:57:43.059Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "debug",
|
||||
stage: "Planning infrastructure",
|
||||
output: "commands will detect it and remind you to do so if necessary.",
|
||||
},
|
||||
{
|
||||
id: 2402,
|
||||
created_at: "2023-03-21T15:57:43.078Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output: "Terraform 1.3.4",
|
||||
},
|
||||
{
|
||||
id: 2403,
|
||||
created_at: "2023-03-21T15:57:43.401Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output: "data.coder_provisioner.me: Refreshing...",
|
||||
},
|
||||
{
|
||||
id: 2404,
|
||||
created_at: "2023-03-21T15:57:43.402Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output: "data.coder_workspace.me: Refreshing...",
|
||||
},
|
||||
{
|
||||
id: 2405,
|
||||
created_at: "2023-03-21T15:57:43.402Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output: "data.coder_parameter.security_groups: Refreshing...",
|
||||
},
|
||||
{
|
||||
id: 2406,
|
||||
created_at: "2023-03-21T15:57:43.402Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"data.coder_provisioner.me: Refresh complete after 0s [id=993f697b-3948-4d31-8377-6c86edc90a83]",
|
||||
},
|
||||
{
|
||||
id: 2407,
|
||||
created_at: "2023-03-21T15:57:43.403Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"data.coder_workspace.me: Refresh complete after 0s [id=ca18ddca-14b5-4f5f-be55-7bfd2e3c2dc9]",
|
||||
},
|
||||
{
|
||||
id: 2408,
|
||||
created_at: "2023-03-21T15:57:43.403Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"data.coder_parameter.security_groups: Refresh complete after 0s [id=9832a15f-267b-4abf-9c23-e4265af0befa]",
|
||||
},
|
||||
{
|
||||
id: 2409,
|
||||
created_at: "2023-03-21T15:57:43.405Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"coder_agent.main: Refreshing state... [id=6c3718cb-605b-4b68-b26f-46dba8767f43]",
|
||||
},
|
||||
{
|
||||
id: 2410,
|
||||
created_at: "2023-03-21T15:57:43.406Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"coder_agent.main: Refresh complete [id=6c3718cb-605b-4b68-b26f-46dba8767f43]",
|
||||
},
|
||||
{
|
||||
id: 2411,
|
||||
created_at: "2023-03-21T15:57:43.407Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"docker_volume.home_volume: Refreshing state... [id=coder-ca18ddca-14b5-4f5f-be55-7bfd2e3c2dc9-home]",
|
||||
},
|
||||
{
|
||||
id: 2412,
|
||||
created_at: "2023-03-21T15:57:43.41Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"coder_app.code-server: Refreshing state... [id=4a45a1cc-9861-4a9c-bd2f-3a2f1abc4c65]",
|
||||
},
|
||||
{
|
||||
id: 2413,
|
||||
created_at: "2023-03-21T15:57:43.411Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "info",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"coder_app.code-server: Refresh complete [id=4a45a1cc-9861-4a9c-bd2f-3a2f1abc4c65]",
|
||||
},
|
||||
{
|
||||
id: 2414,
|
||||
created_at: "2023-03-21T15:57:43.417Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "error",
|
||||
stage: "Planning infrastructure",
|
||||
output:
|
||||
"Error: Unable to inspect volume: Error: No such volume: coder-ca18ddca-14b5-4f5f-be55-7bfd2e3c2dc9-home",
|
||||
},
|
||||
{
|
||||
id: 2415,
|
||||
created_at: "2023-03-21T15:57:43.418Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "error",
|
||||
stage: "Planning infrastructure",
|
||||
output: 'on main.tf line 61, in resource "docker_volume" "home_volume":',
|
||||
},
|
||||
{
|
||||
id: 2416,
|
||||
created_at: "2023-03-21T15:57:43.418Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "error",
|
||||
stage: "Planning infrastructure",
|
||||
output: ' 61: resource "docker_volume" "home_volume" {',
|
||||
},
|
||||
{
|
||||
id: 2417,
|
||||
created_at: "2023-03-21T15:57:43.418Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "error",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2418,
|
||||
created_at: "2023-03-21T15:57:43.419Z",
|
||||
log_source: "provisioner",
|
||||
log_level: "error",
|
||||
stage: "Planning infrastructure",
|
||||
output: "",
|
||||
},
|
||||
{
|
||||
id: 2419,
|
||||
created_at: "2023-03-21T15:57:43.422Z",
|
||||
log_source: "provisioner_daemon",
|
||||
log_level: "info",
|
||||
stage: "Cleaning Up",
|
||||
output: "",
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import Button from "@material-ui/core/Button"
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import RefreshOutlined from "@material-ui/icons/RefreshOutlined"
|
||||
import { Avatar } from "components/Avatar/Avatar"
|
||||
import { AgentRow } from "components/Resources/AgentRow"
|
||||
import { WorkspaceBuildLogs } from "components/WorkspaceBuildLogs/WorkspaceBuildLogs"
|
||||
import {
|
||||
ActiveTransition,
|
||||
WorkspaceBuildProgress,
|
||||
} from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
|
||||
import { WorkspaceStatusBadge } from "components/WorkspaceStatusBadge/WorkspaceStatusBadge"
|
||||
import { FC } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import * as TypesGen from "../../api/typesGenerated"
|
||||
import { AlertBanner } from "../AlertBanner/AlertBanner"
|
||||
@@ -48,6 +52,7 @@ export interface WorkspaceProps {
|
||||
resources?: TypesGen.WorkspaceResource[]
|
||||
builds?: TypesGen.WorkspaceBuild[]
|
||||
canUpdateWorkspace: boolean
|
||||
canUpdateTemplate: boolean
|
||||
hideSSHButton?: boolean
|
||||
hideVSCodeDesktopButton?: boolean
|
||||
workspaceErrors: Partial<Record<WorkspaceErrors, Error | unknown>>
|
||||
@@ -56,6 +61,8 @@ export interface WorkspaceProps {
|
||||
sshPrefix?: string
|
||||
template?: TypesGen.Template
|
||||
quota_budget?: number
|
||||
failedBuildLogs: TypesGen.ProvisionerJobLog[] | undefined
|
||||
handleBuildRetry: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -74,6 +81,7 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
|
||||
resources,
|
||||
builds,
|
||||
canUpdateWorkspace,
|
||||
canUpdateTemplate,
|
||||
workspaceErrors,
|
||||
hideSSHButton,
|
||||
hideVSCodeDesktopButton,
|
||||
@@ -82,10 +90,13 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
|
||||
sshPrefix,
|
||||
template,
|
||||
quota_budget,
|
||||
failedBuildLogs,
|
||||
handleBuildRetry,
|
||||
}) => {
|
||||
const styles = useStyles()
|
||||
const navigate = useNavigate()
|
||||
const serverVersion = buildInfo?.version || ""
|
||||
const { t } = useTranslation("workspacePage")
|
||||
|
||||
const buildError = Boolean(workspaceErrors[WorkspaceErrors.BUILD_ERROR]) && (
|
||||
<AlertBanner
|
||||
@@ -179,6 +190,40 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
|
||||
handleUpdate={handleUpdate}
|
||||
/>
|
||||
|
||||
{failedBuildLogs && (
|
||||
<Stack>
|
||||
<AlertBanner severity="error">
|
||||
<Stack
|
||||
className={styles.fullWidth}
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
<Stack spacing={0}>
|
||||
<span>Workspace build failed</span>
|
||||
<span className={styles.errorDetails}>
|
||||
{workspace.latest_build.job.error}
|
||||
</span>
|
||||
</Stack>
|
||||
|
||||
{canUpdateTemplate && (
|
||||
<div>
|
||||
<Button
|
||||
onClick={handleBuildRetry}
|
||||
startIcon={<RefreshOutlined />}
|
||||
size="small"
|
||||
variant="outlined"
|
||||
>
|
||||
{t("actionButton.retryDebugMode")}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</Stack>
|
||||
</AlertBanner>
|
||||
<WorkspaceBuildLogs logs={failedBuildLogs} />
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{transitionStats !== undefined && (
|
||||
<WorkspaceBuildProgress
|
||||
workspace={workspace}
|
||||
@@ -255,5 +300,14 @@ export const useStyles = makeStyles((theme) => {
|
||||
logs: {
|
||||
border: `1px solid ${theme.palette.divider}`,
|
||||
},
|
||||
|
||||
errorDetails: {
|
||||
color: theme.palette.text.secondary,
|
||||
fontSize: 12,
|
||||
},
|
||||
|
||||
fullWidth: {
|
||||
width: "100%",
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
@@ -104,6 +104,7 @@ const useStyles = makeStyles<
|
||||
padding: theme.spacing(2),
|
||||
paddingLeft: theme.spacing(3),
|
||||
paddingRight: theme.spacing(3),
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
borderBottom: `1px solid ${theme.palette.divider}`,
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
display: "flex",
|
||||
@@ -113,6 +114,7 @@ const useStyles = makeStyles<
|
||||
"&:first-child": {
|
||||
borderTopLeftRadius: theme.shape.borderRadius,
|
||||
borderTopRightRadius: theme.shape.borderRadius,
|
||||
borderTop: 0,
|
||||
},
|
||||
|
||||
"&:last-child": {
|
||||
@@ -121,6 +123,10 @@ const useStyles = makeStyles<
|
||||
borderBottomLeftRadius: theme.shape.borderRadius,
|
||||
borderBottomRightRadius: theme.shape.borderRadius,
|
||||
},
|
||||
|
||||
"& + $header": {
|
||||
borderTop: 0,
|
||||
},
|
||||
},
|
||||
|
||||
duration: {
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"starting": "Starting...",
|
||||
"stopping": "Stopping...",
|
||||
"deleting": "Deleting...",
|
||||
"settings": "Settings"
|
||||
"settings": "Settings",
|
||||
"retryDebugMode": "Try again in debug mode"
|
||||
},
|
||||
"disabledButton": {
|
||||
"canceling": "Canceling",
|
||||
|
||||
@@ -1,22 +1,46 @@
|
||||
import { makeStyles } from "@material-ui/core/styles"
|
||||
import { useQuery } from "@tanstack/react-query"
|
||||
import { useMachine } from "@xstate/react"
|
||||
import { getWorkspaceBuildLogs } from "api/api"
|
||||
import { Workspace } from "api/typesGenerated"
|
||||
import { AlertBanner } from "components/AlertBanner/AlertBanner"
|
||||
import { ChooseOne, Cond } from "components/Conditionals/ChooseOne"
|
||||
import { Loader } from "components/Loader/Loader"
|
||||
import { FC, useEffect } from "react"
|
||||
import { FC, useRef } from "react"
|
||||
import { useParams } from "react-router-dom"
|
||||
import { firstOrItem } from "util/array"
|
||||
import { quotaMachine } from "xServices/quotas/quotasXService"
|
||||
import { workspaceMachine } from "xServices/workspace/workspaceXService"
|
||||
import { WorkspaceReadyPage } from "./WorkspaceReadyPage"
|
||||
import { RequirePermission } from "components/RequirePermission/RequirePermission"
|
||||
|
||||
const useFailedBuildLogs = (workspace: Workspace | undefined) => {
|
||||
const now = useRef(new Date())
|
||||
return useQuery({
|
||||
queryKey: ["logs", workspace?.latest_build.id],
|
||||
queryFn: () => {
|
||||
if (!workspace) {
|
||||
throw new Error(
|
||||
`Build log query being called before workspace is defined`,
|
||||
)
|
||||
}
|
||||
|
||||
return getWorkspaceBuildLogs(workspace.latest_build.id, now.current)
|
||||
},
|
||||
enabled: workspace?.latest_build.job.error !== undefined,
|
||||
})
|
||||
}
|
||||
|
||||
export const WorkspacePage: FC = () => {
|
||||
const { username: usernameQueryParam, workspace: workspaceQueryParam } =
|
||||
useParams()
|
||||
const username = firstOrItem(usernameQueryParam, null)
|
||||
const workspaceName = firstOrItem(workspaceQueryParam, null)
|
||||
const [workspaceState, workspaceSend] = useMachine(workspaceMachine)
|
||||
const { username, workspace: workspaceName } = useParams() as {
|
||||
username: string
|
||||
workspace: string
|
||||
}
|
||||
const [workspaceState, workspaceSend] = useMachine(workspaceMachine, {
|
||||
context: {
|
||||
workspaceName,
|
||||
username,
|
||||
},
|
||||
})
|
||||
const {
|
||||
workspace,
|
||||
getWorkspaceError,
|
||||
@@ -24,23 +48,10 @@ export const WorkspacePage: FC = () => {
|
||||
getTemplateParametersWarning,
|
||||
checkPermissionsError,
|
||||
} = workspaceState.context
|
||||
const [quotaState, quotaSend] = useMachine(quotaMachine)
|
||||
const [quotaState] = useMachine(quotaMachine, { context: { username } })
|
||||
const { getQuotaError } = quotaState.context
|
||||
const styles = useStyles()
|
||||
|
||||
/**
|
||||
* Get workspace, template, and organization on mount and whenever workspaceId changes.
|
||||
* workspaceSend should not change.
|
||||
*/
|
||||
useEffect(() => {
|
||||
username &&
|
||||
workspaceName &&
|
||||
workspaceSend({ type: "GET_WORKSPACE", username, workspaceName })
|
||||
}, [username, workspaceName, workspaceSend])
|
||||
|
||||
useEffect(() => {
|
||||
username && quotaSend({ type: "GET_QUOTA", username })
|
||||
}, [username, quotaSend])
|
||||
const failedBuildLogs = useFailedBuildLogs(workspace)
|
||||
|
||||
return (
|
||||
<RequirePermission
|
||||
@@ -77,6 +88,7 @@ export const WorkspacePage: FC = () => {
|
||||
}
|
||||
>
|
||||
<WorkspaceReadyPage
|
||||
failedBuildLogs={failedBuildLogs.data}
|
||||
workspaceState={workspaceState}
|
||||
quotaState={quotaState}
|
||||
workspaceSend={workspaceSend}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useActor } from "@xstate/react"
|
||||
import { ProvisionerJobLog } from "api/typesGenerated"
|
||||
import { useDashboard } from "components/Dashboard/DashboardProvider"
|
||||
import dayjs from "dayjs"
|
||||
import { useFeatureVisibility } from "hooks/useFeatureVisibility"
|
||||
@@ -31,11 +32,13 @@ interface WorkspaceReadyPageProps {
|
||||
workspaceState: StateFrom<typeof workspaceMachine>
|
||||
quotaState: StateFrom<typeof quotaMachine>
|
||||
workspaceSend: (event: WorkspaceEvent) => void
|
||||
failedBuildLogs: ProvisionerJobLog[] | undefined
|
||||
}
|
||||
|
||||
export const WorkspaceReadyPage = ({
|
||||
workspaceState,
|
||||
quotaState,
|
||||
failedBuildLogs,
|
||||
workspaceSend,
|
||||
}: WorkspaceReadyPageProps): JSX.Element => {
|
||||
const [_, bannerSend] = useActor(
|
||||
@@ -60,6 +63,7 @@ export const WorkspaceReadyPage = ({
|
||||
}
|
||||
const deadline = getDeadline(workspace)
|
||||
const canUpdateWorkspace = Boolean(permissions?.updateWorkspace)
|
||||
const canUpdateTemplate = Boolean(permissions?.updateTemplate)
|
||||
const { t } = useTranslation("workspacePage")
|
||||
const favicon = getFaviconByStatus(workspace.latest_build)
|
||||
const navigate = useNavigate()
|
||||
@@ -86,6 +90,7 @@ export const WorkspaceReadyPage = ({
|
||||
</Helmet>
|
||||
|
||||
<Workspace
|
||||
failedBuildLogs={failedBuildLogs}
|
||||
scheduleProps={{
|
||||
onDeadlineMinus: (hours: number) => {
|
||||
bannerSend({
|
||||
@@ -113,9 +118,11 @@ export const WorkspaceReadyPage = ({
|
||||
handleUpdate={() => workspaceSend({ type: "UPDATE" })}
|
||||
handleCancel={() => workspaceSend({ type: "CANCEL" })}
|
||||
handleSettings={() => navigate("settings")}
|
||||
handleBuildRetry={() => workspaceSend({ type: "RETRY_BUILD" })}
|
||||
resources={workspace.latest_build.resources}
|
||||
builds={builds}
|
||||
canUpdateWorkspace={canUpdateWorkspace}
|
||||
canUpdateTemplate={canUpdateTemplate}
|
||||
hideSSHButton={featureVisibility["browser_only"]}
|
||||
hideVSCodeDesktopButton={featureVisibility["browser_only"]}
|
||||
workspaceErrors={{
|
||||
|
||||
@@ -3,15 +3,11 @@ import * as API from "../../api/api"
|
||||
import { WorkspaceQuota } from "../../api/typesGenerated"
|
||||
|
||||
export type QuotaContext = {
|
||||
username: string
|
||||
quota?: WorkspaceQuota
|
||||
getQuotaError?: Error | unknown
|
||||
}
|
||||
|
||||
export type QuotaEvent = {
|
||||
type: "GET_QUOTA"
|
||||
username: string
|
||||
}
|
||||
|
||||
export const quotaMachine = createMachine(
|
||||
{
|
||||
id: "quotasMachine",
|
||||
@@ -19,21 +15,15 @@ export const quotaMachine = createMachine(
|
||||
tsTypes: {} as import("./quotasXService.typegen").Typegen0,
|
||||
schema: {
|
||||
context: {} as QuotaContext,
|
||||
events: {} as QuotaEvent,
|
||||
services: {
|
||||
getQuota: {
|
||||
data: {} as WorkspaceQuota,
|
||||
},
|
||||
},
|
||||
},
|
||||
context: {},
|
||||
initial: "idle",
|
||||
initial: "gettingQuotas",
|
||||
states: {
|
||||
idle: {
|
||||
on: {
|
||||
GET_QUOTA: "gettingQuotas",
|
||||
},
|
||||
},
|
||||
idle: {},
|
||||
gettingQuotas: {
|
||||
entry: "clearGetQuotaError",
|
||||
invoke: {
|
||||
@@ -67,7 +57,7 @@ export const quotaMachine = createMachine(
|
||||
}),
|
||||
},
|
||||
services: {
|
||||
getQuota: (context, event) => API.getWorkspaceQuota(event.username),
|
||||
getQuota: ({ username }) => API.getWorkspaceQuota(username),
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -52,6 +52,9 @@ const Language = {
|
||||
type Permissions = Record<keyof ReturnType<typeof permissionsToCheck>, boolean>
|
||||
|
||||
export interface WorkspaceContext {
|
||||
// Initial data
|
||||
username: string
|
||||
workspaceName: string
|
||||
// our server side events instance
|
||||
eventSource?: EventSource
|
||||
workspace?: TypesGen.Workspace
|
||||
@@ -73,12 +76,13 @@ export interface WorkspaceContext {
|
||||
checkPermissionsError?: Error | unknown
|
||||
// applications
|
||||
applicationsHost?: string
|
||||
// debug
|
||||
createBuildLogLevel?: TypesGen.CreateWorkspaceBuildRequest["log_level"]
|
||||
// SSH Config
|
||||
sshPrefix?: string
|
||||
}
|
||||
|
||||
export type WorkspaceEvent =
|
||||
| { type: "GET_WORKSPACE"; workspaceName: string; username: string }
|
||||
| { type: "REFRESH_WORKSPACE"; data: TypesGen.ServerSentEvent["data"] }
|
||||
| { type: "START" }
|
||||
| { type: "STOP" }
|
||||
@@ -95,13 +99,18 @@ export type WorkspaceEvent =
|
||||
| { type: "EVENT_SOURCE_ERROR"; error: Error | unknown }
|
||||
| { type: "INCREASE_DEADLINE"; hours: number }
|
||||
| { type: "DECREASE_DEADLINE"; hours: number }
|
||||
| { type: "RETRY_BUILD" }
|
||||
|
||||
export const checks = {
|
||||
readWorkspace: "readWorkspace",
|
||||
updateWorkspace: "updateWorkspace",
|
||||
updateTemplate: "updateTemplate",
|
||||
} as const
|
||||
|
||||
const permissionsToCheck = (workspace: TypesGen.Workspace) => ({
|
||||
const permissionsToCheck = (
|
||||
workspace: TypesGen.Workspace,
|
||||
template: TypesGen.Template,
|
||||
) => ({
|
||||
[checks.readWorkspace]: {
|
||||
object: {
|
||||
resource_type: "workspace",
|
||||
@@ -118,6 +127,13 @@ const permissionsToCheck = (workspace: TypesGen.Workspace) => ({
|
||||
},
|
||||
action: "update",
|
||||
},
|
||||
[checks.updateTemplate]: {
|
||||
object: {
|
||||
resource_type: "template",
|
||||
resource_id: template.id,
|
||||
},
|
||||
action: "update",
|
||||
},
|
||||
})
|
||||
|
||||
export const workspaceMachine = createMachine(
|
||||
@@ -170,17 +186,8 @@ export const workspaceMachine = createMachine(
|
||||
}
|
||||
},
|
||||
},
|
||||
initial: "idle",
|
||||
on: {
|
||||
GET_WORKSPACE: {
|
||||
target: ".gettingWorkspace",
|
||||
internal: false,
|
||||
},
|
||||
},
|
||||
initial: "gettingWorkspace",
|
||||
states: {
|
||||
idle: {
|
||||
tags: "loading",
|
||||
},
|
||||
gettingWorkspace: {
|
||||
entry: ["clearContext"],
|
||||
invoke: {
|
||||
@@ -284,6 +291,23 @@ export const workspaceMachine = createMachine(
|
||||
ASK_DELETE: "askingDelete",
|
||||
UPDATE: "requestingUpdate",
|
||||
CANCEL: "requestingCancel",
|
||||
RETRY_BUILD: [
|
||||
{
|
||||
target: "requestingStart",
|
||||
cond: "lastBuildWasStarting",
|
||||
actions: ["enableDebugMode"],
|
||||
},
|
||||
{
|
||||
target: "requestingStop",
|
||||
cond: "lastBuildWasStopping",
|
||||
actions: ["enableDebugMode"],
|
||||
},
|
||||
{
|
||||
target: "requestingDelete",
|
||||
cond: "lastBuildWasDeleting",
|
||||
actions: ["enableDebugMode"],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
askingDelete: {
|
||||
@@ -330,7 +354,7 @@ export const workspaceMachine = createMachine(
|
||||
id: "startWorkspace",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignBuild"],
|
||||
actions: ["assignBuild", "disableDebugMode"],
|
||||
target: "idle",
|
||||
},
|
||||
],
|
||||
@@ -349,7 +373,7 @@ export const workspaceMachine = createMachine(
|
||||
id: "stopWorkspace",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignBuild"],
|
||||
actions: ["assignBuild", "disableDebugMode"],
|
||||
target: "idle",
|
||||
},
|
||||
],
|
||||
@@ -368,7 +392,7 @@ export const workspaceMachine = createMachine(
|
||||
id: "deleteWorkspace",
|
||||
onDone: [
|
||||
{
|
||||
actions: ["assignBuild"],
|
||||
actions: ["assignBuild", "disableDebugMode"],
|
||||
target: "idle",
|
||||
},
|
||||
],
|
||||
@@ -489,7 +513,7 @@ export const workspaceMachine = createMachine(
|
||||
schedule: {
|
||||
invoke: {
|
||||
id: "scheduleBannerMachine",
|
||||
src: workspaceScheduleBannerMachine,
|
||||
src: "scheduleBannerMachine",
|
||||
data: {
|
||||
workspace: (context: WorkspaceContext) => context.workspace,
|
||||
},
|
||||
@@ -498,11 +522,7 @@ export const workspaceMachine = createMachine(
|
||||
},
|
||||
},
|
||||
error: {
|
||||
on: {
|
||||
GET_WORKSPACE: {
|
||||
target: "gettingWorkspace",
|
||||
},
|
||||
},
|
||||
type: "final",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -646,22 +666,30 @@ export const workspaceMachine = createMachine(
|
||||
return data.parameters
|
||||
},
|
||||
}),
|
||||
// Debug mode when build fails
|
||||
enableDebugMode: assign({ createBuildLogLevel: (_) => "debug" as const }),
|
||||
disableDebugMode: assign({ createBuildLogLevel: (_) => undefined }),
|
||||
},
|
||||
guards: {
|
||||
moreBuildsAvailable,
|
||||
isMissingBuildParameterError: (_, { data }) => {
|
||||
return data instanceof API.MissingBuildParameters
|
||||
},
|
||||
lastBuildWasStarting: ({ workspace }) => {
|
||||
return workspace?.latest_build.transition === "start"
|
||||
},
|
||||
lastBuildWasStopping: ({ workspace }) => {
|
||||
return workspace?.latest_build.transition === "stop"
|
||||
},
|
||||
lastBuildWasDeleting: ({ workspace }) => {
|
||||
return workspace?.latest_build.transition === "delete"
|
||||
},
|
||||
},
|
||||
services: {
|
||||
getWorkspace: async (_, event) => {
|
||||
return await API.getWorkspaceByOwnerAndName(
|
||||
event.username,
|
||||
event.workspaceName,
|
||||
{
|
||||
include_deleted: true,
|
||||
},
|
||||
)
|
||||
getWorkspace: async ({ username, workspaceName }) => {
|
||||
return await API.getWorkspaceByOwnerAndName(username, workspaceName, {
|
||||
include_deleted: true,
|
||||
})
|
||||
},
|
||||
getTemplate: async (context) => {
|
||||
if (context.workspace) {
|
||||
@@ -685,6 +713,7 @@ export const workspaceMachine = createMachine(
|
||||
const startWorkspacePromise = await API.startWorkspace(
|
||||
context.workspace.id,
|
||||
context.workspace.latest_build.template_version_id,
|
||||
context.createBuildLogLevel,
|
||||
)
|
||||
send({ type: "REFRESH_TIMELINE" })
|
||||
return startWorkspacePromise
|
||||
@@ -696,6 +725,7 @@ export const workspaceMachine = createMachine(
|
||||
if (context.workspace) {
|
||||
const stopWorkspacePromise = await API.stopWorkspace(
|
||||
context.workspace.id,
|
||||
context.createBuildLogLevel,
|
||||
)
|
||||
send({ type: "REFRESH_TIMELINE" })
|
||||
return stopWorkspacePromise
|
||||
@@ -707,6 +737,7 @@ export const workspaceMachine = createMachine(
|
||||
if (context.workspace) {
|
||||
const deleteWorkspacePromise = await API.deleteWorkspace(
|
||||
context.workspace.id,
|
||||
context.createBuildLogLevel,
|
||||
)
|
||||
send({ type: "REFRESH_TIMELINE" })
|
||||
return deleteWorkspacePromise
|
||||
@@ -765,18 +796,21 @@ export const workspaceMachine = createMachine(
|
||||
throw Error("Cannot get builds without id")
|
||||
}
|
||||
},
|
||||
checkPermissions: async (context) => {
|
||||
if (context.workspace) {
|
||||
return await API.checkAuthorization({
|
||||
checks: permissionsToCheck(context.workspace),
|
||||
})
|
||||
} else {
|
||||
throw Error("Cannot check permissions workspace id")
|
||||
checkPermissions: async ({ workspace, template }) => {
|
||||
if (!workspace) {
|
||||
throw new Error("Workspace is not set")
|
||||
}
|
||||
if (!template) {
|
||||
throw new Error("Template is not set")
|
||||
}
|
||||
return await API.checkAuthorization({
|
||||
checks: permissionsToCheck(workspace, template),
|
||||
})
|
||||
},
|
||||
getApplicationsHost: async () => {
|
||||
return API.getApplicationsHost()
|
||||
},
|
||||
scheduleBannerMachine: workspaceScheduleBannerMachine,
|
||||
getSSHPrefix: async () => {
|
||||
return API.getDeploymentSSHConfig()
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user