chore: remove template_update_policies experiment (#11250)

This commit is contained in:
Jon Ayers
2023-12-21 13:39:33 -06:00
committed by GitHub
parent 5b071f4d94
commit 0b7d68dc3f
20 changed files with 77 additions and 169 deletions
-9
View File
@@ -78,15 +78,6 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
if !entitlements.Features[codersdk.FeatureAccessControl].Enabled { if !entitlements.Features[codersdk.FeatureAccessControl].Enabled {
return xerrors.Errorf("your license is not entitled to use enterprise access control, so you cannot set --require-active-version") return xerrors.Errorf("your license is not entitled to use enterprise access control, so you cannot set --require-active-version")
} }
experiments, exErr := client.Experiments(inv.Context())
if exErr != nil {
return xerrors.Errorf("get experiments: %w", exErr)
}
if !experiments.Enabled(codersdk.ExperimentTemplateUpdatePolicies) {
return xerrors.Errorf("--require-active-version is an experimental feature, contact an administrator to enable the 'template_update_policies' experiment on your Coder server")
}
} }
} }
-7
View File
@@ -13,7 +13,6 @@ import (
"github.com/coder/coder/v2/cli/clitest" "github.com/coder/coder/v2/cli/clitest"
"github.com/coder/coder/v2/coderd/coderdtest" "github.com/coder/coder/v2/coderd/coderdtest"
"github.com/coder/coder/v2/coderd/database" "github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/provisioner/echo" "github.com/coder/coder/v2/provisioner/echo"
"github.com/coder/coder/v2/provisionersdk/proto" "github.com/coder/coder/v2/provisionersdk/proto"
"github.com/coder/coder/v2/pty/ptytest" "github.com/coder/coder/v2/pty/ptytest"
@@ -398,14 +397,8 @@ func TestTemplateCreate(t *testing.T) {
t.Run("RequireActiveVersionInvalid", func(t *testing.T) { t.Run("RequireActiveVersionInvalid", func(t *testing.T) {
t.Parallel() t.Parallel()
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{
string(codersdk.ExperimentTemplateUpdatePolicies),
}
client := coderdtest.New(t, &coderdtest.Options{ client := coderdtest.New(t, &coderdtest.Options{
IncludeProvisionerDaemon: true, IncludeProvisionerDaemon: true,
DeploymentValues: dv,
}) })
coderdtest.CreateFirstUser(t, client) coderdtest.CreateFirstUser(t, client)
source := clitest.CreateTemplateVersionSource(t, completeWithAgent()) source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
-9
View File
@@ -86,15 +86,6 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
if !entitlements.Features[codersdk.FeatureAccessControl].Enabled { if !entitlements.Features[codersdk.FeatureAccessControl].Enabled {
return xerrors.Errorf("your license is not entitled to use enterprise access control, so you cannot set --require-active-version") return xerrors.Errorf("your license is not entitled to use enterprise access control, so you cannot set --require-active-version")
} }
experiments, exErr := client.Experiments(inv.Context())
if exErr != nil {
return xerrors.Errorf("get experiments: %w", exErr)
}
if !experiments.Enabled(codersdk.ExperimentTemplateUpdatePolicies) {
return xerrors.Errorf("--require-active-version is an experimental feature, contact an administrator to enable the 'template_update_policies' experiment on your Coder server")
}
} }
} }
+2 -4
View File
@@ -8829,15 +8829,13 @@ const docTemplate = `{
"workspace_actions", "workspace_actions",
"tailnet_pg_coordinator", "tailnet_pg_coordinator",
"single_tailnet", "single_tailnet",
"deployment_health_page", "deployment_health_page"
"template_update_policies"
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"ExperimentWorkspaceActions", "ExperimentWorkspaceActions",
"ExperimentTailnetPGCoordinator", "ExperimentTailnetPGCoordinator",
"ExperimentSingleTailnet", "ExperimentSingleTailnet",
"ExperimentDeploymentHealthPage", "ExperimentDeploymentHealthPage"
"ExperimentTemplateUpdatePolicies"
] ]
}, },
"codersdk.ExternalAuth": { "codersdk.ExternalAuth": {
+2 -4
View File
@@ -7907,15 +7907,13 @@
"workspace_actions", "workspace_actions",
"tailnet_pg_coordinator", "tailnet_pg_coordinator",
"single_tailnet", "single_tailnet",
"deployment_health_page", "deployment_health_page"
"template_update_policies"
], ],
"x-enum-varnames": [ "x-enum-varnames": [
"ExperimentWorkspaceActions", "ExperimentWorkspaceActions",
"ExperimentTailnetPGCoordinator", "ExperimentTailnetPGCoordinator",
"ExperimentSingleTailnet", "ExperimentSingleTailnet",
"ExperimentDeploymentHealthPage", "ExperimentDeploymentHealthPage"
"ExperimentTemplateUpdatePolicies"
] ]
}, },
"codersdk.ExternalAuth": { "codersdk.ExternalAuth": {
-1
View File
@@ -2088,7 +2088,6 @@ const (
// Deployment health page // Deployment health page
ExperimentDeploymentHealthPage Experiment = "deployment_health_page" ExperimentDeploymentHealthPage Experiment = "deployment_health_page"
ExperimentTemplateUpdatePolicies Experiment = "template_update_policies"
// Add new experiments here! // Add new experiments here!
// ExperimentExample Experiment = "example" // ExperimentExample Experiment = "example"
) )
+6 -7
View File
@@ -2872,13 +2872,12 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
#### Enumerated Values #### Enumerated Values
| Value | | Value |
| -------------------------- | | ------------------------ |
| `workspace_actions` | | `workspace_actions` |
| `tailnet_pg_coordinator` | | `tailnet_pg_coordinator` |
| `single_tailnet` | | `single_tailnet` |
| `deployment_health_page` | | `deployment_health_page` |
| `template_update_policies` |
## codersdk.ExternalAuth ## codersdk.ExternalAuth
-12
View File
@@ -23,11 +23,6 @@ func TestTemplateCreate(t *testing.T) {
t.Run("RequireActiveVersion", func(t *testing.T) { t.Run("RequireActiveVersion", func(t *testing.T) {
t.Parallel() t.Parallel()
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{
string(codersdk.ExperimentTemplateUpdatePolicies),
}
client, user := coderdenttest.New(t, &coderdenttest.Options{ client, user := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{ Features: license.Features{
@@ -35,7 +30,6 @@ func TestTemplateCreate(t *testing.T) {
}, },
}, },
Options: &coderdtest.Options{ Options: &coderdtest.Options{
DeploymentValues: dv,
IncludeProvisionerDaemon: true, IncludeProvisionerDaemon: true,
}, },
}) })
@@ -124,17 +118,11 @@ func TestTemplateCreate(t *testing.T) {
t.Run("NotEntitled", func(t *testing.T) { t.Run("NotEntitled", func(t *testing.T) {
t.Parallel() t.Parallel()
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{
string(codersdk.ExperimentTemplateUpdatePolicies),
}
client, admin := coderdenttest.New(t, &coderdenttest.Options{ client, admin := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{}, Features: license.Features{},
}, },
Options: &coderdtest.Options{ Options: &coderdtest.Options{
DeploymentValues: dv,
IncludeProvisionerDaemon: true, IncludeProvisionerDaemon: true,
}, },
}) })
-12
View File
@@ -21,11 +21,6 @@ func TestTemplateEdit(t *testing.T) {
t.Run("OK", func(t *testing.T) { t.Run("OK", func(t *testing.T) {
t.Parallel() t.Parallel()
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{
string(codersdk.ExperimentTemplateUpdatePolicies),
}
ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{ ownerClient, owner := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{ Features: license.Features{
@@ -33,7 +28,6 @@ func TestTemplateEdit(t *testing.T) {
}, },
}, },
Options: &coderdtest.Options{ Options: &coderdtest.Options{
DeploymentValues: dv,
IncludeProvisionerDaemon: true, IncludeProvisionerDaemon: true,
}, },
}) })
@@ -64,17 +58,11 @@ func TestTemplateEdit(t *testing.T) {
t.Run("NotEntitled", func(t *testing.T) { t.Run("NotEntitled", func(t *testing.T) {
t.Parallel() t.Parallel()
dv := coderdtest.DeploymentValues(t)
dv.Experiments = []string{
string(codersdk.ExperimentTemplateUpdatePolicies),
}
client, owner := coderdenttest.New(t, &coderdenttest.Options{ client, owner := coderdenttest.New(t, &coderdenttest.Options{
LicenseOptions: &coderdenttest.LicenseOptions{ LicenseOptions: &coderdenttest.LicenseOptions{
Features: license.Features{}, Features: license.Features{},
}, },
Options: &coderdtest.Options{ Options: &coderdtest.Options{
DeploymentValues: dv,
IncludeProvisionerDaemon: true, IncludeProvisionerDaemon: true,
}, },
}) })
-2
View File
@@ -1780,13 +1780,11 @@ export type Experiment =
| "deployment_health_page" | "deployment_health_page"
| "single_tailnet" | "single_tailnet"
| "tailnet_pg_coordinator" | "tailnet_pg_coordinator"
| "template_update_policies"
| "workspace_actions"; | "workspace_actions";
export const Experiments: Experiment[] = [ export const Experiments: Experiment[] = [
"deployment_health_page", "deployment_health_page",
"single_tailnet", "single_tailnet",
"tailnet_pg_coordinator", "tailnet_pg_coordinator",
"template_update_policies",
"workspace_actions", "workspace_actions",
]; ];
@@ -121,11 +121,3 @@ export const useIsWorkspaceActionsEnabled = (): boolean => {
const allowWorkspaceActions = experiments.includes("workspace_actions"); const allowWorkspaceActions = experiments.includes("workspace_actions");
return allowWorkspaceActions && allowAdvancedScheduling; return allowWorkspaceActions && allowAdvancedScheduling;
}; };
export const useTemplatePoliciesEnabled = (): boolean => {
const { entitlements, experiments } = useDashboard();
return (
entitlements.features.access_control.enabled &&
experiments.includes("template_update_policies")
);
};
@@ -52,7 +52,6 @@ export interface TemplateSettingsForm {
// Helpful to show field errors on Storybook // Helpful to show field errors on Storybook
initialTouched?: FormikTouched<UpdateTemplateMeta>; initialTouched?: FormikTouched<UpdateTemplateMeta>;
accessControlEnabled: boolean; accessControlEnabled: boolean;
templatePoliciesEnabled: boolean;
} }
export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
@@ -63,7 +62,6 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
isSubmitting, isSubmitting,
initialTouched, initialTouched,
accessControlEnabled, accessControlEnabled,
templatePoliciesEnabled,
}) => { }) => {
const validationSchema = getValidationSchema(); const validationSchema = getValidationSchema();
const form: FormikContextType<UpdateTemplateMeta> = const form: FormikContextType<UpdateTemplateMeta> =
@@ -180,41 +178,39 @@ export const TemplateSettingsForm: FC<TemplateSettingsForm> = ({
</Stack> </Stack>
</Stack> </Stack>
</label> </label>
{templatePoliciesEnabled && ( <label htmlFor="require_active_version">
<label htmlFor="require_active_version"> <Stack direction="row" spacing={1}>
<Stack direction="row" spacing={1}> <Checkbox
<Checkbox id="require_active_version"
id="require_active_version" name="require_active_version"
name="require_active_version" checked={form.values.require_active_version}
checked={form.values.require_active_version} onChange={form.handleChange}
onChange={form.handleChange} />
/>
<Stack direction="column" spacing={0.5}> <Stack direction="column" spacing={0.5}>
<Stack <Stack
direction="row" direction="row"
alignItems="center" alignItems="center"
spacing={0.5} spacing={0.5}
css={styles.optionText} css={styles.optionText}
> >
Require workspaces automatically update when started. Require workspaces automatically update when started.
<HelpTooltip> <HelpTooltip>
<HelpTooltipTrigger /> <HelpTooltipTrigger />
<HelpTooltipContent> <HelpTooltipContent>
<HelpTooltipText> <HelpTooltipText>
This setting is not enforced for template admins. This setting is not enforced for template admins.
</HelpTooltipText> </HelpTooltipText>
</HelpTooltipContent> </HelpTooltipContent>
</HelpTooltip> </HelpTooltip>
</Stack>
<span css={styles.optionHelperText}>
Workspaces that are manually started or auto-started will
use the active template version.
</span>
</Stack> </Stack>
<span css={styles.optionHelperText}>
Workspaces that are manually started or auto-started will use
the active template version.
</span>
</Stack> </Stack>
</label> </Stack>
)} </label>
</Stack> </Stack>
</FormSection> </FormSection>
@@ -80,7 +80,9 @@ const fillAndSubmitForm = async ({
await userEvent.clear(iconField); await userEvent.clear(iconField);
await userEvent.type(iconField, icon); await userEvent.type(iconField, icon);
const allowCancelJobsField = screen.getByRole("checkbox"); const allowCancelJobsField = screen.getByRole("checkbox", {
name: "Allow users to cancel in-progress workspace jobs. Depending on your template, canceling builds may leave workspaces in an unhealthy state. This option isn't recommended for most use cases.",
});
// checkbox is checked by default, so it must be clicked to get unchecked // checkbox is checked by default, so it must be clicked to get unchecked
if (!allow_user_cancel_workspace_jobs) { if (!allow_user_cancel_workspace_jobs) {
await userEvent.click(allowCancelJobsField); await userEvent.click(allowCancelJobsField);
@@ -10,10 +10,7 @@ import { useTemplateSettings } from "../TemplateSettingsLayout";
import { TemplateSettingsPageView } from "./TemplateSettingsPageView"; import { TemplateSettingsPageView } from "./TemplateSettingsPageView";
import { templateByNameKey } from "api/queries/templates"; import { templateByNameKey } from "api/queries/templates";
import { useOrganizationId } from "hooks"; import { useOrganizationId } from "hooks";
import { import { useDashboard } from "components/Dashboard/DashboardProvider";
useDashboard,
useTemplatePoliciesEnabled,
} from "components/Dashboard/DashboardProvider";
export const TemplateSettingsPage: FC = () => { export const TemplateSettingsPage: FC = () => {
const { template: templateName } = useParams() as { template: string }; const { template: templateName } = useParams() as { template: string };
@@ -23,7 +20,6 @@ export const TemplateSettingsPage: FC = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const { entitlements } = useDashboard(); const { entitlements } = useDashboard();
const accessControlEnabled = entitlements.features.access_control.enabled; const accessControlEnabled = entitlements.features.access_control.enabled;
const templatePoliciesEnabled = useTemplatePoliciesEnabled();
const { const {
mutate: updateTemplate, mutate: updateTemplate,
@@ -62,7 +58,6 @@ export const TemplateSettingsPage: FC = () => {
}); });
}} }}
accessControlEnabled={accessControlEnabled} accessControlEnabled={accessControlEnabled}
templatePoliciesEnabled={templatePoliciesEnabled}
/> />
</> </>
); );
@@ -8,7 +8,6 @@ const meta: Meta<typeof TemplateSettingsPageView> = {
args: { args: {
template: MockTemplate, template: MockTemplate,
accessControlEnabled: true, accessControlEnabled: true,
templatePoliciesEnabled: true,
}, },
}; };
@@ -13,7 +13,6 @@ export interface TemplateSettingsPageViewProps {
typeof TemplateSettingsForm typeof TemplateSettingsForm
>["initialTouched"]; >["initialTouched"];
accessControlEnabled: boolean; accessControlEnabled: boolean;
templatePoliciesEnabled: boolean;
} }
export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
@@ -24,7 +23,6 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
submitError, submitError,
initialTouched, initialTouched,
accessControlEnabled, accessControlEnabled,
templatePoliciesEnabled,
}) => { }) => {
return ( return (
<> <>
@@ -40,7 +38,6 @@ export const TemplateSettingsPageView: FC<TemplateSettingsPageViewProps> = ({
onCancel={onCancel} onCancel={onCancel}
error={submitError} error={submitError}
accessControlEnabled={accessControlEnabled} accessControlEnabled={accessControlEnabled}
templatePoliciesEnabled={templatePoliciesEnabled}
/> />
</> </>
); );
@@ -30,13 +30,11 @@ export type WorkspaceSettingsFormValues = {
export const WorkspaceSettingsForm: FC<{ export const WorkspaceSettingsForm: FC<{
workspace: Workspace; workspace: Workspace;
error: unknown; error: unknown;
templatePoliciesEnabled: boolean;
onCancel: () => void; onCancel: () => void;
onSubmit: (values: WorkspaceSettingsFormValues) => Promise<void>; onSubmit: (values: WorkspaceSettingsFormValues) => Promise<void>;
}> = ({ onCancel, onSubmit, workspace, error, templatePoliciesEnabled }) => { }> = ({ onCancel, onSubmit, workspace, error }) => {
const formEnabled = const formEnabled =
(templatePoliciesEnabled && !workspace.template_require_active_version) || !workspace.template_require_active_version || workspace.allow_renames;
workspace.allow_renames;
const form = useFormik<WorkspaceSettingsFormValues>({ const form = useFormik<WorkspaceSettingsFormValues>({
onSubmit, onSubmit,
@@ -78,39 +76,37 @@ export const WorkspaceSettingsForm: FC<{
/> />
</FormFields> </FormFields>
</FormSection> </FormSection>
{templatePoliciesEnabled && ( <FormSection
<FormSection title="Automatic Updates"
title="Automatic Updates" description="Configure your workspace to automatically update when started."
description="Configure your workspace to automatically update when started." >
> <FormFields>
<FormFields> <TextField
<TextField {...getFieldHelpers("automatic_updates")}
{...getFieldHelpers("automatic_updates")} id="automatic_updates"
id="automatic_updates" label="Update Policy"
label="Update Policy" value={
value={ workspace.template_require_active_version
workspace.template_require_active_version ? "always"
? "always" : form.values.automatic_updates
: form.values.automatic_updates }
} select
select disabled={
disabled={ form.isSubmitting || workspace.template_require_active_version
form.isSubmitting || workspace.template_require_active_version }
} helperText={
helperText={ workspace.template_require_active_version &&
workspace.template_require_active_version && "The template for this workspace requires automatic updates."
"The template for this workspace requires automatic updates." }
} >
> {AutomaticUpdateses.map((value) => (
{AutomaticUpdateses.map((value) => ( <MenuItem value={value} key={value}>
<MenuItem value={value} key={value}> {upperFirst(value)}
{upperFirst(value)} </MenuItem>
</MenuItem> ))}
))} </TextField>
</TextField> </FormFields>
</FormFields> </FormSection>
</FormSection>
)}
{formEnabled && ( {formEnabled && (
<FormFooter onCancel={onCancel} isLoading={form.isSubmitting} /> <FormFooter onCancel={onCancel} isLoading={form.isSubmitting} />
)} )}
@@ -7,7 +7,6 @@ import { useMutation } from "react-query";
import { displaySuccess } from "components/GlobalSnackbar/utils"; import { displaySuccess } from "components/GlobalSnackbar/utils";
import { patchWorkspace, updateWorkspaceAutomaticUpdates } from "api/api"; import { patchWorkspace, updateWorkspaceAutomaticUpdates } from "api/api";
import { WorkspaceSettingsFormValues } from "./WorkspaceSettingsForm"; import { WorkspaceSettingsFormValues } from "./WorkspaceSettingsForm";
import { useTemplatePoliciesEnabled } from "components/Dashboard/DashboardProvider";
const WorkspaceSettingsPage = () => { const WorkspaceSettingsPage = () => {
const params = useParams() as { const params = useParams() as {
@@ -18,7 +17,6 @@ const WorkspaceSettingsPage = () => {
const username = params.username.replace("@", ""); const username = params.username.replace("@", "");
const workspace = useWorkspaceSettings(); const workspace = useWorkspaceSettings();
const navigate = useNavigate(); const navigate = useNavigate();
const templatePoliciesEnabled = useTemplatePoliciesEnabled();
const mutation = useMutation({ const mutation = useMutation({
mutationFn: async (formValues: WorkspaceSettingsFormValues) => { mutationFn: async (formValues: WorkspaceSettingsFormValues) => {
@@ -47,7 +45,6 @@ const WorkspaceSettingsPage = () => {
workspace={workspace} workspace={workspace}
onCancel={() => navigate(`/@${username}/${workspaceName}`)} onCancel={() => navigate(`/@${username}/${workspaceName}`)}
onSubmit={mutation.mutateAsync} onSubmit={mutation.mutateAsync}
templatePoliciesEnabled={templatePoliciesEnabled}
/> />
</> </>
); );
@@ -16,12 +16,6 @@ type Story = StoryObj<typeof WorkspaceSettingsPageView>;
export const Example: Story = {}; export const Example: Story = {};
export const AutoUpdates: Story = {
args: {
templatePoliciesEnabled: true,
},
};
export const RenamesDisabled: Story = { export const RenamesDisabled: Story = {
args: { args: {
workspace: { ...MockWorkspace, allow_renames: false }, workspace: { ...MockWorkspace, allow_renames: false },
@@ -8,7 +8,6 @@ export type WorkspaceSettingsPageViewProps = {
workspace: Workspace; workspace: Workspace;
onCancel: () => void; onCancel: () => void;
onSubmit: ComponentProps<typeof WorkspaceSettingsForm>["onSubmit"]; onSubmit: ComponentProps<typeof WorkspaceSettingsForm>["onSubmit"];
templatePoliciesEnabled: boolean;
}; };
export const WorkspaceSettingsPageView: FC<WorkspaceSettingsPageViewProps> = ({ export const WorkspaceSettingsPageView: FC<WorkspaceSettingsPageViewProps> = ({
@@ -16,7 +15,6 @@ export const WorkspaceSettingsPageView: FC<WorkspaceSettingsPageViewProps> = ({
onSubmit, onSubmit,
error, error,
workspace, workspace,
templatePoliciesEnabled,
}) => { }) => {
return ( return (
<> <>
@@ -33,7 +31,6 @@ export const WorkspaceSettingsPageView: FC<WorkspaceSettingsPageViewProps> = ({
workspace={workspace} workspace={workspace}
onCancel={onCancel} onCancel={onCancel}
onSubmit={onSubmit} onSubmit={onSubmit}
templatePoliciesEnabled={templatePoliciesEnabled}
/> />
</> </>
); );