diff --git a/awx/api/serializers.py b/awx/api/serializers.py
index eb13f150d3..941b465157 100644
--- a/awx/api/serializers.py
+++ b/awx/api/serializers.py
@@ -1365,7 +1365,7 @@ class ExecutionEnvironmentSerializer(BaseSerializer):
class Meta:
model = ExecutionEnvironment
- fields = ('*', 'organization', 'image', 'managed_by_tower', 'credential')
+ fields = ('*', 'organization', 'image', 'managed_by_tower', 'credential', 'container_options')
def get_related(self, obj):
res = super(ExecutionEnvironmentSerializer, self).get_related(obj)
diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentAdd/ExecutionEnvironmentAdd.test.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentAdd/ExecutionEnvironmentAdd.test.jsx
index 5396746223..ede58e5d58 100644
--- a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentAdd/ExecutionEnvironmentAdd.test.jsx
+++ b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentAdd/ExecutionEnvironmentAdd.test.jsx
@@ -2,7 +2,10 @@ import React from 'react';
import { act } from 'react-dom/test-utils';
import { createMemoryHistory } from 'history';
-import { mountWithContexts } from '../../../../testUtils/enzymeHelpers';
+import {
+ mountWithContexts,
+ waitForElement,
+} from '../../../../testUtils/enzymeHelpers';
import { ExecutionEnvironmentsAPI } from '../../../api';
import ExecutionEnvironmentAdd from './ExecutionEnvironmentAdd';
@@ -14,11 +17,30 @@ const mockMe = {
};
const executionEnvironmentData = {
+ name: 'Test EE',
credential: 4,
description: 'A simple EE',
image: 'https://registry.com/image/container',
+ container_options: 'one',
};
+const mockOptions = {
+ data: {
+ actions: {
+ POST: {
+ container_options: {
+ choices: [
+ ['one', 'One'],
+ ['two', 'Two'],
+ ['three', 'Three'],
+ ],
+ },
+ },
+ },
+ },
+};
+
+ExecutionEnvironmentsAPI.readOptions.mockResolvedValue(mockOptions);
ExecutionEnvironmentsAPI.create.mockResolvedValue({
data: {
id: 42,
@@ -61,6 +83,8 @@ describe('', () => {
});
test('handleCancel should return the user back to the execution environments list', async () => {
+ await waitForElement(wrapper, 'ContentLoading', el => el.length === 0);
+
wrapper.find('Button[aria-label="Cancel"]').simulate('click');
expect(history.location.pathname).toEqual('/execution_environments');
});
diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentDetails/ExecutionEnvironmentDetails.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentDetails/ExecutionEnvironmentDetails.jsx
index abb6cf5ddc..64925df1cb 100644
--- a/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentDetails/ExecutionEnvironmentDetails.jsx
+++ b/awx/ui_next/src/screens/ExecutionEnvironment/ExecutionEnvironmentDetails/ExecutionEnvironmentDetails.jsx
@@ -13,11 +13,18 @@ import {
UserDateDetail,
} from '../../../components/DetailList';
import useRequest, { useDismissableError } from '../../../util/useRequest';
+import { toTitleCase } from '../../../util/strings';
import { ExecutionEnvironmentsAPI } from '../../../api';
function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
const history = useHistory();
- const { id, image, description } = executionEnvironment;
+ const {
+ id,
+ name,
+ image,
+ description,
+ container_options,
+ } = executionEnvironment;
const {
request: deleteExecutionEnvironment,
@@ -35,12 +42,25 @@ function ExecutionEnvironmentDetails({ executionEnvironment, i18n }) {
return (
+
+
{executionEnvironment.summary_fields.credential && (
', () => {
let wrapper;
let history;
diff --git a/awx/ui_next/src/screens/ExecutionEnvironment/shared/ExecutionEnvironmentForm.jsx b/awx/ui_next/src/screens/ExecutionEnvironment/shared/ExecutionEnvironmentForm.jsx
index 5d7a16d217..4dd1b695a4 100644
--- a/awx/ui_next/src/screens/ExecutionEnvironment/shared/ExecutionEnvironmentForm.jsx
+++ b/awx/ui_next/src/screens/ExecutionEnvironment/shared/ExecutionEnvironmentForm.jsx
@@ -1,18 +1,28 @@
-import React, { useCallback } from 'react';
+import React, { useCallback, useEffect } from 'react';
import { func, shape } from 'prop-types';
import { Formik, useField, useFormikContext } from 'formik';
import { withI18n } from '@lingui/react';
import { t } from '@lingui/macro';
-import { Form } from '@patternfly/react-core';
+import { Form, FormGroup } from '@patternfly/react-core';
+import { ExecutionEnvironmentsAPI } from '../../../api';
import CredentialLookup from '../../../components/Lookup/CredentialLookup';
import FormActionGroup from '../../../components/FormActionGroup';
import FormField, { FormSubmitError } from '../../../components/FormField';
+import AnsibleSelect from '../../../components/AnsibleSelect';
import { FormColumnLayout } from '../../../components/FormLayout';
import { OrganizationLookup } from '../../../components/Lookup';
+import ContentError from '../../../components/ContentError';
+import ContentLoading from '../../../components/ContentLoading';
import { required, url } from '../../../util/validators';
+import useRequest from '../../../util/useRequest';
-function ExecutionEnvironmentFormFields({ i18n, me, executionEnvironment }) {
+function ExecutionEnvironmentFormFields({
+ i18n,
+ me,
+ options,
+ executionEnvironment,
+}) {
const [credentialField] = useField('credential');
const [organizationField, organizationMeta, organizationHelpers] = useField({
name: 'organization',
@@ -37,8 +47,28 @@ function ExecutionEnvironmentFormFields({ i18n, me, executionEnvironment }) {
[setFieldValue]
);
+ const [
+ containerOptionsField,
+ containerOptionsMeta,
+ containerOptionsHelpers,
+ ] = useField({
+ name: 'container_options',
+ });
+
+ const containerPullChoices = options?.actions?.POST?.container_options?.choices.map(
+ ([value, label]) => ({ value, label, key: value })
+ );
+
return (
<>
+
+
+ {
+ containerOptionsHelpers.setValue(value);
+ }}
+ />
+
{
+ const res = await ExecutionEnvironmentsAPI.readOptions();
+ const { data } = res;
+ return data;
+ }, []),
+ null
+ );
+
+ useEffect(() => {
+ fetchOptions();
+ }, [fetchOptions]);
+
+ if (isLoading || !options) {
+ return ;
+ }
+
+ if (error) {
+ return ;
+ }
+
const initialValues = {
+ name: executionEnvironment.name || '',
image: executionEnvironment.image || '',
+ container_options: executionEnvironment?.container_options || '',
description: executionEnvironment.description || '',
credential: executionEnvironment.summary_fields?.credential || null,
organization: executionEnvironment.summary_fields?.organization || null,
@@ -101,7 +178,12 @@ function ExecutionEnvironmentForm({
{formik => (