mirror of
https://github.com/ansible/awx.git
synced 2026-03-04 10:11:05 -03:30
Add a new extra_credentials endpoint for Jobs and JobTemplates
additionally, add backwards compatible support for `cloud_credential` and `network_credential` in /api/v1/job_templates/ and /api/v1/jobs/. see: #5807
This commit is contained in:
@@ -86,6 +86,7 @@ SUMMARIZABLE_FK_FIELDS = {
|
|||||||
'scm_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
'scm_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
||||||
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
|
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
|
||||||
'credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
|
'credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
|
||||||
|
'vault_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'),
|
||||||
'job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'elapsed'),
|
'job': DEFAULT_SUMMARY_FIELDS + ('status', 'failed', 'elapsed'),
|
||||||
'job_template': DEFAULT_SUMMARY_FIELDS,
|
'job_template': DEFAULT_SUMMARY_FIELDS,
|
||||||
'workflow_job_template': DEFAULT_SUMMARY_FIELDS,
|
'workflow_job_template': DEFAULT_SUMMARY_FIELDS,
|
||||||
@@ -2090,14 +2091,42 @@ class LabelsListMixin(object):
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: remove when API v1 is removed
|
||||||
|
@six.add_metaclass(BaseSerializerMetaclass)
|
||||||
|
class V1JobOptionsSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Credential
|
||||||
|
fields = ('*', 'cloud_credential', 'network_credential')
|
||||||
|
|
||||||
|
V1_FIELDS = {
|
||||||
|
'cloud_credential': models.PositiveIntegerField(blank=True, null=True, default=None),
|
||||||
|
'network_credential': models.PositiveIntegerField(blank=True, null=True, default=None)
|
||||||
|
}
|
||||||
|
|
||||||
|
def build_field(self, field_name, info, model_class, nested_depth):
|
||||||
|
if field_name in self.V1_FIELDS:
|
||||||
|
return self.build_standard_field(field_name,
|
||||||
|
self.V1_FIELDS[field_name])
|
||||||
|
return super(V1JobOptionsSerializer, self).build_field(field_name, info, model_class, nested_depth)
|
||||||
|
|
||||||
|
|
||||||
class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
fields = ('*', 'job_type', 'inventory', 'project', 'playbook',
|
fields = ('*', 'job_type', 'inventory', 'project', 'playbook',
|
||||||
'credential', 'forks', 'limit',
|
'credential', 'vault_credential', 'forks', 'limit',
|
||||||
'verbosity', 'extra_vars', 'job_tags', 'force_handlers',
|
'verbosity', 'extra_vars', 'job_tags', 'force_handlers',
|
||||||
'skip_tags', 'start_at_task', 'timeout', 'store_facts',)
|
'skip_tags', 'start_at_task', 'timeout', 'store_facts',)
|
||||||
|
|
||||||
|
def get_fields(self):
|
||||||
|
fields = super(JobOptionsSerializer, self).get_fields()
|
||||||
|
|
||||||
|
# TODO: remove when API v1 is removed
|
||||||
|
if self.version == 1:
|
||||||
|
fields.update(V1JobOptionsSerializer().get_fields())
|
||||||
|
return fields
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(JobOptionsSerializer, self).get_related(obj)
|
res = super(JobOptionsSerializer, self).get_related(obj)
|
||||||
res['labels'] = self.reverse('api:job_template_label_list', kwargs={'pk': obj.pk})
|
res['labels'] = self.reverse('api:job_template_label_list', kwargs={'pk': obj.pk})
|
||||||
@@ -2107,7 +2136,19 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
|||||||
res['project'] = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk})
|
res['project'] = self.reverse('api:project_detail', kwargs={'pk': obj.project.pk})
|
||||||
if obj.credential:
|
if obj.credential:
|
||||||
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential.pk})
|
res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential.pk})
|
||||||
# TODO: add related links for `extra_credentials`
|
if obj.vault_credential:
|
||||||
|
res['vault_credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.vault_credential.pk})
|
||||||
|
if self.version > 1:
|
||||||
|
view = 'api:%s_extra_credentials_list' % camelcase_to_underscore(obj.__class__.__name__)
|
||||||
|
res['extra_credentials'] = self.reverse(view, kwargs={'pk': obj.pk})
|
||||||
|
else:
|
||||||
|
cloud_cred = obj.cloud_credential
|
||||||
|
if cloud_cred:
|
||||||
|
res['cloud_credential'] = self.reverse('api:credential_detail', kwargs={'pk': cloud_cred})
|
||||||
|
net_cred = obj.network_credential
|
||||||
|
if net_cred:
|
||||||
|
res['cloud_credential'] = self.reverse('api:credential_detail', kwargs={'pk': net_cred})
|
||||||
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def to_representation(self, obj):
|
def to_representation(self, obj):
|
||||||
@@ -2122,9 +2163,38 @@ class JobOptionsSerializer(LabelsListMixin, BaseSerializer):
|
|||||||
ret['playbook'] = ''
|
ret['playbook'] = ''
|
||||||
if 'credential' in ret and not obj.credential:
|
if 'credential' in ret and not obj.credential:
|
||||||
ret['credential'] = None
|
ret['credential'] = None
|
||||||
|
if 'vault_credential' in ret and not obj.vault_credential:
|
||||||
|
ret['vault_credential'] = None
|
||||||
|
if self.version == 1:
|
||||||
|
ret['cloud_credential'] = obj.cloud_credential
|
||||||
|
ret['network_credential'] = obj.network_credential
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def validate(self, attrs):
|
def validate(self, attrs):
|
||||||
|
if self.version == 1: # TODO: remove in 3.3
|
||||||
|
if 'cloud_credential' in attrs:
|
||||||
|
pk = attrs.pop('cloud_credential')
|
||||||
|
for cred in self.instance.cloud_credentials:
|
||||||
|
self.instance.extra_credentials.remove(cred)
|
||||||
|
if pk:
|
||||||
|
cred = Credential.objects.get(pk=pk)
|
||||||
|
if cred.credential_type.kind != 'cloud':
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'cloud_credential': _('You must provide a cloud credential.'),
|
||||||
|
})
|
||||||
|
self.instance.extra_credentials.add(cred)
|
||||||
|
if 'network_credential' in attrs:
|
||||||
|
pk = attrs.pop('network_credential')
|
||||||
|
for cred in self.instance.network_credentials:
|
||||||
|
self.instance.extra_credentials.remove(cred)
|
||||||
|
if pk:
|
||||||
|
cred = Credential.objects.get(pk=pk)
|
||||||
|
if cred.credential_type.kind != 'net':
|
||||||
|
raise serializers.ValidationError({
|
||||||
|
'network_credential': _('You must provide a network credential.'),
|
||||||
|
})
|
||||||
|
self.instance.extra_credentials.add(cred)
|
||||||
|
|
||||||
if 'project' in self.fields and 'playbook' in self.fields:
|
if 'project' in self.fields and 'playbook' in self.fields:
|
||||||
project = attrs.get('project', self.instance and self.instance.project or None)
|
project = attrs.get('project', self.instance and self.instance.project or None)
|
||||||
playbook = attrs.get('playbook', self.instance and self.instance.playbook or '')
|
playbook = attrs.get('playbook', self.instance and self.instance.playbook or '')
|
||||||
|
|||||||
@@ -385,7 +385,9 @@ v1_urls = patterns('awx.api.views',
|
|||||||
v2_urls = patterns('awx.api.views',
|
v2_urls = patterns('awx.api.views',
|
||||||
url(r'^$', 'api_v2_root_view'),
|
url(r'^$', 'api_v2_root_view'),
|
||||||
url(r'^credential_types/', include(credential_type_urls)),
|
url(r'^credential_types/', include(credential_type_urls)),
|
||||||
url(r'^hosts/(?P<pk>[0-9]+)/ansible_facts/$', 'host_ansible_facts_detail'),
|
url(r'^hosts/(?P<pk>[0-9]+)/ansible_facts/$', 'host_ansible_facts_detail'),
|
||||||
|
url(r'^jobs/(?P<pk>[0-9]+)/extra_credentials/$', 'job_extra_credentials_list'),
|
||||||
|
url(r'^job_templates/(?P<pk>[0-9]+)/extra_credentials/$', 'job_template_extra_credentials_list'),
|
||||||
)
|
)
|
||||||
|
|
||||||
urlpatterns = patterns('awx.api.views',
|
urlpatterns = patterns('awx.api.views',
|
||||||
|
|||||||
@@ -2659,6 +2659,21 @@ class JobTemplateNotificationTemplatesSuccessList(SubListCreateAttachDetachAPIVi
|
|||||||
new_in_300 = True
|
new_in_300 = True
|
||||||
|
|
||||||
|
|
||||||
|
class JobTemplateExtraCredentialsList(SubListCreateAttachDetachAPIView):
|
||||||
|
|
||||||
|
model = Credential
|
||||||
|
serializer_class = CredentialSerializer
|
||||||
|
parent_model = JobTemplate
|
||||||
|
relationship = 'extra_credentials'
|
||||||
|
new_in_320 = True
|
||||||
|
new_in_api_v2 = True
|
||||||
|
|
||||||
|
def is_valid_relation(self, parent, sub, created=False):
|
||||||
|
if sub.credential_type.kind not in ('net', 'cloud'):
|
||||||
|
return {'error': _('Extra credentials must be network or cloud.')}
|
||||||
|
return super(JobTemplateExtraCredentialsList, self).is_valid_relation(parent, sub, created)
|
||||||
|
|
||||||
|
|
||||||
class JobTemplateLabelList(DeleteLastUnattachLabelMixin, SubListCreateAttachDetachAPIView):
|
class JobTemplateLabelList(DeleteLastUnattachLabelMixin, SubListCreateAttachDetachAPIView):
|
||||||
|
|
||||||
model = Label
|
model = Label
|
||||||
@@ -3420,6 +3435,21 @@ class JobDetail(RetrieveUpdateDestroyAPIView):
|
|||||||
return super(JobDetail, self).destroy(request, *args, **kwargs)
|
return super(JobDetail, self).destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class JobExtraCredentialsList(SubListCreateAttachDetachAPIView):
|
||||||
|
|
||||||
|
model = Credential
|
||||||
|
serializer_class = CredentialSerializer
|
||||||
|
parent_model = Job
|
||||||
|
relationship = 'extra_credentials'
|
||||||
|
new_in_320 = True
|
||||||
|
new_in_api_v2 = True
|
||||||
|
|
||||||
|
def is_valid_relation(self, parent, sub, created=False):
|
||||||
|
if sub.credential_type.kind not in ('net', 'cloud'):
|
||||||
|
return {'error': _('Extra credentials must be network or cloud.')}
|
||||||
|
return super(JobExtraCredentialsList, self).is_valid_relation(parent, sub, created)
|
||||||
|
|
||||||
|
|
||||||
class JobLabelList(SubListAPIView):
|
class JobLabelList(SubListAPIView):
|
||||||
|
|
||||||
model = Label
|
model = Label
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ from django.core.exceptions import ValidationError
|
|||||||
|
|
||||||
# AWX
|
# AWX
|
||||||
from awx.api.versioning import reverse
|
from awx.api.versioning import reverse
|
||||||
from awx.main.constants import CLOUD_PROVIDERS
|
|
||||||
from awx.main.models.base import * # noqa
|
from awx.main.models.base import * # noqa
|
||||||
from awx.main.models.unified_jobs import * # noqa
|
from awx.main.models.unified_jobs import * # noqa
|
||||||
from awx.main.models.notifications import (
|
from awx.main.models.notifications import (
|
||||||
@@ -197,6 +196,22 @@ class JobOptions(BaseModel):
|
|||||||
def cloud_credentials(self):
|
def cloud_credentials(self):
|
||||||
return [cred for cred in self.extra_credentials.all() if cred.credential_type.kind == 'cloud']
|
return [cred for cred in self.extra_credentials.all() if cred.credential_type.kind == 'cloud']
|
||||||
|
|
||||||
|
# TODO: remove when API v1 is removed
|
||||||
|
@property
|
||||||
|
def cloud_credential(self):
|
||||||
|
try:
|
||||||
|
return self.cloud_credentials[-1].pk
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# TODO: remove when API v1 is removed
|
||||||
|
@property
|
||||||
|
def network_credential(self):
|
||||||
|
try:
|
||||||
|
return self.network_credentials[-1].pk
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def passwords_needed_to_start(self):
|
def passwords_needed_to_start(self):
|
||||||
'''Return list of password field names needed to start the job.'''
|
'''Return list of password field names needed to start the job.'''
|
||||||
|
|||||||
@@ -153,9 +153,6 @@ def mk_job_template(name, job_type='run',
|
|||||||
if jt.credential is None:
|
if jt.credential is None:
|
||||||
jt.ask_credential_on_launch = True
|
jt.ask_credential_on_launch = True
|
||||||
|
|
||||||
jt.network_credential = network_credential
|
|
||||||
jt.cloud_credential = cloud_credential
|
|
||||||
|
|
||||||
jt.project = project
|
jt.project = project
|
||||||
|
|
||||||
jt.survey_spec = spec
|
jt.survey_spec = spec
|
||||||
@@ -164,6 +161,13 @@ def mk_job_template(name, job_type='run',
|
|||||||
|
|
||||||
if persisted:
|
if persisted:
|
||||||
jt.save()
|
jt.save()
|
||||||
|
if cloud_credential:
|
||||||
|
cloud_credential.save()
|
||||||
|
jt.extra_credentials.add(cloud_credential)
|
||||||
|
if network_credential:
|
||||||
|
network_credential.save()
|
||||||
|
jt.extra_credentials.add(network_credential)
|
||||||
|
jt.save()
|
||||||
return jt
|
return jt
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
88
awx/main/tests/functional/api/test_job.py
Normal file
88
awx/main/tests/functional/api/test_job.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from awx.api.versioning import reverse
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: test this with RBAC and lower-priveleged users
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_extra_credential_creation(get, post, organization_factory, job_template_factory, credentialtype_aws):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
job = jt.create_unified_job()
|
||||||
|
|
||||||
|
url = reverse('api:job_extra_credentials_list', kwargs={'version': 'v2', 'pk': job.pk})
|
||||||
|
response = post(url, {
|
||||||
|
'name': 'My Cred',
|
||||||
|
'credential_type': credentialtype_aws.pk,
|
||||||
|
'inputs': {
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
}
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: test this with RBAC and lower-priveleged users
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_attach_extra_credential(get, post, organization_factory, job_template_factory, credential):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
job = jt.create_unified_job()
|
||||||
|
|
||||||
|
url = reverse('api:job_extra_credentials_list', kwargs={'version': 'v2', 'pk': job.pk})
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 0
|
||||||
|
|
||||||
|
response = post(url, {
|
||||||
|
'associate': True,
|
||||||
|
'id': credential.id,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: test this with RBAC and lower-priveleged users
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_detach_extra_credential(get, post, organization_factory, job_template_factory, credential):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
jt.extra_credentials.add(credential)
|
||||||
|
jt.save()
|
||||||
|
job = jt.create_unified_job()
|
||||||
|
|
||||||
|
url = reverse('api:job_extra_credentials_list', kwargs={'version': 'v2', 'pk': job.pk})
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 1
|
||||||
|
|
||||||
|
response = post(url, {
|
||||||
|
'disassociate': True,
|
||||||
|
'id': credential.id,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_attach_extra_credential_wrong_kind_xfail(get, post, organization_factory, job_template_factory, machine_credential):
|
||||||
|
"""Extra credentials only allow net + cloud credentials"""
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
job = jt.create_unified_job()
|
||||||
|
|
||||||
|
url = reverse('api:job_extra_credentials_list', kwargs={'version': 'v2', 'pk': job.pk})
|
||||||
|
response = post(url, {
|
||||||
|
'associate': True,
|
||||||
|
'id': machine_credential.id,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 400
|
||||||
@@ -36,6 +36,133 @@ def test_create(post, project, machine_credential, inventory, alice, grant_proje
|
|||||||
}, alice, expect=expect)
|
}, alice, expect=expect)
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: test this with RBAC and lower-priveleged users
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_extra_credential_creation(get, post, organization_factory, job_template_factory, credentialtype_aws):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
|
||||||
|
url = reverse('api:job_template_extra_credentials_list', kwargs={'version': 'v2', 'pk': jt.pk})
|
||||||
|
response = post(url, {
|
||||||
|
'name': 'My Cred',
|
||||||
|
'credential_type': credentialtype_aws.pk,
|
||||||
|
'inputs': {
|
||||||
|
'username': 'bob',
|
||||||
|
'password': 'secret',
|
||||||
|
}
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: test this with RBAC and lower-priveleged users
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_attach_extra_credential(get, post, organization_factory, job_template_factory, credential):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
|
||||||
|
url = reverse('api:job_template_extra_credentials_list', kwargs={'version': 'v2', 'pk': jt.pk})
|
||||||
|
response = post(url, {
|
||||||
|
'associate': True,
|
||||||
|
'id': credential.id,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 1
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: test this with RBAC and lower-priveleged users
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_detach_extra_credential(get, post, organization_factory, job_template_factory, credential):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
jt.extra_credentials.add(credential)
|
||||||
|
jt.save()
|
||||||
|
|
||||||
|
url = reverse('api:job_template_extra_credentials_list', kwargs={'version': 'v2', 'pk': jt.pk})
|
||||||
|
response = post(url, {
|
||||||
|
'disassociate': True,
|
||||||
|
'id': credential.id,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 204
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_attach_extra_credential_wrong_kind_xfail(get, post, organization_factory, job_template_factory, machine_credential):
|
||||||
|
"""Extra credentials only allow net + cloud credentials"""
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
|
||||||
|
url = reverse('api:job_template_extra_credentials_list', kwargs={'version': 'v2', 'pk': jt.pk})
|
||||||
|
response = post(url, {
|
||||||
|
'associate': True,
|
||||||
|
'id': machine_credential.id,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 400
|
||||||
|
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('count') == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_v1_extra_credentials_detail(get, organization_factory, job_template_factory, credential, net_credential):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
jt.extra_credentials.add(credential)
|
||||||
|
jt.extra_credentials.add(net_credential)
|
||||||
|
jt.save()
|
||||||
|
|
||||||
|
url = reverse('api:job_template_detail', kwargs={'version': 'v1', 'pk': jt.pk})
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.data.get('cloud_credential') == credential.pk
|
||||||
|
assert response.data.get('network_credential') == net_credential.pk
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_v1_set_extra_credentials(get, patch, organization_factory, job_template_factory, credential, net_credential):
|
||||||
|
objs = organization_factory("org", superusers=['admin'])
|
||||||
|
jt = job_template_factory("jt", organization=objs.organization,
|
||||||
|
inventory='test_inv', project='test_proj').job_template
|
||||||
|
jt.save()
|
||||||
|
|
||||||
|
url = reverse('api:job_template_detail', kwargs={'version': 'v1', 'pk': jt.pk})
|
||||||
|
response = patch(url, {
|
||||||
|
'cloud_credential': credential.pk,
|
||||||
|
'network_credential': net_credential.pk
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
url = reverse('api:job_template_detail', kwargs={'version': 'v1', 'pk': jt.pk})
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data.get('cloud_credential') == credential.pk
|
||||||
|
assert response.data.get('network_credential') == net_credential.pk
|
||||||
|
|
||||||
|
url = reverse('api:job_template_detail', kwargs={'version': 'v1', 'pk': jt.pk})
|
||||||
|
response = patch(url, {
|
||||||
|
'cloud_credential': None,
|
||||||
|
'network_credential': None,
|
||||||
|
}, objs.superusers.admin)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
url = reverse('api:job_template_detail', kwargs={'version': 'v1', 'pk': jt.pk})
|
||||||
|
response = get(url, user=objs.superusers.admin)
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.data.get('cloud_credential') is None
|
||||||
|
assert response.data.get('network_credential') is None
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"grant_project, grant_credential, grant_inventory, expect", [
|
"grant_project, grant_credential, grant_inventory, expect", [
|
||||||
|
|||||||
@@ -215,6 +215,12 @@ def credential(credentialtype_aws):
|
|||||||
inputs={'username': 'something', 'password': 'secret'})
|
inputs={'username': 'something', 'password': 'secret'})
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def net_credential(credentialtype_net):
|
||||||
|
return Credential.objects.create(credential_type=credentialtype_net, name='test-cred',
|
||||||
|
inputs={'username': 'something', 'password': 'secret'})
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def machine_credential(credentialtype_ssh):
|
def machine_credential(credentialtype_ssh):
|
||||||
return Credential.objects.create(credential_type=credentialtype_ssh, name='machine-cred',
|
return Credential.objects.create(credential_type=credentialtype_ssh, name='machine-cred',
|
||||||
|
|||||||
Reference in New Issue
Block a user