Skip to content

Commit

Permalink
Add EE to the settings page
Browse files Browse the repository at this point in the history
Allow a system admin to set the global default execution environment.

See: ansible#9088

This PR is also addressing the issue: ansible#9669
  • Loading branch information
nixocio committed Mar 25, 2021
1 parent ef69150 commit 5f11f3e
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 38 deletions.
2 changes: 1 addition & 1 deletion awx/main/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@
default=None,
queryset=ExecutionEnvironment.objects.all(),
label=_('Global default execution environment'),
help_text=_('.'),
help_text=_('The Execution Environment to be used when one has not been configured for a job template.'),
category=_('System'),
category_slug='system',
)
Expand Down
24 changes: 18 additions & 6 deletions awx/ui_next/src/components/Lookup/ExecutionEnvironmentLookup.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function ExecutionEnvironmentLookup({
globallyAvailable,
i18n,
isDefaultEnvironment,
isGlobalDefaultEnvironment,
isDisabled,
onBlur,
onChange,
Expand Down Expand Up @@ -154,17 +155,26 @@ function ExecutionEnvironmentLookup({
</>
);

const renderLabel = (
globalDefaultEnvironment,
defaultExecutionEnvironment
) => {
if (globalDefaultEnvironment) {
return i18n._(t`Global Default Execution Environment`);
}
if (defaultExecutionEnvironment) {
return i18n._(t`Default Execution Environment`);
}
return i18n._(t`Execution Environment`);
};

return (
<FormGroup
fieldId="execution-environment-lookup"
label={
isDefaultEnvironment
? i18n._(t`Default Execution Environment`)
: i18n._(t`Execution Environment`)
}
label={renderLabel(isGlobalDefaultEnvironment, isDefaultEnvironment)}
labelIcon={popoverContent && <Popover content={popoverContent} />}
>
{isDisabled ? (
{tooltip ? (
<Tooltip content={tooltip}>{renderLookup()}</Tooltip>
) : (
renderLookup()
Expand All @@ -180,13 +190,15 @@ ExecutionEnvironmentLookup.propTypes = {
popoverContent: string,
onChange: func.isRequired,
isDefaultEnvironment: bool,
isGlobalDefaultEnvironment: bool,
projectId: oneOfType([number, string]),
organizationId: oneOfType([number, string]),
};

ExecutionEnvironmentLookup.defaultProps = {
popoverContent: '',
isDefaultEnvironment: false,
isGlobalDefaultEnvironment: false,
value: null,
projectId: null,
organizationId: null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ContentError from '../../../../components/ContentError';
import ContentLoading from '../../../../components/ContentLoading';
import { DetailList } from '../../../../components/DetailList';
import RoutedTabs from '../../../../components/RoutedTabs';
import { SettingsAPI } from '../../../../api';
import { SettingsAPI, ExecutionEnvironmentsAPI } from '../../../../api';
import useRequest from '../../../../util/useRequest';
import { useConfig } from '../../../../contexts/Config';
import { useSettings } from '../../../../contexts/Settings';
Expand All @@ -23,7 +23,15 @@ function MiscSystemDetail({ i18n }) {
const { isLoading, error, request, result: system } = useRequest(
useCallback(async () => {
const { data } = await SettingsAPI.readCategory('all');

let DEFAULT_EXECUTION_ENVIRONMENT = '';
if (data.DEFAULT_EXECUTION_ENVIRONMENT) {
const {
data: { name },
} = await ExecutionEnvironmentsAPI.readDetail(
data.DEFAULT_EXECUTION_ENVIRONMENT
);
DEFAULT_EXECUTION_ENVIRONMENT = name;
}
const {
OAUTH2_PROVIDER: {
ACCESS_TOKEN_EXPIRE_SECONDS,
Expand All @@ -49,19 +57,17 @@ function MiscSystemDetail({ i18n }) {
'SESSION_COOKIE_AGE',
'TOWER_URL_BASE'
);

const systemData = {
...pluckedSystemData,
ACCESS_TOKEN_EXPIRE_SECONDS,
REFRESH_TOKEN_EXPIRE_SECONDS,
AUTHORIZATION_CODE_EXPIRE_SECONDS,
DEFAULT_EXECUTION_ENVIRONMENT,
};

const {
OAUTH2_PROVIDER: OAUTH2_PROVIDER_OPTIONS,
...options
} = allOptions;

const systemOptions = {
...options,
ACCESS_TOKEN_EXPIRE_SECONDS: {
Expand All @@ -80,7 +86,6 @@ function MiscSystemDetail({ i18n }) {
label: i18n._(t`Authorization Code Expiration`),
},
};

const mergedData = {};
Object.keys(systemData).forEach(key => {
mergedData[key] = systemOptions[key];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
waitForElement,
} from '../../../../../testUtils/enzymeHelpers';
import { SettingsProvider } from '../../../../contexts/Settings';
import { SettingsAPI } from '../../../../api';
import { SettingsAPI, ExecutionEnvironmentsAPI } from '../../../../api';
import {
assertDetail,
assertVariableDetail,
Expand All @@ -14,13 +14,14 @@ import mockAllOptions from '../../shared/data.allSettingOptions.json';
import MiscSystemDetail from './MiscSystemDetail';

jest.mock('../../../../api/models/Settings');
jest.mock('../../../../api/models/ExecutionEnvironments');

SettingsAPI.readCategory.mockResolvedValue({
data: {
ALLOW_OAUTH2_FOR_EXTERNAL_USERS: false,
AUTH_BASIC_ENABLED: true,
AUTOMATION_ANALYTICS_GATHER_INTERVAL: 14400,
AUTOMATION_ANALYTICS_URL: 'https://example.com',
CUSTOM_VENV_PATHS: [],
INSIGHTS_TRACKING_STATE: false,
LOGIN_REDIRECT_OVERRIDE: 'https://redirect.com',
MANAGE_ORGANIZATION_AUTH: true,
Expand All @@ -36,6 +37,16 @@ SettingsAPI.readCategory.mockResolvedValue({
SESSIONS_PER_USER: -1,
SESSION_COOKIE_AGE: 30000000000,
TOWER_URL_BASE: 'https://towerhost',
DEFAULT_EXECUTION_ENVIRONMENT: 1,
},
});

ExecutionEnvironmentsAPI.readDetail.mockResolvedValue({
data: {
id: 1,
name: 'Foo',
image: 'quay.io/ansible/awx-ee',
pull: 'missing',
},
});

Expand Down Expand Up @@ -110,6 +121,33 @@ describe('<MiscSystemDetail />', () => {
assertDetail(wrapper, 'Red Hat customer username', 'mock name');
assertDetail(wrapper, 'Refresh Token Expiration', '3 seconds');
assertVariableDetail(wrapper, 'Remote Host Headers', '[]');
assertDetail(wrapper, 'Global default execution environment', 'Foo');
});

test('should render execution environment as not configured', async () => {
ExecutionEnvironmentsAPI.readDetail.mockResolvedValue({
data: {},
});
let newWrapper;
await act(async () => {
newWrapper = mountWithContexts(
<SettingsProvider
value={{
...mockAllOptions.actions,
DEFAULT_EXECUTION_ENVIRONMENT: null,
}}
>
<MiscSystemDetail />
</SettingsProvider>
);
});
await waitForElement(newWrapper, 'ContentLoading', el => el.length === 0);

assertDetail(
newWrapper,
'Global default execution environment',
'Not configured'
);
});

test('should hide edit button from non-superusers', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ContentError from '../../../../components/ContentError';
import ContentLoading from '../../../../components/ContentLoading';
import { FormSubmitError } from '../../../../components/FormField';
import { FormColumnLayout } from '../../../../components/FormLayout';
import { ExecutionEnvironmentLookup } from '../../../../components/Lookup';
import { useSettings } from '../../../../contexts/Settings';
import {
BooleanField,
Expand All @@ -20,7 +21,7 @@ import {
} from '../../shared';
import useModal from '../../../../util/useModal';
import useRequest from '../../../../util/useRequest';
import { SettingsAPI } from '../../../../api';
import { SettingsAPI, ExecutionEnvironmentsAPI } from '../../../../api';
import { pluck, formatJson } from '../../shared/settingUtils';

function MiscSystemEdit({ i18n }) {
Expand All @@ -44,7 +45,6 @@ function MiscSystemEdit({ i18n }) {
'AUTH_BASIC_ENABLED',
'AUTOMATION_ANALYTICS_GATHER_INTERVAL',
'AUTOMATION_ANALYTICS_URL',
'CUSTOM_VENV_PATHS',
'INSIGHTS_TRACKING_STATE',
'LOGIN_REDIRECT_OVERRIDE',
'MANAGE_ORGANIZATION_AUTH',
Expand All @@ -55,7 +55,8 @@ function MiscSystemEdit({ i18n }) {
'REMOTE_HOST_HEADERS',
'SESSIONS_PER_USER',
'SESSION_COOKIE_AGE',
'TOWER_URL_BASE'
'TOWER_URL_BASE',
'DEFAULT_EXECUTION_ENVIRONMENT'
);

const systemData = {
Expand Down Expand Up @@ -128,6 +129,7 @@ function MiscSystemEdit({ i18n }) {
AUTHORIZATION_CODE_EXPIRE_SECONDS,
...formData
} = form;

await submitForm({
...formData,
REMOTE_HOST_HEADERS: formatJson(formData.REMOTE_HOST_HEADERS),
Expand All @@ -136,6 +138,8 @@ function MiscSystemEdit({ i18n }) {
REFRESH_TOKEN_EXPIRE_SECONDS,
AUTHORIZATION_CODE_EXPIRE_SECONDS,
},
DEFAULT_EXECUTION_ENVIRONMENT:
formData.DEFAULT_EXECUTION_ENVIRONMENT?.id || null,
});
};

Expand Down Expand Up @@ -178,16 +182,73 @@ function MiscSystemEdit({ i18n }) {
return acc;
}, {});

const executionEnvironmentId =
system?.DEFAULT_EXECUTION_ENVIRONMENT?.value || null;

const {
isLoading: isLoadingExecutionEnvironment,
error: errorExecutionEnvironment,
request: fetchExecutionEnvironment,
result: executionEnvironment,
} = useRequest(
useCallback(async () => {
if (!executionEnvironmentId) {
return '';
}
const { data } = await ExecutionEnvironmentsAPI.readDetail(
executionEnvironmentId
);
return data;
}, [executionEnvironmentId])
);

useEffect(() => {
fetchExecutionEnvironment();
}, [fetchExecutionEnvironment]);

return (
<CardBody>
{isLoading && <ContentLoading />}
{!isLoading && error && <ContentError error={error} />}
{!isLoading && system && (
<Formik initialValues={initialValues(system)} onSubmit={handleSubmit}>
{(isLoading || isLoadingExecutionEnvironment) && <ContentLoading />}
{!(isLoading || isLoadingExecutionEnvironment) && error && (
<ContentError error={error || errorExecutionEnvironment} />
)}
{!(isLoading || isLoadingExecutionEnvironment) && system && (
<Formik
initialValues={{
...initialValues(system),
DEFAULT_EXECUTION_ENVIRONMENT: executionEnvironment
? { id: executionEnvironment.id, name: executionEnvironment.name }
: null,
}}
onSubmit={handleSubmit}
>
{formik => {
return (
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
<FormColumnLayout>
<ExecutionEnvironmentLookup
helperTextInvalid={
formik.errors.DEFAULT_EXECUTION_ENVIRONMENT
}
isValid={
!formik.touched.DEFAULT_EXECUTION_ENVIRONMENT ||
!formik.errors.DEFAULT_EXECUTION_ENVIRONMENT
}
onBlur={() =>
formik.setFieldTouched('DEFAULT_EXECUTION_ENVIRONMENT')
}
value={formik.values.DEFAULT_EXECUTION_ENVIRONMENT}
onChange={value =>
formik.setFieldValue(
'DEFAULT_EXECUTION_ENVIRONMENT',
value
)
}
popoverContent={i18n._(
t`The Execution Environment to be used when one has not been configured for a job template.`
)}
isGlobalDefaultEnvironment
/>
<InputField
name="TOWER_URL_BASE"
config={system.TOWER_URL_BASE}
Expand Down
Loading

0 comments on commit 5f11f3e

Please sign in to comment.