diff --git a/cypress/integration/distroSettings/host_section.ts b/cypress/integration/distroSettings/host_section.ts index 72cc6f2720..43536ee68c 100644 --- a/cypress/integration/distroSettings/host_section.ts +++ b/cypress/integration/distroSettings/host_section.ts @@ -108,16 +108,6 @@ describe("host section", () => { cy.validateToast("success"); // Reset fields - cy.getInputByLabel("Working Directory").clear(); - cy.getInputByLabel("Jasper Credentials Path").clear(); - cy.getInputByLabel("Client Directory").clear(); - cy.getInputByLabel("Shell Path").clear(); - cy.getInputByLabel("Home Volume Format Command").clear(); - cy.getInputByLabel("Number of Files").clear(); - cy.getInputByLabel("Number of CGroup Tasks").clear(); - cy.getInputByLabel("Number of Processes").clear(); - cy.getInputByLabel("Locked Memory (kB)").clear(); - cy.getInputByLabel("Virtual Memory (kB)").clear(); cy.dataCy("delete-item-button").first().click(); cy.dataCy("delete-item-button").first().click(); diff --git a/src/components/SpruceForm/Widgets/LeafyGreenWidgets.tsx b/src/components/SpruceForm/Widgets/LeafyGreenWidgets.tsx index 431f1edb0a..d5218ab86d 100644 --- a/src/components/SpruceForm/Widgets/LeafyGreenWidgets.tsx +++ b/src/components/SpruceForm/Widgets/LeafyGreenWidgets.tsx @@ -42,13 +42,13 @@ export const LeafyGreenTextInput: React.FC< "data-cy": dataCy, description, elementWrapperCSS, - emptyValue = "", inputType, optional, warnings, } = options; const { errors, hasError } = processErrors(rawErrors); + const emptyValue = "emptyValue" in options ? options.emptyValue : ""; const inputProps = { ...(!isNullish(schema.maximum) && { max: schema.maximum }), diff --git a/src/pages/distroSettings/tabs/HostTab/getFormSchema.tsx b/src/pages/distroSettings/tabs/HostTab/getFormSchema.tsx index d30b50f846..689569f432 100644 --- a/src/pages/distroSettings/tabs/HostTab/getFormSchema.tsx +++ b/src/pages/distroSettings/tabs/HostTab/getFormSchema.tsx @@ -6,6 +6,7 @@ import { CardFieldTemplate, FieldRow, } from "components/SpruceForm/FieldTemplates"; +import { size } from "constants/tokens"; import { Arch, BootstrapMethod, Provider, SshKey } from "gql/generated/types"; import { architectureToCopy, @@ -62,6 +63,7 @@ export const getFormSchema = ({ workDir: { type: "string" as "string", title: "Working Directory", + minLength: 1, }, setupAsSudo: { type: "boolean" as "boolean", @@ -73,7 +75,7 @@ export const getFormSchema = ({ }, userSpawnAllowed: { type: "boolean" as "boolean", - title: "Allow users to spawn these hosts for personal use", + title: "Spawnable", }, }, dependencies: { @@ -107,8 +109,7 @@ export const getFormSchema = ({ userSpawnAllowed: { enum: [true] }, isVirtualWorkStation: { type: "boolean" as "boolean", - title: - "Allow spawned hosts of this distro to be used as virtual workstations", + title: "Virtual Workstations", }, }, dependencies: { @@ -196,6 +197,10 @@ export const getFormSchema = ({ margin-bottom: 0; `, }, + workDir: { + "ui:description": + "Absolute path in which the agent run tasks on the host machine", + }, setupScript: { "ui:elementWrapperCSS": css` margin-top: -22px; @@ -207,21 +212,71 @@ export const getFormSchema = ({ "ui:disabled": true, "ui:tooltipDescription": "Static distros are not spawnable.", }), + "ui:description": + "Allow users to spawn these hosts for personal use.", + "ui:bold": true, + }, + isVirtualWorkStation: { + "ui:description": + "Allow spawned hosts of this distro to be used as virtual workstations.", + "ui:bold": true, + }, + icecreamSchedulerHost: { + "ui:elementWrapperCSS": indentCSS, + }, + icecreamConfigPath: { + "ui:elementWrapperCSS": indentCSS, }, }, bootstrapSettings: { "ui:ObjectFieldTemplate": CardFieldTemplate, serviceUser: { + "ui:description": "Username for setting up Evergreen services", // Only visible for Windows ...(!windowsArchitectures.includes(architecture) && { "ui:widget": "hidden", }), }, + jasperBinaryDir: { + "ui:description": + "Absolute native path to the directory containing the Jasper binary", + }, + jasperCredentialsPath: { + "ui:description": + "Absolute native path to the directory containing the Jasper credentials", + }, + clientDir: { + "ui:description": + "Absolute native path to the directory containing the evergreen binary", + }, + shellPath: { + "ui:description": + "Absolute native path to the shell binary file (bash)", + }, resourceLimits: { // Only visible for Linux ...(!linuxArchitectures.includes(architecture) && { "ui:widget": "hidden", }), + numFiles: { + "ui:description": + "Max number of open file handles. Set -1 for unlimited.", + }, + numProcesses: { + "ui:description": "Max number of processes. Set -1 for unlimited.", + }, + numTasks: { + "ui:description": + "Max number of cgroup tasks (threads). Set -1 for unlimited.", + }, + lockedMemory: { + "ui:description": + "Max size (kB) that can be locked into memory. Set -1 for unlimited.", + }, + virtualMemory: { + "ui:description": + "Max size (kB) of available virtual memory. Set -1 for unlimited.", + }, }, env: { "ui:addButtonText": "Add variable", @@ -245,7 +300,7 @@ export const getFormSchema = ({ }, script: { "ui:description": - "The precondition script that must run and succeed.", + "The precondition script that must run and succeed before Jasper can start.", "ui:widget": "textarea", }, }, @@ -253,6 +308,9 @@ export const getFormSchema = ({ }, sshConfig: { "ui:ObjectFieldTemplate": CardFieldTemplate, + user: { + "ui:description": "Username with which to SSH into host machine", + }, sshKey: { "ui:allowDeselect": false, }, @@ -262,9 +320,10 @@ export const getFormSchema = ({ }, sshOptions: { "ui:addButtonText": "Add SSH option", - "ui:descriptionNode": ( + "ui:description": ( <> - Option keywords supported by ssh_config. + Specify option keywords supported by{" "} + ssh_config. ), "ui:orderable": false, @@ -297,10 +356,12 @@ export const getFormSchema = ({ }, acceptableHostIdleTime: { "ui:data-cy": "idle-time-input", + "ui:description": "Set 0 to use global default.", ...(!hasEC2Provider && { "ui:widget": "hidden" }), }, futureHostFraction: { "ui:data-cy": "future-fraction-input", + "ui:description": "Set 0 to use global default.", ...(!hasEC2Provider && { "ui:widget": "hidden" }), }, }, @@ -315,50 +376,66 @@ const bootstrapSettings = { jasperBinaryDir: { type: "string" as "string", title: "Jasper Binary Directory", + minLength: 1, }, jasperCredentialsPath: { type: "string" as "string", title: "Jasper Credentials Path", + minLength: 1, }, clientDir: { type: "string" as "string", title: "Client Directory", + minLength: 1, }, shellPath: { type: "string" as "string", title: "Shell Path", - }, - serviceUser: { - type: "string" as "string", - title: "Service User", + minLength: 1, }, homeVolumeFormatCommand: { type: "string" as "string", title: "Home Volume Format Command", }, + serviceUser: { + type: "string" as "string", + title: "Service User", + }, resourceLimits: { type: "object" as "object", title: "Resource Limits", + required: [ + "numFiles", + "numTasks", + "numProcesses", + "lockedMemoryKb", + "virtualMemoryKb", + ], properties: { numFiles: { type: "number" as "number", title: "Number of Files", + minimum: -1, }, numTasks: { type: "number" as "number", title: "Number of CGroup Tasks", + minimum: -1, }, numProcesses: { type: "number" as "number", title: "Number of Processes", + minimum: -1, }, lockedMemoryKb: { type: "number" as "number", - title: "Locked Memory (kB)", + title: "Locked Memory", + minimum: -1, }, virtualMemoryKb: { type: "number" as "number", title: "Virtual Memory (kB)", + minimum: -1, }, }, }, @@ -421,6 +498,7 @@ const sshConfig = (sshKeys: SshKey[]) => ({ user: { type: "string" as "string", title: "SSH User", + minLength: 1, }, sshKey: { type: "string" as "string", @@ -451,6 +529,12 @@ const sshConfig = (sshKeys: SshKey[]) => ({ const allocation = { type: "object" as "object", title: "Host Allocation", + required: [ + "minimumHosts", + "maximumHosts", + "acceptableHostIdleTime", + "futureHostFraction", + ], properties: { version: { type: "string" as "string", @@ -485,6 +569,7 @@ const allocation = { acceptableHostIdleTime: { type: "number" as "number", title: "Acceptable Host Idle Time (s)", + minimum: 0, }, futureHostFraction: { type: "number" as "number", @@ -494,3 +579,7 @@ const allocation = { }, }, }; + +const indentCSS = css` + margin-left: ${size.m}; +`; diff --git a/src/pages/distroSettings/tabs/HostTab/transformers.ts b/src/pages/distroSettings/tabs/HostTab/transformers.ts index 7448a413f9..e71e286896 100644 --- a/src/pages/distroSettings/tabs/HostTab/transformers.ts +++ b/src/pages/distroSettings/tabs/HostTab/transformers.ts @@ -90,12 +90,15 @@ export const formToGql = (( serviceUser: bootstrapSettings.serviceUser, shellPath: bootstrapSettings.shellPath, }, + homeVolumeSettings: { + formatCommand: bootstrapSettings.homeVolumeFormatCommand, + }, + hostAllocatorSettings: allocation, iceCreamSettings: { configPath: setup.icecreamConfigPath, schedulerHost: setup.icecreamSchedulerHost, }, isVirtualWorkStation: setup.isVirtualWorkStation, - hostAllocatorSettings: allocation, setupAsSudo: setup.setupAsSudo, setup: setup.setupScript, sshKey: sshConfig.sshKey,