Skip to content

Commit

Permalink
DEVPROD-4197: Add host uptime scheduler (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
sophstad authored Apr 9, 2024
1 parent 64dcaf1 commit 78bf3bc
Show file tree
Hide file tree
Showing 22 changed files with 692 additions and 151 deletions.
6 changes: 4 additions & 2 deletions apps/parsley/src/gql/generated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,8 @@ export type DistroInput = {
providerSettingsList: Array<Scalars["Map"]["input"]>;
setup: Scalars["String"]["input"];
setupAsSudo: Scalars["Boolean"]["input"];
sshKey: Scalars["String"]["input"];
/** @deprecated removing this field shortly */
sshKey?: InputMaybe<Scalars["String"]["input"]>;
sshOptions: Array<Scalars["String"]["input"]>;
user: Scalars["String"]["input"];
userSpawnAllowed: Scalars["Boolean"]["input"];
Expand Down Expand Up @@ -2462,7 +2463,8 @@ export type SpruceConfig = {
containerPools?: Maybe<ContainerPoolsConfig>;
githubOrgs: Array<Scalars["String"]["output"]>;
jira?: Maybe<JiraConfig>;
keys: Array<SshKey>;
/** @deprecated removing this field shortly */
keys?: Maybe<Array<SshKey>>;
providers?: Maybe<CloudProviderConfig>;
secretFields: Array<Scalars["String"]["output"]>;
slack?: Maybe<SlackConfig>;
Expand Down
15 changes: 4 additions & 11 deletions apps/spruce/cypress/integration/spawn/host.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,23 +88,16 @@ describe("Navigating to Spawn Host page", () => {
});

describe("Spawn host modal", () => {
it("Should disable 'Never expire' checkbox when max number of unexpirable hosts is met (2)", () => {
it("Should disable 'Unexpirable Host' radio box when max number of unexpirable hosts is met (2)", () => {
cy.visit("/spawn/host");
cy.contains("Spawn a host").click();
cy.dataCy("distro-input").click();
cy.dataCy("distro-option-ubuntu1804-workstation")
.should("be.visible")
.click();
cy.dataCy("never-expire-checkbox").should(
"have.attr",
"aria-checked",
"false",
);
cy.dataCy("never-expire-checkbox").should(
"have.css",
"pointer-events",
"none",
);
cy.dataCy("expirable-radio-box").children().should("have.length", 2);
cy.getInputByLabel("Expirable Host").should("not.be.disabled");
cy.getInputByLabel("Unexpirable Host").should("be.disabled");
});

it("Clicking on the spawn host button should open a spawn host modal.", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exports[`Snapshot Tests ProjectSelect.stories Default 1`] = `
Project
</label>
<div
class="css-gte0m8-Wrapper e17dnzmx4"
class="css-7yazxp-Wrapper e17dnzmx4"
>
<div
class="css-1ry038z-Container e1yw4j304"
Expand Down Expand Up @@ -82,7 +82,7 @@ exports[`Snapshot Tests ProjectSelect.stories WithClickableHeader 1`] = `
Project
</label>
<div
class="css-gte0m8-Wrapper e17dnzmx4"
class="css-7yazxp-Wrapper e17dnzmx4"
>
<div
class="css-1ry038z-Container e1yw4j304"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ exports[`Snapshot Tests SearchableDropdown.stories CustomOption 1`] = `
Custom option select
</label>
<div
class="css-gte0m8-Wrapper e17dnzmx4"
class="css-7yazxp-Wrapper e17dnzmx4"
>
<div
class="css-1ry038z-Container e1yw4j304"
Expand Down Expand Up @@ -82,7 +82,7 @@ exports[`Snapshot Tests SearchableDropdown.stories Default 1`] = `
Searchable Dropdown
</label>
<div
class="css-gte0m8-Wrapper e17dnzmx4"
class="css-7yazxp-Wrapper e17dnzmx4"
>
<div
class="css-1ry038z-Container e1yw4j304"
Expand Down
1 change: 1 addition & 0 deletions apps/spruce/src/components/SearchableDropdown/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ const ScrollableList = styled.div`
`;

const Wrapper = styled.div`
margin-top: ${size.xxs};
width: ${(props: { width?: string }): string =>
props.width ? props.width : ""};
`;
Expand Down
178 changes: 178 additions & 0 deletions apps/spruce/src/components/Spawn/getFormSchema.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
import { css } from "@emotion/react";
import Badge from "@leafygreen-ui/badge";
import { Body } from "@leafygreen-ui/typography";
import widgets from "components/SpruceForm/Widgets";
import { prettifyTimeZone } from "constants/fieldMaps";
import { size } from "constants/tokens";

const defaultStartTime = new Date();
defaultStartTime.setHours(8, 0, 0, 0);
const defaultEndTime = new Date();
defaultEndTime.setHours(20, 0, 0, 0);

type HostUptimeProps = {
hostUptimeValidation?: {
enabledHoursCount: number;
errors: string[];
warnings: string[];
};
timeZone?: string;
totalUptimeHours?: number;
};

export const getHostUptimeSchema = ({
hostUptimeValidation,
timeZone,
}: HostUptimeProps) => ({
schema: {
type: "object" as "object",
title: "",
properties: {
useDefaultUptimeSchedule: {
type: "boolean" as "boolean",
title: "Use default host uptime schedule (Mon–Fri, 8am–8pm)",
default: true,
},
sleepSchedule: {
type: "object" as "object",
title: "",
properties: {
enabledWeekdays: {
type: "array" as "array",
title: "",
default: [false, true, true, true, true, true, false],
items: {
type: "boolean" as "boolean",
},
},
timeSelection: {
type: "object" as "object",
title: "",
properties: {
startTime: {
type: "string" as "string",
title: "Start Time",
default: defaultStartTime.toString(),
},
endTime: {
type: "string" as "string",
title: "End Time",
default: defaultEndTime.toString(),
},
or: {
type: "null" as "null",
},
runContinuously: {
type: "boolean" as "boolean",
title: "Run continuously for enabled days",
},
},
dependencies: {
runContinuously: {
oneOf: [
{
properties: {
runContinuously: { enum: [false] },
},
},
{
properties: {
runContinuously: { enum: [true] },
startTime: { readOnly: true },
endTime: { readOnly: true },
},
},
],
},
},
},
},
},
details: {
type: "null" as "null",
},
},
dependencies: {
useDefaultUptimeSchedule: {
oneOf: [
{
properties: {
useDefaultUptimeSchedule: { enum: [false] },
},
},
{
properties: {
useDefaultUptimeSchedule: { enum: [true] },
sleepSchedule: { readOnly: true },
},
},
],
},
},
},
uiSchema: {
useDefaultUptimeSchedule: {
"ui:bold": true,
},
sleepSchedule: {
enabledWeekdays: {
"ui:addable": false,
"ui:showLabel": false,
"ui:widget": widgets.DayPickerWidget,
},
timeSelection: {
"ui:fieldSetCSS": css`
align-items: center;
display: flex;
gap: ${size.xs};
> * {
width: fit-content;
}
`,
startTime: {
"ui:format": "HH:mm",
"ui:useUtc": false,
"ui:widget": widgets.TimeWidget,
},
endTime: {
"ui:format": "HH:mm",
"ui:useUtc": false,
"ui:widget": widgets.TimeWidget,
},
or: {
"ui:showLabel": false,
"ui:descriptionNode": <Body>or</Body>,
},
runContinuously: {
"ui:elementWrapperCSS": css`
margin-bottom: 0;
white-space: nowrap;
width: fit-content;
`,
},
},
},
details: {
"ui:descriptionNode": (
<Details
timeZone={timeZone}
totalUptimeHours={hostUptimeValidation?.enabledHoursCount}
/>
),
"ui:showLabel": false,
"ui:warnings": hostUptimeValidation?.warnings,
"ui:errors": hostUptimeValidation?.errors,
},
},
});

const Details: React.FC<{ timeZone: string; totalUptimeHours: number }> = ({
timeZone,
totalUptimeHours,
}) => (
<>
All times are displayed in{" "}
<Badge>{prettifyTimeZone.get(timeZone) ?? timeZone}</Badge>{" "}
{totalUptimeHours} host uptime hours per week
</>
);
1 change: 1 addition & 0 deletions apps/spruce/src/components/Spawn/index.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./Layout";
export { DetailsCard } from "./DetailsCard";
export { MountVolumeSelect } from "./MountVolumeSelect";
export { maxUptimeHours, validateUptimeSchedule } from "./utils";
Loading

0 comments on commit 78bf3bc

Please sign in to comment.