Remove insights_credential from inventory

This commit is contained in:
Bill Nottingham
2021-06-16 16:47:42 -04:00
committed by Shane McDonald
parent 0947d30682
commit 1e68519c99
26 changed files with 38 additions and 267 deletions

View File

@@ -144,7 +144,6 @@ SUMMARIZABLE_FK_FIELDS = {
'inventory_sources_with_failures', 'inventory_sources_with_failures',
'organization_id', 'organization_id',
'kind', 'kind',
'insights_credential_id',
), ),
'host': DEFAULT_SUMMARY_FIELDS, 'host': DEFAULT_SUMMARY_FIELDS,
'group': DEFAULT_SUMMARY_FIELDS, 'group': DEFAULT_SUMMARY_FIELDS,
@@ -171,7 +170,6 @@ SUMMARIZABLE_FK_FIELDS = {
'role': ('id', 'role_field'), 'role': ('id', 'role_field'),
'notification_template': DEFAULT_SUMMARY_FIELDS, 'notification_template': DEFAULT_SUMMARY_FIELDS,
'instance_group': ('id', 'name', 'is_container_group'), 'instance_group': ('id', 'name', 'is_container_group'),
'insights_credential': DEFAULT_SUMMARY_FIELDS,
'source_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud', 'credential_type_id'), 'source_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud', 'credential_type_id'),
'target_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud', 'credential_type_id'), 'target_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud', 'credential_type_id'),
'webhook_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud', 'credential_type_id'), 'webhook_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud', 'credential_type_id'),
@@ -1661,7 +1659,6 @@ class InventorySerializer(BaseSerializerWithVariables):
'has_inventory_sources', 'has_inventory_sources',
'total_inventory_sources', 'total_inventory_sources',
'inventory_sources_with_failures', 'inventory_sources_with_failures',
'insights_credential',
'pending_deletion', 'pending_deletion',
) )
@@ -1686,8 +1683,6 @@ class InventorySerializer(BaseSerializerWithVariables):
copy=self.reverse('api:inventory_copy', kwargs={'pk': obj.pk}), copy=self.reverse('api:inventory_copy', kwargs={'pk': obj.pk}),
) )
) )
if obj.insights_credential:
res['insights_credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.insights_credential.pk})
if obj.organization: if obj.organization:
res['organization'] = self.reverse('api:organization_detail', kwargs={'pk': obj.organization.pk}) res['organization'] = self.reverse('api:organization_detail', kwargs={'pk': obj.organization.pk})
return res return res
@@ -2636,7 +2631,6 @@ class CredentialSerializer(BaseSerializer):
if self.instance and credential_type.pk != self.instance.credential_type.pk: if self.instance and credential_type.pk != self.instance.credential_type.pk:
for related_objects in ( for related_objects in (
'ad_hoc_commands', 'ad_hoc_commands',
'insights_inventories',
'unifiedjobs', 'unifiedjobs',
'unifiedjobtemplates', 'unifiedjobtemplates',
'projects', 'projects',

View File

@@ -867,13 +867,11 @@ class InventoryAccess(BaseAccess):
# If no data is specified, just checking for generic add permission? # If no data is specified, just checking for generic add permission?
if not data: if not data:
return Organization.accessible_objects(self.user, 'inventory_admin_role').exists() return Organization.accessible_objects(self.user, 'inventory_admin_role').exists()
return self.check_related('organization', Organization, data, role_field='inventory_admin_role') and self.check_related( return self.check_related('organization', Organization, data, role_field='inventory_admin_role')
'insights_credential', Credential, data, role_field='use_role'
)
@check_superuser @check_superuser
def can_change(self, obj, data): def can_change(self, obj, data):
return self.can_admin(obj, data) and self.check_related('insights_credential', Credential, data, obj=obj, role_field='use_role') return self.can_admin(obj, data)
@check_superuser @check_superuser
def can_admin(self, obj, data): def can_admin(self, obj, data):

View File

@@ -0,0 +1,17 @@
# Generated by Django 2.2.16 on 2021-06-16 21:00
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('main', '0148_unifiedjob_receptor_unit_id'),
]
operations = [
migrations.RemoveField(
model_name='inventory',
name='insights_credential',
),
]

View File

@@ -165,15 +165,6 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin, RelatedJobsMixin):
'admin_role', 'admin_role',
] ]
) )
insights_credential = models.ForeignKey(
'Credential',
related_name='insights_inventories',
help_text=_('Credentials to be used by hosts belonging to this inventory when accessing Red Hat Insights API.'),
on_delete=models.SET_NULL,
blank=True,
null=True,
default=None,
)
pending_deletion = models.BooleanField( pending_deletion = models.BooleanField(
default=False, default=False,
editable=False, editable=False,
@@ -368,13 +359,6 @@ class Inventory(CommonModelNameNotUnique, ResourceMixin, RelatedJobsMixin):
group_pks = self.groups.values_list('pk', flat=True) group_pks = self.groups.values_list('pk', flat=True)
return self.groups.exclude(parents__pk__in=group_pks).distinct() return self.groups.exclude(parents__pk__in=group_pks).distinct()
def clean_insights_credential(self):
if self.kind == 'smart' and self.insights_credential:
raise ValidationError(_("Assignment not allowed for Smart Inventory"))
if self.insights_credential and self.insights_credential.credential_type.kind != 'insights':
raise ValidationError(_("Credential kind must be 'insights'."))
return self.insights_credential
@transaction.atomic @transaction.atomic
def schedule_deletion(self, user_id=None): def schedule_deletion(self, user_id=None):
from awx.main.tasks import delete_inventory from awx.main.tasks import delete_inventory

View File

@@ -5,7 +5,7 @@ import pytest
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from awx.main.models import AdHocCommand, Credential, CredentialType, Job, JobTemplate, Inventory, InventorySource, Project, WorkflowJobNode from awx.main.models import AdHocCommand, Credential, CredentialType, Job, JobTemplate, InventorySource, Project, WorkflowJobNode
from awx.main.utils import decrypt_field from awx.main.utils import decrypt_field
from awx.api.versioning import reverse from awx.api.versioning import reverse
@@ -857,7 +857,6 @@ def test_field_removal(put, organization, admin, credentialtype_ssh):
'relation, related_obj', 'relation, related_obj',
[ [
['ad_hoc_commands', AdHocCommand()], ['ad_hoc_commands', AdHocCommand()],
['insights_inventories', Inventory()],
['unifiedjobs', Job()], ['unifiedjobs', Job()],
['unifiedjobtemplates', JobTemplate()], ['unifiedjobtemplates', JobTemplate()],
['unifiedjobtemplates', InventorySource(source='ec2')], ['unifiedjobtemplates', InventorySource(source='ec2')],

View File

@@ -592,23 +592,3 @@ class TestControlledBySCM:
rando, rando,
expect=403, expect=403,
) )
@pytest.mark.django_db
class TestInsightsCredential:
def test_insights_credential(self, patch, insights_inventory, admin_user, insights_credential):
patch(insights_inventory.get_absolute_url(), {'insights_credential': insights_credential.id}, admin_user, expect=200)
def test_insights_credential_protection(self, post, patch, insights_inventory, alice, insights_credential):
insights_inventory.organization.admin_role.members.add(alice)
insights_inventory.admin_role.members.add(alice)
post(
reverse('api:inventory_list'),
{"name": "test", "organization": insights_inventory.organization.id, "insights_credential": insights_credential.id},
alice,
expect=403,
)
patch(insights_inventory.get_absolute_url(), {'insights_credential': insights_credential.id}, alice, expect=403)
def test_non_insights_credential(self, patch, insights_inventory, admin_user, scm_credential):
patch(insights_inventory.get_absolute_url(), {'insights_credential': scm_credential.id}, admin_user, expect=400)

View File

@@ -1,15 +1,11 @@
import pytest import pytest
from unittest import mock from unittest import mock
import json
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from awx.main.models import ( from awx.main.models import (
UnifiedJob, UnifiedJob,
InventoryUpdate, InventoryUpdate,
Inventory,
Credential,
CredentialType,
InventorySource, InventorySource,
) )
@@ -39,42 +35,6 @@ def test__build_job_explanation():
) )
def test_valid_clean_insights_credential():
cred_type = CredentialType.defaults['insights']()
insights_cred = Credential(credential_type=cred_type)
inv = Inventory(insights_credential=insights_cred)
inv.clean_insights_credential()
def test_invalid_clean_insights_credential():
cred_type = CredentialType.defaults['scm']()
cred = Credential(credential_type=cred_type)
inv = Inventory(insights_credential=cred)
with pytest.raises(ValidationError) as e:
inv.clean_insights_credential()
assert json.dumps(str(e.value)) == json.dumps(str([u"Credential kind must be 'insights'."]))
def test_valid_kind_clean_insights_credential():
inv = Inventory(kind='smart')
inv.clean_insights_credential()
def test_invalid_kind_clean_insights_credential():
cred_type = CredentialType.defaults['insights']()
insights_cred = Credential(credential_type=cred_type)
inv = Inventory(kind='smart', insights_credential=insights_cred)
with pytest.raises(ValidationError) as e:
inv.clean_insights_credential()
assert json.dumps(str(e.value)) == json.dumps(str([u'Assignment not allowed for Smart Inventory']))
class TestControlledBySCM: class TestControlledBySCM:
def test_clean_source_path_valid(self): def test_clean_source_path_valid(self):
inv_src = InventorySource(source_path='/not_real/', source='scm') inv_src = InventorySource(source_path='/not_real/', source='scm')

View File

@@ -67,7 +67,7 @@ describe('<CredentialDetail />', () => {
test('should have proper number of delete detail requests', () => { test('should have proper number of delete detail requests', () => {
expect( expect(
wrapper.find('DeleteButton').prop('deleteDetailsRequests') wrapper.find('DeleteButton').prop('deleteDetailsRequests')
).toHaveLength(6); ).toHaveLength(5);
}); });
test('should render details', () => { test('should render details', () => {

View File

@@ -43,7 +43,7 @@ describe('<CredentialList />', () => {
test('should have proper number of delete detail requests', () => { test('should have proper number of delete detail requests', () => {
expect( expect(
wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests') wrapper.find('ToolbarDeleteButton').prop('deleteDetailsRequests')
).toHaveLength(6); ).toHaveLength(5);
}); });
test('should fetch credentials from api and render the in the list', () => { test('should fetch credentials from api and render the in the list', () => {

View File

@@ -1,53 +1,26 @@
import React, { useState, useEffect } from 'react'; import React, { useState } from 'react';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { PageSection, Card } from '@patternfly/react-core'; import { PageSection, Card } from '@patternfly/react-core';
import { CardBody } from '../../../components/Card'; import { CardBody } from '../../../components/Card';
import ContentLoading from '../../../components/ContentLoading';
import { InventoriesAPI, CredentialTypesAPI } from '../../../api'; import { InventoriesAPI } from '../../../api';
import InventoryForm from '../shared/InventoryForm'; import InventoryForm from '../shared/InventoryForm';
function InventoryAdd() { function InventoryAdd() {
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [credentialTypeId, setCredentialTypeId] = useState(null);
const history = useHistory(); const history = useHistory();
useEffect(() => {
const loadData = async () => {
try {
const {
data: { results: loadedCredentialTypeId },
} = await CredentialTypesAPI.read({ kind: 'insights' });
setCredentialTypeId(loadedCredentialTypeId[0].id);
} catch (err) {
setError(err);
} finally {
setIsLoading(false);
}
};
loadData();
}, [isLoading, credentialTypeId]);
const handleCancel = () => { const handleCancel = () => {
history.push('/inventories'); history.push('/inventories');
}; };
const handleSubmit = async values => { const handleSubmit = async values => {
const { const { instanceGroups, organization, ...remainingValues } = values;
instanceGroups,
organization,
insights_credential,
...remainingValues
} = values;
try { try {
const { const {
data: { id: inventoryId }, data: { id: inventoryId },
} = await InventoriesAPI.create({ } = await InventoriesAPI.create({
organization: organization.id, organization: organization.id,
insights_credential: insights_credential
? insights_credential.id
: null,
...remainingValues, ...remainingValues,
}); });
if (instanceGroups) { if (instanceGroups) {
@@ -68,9 +41,6 @@ function InventoryAdd() {
} }
}; };
if (isLoading) {
return <ContentLoading />;
}
return ( return (
<PageSection> <PageSection>
<Card> <Card>
@@ -78,7 +48,6 @@ function InventoryAdd() {
<InventoryForm <InventoryForm
onCancel={handleCancel} onCancel={handleCancel}
onSubmit={handleSubmit} onSubmit={handleSubmit}
credentialTypeId={credentialTypeId}
submitError={error} submitError={error}
/> />
</CardBody> </CardBody>

View File

@@ -7,7 +7,7 @@ import {
} from '../../../../testUtils/enzymeHelpers'; } from '../../../../testUtils/enzymeHelpers';
import { sleep } from '../../../../testUtils/testUtils'; import { sleep } from '../../../../testUtils/testUtils';
import { InventoriesAPI, CredentialTypesAPI } from '../../../api'; import { InventoriesAPI } from '../../../api';
import InventoryAdd from './InventoryAdd'; import InventoryAdd from './InventoryAdd';
jest.mock('../../../api'); jest.mock('../../../api');
@@ -18,16 +18,6 @@ describe('<InventoryAdd />', () => {
beforeEach(async () => { beforeEach(async () => {
history = createMemoryHistory({ initialEntries: ['/inventories'] }); history = createMemoryHistory({ initialEntries: ['/inventories'] });
CredentialTypesAPI.read.mockResolvedValue({
data: {
results: [
{
id: 14,
name: 'insights',
},
],
},
});
InventoriesAPI.create.mockResolvedValue({ data: { id: 13 } }); InventoriesAPI.create.mockResolvedValue({ data: { id: 13 } });
await act(async () => { await act(async () => {
wrapper = mountWithContexts(<InventoryAdd />, { wrapper = mountWithContexts(<InventoryAdd />, {
@@ -50,7 +40,6 @@ describe('<InventoryAdd />', () => {
wrapper.find('InventoryForm').prop('onSubmit')({ wrapper.find('InventoryForm').prop('onSubmit')({
name: 'new Foo', name: 'new Foo',
organization: { id: 2 }, organization: { id: 2 },
insights_credential: { id: 47 },
instanceGroups, instanceGroups,
}); });
}); });
@@ -58,7 +47,6 @@ describe('<InventoryAdd />', () => {
expect(InventoriesAPI.create).toHaveBeenCalledWith({ expect(InventoriesAPI.create).toHaveBeenCalledWith({
name: 'new Foo', name: 'new Foo',
organization: 2, organization: 2,
insights_credential: 47,
}); });
instanceGroups.map(IG => instanceGroups.map(IG =>
expect(InventoriesAPI.associateInstanceGroup).toHaveBeenCalledWith( expect(InventoriesAPI.associateInstanceGroup).toHaveBeenCalledWith(

View File

@@ -22,10 +22,6 @@ const mockInventory = {
copy: true, copy: true,
adhoc: true, adhoc: true,
}, },
insights_credential: {
id: 1,
name: 'Foo',
},
}, },
created: '2019-10-04T16:56:48.025455Z', created: '2019-10-04T16:56:48.025455Z',
modified: '2019-10-04T16:56:48.025468Z', modified: '2019-10-04T16:56:48.025468Z',
@@ -43,7 +39,6 @@ const mockInventory = {
has_inventory_sources: false, has_inventory_sources: false,
total_inventory_sources: 0, total_inventory_sources: 0,
inventory_sources_with_failures: 0, inventory_sources_with_failures: 0,
insights_credential: null,
pending_deletion: false, pending_deletion: false,
}; };

View File

@@ -3,7 +3,7 @@ import { useHistory } from 'react-router-dom';
import { object } from 'prop-types'; import { object } from 'prop-types';
import { CardBody } from '../../../components/Card'; import { CardBody } from '../../../components/Card';
import { InventoriesAPI, CredentialTypesAPI } from '../../../api'; import { InventoriesAPI } from '../../../api';
import ContentLoading from '../../../components/ContentLoading'; import ContentLoading from '../../../components/ContentLoading';
import InventoryForm from '../shared/InventoryForm'; import InventoryForm from '../shared/InventoryForm';
import { getAddedAndRemoved } from '../../../util/lists'; import { getAddedAndRemoved } from '../../../util/lists';
@@ -13,31 +13,19 @@ function InventoryEdit({ inventory }) {
const [error, setError] = useState(null); const [error, setError] = useState(null);
const [associatedInstanceGroups, setInstanceGroups] = useState(null); const [associatedInstanceGroups, setInstanceGroups] = useState(null);
const [contentLoading, setContentLoading] = useState(true); const [contentLoading, setContentLoading] = useState(true);
const [credentialTypeId, setCredentialTypeId] = useState(null);
const history = useHistory(); const history = useHistory();
const isMounted = useIsMounted(); const isMounted = useIsMounted();
useEffect(() => { useEffect(() => {
const loadData = async () => { const loadData = async () => {
try { try {
const [ const {
{ data: { results: loadedInstanceGroups },
data: { results: loadedInstanceGroups }, } = await InventoriesAPI.readInstanceGroups(inventory.id);
},
{
data: { results: loadedCredentialTypeId },
},
] = await Promise.all([
InventoriesAPI.readInstanceGroups(inventory.id),
CredentialTypesAPI.read({
kind: 'insights',
}),
]);
if (!isMounted.current) { if (!isMounted.current) {
return; return;
} }
setInstanceGroups(loadedInstanceGroups); setInstanceGroups(loadedInstanceGroups);
setCredentialTypeId(loadedCredentialTypeId[0].id);
} catch (err) { } catch (err) {
setError(err); setError(err);
} finally { } finally {
@@ -48,7 +36,7 @@ function InventoryEdit({ inventory }) {
}; };
loadData(); loadData();
/* eslint-disable-next-line react-hooks/exhaustive-deps */ /* eslint-disable-next-line react-hooks/exhaustive-deps */
}, [inventory.id, contentLoading, inventory, credentialTypeId]); }, [inventory.id, contentLoading, inventory]);
const handleCancel = () => { const handleCancel = () => {
const url = const url =
@@ -60,17 +48,9 @@ function InventoryEdit({ inventory }) {
}; };
const handleSubmit = async values => { const handleSubmit = async values => {
const { const { instanceGroups, organization, ...remainingValues } = values;
instanceGroups,
insights_credential,
organization,
...remainingValues
} = values;
try { try {
await InventoriesAPI.update(inventory.id, { await InventoriesAPI.update(inventory.id, {
insights_credential: insights_credential
? insights_credential.id
: null,
organization: organization.id, organization: organization.id,
...remainingValues, ...remainingValues,
}); });
@@ -109,7 +89,6 @@ function InventoryEdit({ inventory }) {
onSubmit={handleSubmit} onSubmit={handleSubmit}
inventory={inventory} inventory={inventory}
instanceGroups={associatedInstanceGroups} instanceGroups={associatedInstanceGroups}
credentialTypeId={credentialTypeId}
submitError={error} submitError={error}
/> />
</CardBody> </CardBody>

View File

@@ -7,7 +7,7 @@ import {
} from '../../../../testUtils/enzymeHelpers'; } from '../../../../testUtils/enzymeHelpers';
import { sleep } from '../../../../testUtils/testUtils'; import { sleep } from '../../../../testUtils/testUtils';
import { InventoriesAPI, CredentialTypesAPI } from '../../../api'; import { InventoriesAPI } from '../../../api';
import InventoryEdit from './InventoryEdit'; import InventoryEdit from './InventoryEdit';
jest.mock('../../../api'); jest.mock('../../../api');
@@ -28,10 +28,6 @@ const mockInventory = {
copy: true, copy: true,
adhoc: true, adhoc: true,
}, },
insights_credential: {
id: 1,
name: 'Foo',
},
}, },
created: '2019-10-04T16:56:48.025455Z', created: '2019-10-04T16:56:48.025455Z',
modified: '2019-10-04T16:56:48.025468Z', modified: '2019-10-04T16:56:48.025468Z',
@@ -49,7 +45,6 @@ const mockInventory = {
has_inventory_sources: false, has_inventory_sources: false,
total_inventory_sources: 0, total_inventory_sources: 0,
inventory_sources_with_failures: 0, inventory_sources_with_failures: 0,
insights_credential: null,
pending_deletion: false, pending_deletion: false,
}; };
@@ -65,16 +60,6 @@ describe('<InventoryEdit />', () => {
let history; let history;
beforeEach(async () => { beforeEach(async () => {
CredentialTypesAPI.read.mockResolvedValue({
data: {
results: [
{
id: 14,
name: 'insights',
},
],
},
});
InventoriesAPI.readInstanceGroups.mockResolvedValue({ InventoriesAPI.readInstanceGroups.mockResolvedValue({
data: { data: {
results: associatedInstanceGroups, results: associatedInstanceGroups,
@@ -117,7 +102,6 @@ describe('<InventoryEdit />', () => {
name: 'Foo', name: 'Foo',
id: 13, id: 13,
organization: { id: 1 }, organization: { id: 1 },
insights_credential: { id: 13 },
instanceGroups, instanceGroups,
}); });
}); });

View File

@@ -47,7 +47,6 @@ const mockInventories = [
has_inventory_sources: false, has_inventory_sources: false,
total_inventory_sources: 0, total_inventory_sources: 0,
inventory_sources_with_failures: 0, inventory_sources_with_failures: 0,
insights_credential: null,
pending_deletion: false, pending_deletion: false,
}, },
{ {
@@ -83,7 +82,6 @@ const mockInventories = [
has_inventory_sources: false, has_inventory_sources: false,
total_inventory_sources: 0, total_inventory_sources: 0,
inventory_sources_with_failures: 0, inventory_sources_with_failures: 0,
insights_credential: null,
pending_deletion: false, pending_deletion: false,
}, },
{ {
@@ -119,7 +117,6 @@ const mockInventories = [
has_inventory_sources: false, has_inventory_sources: false,
total_inventory_sources: 0, total_inventory_sources: 0,
inventory_sources_with_failures: 0, inventory_sources_with_failures: 0,
insights_credential: null,
pending_deletion: false, pending_deletion: false,
}, },
]; ];

View File

@@ -1,7 +1,7 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { Formik, useField, useFormikContext } from 'formik'; import { Formik, useField, useFormikContext } from 'formik';
import { t } from '@lingui/macro'; import { t } from '@lingui/macro';
import { func, number, shape } from 'prop-types'; import { func, shape } from 'prop-types';
import { Form } from '@patternfly/react-core'; import { Form } from '@patternfly/react-core';
import { VariablesField } from '../../../components/CodeEditor'; import { VariablesField } from '../../../components/CodeEditor';
import FormField, { FormSubmitError } from '../../../components/FormField'; import FormField, { FormSubmitError } from '../../../components/FormField';
@@ -9,13 +9,12 @@ import FormActionGroup from '../../../components/FormActionGroup';
import { required } from '../../../util/validators'; import { required } from '../../../util/validators';
import InstanceGroupsLookup from '../../../components/Lookup/InstanceGroupsLookup'; import InstanceGroupsLookup from '../../../components/Lookup/InstanceGroupsLookup';
import OrganizationLookup from '../../../components/Lookup/OrganizationLookup'; import OrganizationLookup from '../../../components/Lookup/OrganizationLookup';
import CredentialLookup from '../../../components/Lookup/CredentialLookup';
import { import {
FormColumnLayout, FormColumnLayout,
FormFullWidthLayout, FormFullWidthLayout,
} from '../../../components/FormLayout'; } from '../../../components/FormLayout';
function InventoryFormFields({ credentialTypeId, inventory }) { function InventoryFormFields({ inventory }) {
const { setFieldValue, setFieldTouched } = useFormikContext(); const { setFieldValue, setFieldTouched } = useFormikContext();
const [organizationField, organizationMeta, organizationHelpers] = useField( const [organizationField, organizationMeta, organizationHelpers] = useField(
'organization' 'organization'
@@ -23,9 +22,6 @@ function InventoryFormFields({ credentialTypeId, inventory }) {
const [instanceGroupsField, , instanceGroupsHelpers] = useField( const [instanceGroupsField, , instanceGroupsHelpers] = useField(
'instanceGroups' 'instanceGroups'
); );
const [insightsCredentialField, insightsCredentialMeta] = useField(
'insights_credential'
);
const handleOrganizationUpdate = useCallback( const handleOrganizationUpdate = useCallback(
value => { value => {
setFieldValue('organization', value); setFieldValue('organization', value);
@@ -34,14 +30,6 @@ function InventoryFormFields({ credentialTypeId, inventory }) {
[setFieldValue, setFieldTouched] [setFieldValue, setFieldTouched]
); );
const handleCredentialUpdate = useCallback(
value => {
setFieldValue('insights_credential', value);
setFieldTouched('insights_credential', true, false);
},
[setFieldValue, setFieldTouched]
);
return ( return (
<> <>
<FormField <FormField
@@ -70,17 +58,6 @@ function InventoryFormFields({ credentialTypeId, inventory }) {
autoPopulate={!inventory?.id} autoPopulate={!inventory?.id}
validate={required(t`Select a value for this field`)} validate={required(t`Select a value for this field`)}
/> />
<CredentialLookup
helperTextInvalid={insightsCredentialMeta.error}
isValid={
!insightsCredentialMeta.touched || !insightsCredentialMeta.error
}
label={t`Insights Credential`}
credentialTypeId={credentialTypeId}
onChange={handleCredentialUpdate}
value={insightsCredentialField.value}
fieldName="insights_credential"
/>
<InstanceGroupsLookup <InstanceGroupsLookup
value={instanceGroupsField.value} value={instanceGroupsField.value}
onChange={value => { onChange={value => {
@@ -116,10 +93,6 @@ function InventoryForm({
(inventory.summary_fields && inventory.summary_fields.organization) || (inventory.summary_fields && inventory.summary_fields.organization) ||
null, null,
instanceGroups: instanceGroups || [], instanceGroups: instanceGroups || [],
insights_credential:
(inventory.summary_fields &&
inventory.summary_fields.insights_credential) ||
null,
}; };
return ( return (
@@ -150,7 +123,6 @@ InventoryForm.propType = {
handleCancel: func.isRequired, handleCancel: func.isRequired,
instanceGroups: shape(), instanceGroups: shape(),
inventory: shape(), inventory: shape(),
credentialTypeId: number.isRequired,
submitError: shape(), submitError: shape(),
}; };

View File

@@ -25,10 +25,6 @@ const inventory = {
copy: true, copy: true,
adhoc: true, adhoc: true,
}, },
insights_credential: {
id: 1,
name: 'Foo',
},
}, },
created: '2019-10-04T16:56:48.025455Z', created: '2019-10-04T16:56:48.025455Z',
modified: '2019-10-04T16:56:48.025468Z', modified: '2019-10-04T16:56:48.025468Z',
@@ -46,7 +42,6 @@ const inventory = {
has_inventory_sources: false, has_inventory_sources: false,
total_inventory_sources: 0, total_inventory_sources: 0,
inventory_sources_with_failures: 0, inventory_sources_with_failures: 0,
insights_credential: null,
pending_deletion: false, pending_deletion: false,
}; };
@@ -89,9 +84,6 @@ describe('<InventoryForm />', () => {
expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Description"]').length).toBe(1);
expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Organization"]').length).toBe(1);
expect(wrapper.find('FormGroup[label="Instance Groups"]').length).toBe(1); expect(wrapper.find('FormGroup[label="Instance Groups"]').length).toBe(1);
expect(wrapper.find('FormGroup[label="Insights Credential"]').length).toBe(
1
);
expect(wrapper.find('VariablesField[label="Variables"]').length).toBe(1); expect(wrapper.find('VariablesField[label="Variables"]').length).toBe(1);
expect(wrapper.find('CodeEditor').prop('value')).toEqual('---'); expect(wrapper.find('CodeEditor').prop('value')).toEqual('---');
}); });
@@ -107,12 +99,6 @@ describe('<InventoryForm />', () => {
wrapper.find('input#inventory-name').simulate('change', { wrapper.find('input#inventory-name').simulate('change', {
target: { value: 'new Foo', name: 'name' }, target: { value: 'new Foo', name: 'name' },
}); });
wrapper.find('CredentialLookup').invoke('onBlur')();
wrapper.find('CredentialLookup').invoke('onChange')({
id: 10,
name: 'credential',
});
}); });
wrapper.update(); wrapper.update();
expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({ expect(wrapper.find('OrganizationLookup').prop('value')).toEqual({
@@ -122,10 +108,6 @@ describe('<InventoryForm />', () => {
expect(wrapper.find('input#inventory-name').prop('value')).toEqual( expect(wrapper.find('input#inventory-name').prop('value')).toEqual(
'new Foo' 'new Foo'
); );
expect(wrapper.find('CredentialLookup').prop('value')).toEqual({
id: 10,
name: 'credential',
});
}); });
test('should call handleCancel when Cancel button is clicked', async () => { test('should call handleCancel when Cancel button is clicked', async () => {

View File

@@ -91,6 +91,5 @@
"has_inventory_sources": false, "has_inventory_sources": false,
"total_inventory_sources": 0, "total_inventory_sources": 0,
"inventory_sources_with_failures": 0, "inventory_sources_with_failures": 0,
"insights_credential": null,
"pending_deletion": false "pending_deletion": false
} }

View File

@@ -90,6 +90,5 @@
"has_inventory_sources": false, "has_inventory_sources": false,
"total_inventory_sources": 0, "total_inventory_sources": 0,
"inventory_sources_with_failures": 0, "inventory_sources_with_failures": 0,
"insights_credential": null,
"pending_deletion": false "pending_deletion": false
} }

View File

@@ -33,9 +33,6 @@ describe('delete details', () => {
getRelatedResourceDeleteCounts( getRelatedResourceDeleteCounts(
relatedResourceDeleteRequests.credential({ id: 1 }) relatedResourceDeleteRequests.credential({ id: 1 })
); );
expect(InventoriesAPI.read).toBeCalledWith({
insights_credential: 1,
});
expect(InventorySourcesAPI.read).toBeCalledWith({ expect(InventorySourcesAPI.read).toBeCalledWith({
credentials__id: 1, credentials__id: 1,
}); });

View File

@@ -58,13 +58,6 @@ export const relatedResourceDeleteRequests = {
request: () => ProjectsAPI.read({ credentials: selected.id }), request: () => ProjectsAPI.read({ credentials: selected.id }),
label: t`Projects`, label: t`Projects`,
}, },
{
request: () =>
InventoriesAPI.read({
insights_credential: selected.id,
}),
label: t`Inventories`,
},
{ {
request: () => request: () =>
InventorySourcesAPI.read({ InventorySourcesAPI.read({

View File

@@ -57,10 +57,6 @@ options:
description: description:
- The host_filter field. Only useful when C(kind=smart). - The host_filter field. Only useful when C(kind=smart).
type: str type: str
insights_credential:
description:
- Credentials to be used by hosts belonging to this inventory when accessing Red Hat Insights API.
type: str
instance_groups: instance_groups:
description: description:
- list of Instance Groups for this Organization to run on. - list of Instance Groups for this Organization to run on.
@@ -110,7 +106,6 @@ def main():
kind=dict(choices=['', 'smart'], default=''), kind=dict(choices=['', 'smart'], default=''),
host_filter=dict(), host_filter=dict(),
instance_groups=dict(type="list", elements='str'), instance_groups=dict(type="list", elements='str'),
insights_credential=dict(),
state=dict(choices=['present', 'absent'], default='present'), state=dict(choices=['present', 'absent'], default='present'),
) )
@@ -126,7 +121,6 @@ def main():
state = module.params.get('state') state = module.params.get('state')
kind = module.params.get('kind') kind = module.params.get('kind')
host_filter = module.params.get('host_filter') host_filter = module.params.get('host_filter')
insights_credential = module.params.get('insights_credential')
# Attempt to look up the related items the user specified (these will fail the module if not found) # Attempt to look up the related items the user specified (these will fail the module if not found)
org_id = module.resolve_name_to_id('organizations', organization) org_id = module.resolve_name_to_id('organizations', organization)
@@ -161,8 +155,6 @@ def main():
inventory_fields['description'] = description inventory_fields['description'] = description
if variables is not None: if variables is not None:
inventory_fields['variables'] = json.dumps(variables) inventory_fields['variables'] = json.dumps(variables)
if insights_credential is not None:
inventory_fields['insights_credential'] = module.resolve_name_to_id('credentials', insights_credential)
association_fields = {} association_fields = {}

View File

@@ -5,11 +5,10 @@ __metaclass__ = type
import pytest import pytest
from awx.main.models import Inventory, Credential from awx.main.models import Inventory, Credential
from awx.main.tests.functional.conftest import insights_credential, credentialtype_insights
@pytest.mark.django_db @pytest.mark.django_db
def test_inventory_create(run_module, admin_user, organization, insights_credential): def test_inventory_create(run_module, admin_user, organization):
# Create an insights credential # Create an insights credential
result = run_module( result = run_module(
@@ -18,7 +17,6 @@ def test_inventory_create(run_module, admin_user, organization, insights_credent
'name': 'foo-inventory', 'name': 'foo-inventory',
'organization': organization.name, 'organization': organization.name,
'variables': {'foo': 'bar', 'another-foo': {'barz': 'bar2'}}, 'variables': {'foo': 'bar', 'another-foo': {'barz': 'bar2'}},
'insights_credential': insights_credential.name,
'state': 'present', 'state': 'present',
}, },
admin_user, admin_user,
@@ -27,7 +25,6 @@ def test_inventory_create(run_module, admin_user, organization, insights_credent
inv = Inventory.objects.get(name='foo-inventory') inv = Inventory.objects.get(name='foo-inventory')
assert inv.variables == '{"foo": "bar", "another-foo": {"barz": "bar2"}}' assert inv.variables == '{"foo": "bar", "another-foo": {"barz": "bar2"}}'
assert inv.insights_credential.name == insights_credential.name
result.pop('module_args', None) result.pop('module_args', None)
result.pop('invocation', None) result.pop('invocation', None)

View File

@@ -41,7 +41,6 @@
inventory: inventory:
name: "{{ inv_name1 }}" name: "{{ inv_name1 }}"
organization: Default organization: Default
insights_credential: "{{ result.id }}"
instance_groups: instance_groups:
- "{{ group_name1 }}" - "{{ group_name1 }}"
state: present state: present
@@ -55,7 +54,6 @@
inventory: inventory:
name: "{{ result.id }}" name: "{{ result.id }}"
organization: Default organization: Default
insights_credential: "{{ cred_name1 }}"
state: present state: present
register: result register: result

View File

@@ -59,14 +59,12 @@ class Inventory(HasCopy, HasCreate, HasInstanceGroups, HasVariables, base.Base):
organization=organization.id, organization=organization.id,
) )
optional_fields = ('host_filter', 'insights_credential', 'kind', 'variables') optional_fields = ('host_filter', 'kind', 'variables')
update_payload(payload, optional_fields, kwargs) update_payload(payload, optional_fields, kwargs)
if 'variables' in payload and isinstance(payload.variables, dict): if 'variables' in payload and isinstance(payload.variables, dict):
payload.variables = json.dumps(payload.variables) payload.variables = json.dumps(payload.variables)
if 'insights_credential' in payload and isinstance(payload.insights_credential, Credential):
payload.insights_credential = payload.insights_credential.id
return payload return payload

View File

@@ -45,7 +45,7 @@ def pk_or_name(v2, model_name, value, page=None):
identity = UNIQUENESS_RULES[model_name][-1] identity = UNIQUENESS_RULES[model_name][-1]
# certain related fields follow a pattern of <foo>_<model> e.g., # certain related fields follow a pattern of <foo>_<model> e.g.,
# insights_credential, target_credential etc... # target_credential etc...
if not page and '_' in model_name: if not page and '_' in model_name:
return pk_or_name(v2, model_name.split('_')[-1], value, page) return pk_or_name(v2, model_name.split('_')[-1], value, page)