Refactors EE Lookup to support prompting. Adds prompting for EE to JT form

Adds prompt on launch buttons to labels, forks, job slicing, timeout, and instance groups

Adds prompting for labels on workflow job template

Updates flags that denote when prompting is necessary in various places

Adds prompting support for timeout, job slicing, forks, labels, instance groups and execution environments to the prompt details

Show prompted ee, forks, job slice and labels on schedule details

Adds support for ee, labels, forks, job slicing and timeout prompting to the node view modal

Add default values when prompting for ee's, forks, job slicing and timeout

Adds launch prompt step for execution environments

Adds fields for timeout, job slicing and forks to other prompts step of launch
This commit is contained in:
mabashian 2022-08-04 11:32:17 -04:00 committed by Alan Rominger
parent d67aef9d8e
commit 04d0e3915c
No known key found for this signature in database
GPG Key ID: C2D7EAAA12B63559
16 changed files with 604 additions and 97 deletions

View File

@ -24,6 +24,12 @@ function canLaunchWithoutPrompt(launchData) {
!launchData.ask_variables_on_launch &&
!launchData.ask_limit_on_launch &&
!launchData.ask_scm_branch_on_launch &&
!launchData.ask_execution_environment_on_launch &&
!launchData.ask_labels_on_launch &&
!launchData.ask_forks_on_launch &&
!launchData.ask_job_slicing_on_launch &&
!launchData.ask_timeout_on_launch &&
!launchData.ask_instance_groups_on_launch &&
!launchData.survey_enabled &&
(!launchData.passwords_needed_to_start ||
launchData.passwords_needed_to_start.length === 0) &&

View File

@ -0,0 +1,116 @@
import React, { useCallback, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { t } from '@lingui/macro';
import { useField } from 'formik';
import { ExecutionEnvironmentsAPI } from 'api';
import { getSearchableKeys } from 'components/PaginatedTable';
import { getQSConfig, parseQueryString } from 'util/qs';
import useRequest from 'hooks/useRequest';
import OptionsList from '../../OptionsList';
import ContentLoading from '../../ContentLoading';
import ContentError from '../../ContentError';
const QS_CONFIG = getQSConfig('execution_environment', {
page: 1,
page_size: 5,
});
function ExecutionEnvironmentStep() {
const [field, , helpers] = useField('execution_environment');
const history = useHistory();
const {
isLoading,
error,
result: {
execution_environments,
count,
relatedSearchableKeys,
searchableKeys,
},
request: fetchExecutionEnvironments,
} = useRequest(
useCallback(async () => {
const params = parseQueryString(QS_CONFIG, history.location.search);
const [{ data }, actionsResponse] = await Promise.all([
ExecutionEnvironmentsAPI.read(params),
ExecutionEnvironmentsAPI.readOptions(),
]);
return {
execution_environments: data.results,
count: data.count,
relatedSearchableKeys: (
actionsResponse?.data?.related_search_fields || []
).map((val) => val.slice(0, -8)),
searchableKeys: getSearchableKeys(actionsResponse.data.actions?.GET),
};
}, [history.location]),
{
count: 0,
execution_environments: [],
relatedSearchableKeys: [],
searchableKeys: [],
}
);
useEffect(() => {
fetchExecutionEnvironments();
}, [fetchExecutionEnvironments]);
if (isLoading) {
return <ContentLoading />;
}
if (error) {
return <ContentError error={error} />;
}
return (
<OptionsList
value={field.value ? [field.value] : []}
options={execution_environments}
optionCount={count}
columns={[
{
name: t`Name`,
key: 'name',
},
{
name: t`Image`,
key: 'image',
},
]}
searchColumns={[
{
name: t`Name`,
key: 'name__icontains',
isDefault: true,
},
{
name: t`Image`,
key: 'image__icontains',
},
]}
sortColumns={[
{
name: t`Name`,
key: 'name',
},
{
name: t`Image`,
key: 'image',
},
]}
searchableKeys={searchableKeys}
relatedSearchableKeys={relatedSearchableKeys}
header={t`Execution Environments`}
name="execution_environment"
qsConfig={QS_CONFIG}
readOnly
selectItem={helpers.setValue}
deselectItem={() => field.onChange(null)}
/>
);
}
export default ExecutionEnvironmentStep;

View File

@ -29,6 +29,23 @@ function OtherPromptsStep({ launchConfig, variablesMode, onVarModeChange }) {
}}
>
{launchConfig.ask_job_type_on_launch && <JobTypeField />}
{launchConfig.ask_scm_branch_on_launch && (
<FormField
id="prompt-scm-branch"
name="scm_branch"
label={t`Source Control Branch`}
tooltip={t`Select a branch for the workflow. This branch is applied to all job template nodes that prompt for a branch`}
/>
)}
{launchConfig.ask_forks_on_launch && (
<FormField
id="prompt-forks"
name="forks"
label={t`Forks`}
type="number"
min="0"
/>
)}
{launchConfig.ask_limit_on_launch && (
<FormField
id="prompt-limit"
@ -40,15 +57,25 @@ function OtherPromptsStep({ launchConfig, variablesMode, onVarModeChange }) {
information and examples on patterns.`}
/>
)}
{launchConfig.ask_scm_branch_on_launch && (
{launchConfig.ask_verbosity_on_launch && <VerbosityField />}
{launchConfig.ask_job_slicing_on_launch && (
<FormField
id="prompt-scm-branch"
name="scm_branch"
label={t`Source Control Branch`}
tooltip={t`Select a branch for the workflow. This branch is applied to all job template nodes that prompt for a branch`}
id="prompt-job-slicing"
name="job_slice_count"
label={t`Job Slicing`}
type="number"
min="1"
/>
)}
{launchConfig.ask_timeout_on_launch && (
<FormField
id="prompt-timeout"
name="timeout"
label={t`Timeout`}
type="number"
min="0"
/>
)}
{launchConfig.ask_verbosity_on_launch && <VerbosityField />}
{launchConfig.ask_diff_mode_on_launch && <ShowChangesToggle />}
{launchConfig.ask_tags_on_launch && (
<TagField

View File

@ -0,0 +1,45 @@
import React from 'react';
import { t } from '@lingui/macro';
import ExecutionEnvironmentStep from './ExecutionEnvironmentStep';
import StepName from './StepName';
const STEP_ID = 'executionEnvironment';
export default function useExecutionEnvironmentStep(launchConfig, resource) {
return {
step: getStep(launchConfig, resource),
initialValues: getInitialValues(launchConfig, resource),
isReady: true,
contentError: null,
hasError: false,
setTouched: (setFieldTouched) => {
setFieldTouched('execution_environment', true, false);
},
validate: () => {},
};
}
function getStep(launchConfig) {
if (!launchConfig.ask_inventory_on_launch) {
return null;
}
return {
id: STEP_ID,
name: (
<StepName id="execution-environment-step">
{t`Execution Environment`}
</StepName>
),
component: <ExecutionEnvironmentStep />,
enableNext: true,
};
}
function getInitialValues(launchConfig, resource) {
if (!launchConfig.ask_execution_environment_on_launch) {
return {};
}
return {
inventory: resource?.summary_fields?.execution_environment || null,
};
}

View File

@ -27,6 +27,10 @@ const FIELD_NAMES = [
'job_tags',
'skip_tags',
'extra_vars',
'labels',
'timeout',
'job_slice_count',
'forks',
];
export default function useOtherPromptsStep(launchConfig, resource) {
@ -105,7 +109,11 @@ function shouldShowPrompt(launchConfig) {
launchConfig.ask_skip_tags_on_launch ||
launchConfig.ask_variables_on_launch ||
launchConfig.ask_scm_branch_on_launch ||
launchConfig.ask_diff_mode_on_launch
launchConfig.ask_diff_mode_on_launch ||
launchConfig.ask_labels_on_launch ||
launchConfig.ask_forks_on_launch ||
launchConfig.ask_job_slicing_on_launch ||
launchConfig.ask_timeout_on_launch
);
}
@ -140,5 +148,14 @@ function getInitialValues(launchConfig, resource) {
if (launchConfig.ask_diff_mode_on_launch) {
initialValues.diff_mode = resource?.diff_mode || false;
}
if (launchConfig.ask_forks_on_launch) {
initialValues.forks = resource?.forks || 0;
}
if (launchConfig.ask_job_slicing_on_launch) {
initialValues.job_slice_count = resource?.job_slice_count || 1;
}
if (launchConfig.ask_timeout_on_launch) {
initialValues.timeout = resource?.timeout || 0;
}
return initialValues;
}

View File

@ -3,6 +3,7 @@ import { useFormikContext } from 'formik';
import useInventoryStep from './steps/useInventoryStep';
import useCredentialsStep from './steps/useCredentialsStep';
import useCredentialPasswordsStep from './steps/useCredentialPasswordsStep';
import useExecutionEnvironmentStep from './steps/useExecutionEnvironmentStep';
import useOtherPromptsStep from './steps/useOtherPromptsStep';
import useSurveyStep from './steps/useSurveyStep';
import usePreviewStep from './steps/usePreviewStep';
@ -56,6 +57,7 @@ export default function useLaunchSteps(launchConfig, surveyConfig, resource) {
showCredentialPasswordsStep(launchConfig, formikValues.credentials),
visited
),
useExecutionEnvironmentStep(launchConfig, resource),
useOtherPromptsStep(launchConfig, resource),
useSurveyStep(launchConfig, surveyConfig, resource, visited),
];
@ -143,6 +145,7 @@ export default function useLaunchSteps(launchConfig, surveyConfig, resource) {
inventory: true,
credentials: true,
credentialPasswords: true,
executionEnvironment: true,
other: true,
survey: true,
preview: true,

View File

@ -10,9 +10,9 @@ import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
import useRequest from 'hooks/useRequest';
import Popover from '../Popover';
import OptionsList from '../OptionsList';
import Lookup from './Lookup';
import LookupErrorMessage from './shared/LookupErrorMessage';
import FieldWithPrompt from '../FieldWithPrompt';
const QS_CONFIG = getQSConfig('execution_environments', {
page: 1,
@ -36,6 +36,9 @@ function ExecutionEnvironmentLookup({
value,
fieldName,
overrideLabel,
isPromptableField,
promptId,
promptName,
}) {
const location = useLocation();
const {
@ -150,49 +153,52 @@ function ExecutionEnvironmentLookup({
}, [fetchExecutionEnvironments]);
const renderLookup = () => (
<Lookup
id={id}
header={t`Execution Environment`}
value={value}
onBlur={onBlur}
onChange={onChange}
onUpdate={fetchExecutionEnvironments}
onDebounce={checkExecutionEnvironmentName}
fieldName={fieldName}
validate={validate}
qsConfig={QS_CONFIG}
isLoading={isLoading || isProjectLoading}
isDisabled={isDisabled}
renderOptionsList={({ state, dispatch, canDelete }) => (
<OptionsList
value={state.selectedItems}
options={executionEnvironments}
optionCount={count}
searchColumns={[
{
name: t`Name`,
key: 'name__icontains',
isDefault: true,
},
]}
sortColumns={[
{
name: t`Name`,
key: 'name',
},
]}
searchableKeys={searchableKeys}
relatedSearchableKeys={relatedSearchableKeys}
multiple={state.multiple}
header={t`Execution Environment`}
name="executionEnvironments"
qsConfig={QS_CONFIG}
readOnly={!canDelete}
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
/>
)}
/>
<>
<Lookup
id={id}
header={t`Execution Environment`}
value={value}
onBlur={onBlur}
onChange={onChange}
onUpdate={fetchExecutionEnvironments}
onDebounce={checkExecutionEnvironmentName}
fieldName={fieldName}
validate={validate}
qsConfig={QS_CONFIG}
isLoading={isLoading || isProjectLoading}
isDisabled={isDisabled}
renderOptionsList={({ state, dispatch, canDelete }) => (
<OptionsList
value={state.selectedItems}
options={executionEnvironments}
optionCount={count}
searchColumns={[
{
name: t`Name`,
key: 'name__icontains',
isDefault: true,
},
]}
sortColumns={[
{
name: t`Name`,
key: 'name',
},
]}
searchableKeys={searchableKeys}
relatedSearchableKeys={relatedSearchableKeys}
multiple={state.multiple}
header={t`Execution Environment`}
name="executionEnvironments"
qsConfig={QS_CONFIG}
readOnly={!canDelete}
selectItem={(item) => dispatch({ type: 'SELECT_ITEM', item })}
deselectItem={(item) => dispatch({ type: 'DESELECT_ITEM', item })}
/>
)}
/>
<LookupErrorMessage error={error || fetchProjectError} />
</>
);
const renderLabel = () => {
@ -202,7 +208,21 @@ function ExecutionEnvironmentLookup({
return t`Execution Environment`;
};
return (
return isPromptableField ? (
<FieldWithPrompt
fieldId={id}
label={renderLabel()}
promptId={promptId}
promptName={promptName}
tooltip={popoverContent}
>
{tooltip && isDisabled ? (
<Tooltip content={tooltip}>{renderLookup()}</Tooltip>
) : (
renderLookup()
)}
</FieldWithPrompt>
) : (
<FormGroup
fieldId={id}
label={renderLabel()}

View File

@ -1,7 +1,6 @@
import React, { useCallback, useEffect } from 'react';
import { arrayOf, string, func, bool } from 'prop-types';
import { withRouter } from 'react-router-dom';
import { t, Trans } from '@lingui/macro';
import { FormGroup } from '@patternfly/react-core';
import { InstanceGroupsAPI } from 'api';
@ -13,6 +12,7 @@ import Popover from '../Popover';
import OptionsList from '../OptionsList';
import Lookup from './Lookup';
import LookupErrorMessage from './shared/LookupErrorMessage';
import FieldWithPrompt from '../FieldWithPrompt';
const QS_CONFIG = getQSConfig('instance-groups', {
page: 1,
@ -21,6 +21,7 @@ const QS_CONFIG = getQSConfig('instance-groups', {
});
function InstanceGroupsLookup({
id,
value,
onChange,
tooltip,
@ -29,6 +30,9 @@ function InstanceGroupsLookup({
history,
fieldName,
validate,
isPromptableField,
promptId,
promptName,
}) {
const {
result: { instanceGroups, count, relatedSearchableKeys, searchableKeys },
@ -63,13 +67,8 @@ function InstanceGroupsLookup({
fetchInstanceGroups();
}, [fetchInstanceGroups]);
return (
<FormGroup
className={className}
label={t`Instance Groups`}
labelIcon={tooltip && <Popover content={tooltip} />}
fieldId="org-instance-groups"
>
const renderLookup = () => (
<>
<Lookup
id="org-instance-groups"
header={t`Instance Groups`}
@ -133,11 +132,33 @@ function InstanceGroupsLookup({
)}
/>
<LookupErrorMessage error={error} />
</>
);
return isPromptableField ? (
<FieldWithPrompt
fieldId={id}
label={t`Instance Groups`}
promptId={promptId}
promptName={promptName}
tooltip={tooltip}
>
{renderLookup()}
</FieldWithPrompt>
) : (
<FormGroup
className={className}
label={t`Instance Groups`}
labelIcon={tooltip && <Popover content={tooltip} />}
fieldId={id}
>
{renderLookup()}
</FormGroup>
);
}
InstanceGroupsLookup.propTypes = {
id: string,
value: arrayOf(InstanceGroup).isRequired,
tooltip: string,
onChange: func.isRequired,
@ -148,6 +169,7 @@ InstanceGroupsLookup.propTypes = {
};
InstanceGroupsLookup.defaultProps = {
id: 'org-instance-groups',
tooltip: '',
className: '',
required: false,

View File

@ -71,7 +71,13 @@ function hasPromptData(launchData) {
launchData.ask_skip_tags_on_launch ||
launchData.ask_tags_on_launch ||
launchData.ask_variables_on_launch ||
launchData.ask_verbosity_on_launch
launchData.ask_verbosity_on_launch ||
launchData.ask_execution_environment_on_launch ||
launchData.ask_labels_on_launch ||
launchData.ask_forks_on_launch ||
launchData.ask_job_slicing_on_launch ||
launchData.ask_timeout_on_launch ||
launchData.ask_instance_groups_on_launch
);
}
@ -206,6 +212,36 @@ function PromptDetail({
value={overrides.inventory?.name}
/>
)}
{launchConfig.ask_execution_environment_on_launch && (
<Detail
label={t`Execution Environment`}
value={overrides.execution_environment?.name}
/>
)}
{launchConfig.ask_instance_groups_on_launch && (
<Detail
fullWidth
label={t`Instance Groups`}
rows={4}
value={
<ChipGroup
numChips={5}
totalChips={overrides.instance_groups.length}
ouiaId="prompt-instance-groups-chips"
>
{overrides.instance_groups.map((instance_group) => (
<Chip
key={instance_group.id}
ouiaId={`instance-group-${instance_group.id}-chip`}
isReadOnly
>
{instance_group.name}
</Chip>
))}
</ChipGroup>
}
/>
)}
{launchConfig.ask_scm_branch_on_launch && (
<Detail
label={t`Source Control Branch`}
@ -278,6 +314,42 @@ function PromptDetail({
}
/>
)}
{launchConfig.ask_labels_on_launch && (
<Detail
fullWidth
label={t`Labels`}
value={
<ChipGroup
numChips={5}
totalChips={overrides.labels.length}
ouiaId="prompt-label-chips"
>
{overrides.labels.map((label) => (
<Chip
key={label.id}
ouiaId={`label-${label.id}-chip`}
isReadOnly
>
{label.name}
</Chip>
))}
</ChipGroup>
}
isEmpty={overrides.labels.length === 0}
/>
)}
{launchConfig.ask_forks_on_launch && (
<Detail label={t`Forks`} value={overrides.forks} />
)}
{launchConfig.ask_job_slicing_on_launch && (
<Detail
label={t`Job Slicing`}
value={overrides.job_slice_count}
/>
)}
{launchConfig.ask_timeout_on_launch && (
<Detail label={t`Timeout`} value={overrides.timeout} />
)}
{launchConfig.ask_diff_mode_on_launch && (
<Detail
label={t`Show Changes`}

View File

@ -73,10 +73,14 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
diff_mode,
dtend,
dtstart,
execution_environment,
extra_data,
forks,
inventory,
job_slice_count,
job_tags,
job_type,
labels,
limit,
modified,
name,
@ -85,6 +89,7 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
scm_branch,
skip_tags,
summary_fields,
timeout,
timezone,
verbosity,
} = schedule;
@ -185,6 +190,11 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
ask_tags_on_launch,
ask_variables_on_launch,
ask_verbosity_on_launch,
ask_execution_environment_on_launch,
ask_labels_on_launch,
ask_forks_on_launch,
ask_job_slicing_on_launch,
ask_timeout_on_launch,
survey_enabled,
} = launchData || {};
@ -239,6 +249,12 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
const showJobTypeDetail = ask_job_type_on_launch && job_type;
const showSCMBranchDetail = ask_scm_branch_on_launch && scm_branch;
const showVerbosityDetail = ask_verbosity_on_launch && VERBOSITY()[verbosity];
const showExecutionEnvironmentDetail =
ask_execution_environment_on_launch && execution_environment;
const showLabelsDetail = ask_labels_on_launch && labels && labels.length > 0;
const showForksDetail = ask_forks_on_launch;
const showJobSlicingDetail = ask_job_slicing_on_launch;
const showTimeoutDetail = ask_timeout_on_launch;
const showPromptedFields =
showCredentialsDetail ||
@ -250,7 +266,12 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
showSkipTagsDetail ||
showTagsDetail ||
showVerbosityDetail ||
showVariablesDetail;
showVariablesDetail ||
showExecutionEnvironmentDetail ||
showLabelsDetail ||
showForksDetail ||
showJobSlicingDetail ||
showTimeoutDetail;
if (isLoading) {
return <ContentLoading />;
@ -402,11 +423,20 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
dataCy="schedule-inventory"
/>
)}
{ask_verbosity_on_launch && (
{showExecutionEnvironmentDetail && (
<Detail
label={t`Verbosity`}
value={VERBOSITY()[verbosity]}
dataCy="schedule-verbosity"
label={t`Execution Environment`}
value={
summary_fields?.execution_environment ? (
<Link
to={`/execution_environments/${summary_fields?.execution_environment?.id}/details`}
>
{summary_fields?.execution_environment?.name}
</Link>
) : (
' '
)
}
/>
)}
{ask_scm_branch_on_launch && (
@ -419,6 +449,18 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
{ask_limit_on_launch && (
<Detail label={t`Limit`} value={limit} dataCy="schedule-limit" />
)}
{ask_forks_on_launch && <Detail label={t`Forks`} value={forks} />}
{ask_limit_on_launch && <Detail label={t`Limit`} value={limit} />}
{ask_verbosity_on_launch && (
<Detail
label={t`Verbosity`}
value={VERBOSITY()[verbosity]}
dataCy="schedule-verbosity"
/>
)}
{ask_timeout_on_launch && (
<Detail label={t`Timeout`} value={timeout} />
)}
{showDiffModeDetail && (
<Detail
label={t`Show Changes`}
@ -426,6 +468,9 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
dataCy="schedule-show-changes"
/>
)}
{ask_job_slicing_on_launch && (
<Detail label={t`Job Slicing`} value={job_slice_count} />
)}
{showCredentialsDetail && (
<Detail
fullWidth
@ -449,6 +494,26 @@ function ScheduleDetail({ hasDaysToKeepField, schedule, surveyConfig }) {
dataCy="schedule-credentials"
/>
)}
{showLabelsDetail && (
<Detail
fullWidth
label={t`Labels`}
value={
<ChipGroup
numChips={5}
totalChips={summary_fields.labels.results.length}
ouiaId="schedule-label-chips"
>
{summary_fields.labels.results.map((l) => (
<Chip key={l.id} ouiaId={`label-${l.id}-chip`} isReadOnly>
{l.name}
</Chip>
))}
</ChipGroup>
}
isEmpty={summary_fields.labels.results.length === 0}
/>
)}
{showTagsDetail && (
<Detail
fullWidth

View File

@ -225,6 +225,12 @@ function ScheduleForm({
launchConfig.ask_scm_branch_on_launch ||
launchConfig.ask_tags_on_launch ||
launchConfig.ask_skip_tags_on_launch ||
launchConfig.ask_execution_environment_on_launch ||
launchConfig.ask_labels_on_launch ||
launchConfig.ask_forks_on_launch ||
launchConfig.ask_job_slicing_on_launch ||
launchConfig.ask_timeout_on_launch ||
launchConfig.ask_instance_groups_on_launch ||
launchConfig.survey_enabled ||
launchConfig.inventory_needed_to_start ||
launchConfig.variables_needed_to_start?.length > 0)

View File

@ -1,12 +1,10 @@
import React, { useContext, useEffect, useCallback } from 'react';
import { t } from '@lingui/macro';
import { Button, Modal } from '@patternfly/react-core';
import {
WorkflowDispatchContext,
WorkflowStateContext,
} from 'contexts/Workflow';
import ContentError from 'components/ContentError';
import ContentLoading from 'components/ContentLoading';
import PromptDetail from 'components/PromptDetail';
@ -157,6 +155,22 @@ function NodeViewModal({ readOnly }) {
if (launchConfig.ask_inventory_on_launch) {
overrides.inventory = originalNodeObject.summary_fields.inventory;
}
if (launchConfig.ask_execution_environment_on_launch) {
overrides.execution_environment =
originalNodeObject.summary_fields.execution_environment;
}
if (launchConfig.ask_labels_on_launch) {
overrides.labels = originalNodeObject.labels;
}
if (launchConfig.ask_forks_on_launch) {
overrides.forks = originalNodeObject.forks;
}
if (launchConfig.ask_job_slicing_on_launch) {
overrides.job_slice_count = originalNodeObject.job_slice_count;
}
if (launchConfig.ask_timeout_on_launch) {
overrides.timeout = originalNodeObject.timeout;
}
if (launchConfig.ask_scm_branch_on_launch) {
overrides.scm_branch = originalNodeObject.scm_branch;
}

View File

@ -3,6 +3,7 @@ import { useFormikContext } from 'formik';
import { t } from '@lingui/macro';
import useInventoryStep from 'components/LaunchPrompt/steps/useInventoryStep';
import useCredentialsStep from 'components/LaunchPrompt/steps/useCredentialsStep';
import useExecutionEnvironmentStep from 'components/LaunchPrompt/steps/useExecutionEnvironmentStep';
import useOtherPromptsStep from 'components/LaunchPrompt/steps/useOtherPromptsStep';
import useSurveyStep from 'components/LaunchPrompt/steps/useSurveyStep';
import usePreviewStep from 'components/LaunchPrompt/steps/usePreviewStep';
@ -26,6 +27,12 @@ function showPreviewStep(nodeType, launchConfig) {
launchConfig.ask_variables_on_launch ||
launchConfig.ask_limit_on_launch ||
launchConfig.ask_scm_branch_on_launch ||
launchConfig.ask_execution_environment_on_launch ||
launchConfig.ask_labels_on_launch ||
launchConfig.ask_forks_on_launch ||
launchConfig.ask_job_slicing_on_launch ||
launchConfig.ask_timeout_on_launch ||
launchConfig.ask_instance_groups_on_launch ||
launchConfig.survey_enabled ||
(launchConfig.variables_needed_to_start &&
launchConfig.variables_needed_to_start.length > 0)
@ -129,6 +136,20 @@ const getNodeToEditDefaultValues = (
}
}
if (launchConfig.ask_execution_environment_on_launch) {
if (nodeToEdit?.promptValues) {
initialValues.execution_environment =
nodeToEdit?.promptValues?.execution_environment;
} else if (
nodeToEdit?.originalNodeObject?.summary_fields?.execution_environment
) {
initialValues.execution_environment =
nodeToEdit?.originalNodeObject?.summary_fields?.execution_environment;
} else {
initialValues.execution_environment = null;
}
}
if (launchConfig.ask_credential_on_launch) {
if (nodeToEdit?.promptValues?.credentials) {
initialValues.credentials = nodeToEdit?.promptValues?.credentials;
@ -197,6 +218,15 @@ const getNodeToEditDefaultValues = (
if (launchConfig.ask_diff_mode_on_launch) {
initialValues.diff_mode = sourceOfValues?.diff_mode || false;
}
if (launchConfig.ask_forks_on_launch) {
initialValues.forks = sourceOfValues?.forks || 0;
}
if (launchConfig.ask_job_slicing_on_launch) {
initialValues.job_slice_count = sourceOfValues?.job_slice_count || 1;
}
if (launchConfig.ask_timeout_on_launch) {
initialValues.timeout = sourceOfValues?.timeout || 0;
}
if (launchConfig.ask_variables_on_launch) {
const newExtraData = { ...sourceOfValues.extra_data };
@ -258,6 +288,7 @@ export default function useWorkflowNodeSteps(
useDaysToKeepStep(),
useInventoryStep(launchConfig, resource, visited),
useCredentialsStep(launchConfig, resource, resourceDefaultCredentials),
useExecutionEnvironmentStep(launchConfig, resource),
useOtherPromptsStep(launchConfig, resource),
useSurveyStep(launchConfig, surveyConfig, resource, visited),
];
@ -348,6 +379,7 @@ export default function useWorkflowNodeSteps(
setVisited({
inventory: true,
credentials: true,
executionEnvironment: true,
other: true,
survey: true,
preview: true,

View File

@ -6,7 +6,7 @@ const jtHelpTextStrings = () => ({
jobType: t`For job templates, select run to execute the playbook. Select check to only check playbook syntax, test environment setup, and report problems without executing the playbook.`,
inventory: t`Select the inventory containing the hosts you want this job to manage.`,
project: t`Select the project containing the playbook you want this job to execute.`,
executionEnvironmentForm: t`Select the execution environment for this job template.`,
executionEnvironmentForm: t`The container image to be used for execution.`,
executionEnvironmentDetail: t`The execution environment that will be used when launching this job template. The resolved execution environment can be overridden by explicitly assigning a different one to this job template.`,
playbook: t`Select the playbook to be executed by this job.`,
credentials: t`Select credentials for accessing the nodes this job will be ran against. You can only select one credential of each type. For machine credentials (SSH), checking "Prompt on launch" without selecting credentials will require you to select a machine credential at run time. If you select credentials and check "Prompt on launch", the selected credential(s) become the defaults that can be updated at run time.`,
@ -24,7 +24,7 @@ const jtHelpTextStrings = () => ({
webhookURL: t`Webhook services can launch jobs with this workflow job template by making a POST request to this URL.`,
webhookKey: t`Webhook services can use this as a shared secret.`,
webhookCredential: t`Optionally select the credential to use to send status updates back to the webhook service.`,
sourceControlBranch: t`Select a branch for the workflow. This branch is applied to all job template nodes that prompt for a branch.`,
sourceControlBranch: t`Branch to use in job run. Project default used if blank. Only allowed if project allow_override field is set to true.`,
provisioningCallbacks: (brandName = '') =>
t`Enables creation of a provisioning callback URL. Using the URL a host can contact ${brandName} and request a configuration update using this job template.`,
privilegeEscalation: t`If enabled, run this playbook as an administrator.`,

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import { t } from '@lingui/macro';
import { withFormik, useField } from 'formik';
import {
@ -87,6 +86,10 @@ function JobTemplateForm({
const [credentialField, , credentialHelpers] = useField('credentials');
const [labelsField, , labelsHelpers] = useField('labels');
const [limitField, limitMeta, limitHelpers] = useField('limit');
const [forksField, forksMeta, forksHelpers] = useField('forks');
const [jobSliceCountField, jobSliceCountMeta, jobSliceCountHelpers] =
useField('job_slice_count');
const [timeoutField, timeoutMeta, timeoutHelpers] = useField('timeout');
const [diffModeField, , diffModeHelpers] = useField('diff_mode');
const [instanceGroupsField, , instanceGroupsHelpers] =
useField('instanceGroups');
@ -321,6 +324,9 @@ function JobTemplateForm({
globallyAvailable
isDisabled={!projectField.value?.id}
projectId={projectField.value?.id}
promptId="template-ask-execution-environment-on-launch"
promptName="ask_execution_environment_on_launch"
isPromptableField
/>
{projectField.value?.allow_override && (
@ -376,10 +382,12 @@ function JobTemplateForm({
onError={setContentError}
/>
</FieldWithPrompt>
<FormGroup
label={t`Labels`}
labelIcon={<Popover content={helpText.labels} />}
<FieldWithPrompt
fieldId="template-labels"
label={t`Labels`}
promptId="template-ask-labels-on-launch"
promptName="ask_labels_on_launch"
tooltip={helpText.labels}
>
<LabelSelect
value={labelsField.value}
@ -387,7 +395,7 @@ function JobTemplateForm({
onError={setContentError}
createText={t`Create`}
/>
</FormGroup>
</FieldWithPrompt>
<VariablesField
id="template-variables"
name="extra_vars"
@ -396,14 +404,26 @@ function JobTemplateForm({
tooltip={helpText.variables}
/>
<FormColumnLayout>
<FormField
id="template-forks"
name="forks"
type="number"
min="0"
<FieldWithPrompt
fieldId="template-forks"
label={t`Forks`}
promptId="template-ask-forks-on-launch"
promptName="ask_forks_on_launch"
tooltip={helpText.forks}
/>
>
<TextInput
id="template-forks"
{...forksField}
validated={
!forksMeta.touched || !forksMeta.error ? 'default' : 'error'
}
onChange={(value) => {
forksHelpers.setValue(value);
}}
type="number"
min="0"
/>
</FieldWithPrompt>
<FieldWithPrompt
fieldId="template-limit"
label={t`Limit`}
@ -428,22 +448,50 @@ function JobTemplateForm({
promptName="ask_verbosity_on_launch"
tooltip={helpText.verbosity}
/>
<FormField
id="template-job-slicing"
name="job_slice_count"
type="number"
min="1"
<FieldWithPrompt
fieldId="template-job-slicing"
label={t`Job Slicing`}
promptId="template-ask-job-slicing-on-launch"
promptName="ask_job_slicing_on_launch"
tooltip={helpText.jobSlicing}
/>
<FormField
id="template-timeout"
name="timeout"
type="number"
min="0"
>
<TextInput
id="template-job-slicing"
{...jobSliceCountField}
validated={
!jobSliceCountMeta.touched || !jobSliceCountMeta.error
? 'default'
: 'error'
}
onChange={(value) => {
jobSliceCountHelpers.setValue(value);
}}
type="number"
min="1"
/>
</FieldWithPrompt>
<FieldWithPrompt
fieldId="template-timeout"
label={t`Timeout`}
promptId="template-ask-timeout-on-launch"
promptName="ask_timeout_on_launch"
tooltip={helpText.timeout}
/>
>
<TextInput
id="template-timeout"
{...timeoutField}
validated={
!timeoutMeta.touched || !timeoutMeta.error
? 'default'
: 'error'
}
onChange={(value) => {
timeoutHelpers.setValue(value);
}}
type="number"
min="0"
/>
</FieldWithPrompt>
<FieldWithPrompt
fieldId="template-diff-mode"
label={t`Show Changes`}
@ -464,6 +512,9 @@ function JobTemplateForm({
onChange={(value) => instanceGroupsHelpers.setValue(value)}
tooltip={helpText.instanceGroups}
fieldName="instanceGroups"
promptId="template-ask-instance-groups-on-launch"
promptName="ask_instance_groups_on_launch"
isPromptableField
/>
<FieldWithPrompt
fieldId="template-tags"
@ -646,12 +697,20 @@ const FormikApp = withFormik({
allow_simultaneous: template.allow_simultaneous || false,
ask_credential_on_launch: template.ask_credential_on_launch || false,
ask_diff_mode_on_launch: template.ask_diff_mode_on_launch || false,
ask_execution_environment_on_launch:
template.ask_execution_environment_on_launch || false,
ask_forks_on_launch: template.ask_forks_on_launch || false,
ask_instance_groups_on_launch:
template.ask_instance_groups_on_launch || false,
ask_inventory_on_launch: template.ask_inventory_on_launch || false,
ask_job_slicing_on_launch: template.ask_job_slicing_on_launch || false,
ask_job_type_on_launch: template.ask_job_type_on_launch || false,
ask_labels_on_launch: template.ask_labels_on_launch || false,
ask_limit_on_launch: template.ask_limit_on_launch || false,
ask_scm_branch_on_launch: template.ask_scm_branch_on_launch || false,
ask_skip_tags_on_launch: template.ask_skip_tags_on_launch || false,
ask_tags_on_launch: template.ask_tags_on_launch || false,
ask_timeout_on_launch: template.ask_timeout_on_launch || false,
ask_variables_on_launch: template.ask_variables_on_launch || false,
ask_verbosity_on_launch: template.ask_verbosity_on_launch || false,
become_enabled: template.become_enabled || false,

View File

@ -186,10 +186,12 @@ function WorkflowJobTemplateForm({
</FieldWithPrompt>
</FormColumnLayout>
<FormFullWidthLayout>
<FormGroup
label={t`Labels`}
labelIcon={<Popover content={helpText.labels} />}
<FieldWithPrompt
fieldId="template-labels"
label={t`Labels`}
promptId="template-ask-labels-on-launch"
promptName="ask_labels_on_launch"
tooltip={helpText.labels}
>
<LabelSelect
value={labelsField.value}
@ -197,7 +199,7 @@ function WorkflowJobTemplateForm({
onError={setContentError}
createText={t`Create`}
/>
</FormGroup>
</FieldWithPrompt>
</FormFullWidthLayout>
<FormFullWidthLayout>
<VariablesField
@ -283,6 +285,7 @@ const FormikApp = withFormik({
allow_simultaneous: template.allow_simultaneous || false,
webhook_credential: template?.summary_fields?.webhook_credential || null,
webhook_service: template.webhook_service || '',
ask_labels_on_launch: template.ask_labels_on_launch || false,
ask_limit_on_launch: template.ask_limit_on_launch || false,
ask_inventory_on_launch: template.ask_inventory_on_launch || false,
ask_variables_on_launch: template.ask_variables_on_launch || false,