diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json
index 342ce0cdd..e11541edb 100644
--- a/frontend/src/locale/en.json
+++ b/frontend/src/locale/en.json
@@ -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": {
diff --git a/frontend/src/pages/Fleets/Add/FleetFormFields/constants.tsx b/frontend/src/pages/Fleets/Add/FleetFormFields/constants.tsx
index 372c4dcfa..7904a1774 100644
--- a/frontend/src/pages/Fleets/Add/FleetFormFields/constants.tsx
+++ b/frontend/src/pages/Fleets/Add/FleetFormFields/constants.tsx
@@ -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:
Min number of instances
,
body: (
@@ -78,6 +86,26 @@ export const FLEET_IDLE_DURATION_INFO = {
),
};
+export const FLEET_SPOT_POLICY_INFO = {
+ header: Spot policy
,
+ body: (
+ <>
+
+ 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.
+
+
+ If you set spot_policy to auto, the fleet will allow the use of both types of
+ instances: on-demand and spot.
+
+
+ Note that run configurations must specify their own spot_policy, which by default is always{' '}
+ on-demand.
+
+ >
+ ),
+};
+
const requiredFieldError = 'This is required field';
const numberFieldError = 'This is number field';
diff --git a/frontend/src/pages/Fleets/Add/FleetFormFields/index.tsx b/frontend/src/pages/Fleets/Add/FleetFormFields/index.tsx
index a19e70136..4aa04c393 100644
--- a/frontend/src/pages/Fleets/Add/FleetFormFields/index.tsx
+++ b/frontend/src/pages/Fleets/Add/FleetFormFields/index.tsx
@@ -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';
@@ -64,6 +69,22 @@ export function FleetFormFields({
type="number"
/>
+ 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' },
+ ]}
+ />
+
openHelpPanel(FLEET_IDLE_DURATION_INFO)} />}
label={t('fleets.edit.idle_duration')}
diff --git a/frontend/src/pages/Fleets/Add/FleetFormFields/type.ts b/frontend/src/pages/Fleets/Add/FleetFormFields/type.ts
index 7351a60a6..835ca8373 100644
--- a/frontend/src/pages/Fleets/Add/FleetFormFields/type.ts
+++ b/frontend/src/pages/Fleets/Add/FleetFormFields/type.ts
@@ -12,4 +12,5 @@ export type FleetFormFields = {
min_instances: number;
max_instances?: number;
idle_duration?: string;
+ spot_policy: TSpotPolicy;
};
diff --git a/frontend/src/pages/Fleets/Add/index.tsx b/frontend/src/pages/Fleets/Add/index.tsx
index b661d96cb..d7390fc93 100644
--- a/frontend/src/pages/Fleets/Add/index.tsx
+++ b/frontend/src/pages/Fleets/Add/index.tsx
@@ -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';
@@ -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 = () => {
@@ -52,9 +58,8 @@ export const FleetAdd: React.FC = () => {
const formMethods = useForm({
resolver,
defaultValues: {
+ ...fleetFormDefaultValues,
project_name: paramProjectName,
- min_instances: 0,
- idle_duration: '5m',
},
});
@@ -62,7 +67,7 @@ export const FleetAdd: React.FC = () => {
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: {
@@ -74,6 +79,7 @@ export const FleetAdd: React.FC = () => {
...(max_instances ? { max: max_instances } : {}),
},
...(idle_duration ? { idle_duration } : {}),
+ spot_policy,
},
profile: {},
},
@@ -185,7 +191,13 @@ export const FleetAdd: React.FC = () => {
};
const getDefaultFleetSummary = () => {
- const summaryFields: Array = ['name', 'min_instances', 'max_instances', 'idle_duration'];
+ const summaryFields: Array = [
+ 'name',
+ 'min_instances',
+ 'max_instances',
+ 'idle_duration',
+ 'spot_policy',
+ ];
const result: string[] = [];
diff --git a/frontend/src/pages/Project/Add/index.tsx b/frontend/src/pages/Project/Add/index.tsx
index 23a9eb0a1..23cbc4c00 100644
--- a/frontend/src/pages/Project/Add/index.tsx
+++ b/frontend/src/pages/Project/Add/index.tsx
@@ -16,6 +16,7 @@ import { useCreateProjectMutation } from 'services/project';
import { FleetFormFields } from 'pages/Fleets/Add/FleetFormFields';
import {
+ fleetFormDefaultValues,
getMaxInstancesValidator,
getMinInstancesValidator,
idleDurationValidator,
@@ -51,6 +52,7 @@ const projectValidationSchema = yup.object({
is: true,
then: idleDurationValidator,
}),
+ spot_policy: yup.string().required(requiredFieldError),
}),
});
@@ -72,9 +74,8 @@ export const ProjectAdd: React.FC = () => {
defaultValues: {
is_public: false,
fleet: {
+ ...fleetFormDefaultValues,
enable_default: true,
- min_instances: 0,
- idle_duration: '5m',
},
},
});
@@ -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 {
@@ -106,6 +107,7 @@ export const ProjectAdd: React.FC = () => {
...(max_instances ? { max: max_instances } : {}),
},
...(idle_duration ? { idle_duration } : {}),
+ spot_policy,
},
profile: {},
},
@@ -231,7 +233,13 @@ export const ProjectAdd: React.FC = () => {
};
const getDefaultFleetSummary = () => {
- const summaryFields: Array = ['name', 'min_instances', 'max_instances', 'idle_duration'];
+ const summaryFields: Array = [
+ 'name',
+ 'min_instances',
+ 'max_instances',
+ 'idle_duration',
+ 'spot_policy',
+ ];
const result: string[] = [];
diff --git a/frontend/src/pages/Project/CreateWizard/index.tsx b/frontend/src/pages/Project/CreateWizard/index.tsx
index 83cf3804d..ecf2d413e 100644
--- a/frontend/src/pages/Project/CreateWizard/index.tsx
+++ b/frontend/src/pages/Project/CreateWizard/index.tsx
@@ -30,6 +30,7 @@ import { useCreateWizardProjectMutation } from 'services/project';
import { FleetFormFields } from '../../Fleets/Add/FleetFormFields';
import {
+ fleetFormDefaultValues,
getMaxInstancesValidator,
getMinInstancesValidator,
idleDurationValidator,
@@ -69,6 +70,7 @@ const projectValidationSchema = yup.object({
is: true,
then: idleDurationValidator,
}),
+ spot_policy: yup.string().required(requiredFieldError),
}),
});
@@ -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',
},
},
});
@@ -319,6 +320,7 @@ export const CreateProjectWizard: React.FC = () => {
'min_instances',
'max_instances',
'idle_duration',
+ 'spot_policy',
];
const result: string[] = [];