mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: remove workspace_actions experiment (#10030)
This commit is contained in:
@@ -47,16 +47,6 @@ func (r *RootCmd) templateCreate() *clibase.Cmd {
|
||||
),
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
if failureTTL != 0 || inactivityTTL != 0 || maxTTL != 0 {
|
||||
// This call can be removed when workspace_actions is no longer experimental
|
||||
experiments, exErr := client.Experiments(inv.Context())
|
||||
if exErr != nil {
|
||||
return xerrors.Errorf("get experiments: %w", exErr)
|
||||
}
|
||||
|
||||
if !experiments.Enabled(codersdk.ExperimentWorkspaceActions) {
|
||||
return xerrors.Errorf("--failure-ttl and --inactivityTTL are experimental features. Use the workspace_actions CODER_EXPERIMENTS flag to set these configuration values.")
|
||||
}
|
||||
|
||||
entitlements, err := client.Entitlements(inv.Context())
|
||||
var sdkErr *codersdk.Error
|
||||
if xerrors.As(err, &sdkErr) && sdkErr.StatusCode() == http.StatusNotFound {
|
||||
|
||||
@@ -41,18 +41,6 @@ func (r *RootCmd) templateEdit() *clibase.Cmd {
|
||||
),
|
||||
Short: "Edit the metadata of a template by name.",
|
||||
Handler: func(inv *clibase.Invocation) error {
|
||||
// This clause can be removed when workspace_actions is no longer experimental
|
||||
if failureTTL != 0 || inactivityTTL != 0 {
|
||||
experiments, exErr := client.Experiments(inv.Context())
|
||||
if exErr != nil {
|
||||
return xerrors.Errorf("get experiments: %w", exErr)
|
||||
}
|
||||
|
||||
if !experiments.Enabled(codersdk.ExperimentWorkspaceActions) {
|
||||
return xerrors.Errorf("--failure-ttl and --inactivityTTL are experimental features. Use the workspace_actions CODER_EXPERIMENTS flag to set these configuration values.")
|
||||
}
|
||||
}
|
||||
|
||||
unsetAutostopRequirementDaysOfWeek := len(autostopRequirementDaysOfWeek) == 1 && autostopRequirementDaysOfWeek[0] == "none"
|
||||
requiresEntitlement := (len(autostopRequirementDaysOfWeek) > 0 && !unsetAutostopRequirementDaysOfWeek) ||
|
||||
autostopRequirementWeeks > 0 ||
|
||||
|
||||
Generated
-2
@@ -8171,7 +8171,6 @@ const docTemplate = `{
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"moons",
|
||||
"workspace_actions",
|
||||
"tailnet_pg_coordinator",
|
||||
"single_tailnet",
|
||||
"template_autostop_requirement",
|
||||
@@ -8179,7 +8178,6 @@ const docTemplate = `{
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ExperimentMoons",
|
||||
"ExperimentWorkspaceActions",
|
||||
"ExperimentTailnetPGCoordinator",
|
||||
"ExperimentSingleTailnet",
|
||||
"ExperimentTemplateAutostopRequirement",
|
||||
|
||||
Generated
-2
@@ -7319,7 +7319,6 @@
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"moons",
|
||||
"workspace_actions",
|
||||
"tailnet_pg_coordinator",
|
||||
"single_tailnet",
|
||||
"template_autostop_requirement",
|
||||
@@ -7327,7 +7326,6 @@
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"ExperimentMoons",
|
||||
"ExperimentWorkspaceActions",
|
||||
"ExperimentTailnetPGCoordinator",
|
||||
"ExperimentSingleTailnet",
|
||||
"ExperimentTemplateAutostopRequirement",
|
||||
|
||||
@@ -1958,9 +1958,6 @@ const (
|
||||
// feature is not yet complete in functionality.
|
||||
ExperimentMoons Experiment = "moons"
|
||||
|
||||
// https://github.com/coder/coder/milestone/19
|
||||
ExperimentWorkspaceActions Experiment = "workspace_actions"
|
||||
|
||||
// ExperimentTailnetPGCoordinator enables the PGCoord in favor of the pubsub-
|
||||
// only Coordinator
|
||||
ExperimentTailnetPGCoordinator Experiment = "tailnet_pg_coordinator"
|
||||
|
||||
Generated
-1
@@ -2752,7 +2752,6 @@ AuthorizationObject can represent a "set" of objects, such as: all workspaces in
|
||||
| Value |
|
||||
| ------------------------------- |
|
||||
| `moons` |
|
||||
| `workspace_actions` |
|
||||
| `tailnet_pg_coordinator` |
|
||||
| `single_tailnet` |
|
||||
| `template_autostop_requirement` |
|
||||
|
||||
Generated
+1
-3
@@ -1662,15 +1662,13 @@ export type Experiment =
|
||||
| "moons"
|
||||
| "single_tailnet"
|
||||
| "tailnet_pg_coordinator"
|
||||
| "template_autostop_requirement"
|
||||
| "workspace_actions";
|
||||
| "template_autostop_requirement";
|
||||
export const Experiments: Experiment[] = [
|
||||
"deployment_health_page",
|
||||
"moons",
|
||||
"single_tailnet",
|
||||
"tailnet_pg_coordinator",
|
||||
"template_autostop_requirement",
|
||||
"workspace_actions",
|
||||
];
|
||||
|
||||
// From codersdk/deployment.go
|
||||
|
||||
@@ -110,13 +110,3 @@ export const useDashboard = (): DashboardProviderValue => {
|
||||
|
||||
return context;
|
||||
};
|
||||
|
||||
export const useIsWorkspaceActionsEnabled = (): boolean => {
|
||||
const { entitlements, experiments } = useDashboard();
|
||||
const allowAdvancedScheduling =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
// This check can be removed when https://github.com/coder/coder/milestone/19
|
||||
// is merged up
|
||||
const allowWorkspaceActions = experiments.includes("workspace_actions");
|
||||
return allowWorkspaceActions && allowAdvancedScheduling;
|
||||
};
|
||||
|
||||
@@ -14,20 +14,11 @@ interface DormantDeletionStatProps {
|
||||
export const DormantDeletionStat: FC<DormantDeletionStatProps> = ({
|
||||
workspace,
|
||||
}) => {
|
||||
const { entitlements, experiments } = useDashboard();
|
||||
const { entitlements } = useDashboard();
|
||||
const allowAdvancedScheduling =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
// This check can be removed when https://github.com/coder/coder/milestone/19
|
||||
// is merged up
|
||||
const allowWorkspaceActions = experiments.includes("workspace_actions");
|
||||
|
||||
if (
|
||||
!displayDormantDeletion(
|
||||
workspace,
|
||||
allowAdvancedScheduling,
|
||||
allowWorkspaceActions,
|
||||
)
|
||||
) {
|
||||
if (!displayDormantDeletion(workspace, allowAdvancedScheduling)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,20 +9,11 @@ export const DormantDeletionText = ({
|
||||
}: {
|
||||
workspace: Workspace;
|
||||
}): JSX.Element | null => {
|
||||
const { entitlements, experiments } = useDashboard();
|
||||
const { entitlements } = useDashboard();
|
||||
const allowAdvancedScheduling =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
// This check can be removed when https://github.com/coder/coder/milestone/19
|
||||
// is merged up
|
||||
const allowWorkspaceActions = experiments.includes("workspace_actions");
|
||||
|
||||
if (
|
||||
!displayDormantDeletion(
|
||||
workspace,
|
||||
allowAdvancedScheduling,
|
||||
allowWorkspaceActions,
|
||||
)
|
||||
) {
|
||||
if (!displayDormantDeletion(workspace, allowAdvancedScheduling)) {
|
||||
return null;
|
||||
}
|
||||
return <StyledSpan role="status">Impending deletion</StyledSpan>;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Workspace } from "api/typesGenerated";
|
||||
import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider";
|
||||
import { useDashboard } from "components/Dashboard/DashboardProvider";
|
||||
import { Alert } from "components/Alert/Alert";
|
||||
import { formatDistanceToNow } from "date-fns";
|
||||
import Link from "@mui/material/Link";
|
||||
@@ -21,7 +21,9 @@ export const DormantWorkspaceBanner = ({
|
||||
shouldRedisplayBanner: boolean;
|
||||
count?: Count;
|
||||
}): JSX.Element | null => {
|
||||
const experimentEnabled = useIsWorkspaceActionsEnabled();
|
||||
const { entitlements } = useDashboard();
|
||||
const schedulingEnabled =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
|
||||
if (!workspaces) {
|
||||
return null;
|
||||
@@ -37,7 +39,7 @@ export const DormantWorkspaceBanner = ({
|
||||
|
||||
if (
|
||||
// Only show this if the experiment is included.
|
||||
!experimentEnabled ||
|
||||
!schedulingEnabled ||
|
||||
!hasDormantWorkspaces ||
|
||||
// Banners should be redisplayed after dismissal when additional workspaces are newly scheduled for deletion
|
||||
!shouldRedisplayBanner
|
||||
|
||||
@@ -4,53 +4,39 @@ import { displayDormantDeletion } from "./utils";
|
||||
|
||||
describe("displayDormantDeletion", () => {
|
||||
const today = new Date();
|
||||
it.each<[string, boolean, boolean, boolean]>([
|
||||
it.each<[string, boolean, boolean]>([
|
||||
[
|
||||
new Date(new Date().setDate(today.getDate() + 15)).toISOString(),
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
], // today + 15 days out
|
||||
[
|
||||
new Date(new Date().setDate(today.getDate() + 14)).toISOString(),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
], // today + 14
|
||||
[
|
||||
new Date(new Date().setDate(today.getDate() + 13)).toISOString(),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
], // today + 13
|
||||
[
|
||||
new Date(new Date().setDate(today.getDate() + 1)).toISOString(),
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
], // today + 1
|
||||
[new Date().toISOString(), true, true, true], // today + 0
|
||||
[new Date().toISOString(), false, true, false], // Advanced Scheduling off
|
||||
[new Date().toISOString(), true, false, false], // Workspace Actions off
|
||||
[new Date().toISOString(), true, true], // today + 0
|
||||
[new Date().toISOString(), false, false], // Advanced Scheduling off
|
||||
])(
|
||||
`deleting_at=%p, allowAdvancedScheduling=%p, AllowWorkspaceActions=%p, shouldDisplay=%p`,
|
||||
(
|
||||
deleting_at,
|
||||
allowAdvancedScheduling,
|
||||
allowWorkspaceActions,
|
||||
shouldDisplay,
|
||||
) => {
|
||||
`deleting_at=%p, allowAdvancedScheduling=%p, shouldDisplay=%p`,
|
||||
(deleting_at, allowAdvancedScheduling, shouldDisplay) => {
|
||||
const workspace: TypesGen.Workspace = {
|
||||
...Mocks.MockWorkspace,
|
||||
deleting_at,
|
||||
};
|
||||
expect(
|
||||
displayDormantDeletion(
|
||||
workspace,
|
||||
allowAdvancedScheduling,
|
||||
allowWorkspaceActions,
|
||||
),
|
||||
).toBe(shouldDisplay);
|
||||
expect(displayDormantDeletion(workspace, allowAdvancedScheduling)).toBe(
|
||||
shouldDisplay,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -14,14 +14,9 @@ const IMPENDING_DELETION_DISPLAY_THRESHOLD = 14; // 14 days
|
||||
export const displayDormantDeletion = (
|
||||
workspace: Workspace,
|
||||
allowAdvancedScheduling: boolean,
|
||||
allowWorkspaceActions: boolean,
|
||||
) => {
|
||||
const today = new Date();
|
||||
if (
|
||||
!workspace.deleting_at ||
|
||||
!allowAdvancedScheduling ||
|
||||
!allowWorkspaceActions
|
||||
) {
|
||||
if (!workspace.deleting_at || !allowAdvancedScheduling) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
|
||||
+1
-7
@@ -30,13 +30,7 @@ const meta: Meta<typeof GeneralSettingsPageView> = {
|
||||
description:
|
||||
"Enable one or more experiments. These are not ready for production. Separate multiple experiments with commas, or enter '*' to opt-in to all available experiments.",
|
||||
flag: "experiments",
|
||||
value: [
|
||||
"*",
|
||||
"moons",
|
||||
"workspace_actions",
|
||||
"single_tailnet",
|
||||
"deployment_health_page",
|
||||
],
|
||||
value: ["*", "moons", "single_tailnet", "deployment_health_page"],
|
||||
flag_shorthand: "",
|
||||
hidden: false,
|
||||
},
|
||||
|
||||
@@ -50,7 +50,6 @@ export interface TemplateScheduleForm {
|
||||
isSubmitting: boolean;
|
||||
error?: unknown;
|
||||
allowAdvancedScheduling: boolean;
|
||||
allowWorkspaceActions: boolean;
|
||||
allowAutostopRequirement: boolean;
|
||||
// Helpful to show field errors on Storybook
|
||||
initialTouched?: FormikTouched<UpdateTemplateMeta>;
|
||||
@@ -62,7 +61,6 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
||||
onCancel,
|
||||
error,
|
||||
allowAdvancedScheduling,
|
||||
allowWorkspaceActions,
|
||||
allowAutostopRequirement,
|
||||
isSubmitting,
|
||||
initialTouched,
|
||||
@@ -459,7 +457,7 @@ export const TemplateScheduleForm: FC<TemplateScheduleForm> = ({
|
||||
</Stack>
|
||||
</Stack>
|
||||
</FormSection>
|
||||
{allowAdvancedScheduling && allowWorkspaceActions && (
|
||||
{allowAdvancedScheduling && (
|
||||
<>
|
||||
<FormSection
|
||||
title="Failure Cleanup"
|
||||
|
||||
@@ -123,9 +123,6 @@ describe("TemplateSchedulePage", () => {
|
||||
jest
|
||||
.spyOn(API, "getEntitlements")
|
||||
.mockResolvedValue(MockEntitlementsWithScheduling);
|
||||
|
||||
// remove when https://github.com/coder/coder/milestone/19 is completed.
|
||||
jest.spyOn(API, "getExperiments").mockResolvedValue(["workspace_actions"]);
|
||||
});
|
||||
|
||||
it("Calls the API when user fills in and submits a form", async () => {
|
||||
|
||||
@@ -18,12 +18,11 @@ const TemplateSchedulePage: FC = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const orgId = useOrganizationId();
|
||||
const { template } = useTemplateSettings();
|
||||
const { entitlements, experiments } = useDashboard();
|
||||
const { entitlements } = useDashboard();
|
||||
const allowAdvancedScheduling =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
// This check can be removed when https://github.com/coder/coder/milestone/19
|
||||
// is merged up
|
||||
const allowWorkspaceActions = experiments.includes("workspace_actions");
|
||||
const allowAutostopRequirement =
|
||||
entitlements.features["template_autostop_requirement"].enabled;
|
||||
const { clearLocal } = useLocalStorage();
|
||||
@@ -54,7 +53,6 @@ const TemplateSchedulePage: FC = () => {
|
||||
</Helmet>
|
||||
<TemplateSchedulePageView
|
||||
allowAdvancedScheduling={allowAdvancedScheduling}
|
||||
allowWorkspaceActions={allowWorkspaceActions}
|
||||
allowAutostopRequirement={allowAutostopRequirement}
|
||||
isSubmitting={isSubmitting}
|
||||
template={template}
|
||||
|
||||
-1
@@ -31,7 +31,6 @@ type Story = StoryObj<typeof TemplateSchedulePageView>;
|
||||
|
||||
const defaultArgs = {
|
||||
allowAdvancedScheduling: true,
|
||||
allowWorkspaceActions: true,
|
||||
template: MockTemplate,
|
||||
onSubmit: action("onSubmit"),
|
||||
onCancel: action("cancel"),
|
||||
|
||||
@@ -14,7 +14,6 @@ export interface TemplateSchedulePageViewProps {
|
||||
typeof TemplateScheduleForm
|
||||
>["initialTouched"];
|
||||
allowAdvancedScheduling: boolean;
|
||||
allowWorkspaceActions: boolean;
|
||||
allowAutostopRequirement: boolean;
|
||||
}
|
||||
|
||||
@@ -24,7 +23,6 @@ export const TemplateSchedulePageView: FC<TemplateSchedulePageViewProps> = ({
|
||||
onSubmit,
|
||||
isSubmitting,
|
||||
allowAdvancedScheduling,
|
||||
allowWorkspaceActions,
|
||||
allowAutostopRequirement,
|
||||
submitError,
|
||||
initialTouched,
|
||||
@@ -39,7 +37,6 @@ export const TemplateSchedulePageView: FC<TemplateSchedulePageViewProps> = ({
|
||||
|
||||
<TemplateScheduleForm
|
||||
allowAdvancedScheduling={allowAdvancedScheduling}
|
||||
allowWorkspaceActions={allowWorkspaceActions}
|
||||
allowAutostopRequirement={allowAutostopRequirement}
|
||||
initialTouched={initialTouched}
|
||||
isSubmitting={isSubmitting}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
import { usePagination } from "hooks/usePagination";
|
||||
import { Workspace } from "api/typesGenerated";
|
||||
import {
|
||||
useDashboard,
|
||||
useIsWorkspaceActionsEnabled,
|
||||
} from "components/Dashboard/DashboardProvider";
|
||||
import { useDashboard } from "components/Dashboard/DashboardProvider";
|
||||
import { type FC, useEffect, useState } from "react";
|
||||
import { Helmet } from "react-helmet-async";
|
||||
import { pageTitle } from "utils/page";
|
||||
@@ -54,13 +51,16 @@ const WorkspacesPage: FC = () => {
|
||||
query: filterProps.filter.query,
|
||||
});
|
||||
|
||||
const experimentEnabled = useIsWorkspaceActionsEnabled();
|
||||
const { entitlements } = useDashboard();
|
||||
const schedulingEnabled =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
|
||||
// If workspace actions are enabled we need to fetch the dormant
|
||||
// workspaces as well. This lets us determine whether we should
|
||||
// show a banner to the user indicating that some of their workspaces
|
||||
// are at risk of being deleted.
|
||||
useEffect(() => {
|
||||
if (experimentEnabled) {
|
||||
if (schedulingEnabled) {
|
||||
const includesDormant = filterProps.filter.query.includes("dormant_at");
|
||||
const dormantQuery = includesDormant
|
||||
? filterProps.filter.query
|
||||
@@ -82,12 +82,11 @@ const WorkspacesPage: FC = () => {
|
||||
// like dormant workspaces don't exist.
|
||||
setDormantWorkspaces([]);
|
||||
}
|
||||
}, [experimentEnabled, data, filterProps.filter.query]);
|
||||
}, [schedulingEnabled, data, filterProps.filter.query]);
|
||||
const updateWorkspace = useWorkspaceUpdate(queryKey);
|
||||
const [checkedWorkspaces, setCheckedWorkspaces] = useState<Workspace[]>([]);
|
||||
const [isDeletingAll, setIsDeletingAll] = useState(false);
|
||||
const [urlSearchParams] = searchParamsResult;
|
||||
const { entitlements } = useDashboard();
|
||||
const canCheckWorkspaces =
|
||||
entitlements.features["workspace_batch_actions"].enabled;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { FC } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import { useIsWorkspaceActionsEnabled } from "components/Dashboard/DashboardProvider";
|
||||
import { useDashboard } from "components/Dashboard/DashboardProvider";
|
||||
|
||||
import { Avatar, AvatarProps } from "components/Avatar/Avatar";
|
||||
import { Palette, PaletteColor } from "@mui/material/styles";
|
||||
import { TemplateFilterMenu, StatusFilterMenu } from "./menus";
|
||||
@@ -68,7 +69,9 @@ export const WorkspacesFilter = ({
|
||||
error,
|
||||
menus,
|
||||
}: WorkspaceFilterProps) => {
|
||||
const actionsEnabled = useIsWorkspaceActionsEnabled();
|
||||
const { entitlements } = useDashboard();
|
||||
const actionsEnabled =
|
||||
entitlements.features["advanced_template_scheduling"].enabled;
|
||||
const presets = actionsEnabled ? PRESET_FILTERS : PRESETS_WITH_DORMANT;
|
||||
|
||||
return (
|
||||
|
||||
@@ -1924,10 +1924,7 @@ export const MockEntitlementsWithScheduling: TypesGen.Entitlements = {
|
||||
}),
|
||||
};
|
||||
|
||||
export const MockExperiments: TypesGen.Experiment[] = [
|
||||
"workspace_actions",
|
||||
"moons",
|
||||
];
|
||||
export const MockExperiments: TypesGen.Experiment[] = ["moons"];
|
||||
|
||||
export const MockAuditLog: TypesGen.AuditLog = {
|
||||
id: "fbd2116a-8961-4954-87ae-e4575bd29ce0",
|
||||
|
||||
Reference in New Issue
Block a user