-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Use undefined fields when adding new app args * remove undefined fields * Job Type * Consistent headings for job steps * Validate execution options with batch type jobs * Stringify non string values in description lists, allow useDetails for systems to specify request parameters * Refactor DescriptionListValue * Remove console statement * Refactor JSONDisplay * Refactor tabs, system detail tabs with json * Handle object keys that are Javascript Sets * Added permission checking to file toolbar * fix unit test * linting * task/TUI-311 - rework of wizard form (#312) * WIP * Fix next step * Thunks for validation function * Manual validation check * Skip to end button * Refactor steps to JobStep array structure * Job submission step * linting * Remove unused component * Refactor generateJobDefaults * restore reinitialize formik * Manually trigger handleSubmit * Shorten default job name, limit to 64 characters (#313) * task/TUI-314 -- filter exec systems when selecting batch jobType (#315) * Filter selectable systems if jobType is batch * Change effect chain * queue validation * Fix default logical queue bug * Display computed defaults in dropdowns * Enforce selection of execution system * No system select text * Remove option text for undefined values * linting * task/TUI-314 -- allow undefined values for app/system defaults (#316) * Patch setFieldValue from Formik * exec system utils * Use default system and queue computation * prettier * undefined option values * Allow jobType to be undefined and find defaults * unit tests * reduce memos * Prettier * Refactor exec system queue validation logic * Fix exec system queue default bug * Handle default system is invalid for batch jobs
- Loading branch information
1 parent
8060c24
commit b4f132b
Showing
29 changed files
with
1,347 additions
and
838 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { Apps, Jobs, Systems } from '@tapis/tapis-typescript'; | ||
import { generateRequiredFileInputsFromApp } from 'tapis-api/utils/jobFileInputs'; | ||
import { generateRequiredFileInputArraysFromApp } from 'tapis-api/utils/jobFileInputArrays'; | ||
import { generateJobArgsFromSpec } from 'tapis-api/utils/jobArgs'; | ||
|
||
const generateJobDefaults = ({ | ||
app, | ||
systems, | ||
}: { | ||
app?: Apps.TapisApp; | ||
systems: Array<Systems.TapisSystem>; | ||
}): Partial<Jobs.ReqSubmitJob> => { | ||
if (!app) { | ||
return {}; | ||
} | ||
|
||
const defaultValues: Partial<Jobs.ReqSubmitJob> = { | ||
name: `${app.id}-${app.version}`, | ||
description: app.description, | ||
appId: app.id, | ||
appVersion: app.version, | ||
archiveOnAppError: app.jobAttributes?.archiveOnAppError ?? true, | ||
archiveSystemId: app.jobAttributes?.archiveSystemId, | ||
archiveSystemDir: app.jobAttributes?.archiveSystemDir, | ||
nodeCount: app.jobAttributes?.nodeCount, | ||
coresPerNode: app.jobAttributes?.coresPerNode, | ||
jobType: app.jobType, | ||
memoryMB: app.jobAttributes?.memoryMB, | ||
maxMinutes: app.jobAttributes?.maxMinutes, | ||
isMpi: app.jobAttributes?.isMpi, | ||
mpiCmd: app.jobAttributes?.mpiCmd, | ||
cmdPrefix: app.jobAttributes?.cmdPrefix, | ||
fileInputs: generateRequiredFileInputsFromApp(app), | ||
fileInputArrays: generateRequiredFileInputArraysFromApp(app), | ||
parameterSet: { | ||
appArgs: generateJobArgsFromSpec( | ||
app.jobAttributes?.parameterSet?.appArgs ?? [] | ||
), | ||
containerArgs: generateJobArgsFromSpec( | ||
app.jobAttributes?.parameterSet?.containerArgs ?? [] | ||
), | ||
schedulerOptions: generateJobArgsFromSpec( | ||
app.jobAttributes?.parameterSet?.schedulerOptions ?? [] | ||
), | ||
archiveFilter: app.jobAttributes?.parameterSet?.archiveFilter, | ||
envVariables: app.jobAttributes?.parameterSet?.envVariables, | ||
}, | ||
}; | ||
return defaultValues; | ||
}; | ||
|
||
export default generateJobDefaults; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
import { Apps, Systems } from '@tapis/tapis-typescript'; | ||
import '@testing-library/jest-dom/extend-expect'; | ||
import { tapisApp } from 'fixtures/apps.fixtures'; | ||
import { tapisSystem } from 'fixtures/systems.fixtures'; | ||
import { | ||
computeDefaultQueue, | ||
computeDefaultSystem, | ||
validateExecSystem, | ||
ValidateExecSystemResult, | ||
} from './jobExecSystem'; | ||
|
||
describe('Job Exec System utils', () => { | ||
it('determines default system', () => { | ||
expect(computeDefaultSystem(tapisApp)).toEqual({ | ||
source: 'app', | ||
systemId: 'testuser2.execution', | ||
}); | ||
}); | ||
it('determines the default logical queue from an app', () => { | ||
expect(computeDefaultQueue({}, tapisApp, [tapisSystem])).toEqual({ | ||
source: 'app', | ||
queue: 'tapisNormal', | ||
}); | ||
}); | ||
it('determines the default logical queue from the default system specified by the app', () => { | ||
const tapisAppNoQueue = JSON.parse( | ||
JSON.stringify(tapisApp) | ||
) as Apps.TapisApp; | ||
tapisAppNoQueue.jobAttributes!.execSystemLogicalQueue = undefined; | ||
expect(computeDefaultQueue({}, tapisAppNoQueue, [tapisSystem])).toEqual({ | ||
source: 'app system', | ||
queue: 'tapisNormal', | ||
}); | ||
}); | ||
it('determines the default logical queue from a system default, where the system is selected by the job', () => { | ||
const tapisAppNoQueue = JSON.parse( | ||
JSON.stringify(tapisApp) | ||
) as Apps.TapisApp; | ||
tapisAppNoQueue.jobAttributes!.execSystemLogicalQueue = undefined; | ||
expect( | ||
computeDefaultQueue( | ||
{ execSystemId: 'testuser2.execution' }, | ||
tapisAppNoQueue, | ||
[tapisSystem] | ||
) | ||
).toEqual({ | ||
source: 'system', | ||
queue: 'tapisNormal', | ||
}); | ||
}); | ||
it('determines that there is no computed queue if a system does not exist', () => { | ||
const tapisAppNoQueue = JSON.parse( | ||
JSON.stringify(tapisApp) | ||
) as Apps.TapisApp; | ||
tapisAppNoQueue.jobAttributes!.execSystemLogicalQueue = undefined; | ||
expect(computeDefaultQueue({}, tapisAppNoQueue, [])).toEqual({ | ||
source: undefined, | ||
queue: undefined, | ||
}); | ||
}); | ||
it('detects a valid job request that satisfies exec system options', () => { | ||
expect(validateExecSystem({}, tapisApp, [tapisSystem])).toBe( | ||
ValidateExecSystemResult.Complete | ||
); | ||
}); | ||
it('skips queue validation if a complete job request will be a FORK job', () => { | ||
expect( | ||
validateExecSystem({ jobType: Apps.JobTypeEnum.Fork }, tapisApp, [ | ||
tapisSystem, | ||
]) | ||
).toBe(ValidateExecSystemResult.Complete); | ||
}); | ||
it('detects an invalid job request if the specified exec system is missing', () => { | ||
expect(validateExecSystem({}, tapisApp, [])).toBe( | ||
ValidateExecSystemResult.ErrorExecSystemNotFound | ||
); | ||
}); | ||
it('detects an invalid job request if an exec system is not specified', () => { | ||
const tapisAppNoSystem = JSON.parse( | ||
JSON.stringify(tapisApp) | ||
) as Apps.TapisApp; | ||
tapisAppNoSystem.jobAttributes!.execSystemId = undefined; | ||
expect(validateExecSystem({}, tapisAppNoSystem, [])).toBe( | ||
ValidateExecSystemResult.ErrorNoExecSystem | ||
); | ||
}); | ||
it('detects an invalid job request if a queue cannot be found', () => { | ||
const tapisAppNoQueue = JSON.parse( | ||
JSON.stringify(tapisApp) | ||
) as Apps.TapisApp; | ||
tapisAppNoQueue.jobAttributes!.execSystemLogicalQueue = undefined; | ||
const tapisSystemNoDefaultQueue = JSON.parse( | ||
JSON.stringify(tapisSystem) | ||
) as Systems.TapisSystem; | ||
tapisSystemNoDefaultQueue.batchDefaultLogicalQueue = undefined; | ||
expect( | ||
validateExecSystem({}, tapisAppNoQueue, [tapisSystemNoDefaultQueue]) | ||
).toBe(ValidateExecSystemResult.ErrorNoQueue); | ||
}); | ||
it('detects an invalid job request if the job is a batch job but the selected system has no queues', () => { | ||
const tapisSystemNoQueue = JSON.parse( | ||
JSON.stringify(tapisSystem) | ||
) as Systems.TapisSystem; | ||
tapisSystemNoQueue.batchLogicalQueues = []; | ||
expect( | ||
validateExecSystem({ jobType: Apps.JobTypeEnum.Batch }, tapisApp, [ | ||
tapisSystemNoQueue, | ||
]) | ||
).toBe(ValidateExecSystemResult.ErrorExecSystemNoQueues); | ||
}); | ||
it('detects an invalid job request if the job specifies a queue that the exec system does not have', () => { | ||
expect( | ||
validateExecSystem({ execSystemLogicalQueue: 'badQueue' }, tapisApp, [ | ||
tapisSystem, | ||
]) | ||
).toBe(ValidateExecSystemResult.ErrorQueueNotFound); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
import { Apps, Jobs, Systems } from '@tapis/tapis-typescript'; | ||
|
||
type DefaultSystem = { | ||
source?: 'app'; | ||
systemId?: string; | ||
}; | ||
|
||
/** | ||
* Computes the default execution system ID that will be used | ||
* | ||
* @param app | ||
* @returns | ||
*/ | ||
export const computeDefaultSystem = (app: Apps.TapisApp): DefaultSystem => { | ||
if (app.jobAttributes?.execSystemId) { | ||
return { | ||
source: 'app', | ||
systemId: app.jobAttributes?.execSystemId, | ||
}; | ||
} | ||
return { | ||
source: undefined, | ||
systemId: undefined, | ||
}; | ||
}; | ||
|
||
type DefaultQueue = { | ||
source?: 'app' | 'system' | 'app system'; | ||
queue?: string; | ||
}; | ||
|
||
/** | ||
* Computes the logical queue that will be used, if the job does not | ||
* specify one | ||
* | ||
* @param job | ||
* @param app | ||
* @param systems | ||
* @returns | ||
*/ | ||
export const computeDefaultQueue = ( | ||
job: Partial<Jobs.ReqSubmitJob>, | ||
app: Apps.TapisApp, | ||
systems: Array<Systems.TapisSystem> | ||
): DefaultQueue => { | ||
// If the app specifies the logical queue, use that | ||
if (app.jobAttributes?.execSystemLogicalQueue) { | ||
return { | ||
source: 'app', | ||
queue: app.jobAttributes?.execSystemLogicalQueue, | ||
}; | ||
} | ||
|
||
// If the job specifies a system, look for its default logical queue | ||
if (job.execSystemId) { | ||
const selectedSystem = systems.find( | ||
(system) => system.id === job.execSystemId | ||
); | ||
if (selectedSystem?.batchDefaultLogicalQueue) { | ||
return { | ||
source: 'system', | ||
queue: selectedSystem.batchDefaultLogicalQueue, | ||
}; | ||
} | ||
} | ||
|
||
// If the app specifies a system, look for its default logical queue | ||
if (app.jobAttributes?.execSystemId) { | ||
const appSystem = systems.find( | ||
(system) => system.id === app.jobAttributes?.execSystemId | ||
); | ||
if (appSystem?.batchDefaultLogicalQueue) { | ||
return { | ||
source: 'app system', | ||
queue: appSystem.batchDefaultLogicalQueue, | ||
}; | ||
} | ||
} | ||
|
||
// Return a result that has no computed default logical queue | ||
return { | ||
source: undefined, | ||
queue: undefined, | ||
}; | ||
}; | ||
|
||
type DefaultJobType = { | ||
source: 'app' | 'app system' | 'system' | 'tapis'; | ||
jobType: Apps.JobTypeEnum; | ||
}; | ||
|
||
/** | ||
* Determines the default jobType if one is not specified in the jobType field in a job | ||
* using the algorithm specified at: | ||
* | ||
* https://tapis.readthedocs.io/en/latest/technical/jobs.html#job-type | ||
* | ||
* @param job | ||
* @param app | ||
* @param systems | ||
* @returns | ||
*/ | ||
export const computeDefaultJobType = ( | ||
job: Partial<Jobs.ReqSubmitJob>, | ||
app: Apps.TapisApp, | ||
systems: Array<Systems.TapisSystem> | ||
): DefaultJobType => { | ||
if (app.jobType) { | ||
return { | ||
source: 'app', | ||
jobType: app.jobType!, | ||
}; | ||
} | ||
if (job?.execSystemId) { | ||
const selectedSystem = systems.find( | ||
(system) => system.id === job.execSystemId | ||
); | ||
if (selectedSystem?.canRunBatch) { | ||
return { | ||
source: 'system', | ||
jobType: Apps.JobTypeEnum.Batch, | ||
}; | ||
} | ||
} | ||
if (app.jobAttributes?.execSystemId) { | ||
const appSystem = systems.find( | ||
(system) => system.id === app.jobAttributes?.execSystemId | ||
); | ||
if (appSystem?.canRunBatch) { | ||
return { | ||
source: 'app system', | ||
jobType: Apps.JobTypeEnum.Batch, | ||
}; | ||
} | ||
} | ||
return { | ||
source: 'tapis', | ||
jobType: Apps.JobTypeEnum.Fork, | ||
}; | ||
}; | ||
|
||
export enum ValidateExecSystemResult { | ||
Complete = 'COMPLETE', | ||
ErrorNoExecSystem = 'ERROR_NO_EXEC_SYSTEM', | ||
ErrorExecSystemNotFound = 'ERROR_EXEC_SYSTEM_NOT_FOUND', | ||
ErrorExecSystemNoQueues = 'ERROR_EXEC_SYSTEM_NO_QUEUES', | ||
ErrorNoQueue = 'ERROR_NO_QUEUE', | ||
ErrorQueueNotFound = 'ERROR_QUEUE_NOT_FOUND', | ||
} | ||
|
||
export const validateExecSystem = ( | ||
job: Partial<Jobs.ReqSubmitJob>, | ||
app: Apps.TapisApp, | ||
systems: Array<Systems.TapisSystem> | ||
): ValidateExecSystemResult => { | ||
const defaultSystem = computeDefaultSystem(app); | ||
|
||
// Check that an exec system can be computed | ||
if (!job.execSystemId && !defaultSystem?.systemId) { | ||
return ValidateExecSystemResult.ErrorNoExecSystem; | ||
} | ||
|
||
const computedSystem = systems.find( | ||
(system) => system.id === (job.execSystemId ?? defaultSystem?.systemId) | ||
); | ||
if (!computedSystem) { | ||
return ValidateExecSystemResult.ErrorExecSystemNotFound; | ||
} | ||
|
||
// If the job will be a FORK job, skip queue validation | ||
const computedJobType = computeDefaultJobType(job, app, systems); | ||
if ( | ||
job.jobType !== Apps.JobTypeEnum.Batch && | ||
computedJobType.jobType === Apps.JobTypeEnum.Fork | ||
) { | ||
return ValidateExecSystemResult.Complete; | ||
} | ||
|
||
// If the job will be a BATCH job, make sure that the selected execution system | ||
// has queues | ||
if (!computedSystem.batchLogicalQueues?.length) { | ||
return ValidateExecSystemResult.ErrorExecSystemNoQueues; | ||
} | ||
|
||
const defaultQueue = computeDefaultQueue(job, app, systems); | ||
|
||
// If the job type will be a BATCH job, ensure that a queue is specified | ||
// If no queue exists, there must be a fallback to the app or system default | ||
if (!job.execSystemLogicalQueue && !defaultQueue.queue) { | ||
return ValidateExecSystemResult.ErrorNoQueue; | ||
} | ||
|
||
// Check to see that the logical queue selected exists on the selected system | ||
const selectedSystem = systems.find( | ||
(system) => system.id === (job.execSystemId ?? defaultSystem?.systemId) | ||
); | ||
if ( | ||
!selectedSystem?.batchLogicalQueues?.some( | ||
(queue) => | ||
queue.name === (job.execSystemLogicalQueue ?? defaultQueue.queue) | ||
) | ||
) { | ||
return ValidateExecSystemResult.ErrorQueueNotFound; | ||
} | ||
|
||
return ValidateExecSystemResult.Complete; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
import { Jobs } from '@tapis/tapis-typescript'; | ||
|
||
export const jobRequiredFieldsComplete = (job: Partial<Jobs.ReqSubmitJob>) => { | ||
return !!job.name && !!job.appId && !!job.appVersion && !!job.execSystemId; | ||
return !!job.name && !!job.appId && !!job.appVersion; | ||
}; |
Oops, something went wrong.