mirror of
https://github.com/ansible/awx.git
synced 2026-03-07 19:51:08 -03:30
Adds managed_by_policy checkbox to instances form. Adds warnings when associating or disassociating instances from instance groups. (#13994)
This commit is contained in:
@@ -27,6 +27,7 @@ function AssociateModal({
|
|||||||
isModalOpen = false,
|
isModalOpen = false,
|
||||||
displayKey = 'name',
|
displayKey = 'name',
|
||||||
ouiaId,
|
ouiaId,
|
||||||
|
modalNote,
|
||||||
}) {
|
}) {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { selected, handleSelect } = useSelected([]);
|
const { selected, handleSelect } = useSelected([]);
|
||||||
@@ -120,6 +121,7 @@ function AssociateModal({
|
|||||||
</Button>,
|
</Button>,
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
{modalNote}
|
||||||
<OptionsList
|
<OptionsList
|
||||||
displayKey={displayKey}
|
displayKey={displayKey}
|
||||||
contentError={contentError}
|
contentError={contentError}
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import React, { useState, useEffect, useContext } from 'react';
|
import React, { useState, useEffect, useContext } from 'react';
|
||||||
import { arrayOf, func, shape, string, oneOfType, number } from 'prop-types';
|
import {
|
||||||
|
arrayOf,
|
||||||
|
func,
|
||||||
|
shape,
|
||||||
|
string,
|
||||||
|
oneOfType,
|
||||||
|
number,
|
||||||
|
node,
|
||||||
|
} from 'prop-types';
|
||||||
|
|
||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Button, Tooltip, DropdownItem } from '@patternfly/react-core';
|
import { Button, Tooltip, DropdownItem } from '@patternfly/react-core';
|
||||||
@@ -180,7 +188,7 @@ DisassociateButton.propTypes = {
|
|||||||
})
|
})
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
modalNote: string,
|
modalNote: node,
|
||||||
modalTitle: string,
|
modalTitle: string,
|
||||||
onDisassociate: func.isRequired,
|
onDisassociate: func.isRequired,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { useParams, useHistory } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { t, Plural } from '@lingui/macro';
|
import { t, Trans, Plural } from '@lingui/macro';
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Progress,
|
Progress,
|
||||||
@@ -71,6 +71,10 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
|
|||||||
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
|
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
|
||||||
const [forks, setForks] = useState();
|
const [forks, setForks] = useState();
|
||||||
|
|
||||||
|
const policyRulesDocsLink = `${getDocsBaseUrl(
|
||||||
|
config
|
||||||
|
)}/html/administration/containers_instance_groups.html#ag-instance-group-policies`;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isLoading,
|
isLoading,
|
||||||
error: contentError,
|
error: contentError,
|
||||||
@@ -319,6 +323,23 @@ function InstanceDetails({ setBreadcrumb, instanceGroup }) {
|
|||||||
itemsToDisassociate={[instance]}
|
itemsToDisassociate={[instance]}
|
||||||
isProtectedInstanceGroup={instanceGroup.name === 'controlplane'}
|
isProtectedInstanceGroup={instanceGroup.name === 'controlplane'}
|
||||||
modalTitle={t`Disassociate instance from instance group?`}
|
modalTitle={t`Disassociate instance from instance group?`}
|
||||||
|
modalNote={
|
||||||
|
instance.managed_by_policy ? (
|
||||||
|
<Trans>
|
||||||
|
<b>
|
||||||
|
Note: This instance may be re-associated with this
|
||||||
|
instance group if it is managed by{' '}
|
||||||
|
<a
|
||||||
|
href={policyRulesDocsLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
policy rules.
|
||||||
|
</a>
|
||||||
|
</b>
|
||||||
|
</Trans>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<InstanceToggle
|
<InstanceToggle
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useCallback, useEffect, useState } from 'react';
|
import React, { useCallback, useEffect, useState } from 'react';
|
||||||
import { t } from '@lingui/macro';
|
import { t, Trans } from '@lingui/macro';
|
||||||
import { useLocation, useParams } from 'react-router-dom';
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import 'styled-components/macro';
|
import 'styled-components/macro';
|
||||||
|
|
||||||
@@ -22,6 +22,8 @@ import useRequest, {
|
|||||||
import useSelected from 'hooks/useSelected';
|
import useSelected from 'hooks/useSelected';
|
||||||
import { InstanceGroupsAPI, InstancesAPI } from 'api';
|
import { InstanceGroupsAPI, InstancesAPI } from 'api';
|
||||||
import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
|
import { getQSConfig, parseQueryString, mergeParams } from 'util/qs';
|
||||||
|
import getDocsBaseUrl from 'util/getDocsBaseUrl';
|
||||||
|
import { useConfig } from 'contexts/Config';
|
||||||
import HealthCheckButton from 'components/HealthCheckButton/HealthCheckButton';
|
import HealthCheckButton from 'components/HealthCheckButton/HealthCheckButton';
|
||||||
import HealthCheckAlert from 'components/HealthCheckAlert';
|
import HealthCheckAlert from 'components/HealthCheckAlert';
|
||||||
import InstanceListItem from './InstanceListItem';
|
import InstanceListItem from './InstanceListItem';
|
||||||
@@ -33,6 +35,7 @@ const QS_CONFIG = getQSConfig('instance', {
|
|||||||
});
|
});
|
||||||
|
|
||||||
function InstanceList({ instanceGroup }) {
|
function InstanceList({ instanceGroup }) {
|
||||||
|
const config = useConfig();
|
||||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
|
const [showHealthCheckAlert, setShowHealthCheckAlert] = useState(false);
|
||||||
const [pendingHealthCheck, setPendingHealthCheck] = useState(false);
|
const [pendingHealthCheck, setPendingHealthCheck] = useState(false);
|
||||||
@@ -40,6 +43,10 @@ function InstanceList({ instanceGroup }) {
|
|||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const { id: instanceGroupId } = useParams();
|
const { id: instanceGroupId } = useParams();
|
||||||
|
|
||||||
|
const policyRulesDocsLink = `${getDocsBaseUrl(
|
||||||
|
config
|
||||||
|
)}/html/administration/containers_instance_groups.html#ag-instance-group-policies`;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
result: {
|
result: {
|
||||||
instances,
|
instances,
|
||||||
@@ -262,6 +269,25 @@ function InstanceList({ instanceGroup }) {
|
|||||||
itemsToDisassociate={selected}
|
itemsToDisassociate={selected}
|
||||||
modalTitle={t`Disassociate instance from instance group?`}
|
modalTitle={t`Disassociate instance from instance group?`}
|
||||||
isProtectedInstanceGroup={instanceGroup.name === 'controlplane'}
|
isProtectedInstanceGroup={instanceGroup.name === 'controlplane'}
|
||||||
|
modalNote={
|
||||||
|
selected.some(
|
||||||
|
(instance) => instance.managed_by_policy === true
|
||||||
|
) ? (
|
||||||
|
<Trans>
|
||||||
|
<b>
|
||||||
|
Note: Instances may be re-associated with this instance
|
||||||
|
group if they are managed by{' '}
|
||||||
|
<a
|
||||||
|
href={policyRulesDocsLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
policy rules.
|
||||||
|
</a>
|
||||||
|
</b>
|
||||||
|
</Trans>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
/>,
|
/>,
|
||||||
<HealthCheckButton
|
<HealthCheckButton
|
||||||
isDisabled={!canAdd || !canRunHealthCheck}
|
isDisabled={!canAdd || !canRunHealthCheck}
|
||||||
@@ -321,6 +347,24 @@ function InstanceList({ instanceGroup }) {
|
|||||||
{ key: 'hostname', name: t`Name` },
|
{ key: 'hostname', name: t`Name` },
|
||||||
{ key: 'node_type', name: t`Node Type` },
|
{ key: 'node_type', name: t`Node Type` },
|
||||||
]}
|
]}
|
||||||
|
modalNote={
|
||||||
|
<b>
|
||||||
|
<Trans>
|
||||||
|
<b>
|
||||||
|
Note: Manually associated instances may be automatically
|
||||||
|
disassociated from an instance group if the instance is
|
||||||
|
managed by{' '}
|
||||||
|
<a
|
||||||
|
href={policyRulesDocsLink}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
>
|
||||||
|
policy rules.
|
||||||
|
</a>
|
||||||
|
</b>
|
||||||
|
</Trans>
|
||||||
|
</b>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{error && (
|
{error && (
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ function InstanceFormFields() {
|
|||||||
label={t`Enable Instance`}
|
label={t`Enable Instance`}
|
||||||
tooltip={t`Set the instance enabled or disabled. If disabled, jobs will not be assigned to this instance.`}
|
tooltip={t`Set the instance enabled or disabled. If disabled, jobs will not be assigned to this instance.`}
|
||||||
/>
|
/>
|
||||||
|
<CheckboxField
|
||||||
|
id="managed-by-policy"
|
||||||
|
name="managed_by_policy"
|
||||||
|
label={t`Managed by Policy`}
|
||||||
|
tooltip={t`Controls whether or not this instance is managed by policy. If enabled, the instance will be available for automatic assignment to and unassignment from instance groups based on policy rules.`}
|
||||||
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@@ -79,6 +85,7 @@ function InstanceForm({
|
|||||||
node_state: 'installed',
|
node_state: 'installed',
|
||||||
listener_port: 27199,
|
listener_port: 27199,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
managed_by_policy: true,
|
||||||
}}
|
}}
|
||||||
onSubmit={(values) => {
|
onSubmit={(values) => {
|
||||||
handleSubmit(values);
|
handleSubmit(values);
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ describe('<InstanceForm />', () => {
|
|||||||
expect(handleSubmit).toBeCalledWith({
|
expect(handleSubmit).toBeCalledWith({
|
||||||
description: 'This is a repeat song',
|
description: 'This is a repeat song',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
managed_by_policy: true,
|
||||||
hostname: 'new Foo',
|
hostname: 'new Foo',
|
||||||
listener_port: 'This is a repeat song',
|
listener_port: 'This is a repeat song',
|
||||||
node_state: 'installed',
|
node_state: 'installed',
|
||||||
|
|||||||
Reference in New Issue
Block a user