Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions frontend/src/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,14 @@
"name_placeholder": "Optional",
"name_constraint": "Example: 'my-fleet' or 'default'. If not specified, generated automatically.",
"min_instances": "Min number of instances",
"min_instances_description": "Set it `0` to provision instances only when required",
"min_instances_description": "Set it '0' to provision instances only when required",
"max_instances": "Max number of instances",
"max_instances_description": "Required only if you want to set an upper limit",
"max_instances_placeholder": "Optional",
"idle_duration": "Idle duration",
"idle_duration_description": "Example: '0s', '1m', '1h'"
"idle_duration_description": "Example: '0s', '1m', '1h'",
"spot_policy": "Spot policy",
"spot_policy_description": "Set it to 'auto' to allow the use of both on-demand and spot instances"
}
},
"volume": {
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/pages/Fleets/Add/FleetFormFields/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ import React from 'react';
import { get } from 'lodash';
import * as yup from 'yup';

import { FleetFormFields } from './type';

export const fleetFormDefaultValues: FleetFormFields = {
min_instances: 0,
idle_duration: '5m',
spot_policy: 'auto',
};

export const FLEET_MIN_INSTANCES_INFO = {
header: <h2>Min number of instances</h2>,
body: (
Expand Down Expand Up @@ -78,6 +86,26 @@ export const FLEET_IDLE_DURATION_INFO = {
),
};

export const FLEET_SPOT_POLICY_INFO = {
header: <h2>Spot policy</h2>,
body: (
<>
<p>
Some backends may support spot instances, also known as preemptive instances. Such instances come at a
significantly lower price but can be interrupted by the cloud provider at any time.
</p>
<p>
If you set <code>spot_policy</code> to <code>auto</code>, the fleet will allow the use of both types of
instances: <code>on-demand</code> and <code>spot</code>.
</p>
<p>
Note that run configurations must specify their own <code>spot_policy</code>, which by default is always{' '}
<code>on-demand</code>.
</p>
</>
),
};

const requiredFieldError = 'This is required field';
const numberFieldError = 'This is number field';

Expand Down
25 changes: 23 additions & 2 deletions frontend/src/pages/Fleets/Add/FleetFormFields/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import { FormInput, InfoLink, SpaceBetween } from 'components';
import { FormInput, FormSelect, InfoLink, SpaceBetween } from 'components';

import { useHelpPanel } from 'hooks';

import { FLEET_IDLE_DURATION_INFO, FLEET_MAX_INSTANCES_INFO, FLEET_MIN_INSTANCES_INFO } from './constants';
import {
FLEET_IDLE_DURATION_INFO,
FLEET_MAX_INSTANCES_INFO,
FLEET_MIN_INSTANCES_INFO,
FLEET_SPOT_POLICY_INFO,
} from './constants';
import { FleetFormFieldsProps } from './type';

import type { FieldValues } from 'react-hook-form/dist/types/fields';
Expand Down Expand Up @@ -64,6 +69,22 @@ export function FleetFormFields<T extends FieldValues = FieldValues>({
type="number"
/>

<FormSelect
info={<InfoLink onFollow={() => openHelpPanel(FLEET_SPOT_POLICY_INFO)} />}
label={t('fleets.edit.spot_policy')}
constraintText={t('fleets.edit.spot_policy_description')}
control={control}
//eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
name={getFieldNameWitPrefix(`spot_policy`)}
disabled={disabledAllFields}
options={[
{ label: 'auto', value: 'auto' },
{ label: 'on-demand', value: 'on-demand' },
{ label: 'spot', value: 'spot' },
]}
/>

<FormInput
info={<InfoLink onFollow={() => openHelpPanel(FLEET_IDLE_DURATION_INFO)} />}
label={t('fleets.edit.idle_duration')}
Expand Down
1 change: 1 addition & 0 deletions frontend/src/pages/Fleets/Add/FleetFormFields/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ export type FleetFormFields = {
min_instances: number;
max_instances?: number;
idle_duration?: string;
spot_policy: TSpotPolicy;
};
22 changes: 17 additions & 5 deletions frontend/src/pages/Fleets/Add/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ import { useApplyFleetMutation } from 'services/fleet';
import { DEFAULT_FLEET_INFO } from 'pages/Project/constants';
import { useYupValidationResolver } from 'pages/Project/hooks/useYupValidationResolver';

import { getMaxInstancesValidator, getMinInstancesValidator, idleDurationValidator } from './FleetFormFields/constants';
import {
fleetFormDefaultValues,
getMaxInstancesValidator,
getMinInstancesValidator,
idleDurationValidator,
} from './FleetFormFields/constants';
import { FleetFormFields } from './FleetFormFields';

import { IFleetWizardForm } from './types';
Expand All @@ -33,6 +38,7 @@ const fleetValidationSchema = yup.object({
min_instances: getMinInstancesValidator('max_instances'),
max_instances: getMaxInstancesValidator('min_instances'),
idle_duration: idleDurationValidator,
spot_policy: yup.string().required(requiredFieldError),
});

export const FleetAdd: React.FC = () => {
Expand All @@ -52,17 +58,16 @@ export const FleetAdd: React.FC = () => {
const formMethods = useForm<IFleetWizardForm>({
resolver,
defaultValues: {
...fleetFormDefaultValues,
project_name: paramProjectName,
min_instances: 0,
idle_duration: '5m',
},
});

const { handleSubmit, control, clearErrors, trigger, watch, getValues } = formMethods;
const formValues = watch();

const getFormValuesForFleetApplying = (): IApplyFleetPlanRequestRequest => {
const { min_instances, max_instances, idle_duration, name } = getValues();
const { min_instances, max_instances, idle_duration, name, spot_policy } = getValues();

return {
plan: {
Expand All @@ -74,6 +79,7 @@ export const FleetAdd: React.FC = () => {
...(max_instances ? { max: max_instances } : {}),
},
...(idle_duration ? { idle_duration } : {}),
spot_policy,
},
profile: {},
},
Expand Down Expand Up @@ -185,7 +191,13 @@ export const FleetAdd: React.FC = () => {
};

const getDefaultFleetSummary = () => {
const summaryFields: Array<keyof IFleetWizardForm> = ['name', 'min_instances', 'max_instances', 'idle_duration'];
const summaryFields: Array<keyof IFleetWizardForm> = [
'name',
'min_instances',
'max_instances',
'idle_duration',
'spot_policy',
];

const result: string[] = [];

Expand Down
16 changes: 12 additions & 4 deletions frontend/src/pages/Project/Add/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { useCreateProjectMutation } from 'services/project';

import { FleetFormFields } from 'pages/Fleets/Add/FleetFormFields';
import {
fleetFormDefaultValues,
getMaxInstancesValidator,
getMinInstancesValidator,
idleDurationValidator,
Expand Down Expand Up @@ -51,6 +52,7 @@ const projectValidationSchema = yup.object({
is: true,
then: idleDurationValidator,
}),
spot_policy: yup.string().required(requiredFieldError),
}),
});

Expand All @@ -72,9 +74,8 @@ export const ProjectAdd: React.FC = () => {
defaultValues: {
is_public: false,
fleet: {
...fleetFormDefaultValues,
enable_default: true,
min_instances: 0,
idle_duration: '5m',
},
},
});
Expand All @@ -93,7 +94,7 @@ export const ProjectAdd: React.FC = () => {

const getFormValuesForFleetApplying = (): IApplyFleetPlanRequestRequest => {
const {
fleet: { min_instances, max_instances, idle_duration, name },
fleet: { min_instances, max_instances, idle_duration, name, spot_policy },
} = getValues();

return {
Expand All @@ -106,6 +107,7 @@ export const ProjectAdd: React.FC = () => {
...(max_instances ? { max: max_instances } : {}),
},
...(idle_duration ? { idle_duration } : {}),
spot_policy,
},
profile: {},
},
Expand Down Expand Up @@ -231,7 +233,13 @@ export const ProjectAdd: React.FC = () => {
};

const getDefaultFleetSummary = () => {
const summaryFields: Array<keyof IProjectForm['fleet']> = ['name', 'min_instances', 'max_instances', 'idle_duration'];
const summaryFields: Array<keyof IProjectForm['fleet']> = [
'name',
'min_instances',
'max_instances',
'idle_duration',
'spot_policy',
];

const result: string[] = [];

Expand Down
6 changes: 4 additions & 2 deletions frontend/src/pages/Project/CreateWizard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { useCreateWizardProjectMutation } from 'services/project';

import { FleetFormFields } from '../../Fleets/Add/FleetFormFields';
import {
fleetFormDefaultValues,
getMaxInstancesValidator,
getMinInstancesValidator,
idleDurationValidator,
Expand Down Expand Up @@ -69,6 +70,7 @@ const projectValidationSchema = yup.object({
is: true,
then: idleDurationValidator,
}),
spot_policy: yup.string().required(requiredFieldError),
}),
});

Expand Down Expand Up @@ -127,9 +129,8 @@ export const CreateProjectWizard: React.FC = () => {
defaultValues: {
project_type: 'gpu_marketplace',
fleet: {
...fleetFormDefaultValues,
enable_default: true,
min_instances: 0,
idle_duration: '5m',
},
},
});
Expand Down Expand Up @@ -319,6 +320,7 @@ export const CreateProjectWizard: React.FC = () => {
'min_instances',
'max_instances',
'idle_duration',
'spot_policy',
];

const result: string[] = [];
Expand Down