mirror of https://github.com/coder/coder.git
fix(site): fix template schedule update overriding other settings (#13114)
This commit is contained in:
parent
f2dd0a8e5d
commit
71a03a8b1d
|
@ -232,7 +232,7 @@ type UpdateTemplateMeta struct {
|
|||
// RequireActiveVersion mandates workspaces built using this template
|
||||
// use the active version of the template. This option has no
|
||||
// effect on template admins.
|
||||
RequireActiveVersion bool `json:"require_active_version"`
|
||||
RequireActiveVersion bool `json:"require_active_version,omitempty"`
|
||||
// DeprecationMessage if set, will mark the template as deprecated and block
|
||||
// any new workspaces from using this template.
|
||||
// If passed an empty string, will remove the deprecated message, making
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
import { beforeCoderTest } from "../hooks";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(({ page }) => beforeCoderTest(page));
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import { expect, test } from "@playwright/test";
|
||||
import { createTemplate, createTemplateVersion, getTemplate } from "api/api";
|
||||
import { getCurrentOrgId, setupApiCalls } from "../../api";
|
||||
import { beforeCoderTest } from "../../hooks";
|
||||
|
||||
test.beforeEach(({ page }) => beforeCoderTest(page));
|
||||
|
||||
test("update template schedule settings without override other settings", async ({
|
||||
page,
|
||||
baseURL,
|
||||
}) => {
|
||||
await setupApiCalls(page);
|
||||
const orgId = await getCurrentOrgId();
|
||||
const templateVersion = await createTemplateVersion(orgId, {
|
||||
storage_method: "file" as const,
|
||||
provisioner: "echo",
|
||||
user_variable_values: [],
|
||||
example_id: "docker",
|
||||
tags: {},
|
||||
});
|
||||
const template = await createTemplate(orgId, {
|
||||
name: "test-template",
|
||||
display_name: "Test Template",
|
||||
template_version_id: templateVersion.id,
|
||||
disable_everyone_group_access: false,
|
||||
require_active_version: true,
|
||||
});
|
||||
|
||||
await page.goto(`${baseURL}/templates/${template.name}/settings/schedule`, {
|
||||
waitUntil: "domcontentloaded",
|
||||
});
|
||||
await page.getByLabel("Default autostop (hours)").fill("48");
|
||||
await page.getByRole("button", { name: "Submit" }).click();
|
||||
await expect(page.getByText("Template updated successfully")).toBeVisible();
|
||||
|
||||
const updatedTemplate = await getTemplate(template.id);
|
||||
// Validate that the template data remains consistent, with the exception of
|
||||
// the 'default_ttl_ms' field (updated during the test) and the 'updated at'
|
||||
// field (automatically updated by the backend).
|
||||
expect({
|
||||
...template,
|
||||
default_ttl_ms: 48 * 60 * 60 * 1000,
|
||||
updated_at: updatedTemplate.updated_at,
|
||||
}).toStrictEqual(updatedTemplate);
|
||||
});
|
|
@ -1320,7 +1320,7 @@ export interface UpdateTemplateMeta {
|
|||
readonly time_til_dormant_autodelete_ms?: number;
|
||||
readonly update_workspace_last_used_at: boolean;
|
||||
readonly update_workspace_dormant_at: boolean;
|
||||
readonly require_active_version: boolean;
|
||||
readonly require_active_version?: boolean;
|
||||
readonly deprecation_message?: string;
|
||||
readonly disable_everyone_group_access: boolean;
|
||||
readonly max_port_share_level?: WorkspaceAgentPortShareLevel;
|
||||
|
|
|
@ -22,9 +22,13 @@ export const StackLabel: FC<ComponentProps<typeof Stack>> = (props) => {
|
|||
export const StackLabelHelperText: FC<FormHelperTextProps> = (props) => {
|
||||
return (
|
||||
<FormHelperText
|
||||
css={{
|
||||
css={(theme) => ({
|
||||
marginTop: 0,
|
||||
}}
|
||||
|
||||
"& strong": {
|
||||
color: theme.palette.text.primary,
|
||||
},
|
||||
})}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import type { Interpolation, Theme } from "@emotion/react";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import FormHelperText from "@mui/material/FormHelperText";
|
||||
import MenuItem from "@mui/material/MenuItem";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import { type FormikContextType, type FormikTouched, useFormik } from "formik";
|
||||
|
@ -17,14 +18,12 @@ import {
|
|||
HorizontalForm,
|
||||
FormFooter,
|
||||
} from "components/Form/Form";
|
||||
import {
|
||||
HelpTooltip,
|
||||
HelpTooltipContent,
|
||||
HelpTooltipText,
|
||||
HelpTooltipTrigger,
|
||||
} from "components/HelpTooltip/HelpTooltip";
|
||||
import { IconField } from "components/IconField/IconField";
|
||||
import { Stack } from "components/Stack/Stack";
|
||||
import {
|
||||
StackLabel,
|
||||
StackLabelHelperText,
|
||||
} from "components/StackLabel/StackLabel";
|
||||
import {
|
||||
getFormHelpers,
|
||||
nameValidator,
|
||||
|
@ -160,92 +159,74 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||
title="Operations"
|
||||
description="Regulate actions allowed on workspaces created from this template."
|
||||
>
|
||||
<Stack direction="column" spacing={5}>
|
||||
<label htmlFor="allow_user_cancel_workspace_jobs">
|
||||
<Stack direction="row" spacing={1}>
|
||||
<FormFields spacing={6}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
size="small"
|
||||
id="allow_user_cancel_workspace_jobs"
|
||||
name="allow_user_cancel_workspace_jobs"
|
||||
disabled={isSubmitting}
|
||||
checked={form.values.allow_user_cancel_workspace_jobs}
|
||||
onChange={form.handleChange}
|
||||
/>
|
||||
|
||||
<Stack direction="column" spacing={0.5}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0.5}
|
||||
css={styles.optionText}
|
||||
>
|
||||
Allow users to cancel in-progress workspace jobs.
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
If checked, users may be able to corrupt their
|
||||
workspace.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span css={styles.optionHelperText}>
|
||||
}
|
||||
label={
|
||||
<StackLabel>
|
||||
Allow users to cancel in-progress workspace jobs.
|
||||
<StackLabelHelperText>
|
||||
Depending on your template, canceling builds may leave
|
||||
workspaces in an unhealthy state. This option isn't
|
||||
recommended for most use cases.
|
||||
</span>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</label>
|
||||
<Stack spacing={2}>
|
||||
<label htmlFor="require_active_version">
|
||||
<Stack direction="row" spacing={1}>
|
||||
<Checkbox
|
||||
id="require_active_version"
|
||||
name="require_active_version"
|
||||
checked={form.values.require_active_version}
|
||||
onChange={form.handleChange}
|
||||
disabled={
|
||||
!template.require_active_version &&
|
||||
!advancedSchedulingEnabled
|
||||
}
|
||||
/>
|
||||
recommended for most use cases.{" "}
|
||||
<strong>
|
||||
If checked, users may be able to corrupt their workspace.
|
||||
</strong>
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
}
|
||||
/>
|
||||
|
||||
<Stack direction="column" spacing={0.5}>
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
spacing={0.5}
|
||||
css={styles.optionText}
|
||||
>
|
||||
Require workspaces automatically update when started.
|
||||
<HelpTooltip>
|
||||
<HelpTooltipTrigger />
|
||||
<HelpTooltipContent>
|
||||
<HelpTooltipText>
|
||||
This setting is not enforced for template admins.
|
||||
</HelpTooltipText>
|
||||
</HelpTooltipContent>
|
||||
</HelpTooltip>
|
||||
</Stack>
|
||||
<span css={styles.optionHelperText}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Checkbox
|
||||
size="small"
|
||||
id="require_active_version"
|
||||
name="require_active_version"
|
||||
checked={form.values.require_active_version}
|
||||
onChange={form.handleChange}
|
||||
disabled={
|
||||
!template.require_active_version && !advancedSchedulingEnabled
|
||||
}
|
||||
/>
|
||||
}
|
||||
label={
|
||||
<StackLabel>
|
||||
Require workspaces automatically update when started.
|
||||
<StackLabelHelperText>
|
||||
<span>
|
||||
Workspaces that are manually started or auto-started will
|
||||
use the active template version.
|
||||
use the active template version.{" "}
|
||||
<strong>
|
||||
This setting is not enforced for template admins.
|
||||
</strong>
|
||||
</span>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</label>
|
||||
|
||||
{!advancedSchedulingEnabled && (
|
||||
<Stack direction="row">
|
||||
<EnterpriseBadge />
|
||||
<span css={styles.optionHelperText}>
|
||||
Enterprise license required to enabled.
|
||||
</span>
|
||||
</Stack>
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
{!advancedSchedulingEnabled && (
|
||||
<Stack
|
||||
direction="row"
|
||||
spacing={2}
|
||||
alignItems="center"
|
||||
css={{ marginTop: 16 }}
|
||||
>
|
||||
<EnterpriseBadge />
|
||||
<span>Enterprise license required to enabled.</span>
|
||||
</Stack>
|
||||
)}
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
}
|
||||
/>
|
||||
</FormFields>
|
||||
</FormSection>
|
||||
|
||||
<FormSection
|
||||
|
@ -265,13 +246,13 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||
label="Deprecation Message"
|
||||
/>
|
||||
{!accessControlEnabled && (
|
||||
<Stack direction="row">
|
||||
<Stack direction="row" spacing={2} alignItems="center">
|
||||
<EnterpriseBadge />
|
||||
<span css={styles.optionHelperText}>
|
||||
<FormHelperText>
|
||||
Enterprise license required to deprecate templates.
|
||||
{template.deprecated &&
|
||||
" You cannot change the message, but you may remove it to mark this template as no longer deprecated."}
|
||||
</span>
|
||||
</FormHelperText>
|
||||
</Stack>
|
||||
)}
|
||||
</FormFields>
|
||||
|
@ -306,11 +287,11 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||
<MenuItem value="public">Public</MenuItem>
|
||||
</TextField>
|
||||
{!portSharingControlsEnabled && (
|
||||
<Stack direction="row">
|
||||
<Stack direction="row" spacing={2} alignItems="center">
|
||||
<EnterpriseBadge />
|
||||
<span css={styles.optionHelperText}>
|
||||
<FormHelperText>
|
||||
Enterprise license required to control max port sharing level.
|
||||
</span>
|
||||
</FormHelperText>
|
||||
</Stack>
|
||||
)}
|
||||
</FormFields>
|
||||
|
@ -321,15 +302,3 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
|
|||
</HorizontalForm>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = {
|
||||
optionText: (theme) => ({
|
||||
fontSize: 16,
|
||||
color: theme.palette.text.primary,
|
||||
}),
|
||||
|
||||
optionHelperText: (theme) => ({
|
||||
fontSize: 12,
|
||||
color: theme.palette.text.secondary,
|
||||
}),
|
||||
} satisfies Record<string, Interpolation<Theme>>;
|
||||
|
|
|
@ -234,7 +234,6 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
allow_user_autostop: form.values.allow_user_autostop,
|
||||
update_workspace_last_used_at: form.values.update_workspace_last_used_at,
|
||||
update_workspace_dormant_at: form.values.update_workspace_dormant_at,
|
||||
require_active_version: false,
|
||||
disable_everyone_group_access: false,
|
||||
});
|
||||
};
|
||||
|
@ -533,14 +532,9 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
|||
<StackLabelHelperText>
|
||||
When enabled, Coder will permanently delete dormant
|
||||
workspaces after a period of time.{" "}
|
||||
<span
|
||||
css={(theme) => ({
|
||||
fontWeight: 500,
|
||||
color: theme.palette.text.primary,
|
||||
})}
|
||||
>
|
||||
<strong>
|
||||
Once a workspace is deleted it cannot be recovered.
|
||||
</span>
|
||||
</strong>
|
||||
</StackLabelHelperText>
|
||||
</StackLabel>
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue