diff --git a/src/components/JobWizard/JobWizard.js b/src/components/JobWizard/JobWizard.js
index 52e991b54..043629bf1 100644
--- a/src/components/JobWizard/JobWizard.js
+++ b/src/components/JobWizard/JobWizard.js
@@ -411,7 +411,7 @@ const JobWizard = ({
ref: scheduleButtonRef
},
{
- id:'run-btn',
+ id: 'run-btn',
label:
mode === PANEL_EDIT_MODE
? 'Save'
@@ -483,11 +483,14 @@ const JobWizard = ({
/>
diff --git a/src/components/JobWizard/JobWizard.util.js b/src/components/JobWizard/JobWizard.util.js
index 69f2f1f44..f575c80fa 100644
--- a/src/components/JobWizard/JobWizard.util.js
+++ b/src/components/JobWizard/JobWizard.util.js
@@ -29,7 +29,7 @@ import {
merge,
omit,
set,
- unionBy
+ some
} from 'lodash'
import {
ADVANCED_STEP,
@@ -83,7 +83,6 @@ import { generateObjectFromKeyValue, parseObjectToKeyValue } from 'igz-controls/
import { getDefaultSchedule, scheduleDataInitialState } from '../SheduleWizard/scheduleWizard.util'
import { getErrorDetail } from 'igz-controls/utils/common.util'
import { getPreemptionMode } from '../../utils/getPreemptionMode'
-import { isEveryObjectValueEmpty } from '../../utils/isEveryObjectValueEmpty'
import { trimSplit } from '../../utils'
const volumeTypesMap = {
@@ -105,21 +104,21 @@ export const generateJobWizardData = (
defaultData,
currentProjectName,
isEditMode,
- prePopulatedData
+ prePopulatedData,
+ selectedVersion
) => {
- const functions = selectedFunctionData.functions
- const functionInfo = getFunctionInfo(selectedFunctionData)
+ const functionInfo = getFunctionInfo(selectedFunctionData, selectedVersion)
const defaultResources = frontendSpec?.default_function_pod_resources ?? {}
- const functionParameters = getFunctionParameters(functions, functionInfo.handler)
- const [functionPriorityClassName] = getFunctionPriorityClass(functions)
- const [limits] = getLimits(functions)
- const [requests] = getRequests(functions)
- const environmentVariables = getEnvironmentVariables(functions)
- const [preemptionMode] = getFunctionPreemptionMode(functions)
+ const functionParameters = getFunctionParameters(functionInfo.function, functionInfo.handler)
+ const functionPriorityClassName = getFunctionPriorityClass(functionInfo.function)
+ const limits = getLimits(functionInfo.function)
+ const requests = getRequests(functionInfo.function)
+ const environmentVariables = getEnvironmentVariables(functionInfo.function)
+ const preemptionMode = getFunctionPreemptionMode(functionInfo.function)
const jobPriorityClassName =
functionPriorityClassName || frontendSpec.default_function_priority_class_name || ''
- const nodeSelectorTable = getNodeSelectors(functions)
- const volumesTable = getVolumesData(functions)
+ const nodeSelectorTable = getNodeSelectors(functionInfo.function)
+ const volumesTable = getVolumes(functionInfo.function)
const gpuType = getLimitsGpuType(limits)
const scheduleData = defaultData?.schedule
? getDefaultSchedule(defaultData.schedule)
@@ -203,15 +202,15 @@ export const generateJobWizardData = (
export const generateJobWizardDefaultData = (
frontendSpec,
- selectedFunctionData,
+ selectedFunction,
defaultData,
currentProjectName,
isEditMode
) => {
if (isEmpty(defaultData)) return [{}, {}]
- const runInfo = getRunDefaultInfo(defaultData, selectedFunctionData)
- const functionParameters = getFunctionDefaultParameters(selectedFunctionData, runInfo.handler)
+ const runInfo = getRunDefaultInfo(defaultData, selectedFunction)
+ const functionParameters = getFunctionDefaultParameters(selectedFunction, runInfo.handler)
const [predefinedParameters, customParameters] = parseDefaultParameters(
functionParameters,
defaultData.task.spec.parameters,
@@ -241,7 +240,7 @@ export const generateJobWizardDefaultData = (
handler: runInfo.handler,
handlerData: runInfo.handlerData,
labels: runInfo.labels,
- image: parseImageData(selectedFunctionData, frontendSpec, currentProjectName)
+ image: parseImageData(selectedFunction, frontendSpec, currentProjectName)
},
[DATA_INPUTS_STEP]: {
dataInputsTable: parseDefaultDataInputs(functionParameters, defaultData.task.spec.inputs)
@@ -299,12 +298,9 @@ export const generateJobWizardDefaultData = (
return [jobFormData, jobAdditionalData]
}
-export const getHandlerData = (selectedFunctionData, handler) => {
- const currentFunction = selectedFunctionData?.functions
- ? chain(selectedFunctionData.functions).orderBy('metadata.updated', 'desc').get(0).value()
- : selectedFunctionData
- const handlerData = get(currentFunction, ['spec', 'entry_points', handler], {})
- const outputs = (handlerData?.outputs ?? []).filter(output => !isEveryObjectValueEmpty(output))
+export const getHandlerData = (selectedFunction, handler) => {
+ const handlerData = get(selectedFunction, ['spec', 'entry_points', handler], {})
+ const outputs = (handlerData?.outputs ?? []).filter(output => output.type)
return {
doc: handlerData?.doc,
@@ -313,109 +309,96 @@ export const getHandlerData = (selectedFunctionData, handler) => {
}
}
-const getFunctionInfo = selectedFunctionData => {
+const getFunctionInfo = (selectedFunctionData, preSelectedVersion) => {
const functions = selectedFunctionData?.functions
if (!isEmpty(functions)) {
const versionOptions = getVersionOptions(functions)
- const handlerOptions = getHandlerOptions(functions)
- const { defaultVersion, defaultHandler } = getDefaultHandlerAndVersion(
- versionOptions,
- handlerOptions,
- functions
- )
- const currentFunctionVersion = selectedFunctionData.tag || defaultVersion
- const currentFunction =
- functions.find(func => func.metadata.tag === currentFunctionVersion) ?? functions[0]
+ const selectedVersion = some(versionOptions, { id: preSelectedVersion })
+ ? preSelectedVersion
+ : getDefaultVersion(versionOptions)
+ const handlerOptions = getHandlerOptions(functions, selectedVersion)
+ const defaultHandler = getDefaultHandler(handlerOptions, functions, selectedVersion)
+ const selectedFunction = getSelectedFunction(functions, selectedVersion)
return {
name: selectedFunctionData.name,
handler: defaultHandler,
- version: currentFunctionVersion,
- handlerData: getHandlerData(currentFunction, defaultHandler),
+ version: selectedVersion,
+ handlerData: getHandlerData(selectedFunction, defaultHandler),
handlerOptions,
versionOptions,
- function: currentFunction || {}
+ function: selectedFunction || {}
}
}
}
-const getRunDefaultInfo = (defaultData, selectedFunctionData) => {
+const getRunDefaultInfo = (defaultData, selectedFunction) => {
return {
labels: parseChipsData(defaultData.task?.metadata?.labels),
name: defaultData.task?.metadata?.name || '',
handler: defaultData.task?.spec?.handler,
- handlerData: getHandlerData(selectedFunctionData, defaultData.task?.spec?.handler),
+ handlerData: getHandlerData(selectedFunction, defaultData.task?.spec?.handler),
handlerOptions: [],
version: '',
versionOptions: []
}
}
-const getHandlerOptions = selectedFunctions => {
+const getHandlerOptions = (selectedFunctions, selectedVersion) => {
return chain(selectedFunctions)
- .map(func => Object.values(func.spec?.entry_points ?? {}))
+ .find(func => func.metadata.tag === selectedVersion)
+ .get('spec.entry_points', {})
+ .values()
.flatten()
.map(entry_point => ({
label: entry_point.name,
id: entry_point.name,
subLabel: entry_point.doc
}))
- .uniqBy('label')
.value()
}
const getVersionOptions = selectedFunctions => {
- const versionOptions = unionBy(
- selectedFunctions.map(func => {
+ const versionOptions = chain(selectedFunctions)
+ .map(func => {
+ if (!func.metadata.tag) return null
+
return {
label: func.metadata.tag || 'N/A',
id: func.metadata.tag || TAG_NA
}
- }),
- 'id'
- )
+ })
+ .compact()
+ .unionBy('id')
+ .value()
return versionOptions.length ? versionOptions : [{ label: 'N/A', id: TAG_NA }]
}
-const getDefaultHandler = (handlerOptions, selectedFunctions) => {
+const getDefaultHandler = (handlerOptions, selectedFunctions, selectedVersion) => {
let handler = ''
-
- const latestFunction = selectedFunctions.find(item => item.metadata.tag === TAG_LATEST)
+ const selectedFunction = selectedFunctions.find(item => item.metadata.tag === selectedVersion)
if (handlerOptions.length) {
handler = handlerOptions[0]?.id
- } else if (latestFunction) {
- handler = latestFunction.spec.default_handler || FUNCTION_DEFAULT_HANDLER
} else {
- handler = selectedFunctions[0]?.spec.default_handler || FUNCTION_DEFAULT_HANDLER
+ handler = selectedFunction?.spec?.default_handler || FUNCTION_DEFAULT_HANDLER
}
return handler
}
-const getDefaultHandlerAndVersion = (versionOptions, handlerOptions, selectedFunctions) => {
- const defaultVersion =
- versionOptions.find(version => version.id === TAG_LATEST)?.id || versionOptions[0].id || ''
-
- const defaultHandler = getDefaultHandler(handlerOptions, selectedFunctions)
+const getDefaultVersion = versionOptions => {
+ return versionOptions.find(version => version.id === TAG_LATEST)?.id || versionOptions[0].id || ''
+}
- return {
- defaultVersion,
- defaultHandler
- }
+const getSelectedFunction = (functions, selectedVersion) => {
+ return functions.find(func => func.metadata.tag === selectedVersion) ?? functions[0]
}
export const getFunctionParameters = (selectedFunction, handler) => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.entry_points ? func.spec.entry_points[handler]?.parameters ?? [] : []
- })
- .flatten()
- .unionBy('name')
- .value()
+ return get(selectedFunction, ['spec', 'entry_points', handler, 'parameters'], [])
}
export const getFunctionDefaultParameters = (selectedFunction, handler) => {
@@ -425,72 +408,27 @@ export const getFunctionDefaultParameters = (selectedFunction, handler) => {
}
const getFunctionPriorityClass = selectedFunction => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.priority_class_name
- })
- .flatten()
- .unionBy('name')
- .value()
+ return get(selectedFunction, 'spec.priority_class_name', '')
}
const getLimits = selectedFunction => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.resources?.limits ? func.spec.resources?.limits : {}
- })
- .filter(limits => !isEveryObjectValueEmpty(limits))
- .flatten()
- .unionBy('name')
- .value()
+ return get(selectedFunction, 'spec.resources.limits', {})
}
const getRequests = selectedFunction => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.resources?.requests ? func.spec.resources.requests : {}
- })
- .filter(request => !isEveryObjectValueEmpty(request))
- .flatten()
- .unionBy('name')
- .value()
+ return get(selectedFunction, 'spec.resources.requests', {})
}
const getEnvironmentVariables = selectedFunction => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.env ?? []
- })
- .flatten()
- .unionBy('name')
- .value()
+ return get(selectedFunction, 'spec.env', [])
}
const getFunctionPreemptionMode = selectedFunction => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.preemption_mode ?? ''
- })
- .flatten()
- .unionBy('key')
- .value()
+ return get(selectedFunction, 'spec.preemption_mode', '')
}
const getNodeSelectors = selectedFunction => {
- return chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => {
- return func.spec.node_selector ?? {}
- })
- .map(parseObjectToKeyValue)
- .flatten()
- .unionBy('data.key')
- .value()
+ return parseObjectToKeyValue(get(selectedFunction, 'spec.node_selector', {}))
}
const getVolumeType = volume => {
@@ -505,20 +443,9 @@ const getVolumeType = volume => {
}
}
-const getVolumesData = selectedFunction => {
- const volumes = chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => func.spec.volumes ?? [])
- .flatten()
- .unionBy('name')
- .value()
-
- const volumeMounts = chain(selectedFunction)
- .orderBy('metadata.updated', 'desc')
- .map(func => func.spec.volume_mounts ?? [])
- .flatten()
- .unionBy('name')
- .value()
+const getVolumes = selectedFunction => {
+ const volumes = get(selectedFunction, 'spec.volumes', [])
+ const volumeMounts = get(selectedFunction, 'spec.volume_mounts', [])
return parseVolumes(volumes, volumeMounts)
}
@@ -922,13 +849,19 @@ const generateParameters = parametersTableData => {
parametersTableData?.predefined
?.filter(parameter => !parameter.data.isHyper && parameter.data.isChecked)
.forEach(parameter => {
- parameters[parameter.data.name] = convertParameterValue(parameter.data.value, parameter.data.type)
+ parameters[parameter.data.name] = convertParameterValue(
+ parameter.data.value,
+ parameter.data.type
+ )
})
parametersTableData?.custom
?.filter(parameter => !parameter.data.isHyper && parameter.data.isChecked)
.forEach(parameter => {
- parameters[parameter.data.name] = convertParameterValue(parameter.data.value, parameter.data.type)
+ parameters[parameter.data.name] = convertParameterValue(
+ parameter.data.value,
+ parameter.data.type
+ )
})
return parameters
@@ -1061,10 +994,10 @@ export const generateJobRequestData = (
mode,
isSchedule
) => {
- let selectedFunction = selectedFunctionData?.functions?.find(
- func => func.metadata.tag === formData[RUN_DETAILS_STEP].version
+ const selectedFunction = getSelectedFunction(
+ selectedFunctionData?.functions,
+ formData[RUN_DETAILS_STEP].version
)
- selectedFunction ??= selectedFunctionData?.functions?.[0]
const [volume_mounts, volumes] = generateVolumes(formData[RESOURCES_STEP].volumesTable)
const postData = {
diff --git a/src/components/JobWizard/JobWizardSteps/JobWizardRunDetails/JobWizardRunDetails.js b/src/components/JobWizard/JobWizardSteps/JobWizardRunDetails/JobWizardRunDetails.js
index 0713a81f9..fba489bff 100644
--- a/src/components/JobWizard/JobWizardSteps/JobWizardRunDetails/JobWizardRunDetails.js
+++ b/src/components/JobWizard/JobWizardSteps/JobWizardRunDetails/JobWizardRunDetails.js
@@ -19,7 +19,7 @@ such restriction.
*/
import React, { useMemo, useState } from 'react'
import PropTypes from 'prop-types'
-import { get, isEmpty, set } from 'lodash'
+import { get, isEmpty, omit, set } from 'lodash'
import { OnChange } from 'react-final-form-listeners'
import { FieldArray } from 'react-final-form-arrays'
@@ -47,6 +47,7 @@ import { getChipOptions } from '../../../../utils/getChipOptions'
import { getValidationRules } from 'igz-controls/utils/validation.util'
import { openPopUp } from 'igz-controls/utils/common.util'
import {
+ generateJobWizardData,
getFunctionParameters,
getHandlerData,
parseDataInputs,
@@ -57,17 +58,22 @@ import './jobWizardRunDetails.scss'
const JobWizardRunDetails = ({
formState,
+ frontendSpec,
isBatchInference,
isEditMode,
jobAdditionalData,
+ params,
prePopulatedData,
selectedFunctionData,
+ setJobData,
stepIsActive
}) => {
const handlerPath = `${RUN_DETAILS_STEP}.handler`
+ const versionPath = `${RUN_DETAILS_STEP}.version`
const imageSourcePath = `${RUN_DETAILS_STEP}.image.imageSource`
const outputsPath = `${RUN_DETAILS_STEP}.handlerData.outputs`
- const [spyOnHandlerChange, setspyOnHandlerChange] = useState(true)
+ const [spyOnHandlerChange, setSpyOnHandlerChange] = useState(true)
+ const [spyOnVersionChange, setSpyOnVersionChange] = useState(true)
const commonImageWarningMsg =
'The image must include all the software packages that are required to run the function. ' +
'For example, for an XGBoost model, ensure that the image includes the correct XGboost package and version'
@@ -80,13 +86,60 @@ const JobWizardRunDetails = ({
[formState.values, imageSourcePath]
)
+ const handleVersionChange = version => {
+ const [jobFormData, jobAdditionalData] = generateJobWizardData(
+ frontendSpec,
+ selectedFunctionData,
+ null,
+ params.projectName,
+ isEditMode,
+ null,
+ version
+ )
+ setJobData(formState, jobFormData, jobAdditionalData)
+
+ setSpyOnVersionChange(true)
+ }
+
+ const onVersionChange = (currentVersion, prevVersion) => {
+ setSpyOnVersionChange(false)
+ const values = omit(formState.values, [versionPath])
+ const initialValues = omit(formState.initialValues, [versionPath])
+
+ if (areFormValuesChanged(values, initialValues)) {
+ openPopUp(ConfirmDialog, {
+ cancelButton: {
+ label: 'Cancel',
+ variant: TERTIARY_BUTTON,
+ handler: () => {
+ formState.form.change(versionPath, prevVersion)
+ setSpyOnVersionChange(true)
+ }
+ },
+ confirmButton: {
+ label: 'OK',
+ variant: SECONDARY_BUTTON,
+ handler: () => {
+ handleVersionChange(currentVersion)
+ }
+ },
+ header: 'Are you sure?',
+ message: 'You have unsaved changes. All changes will be lost'
+ })
+ } else {
+ handleVersionChange(currentVersion)
+ }
+ }
+
const handleHandlerChange = handler => {
- setspyOnHandlerChange(true)
+ const selectedVersionedFunction = selectedFunctionData.functions?.find(
+ func => func.metadata.tag === formState.values[RUN_DETAILS_STEP].version
+ )
- const functionParameters = getFunctionParameters(selectedFunctionData.functions, handler)
+ const functionParameters = getFunctionParameters(selectedVersionedFunction, handler)
const dataInputs = parseDataInputs(functionParameters, prePopulatedData?.dataInputs)
const predefinedParameters = parsePredefinedParameters(functionParameters)
- const handlerData = getHandlerData(selectedFunctionData, handler)
+ const handlerData = getHandlerData(selectedVersionedFunction, handler)
set(formState.initialValues, `${DATA_INPUTS_STEP}.dataInputsTable`, dataInputs)
set(
@@ -102,10 +155,12 @@ const JobWizardRunDetails = ({
`${PARAMETERS_STEP}.parametersTable.custom`,
get(formState.initialValues, `${PARAMETERS_STEP}.parametersTable.custom`, [])
)
+
+ setSpyOnHandlerChange(true)
}
- const onHandlerChange = (value, prevValue) => {
- setspyOnHandlerChange(false)
+ const onHandlerChange = (currentHandler, prevHandler) => {
+ setSpyOnHandlerChange(false)
const dataInputsAreChanged = areFormValuesChanged(
formState.initialValues[DATA_INPUTS_STEP].dataInputsTable,
@@ -126,22 +181,22 @@ const JobWizardRunDetails = ({
label: 'Cancel',
variant: TERTIARY_BUTTON,
handler: () => {
- formState.form.change(handlerPath, prevValue)
- setspyOnHandlerChange(true)
+ formState.form.change(handlerPath, prevHandler)
+ setSpyOnHandlerChange(true)
}
},
confirmButton: {
label: 'OK',
variant: SECONDARY_BUTTON,
handler: () => {
- handleHandlerChange(value)
+ handleHandlerChange(currentHandler)
}
},
header: 'Are you sure?',
message: 'Changes made to the Data Inputs and Parameters sections will be lost'
})
} else {
- handleHandlerChange(value)
+ handleHandlerChange(currentHandler)
}
}
@@ -153,10 +208,7 @@ const JobWizardRunDetails = ({
{!isBatchInference && (
-
+
)}
@@ -172,7 +224,7 @@ const JobWizardRunDetails = ({
{jobAdditionalData.versionOptions?.length !== 0 && (
) : (
-
+
)
) : null}
@@ -320,6 +368,9 @@ const JobWizardRunDetails = ({
{stepIsActive && spyOnHandlerChange && (
{onHandlerChange}
)}
+ {stepIsActive && spyOnVersionChange && (
+ {onVersionChange}
+ )}
)
)
@@ -327,11 +378,14 @@ const JobWizardRunDetails = ({
JobWizardRunDetails.propTypes = {
formState: PropTypes.shape({}).isRequired,
+ frontendSpec: PropTypes.shape({}).isRequired,
isBatchInference: PropTypes.bool.isRequired,
isEditMode: PropTypes.bool.isRequired,
jobAdditionalData: PropTypes.shape({}).isRequired,
+ params: PropTypes.shape({}).isRequired,
prePopulatedData: PropTypes.shape({}).isRequired,
- selectedFunctionData: PropTypes.shape({}).isRequired
+ selectedFunctionData: PropTypes.shape({}).isRequired,
+ setJobData: PropTypes.func.isRequired
}
export default JobWizardRunDetails