Feature: saved launchtime configurations

Consolidate prompts accept/reject logic in unified models
Break out accept/reject logic for variables
Surface new promptable fields on WFJT nodes, schedules

Make schedules and workflows accurately reject variables
  that are not allowed by the prompting
  rules or the survey rules on the template

Validate against unallowed extra_data in system job schedules
Prevent schedule or WFJT node POST/PATCH with unprompted data
Move system job days validation to new mechanism
Add new psuedo-field for WFJT node credential
Add validation for node related credentials
Add related config model to unified job
Use JobLaunchConfig model for launch RBAC check

Support credential overwrite behavior with multi-creds
  change modern manual launch to use merge behavior
Refactor JobLaunchSerializer, self.instance=None
Modularize job launch view to create "modern" data
Auto-create config object with every job
Add create schedule endpoint for jobs
This commit is contained in:
AlanCoding
2017-11-01 12:08:50 -04:00
parent cd8a4b4669
commit 34a8e0a9b6
49 changed files with 2343 additions and 884 deletions

View File

@@ -0,0 +1,188 @@
import pytest
from awx.api.versioning import reverse
from awx.main.models.jobs import JobTemplate
from awx.main.models.workflow import WorkflowJobTemplateNode
from awx.main.models.credential import Credential
@pytest.fixture
def job_template(inventory, project):
# need related resources set for these tests
return JobTemplate.objects.create(
name='test-job_template',
inventory=inventory,
project=project
)
@pytest.fixture
def node(workflow_job_template, post, admin_user, job_template):
return WorkflowJobTemplateNode.objects.create(
workflow_job_template=workflow_job_template,
unified_job_template=job_template
)
@pytest.mark.django_db
def test_blank_UJT_unallowed(workflow_job_template, post, admin_user):
url = reverse('api:workflow_job_template_workflow_nodes_list',
kwargs={'pk': workflow_job_template.pk})
r = post(url, {}, user=admin_user, expect=400)
assert 'unified_job_template' in r.data
@pytest.mark.django_db
def test_cannot_remove_UJT(node, patch, admin_user):
r = patch(
node.get_absolute_url(),
data={'unified_job_template': None},
user=admin_user,
expect=400
)
assert 'unified_job_template' in r.data
@pytest.mark.django_db
def test_node_rejects_unprompted_fields(inventory, project, workflow_job_template, post, admin_user):
job_template = JobTemplate.objects.create(
inventory = inventory,
project = project,
playbook = 'helloworld.yml',
ask_limit_on_launch = False
)
url = reverse('api:workflow_job_template_workflow_nodes_list',
kwargs={'pk': workflow_job_template.pk, 'version': 'v1'})
r = post(url, {'unified_job_template': job_template.pk, 'limit': 'webservers'},
user=admin_user, expect=400)
assert 'limit' in r.data
assert 'not configured to prompt on launch' in r.data['limit'][0]
@pytest.mark.django_db
def test_node_accepts_prompted_fields(inventory, project, workflow_job_template, post, admin_user):
job_template = JobTemplate.objects.create(
inventory = inventory,
project = project,
playbook = 'helloworld.yml',
ask_limit_on_launch = True
)
url = reverse('api:workflow_job_template_workflow_nodes_list',
kwargs={'pk': workflow_job_template.pk, 'version': 'v1'})
post(url, {'unified_job_template': job_template.pk, 'limit': 'webservers'},
user=admin_user, expect=201)
@pytest.mark.django_db
class TestNodeCredentials:
'''
The supported way to provide credentials on launch is through a list
under the "credentials" key - WFJT nodes have a many-to-many relationship
corresponding to this, and it must follow rules consistent with other prompts
'''
def test_not_allows_non_job_models(self, post, admin_user, workflow_job_template,
project, machine_credential):
node = WorkflowJobTemplateNode.objects.create(
workflow_job_template=workflow_job_template,
unified_job_template=project
)
r = post(
reverse(
'api:workflow_job_template_node_credentials_list',
kwargs = {'pk': node.pk}
),
data = {'id': machine_credential.pk},
user = admin_user,
expect = 400
)
assert 'cannot accept credentials on launch' in str(r.data['msg'])
@pytest.mark.django_db
class TestOldCredentialField:
'''
The field `credential` on JTs & WFJT nodes is deprecated, but still supported
TODO: remove tests when JT vault_credential / credential / other stuff
is removed
'''
def test_credential_accepted_create(self, workflow_job_template, post, admin_user,
job_template, machine_credential):
r = post(
reverse(
'api:workflow_job_template_workflow_nodes_list',
kwargs = {'pk': workflow_job_template.pk}
),
data = {'credential': machine_credential.pk, 'unified_job_template': job_template.pk},
user = admin_user,
expect = 201
)
assert r.data['credential'] == machine_credential.pk
node = WorkflowJobTemplateNode.objects.get(pk=r.data['id'])
assert list(node.credentials.all()) == [machine_credential]
@pytest.mark.parametrize('role,code', [
['use_role', 201],
['read_role', 403]
])
def test_credential_rbac(self, role, code, workflow_job_template, post, rando,
job_template, machine_credential):
role_obj = getattr(machine_credential, role)
role_obj.members.add(rando)
job_template.execute_role.members.add(rando)
workflow_job_template.admin_role.members.add(rando)
post(
reverse(
'api:workflow_job_template_workflow_nodes_list',
kwargs = {'pk': workflow_job_template.pk}
),
data = {'credential': machine_credential.pk, 'unified_job_template': job_template.pk},
user = rando,
expect = code
)
def test_credential_add_remove(self, node, patch, machine_credential, admin_user):
node.unified_job_template.ask_credential_on_launch = True
node.unified_job_template.save()
url = node.get_absolute_url()
patch(
url,
data = {'credential': machine_credential.pk},
user = admin_user,
expect = 200
)
node.refresh_from_db()
assert node.credential == machine_credential.pk
patch(
url,
data = {'credential': None},
user = admin_user,
expect = 200
)
node.refresh_from_db()
assert list(node.credentials.values_list('pk', flat=True)) == []
def test_credential_replace(self, node, patch, credentialtype_ssh, admin_user):
node.unified_job_template.ask_credential_on_launch = True
node.unified_job_template.save()
cred1 = Credential.objects.create(
credential_type=credentialtype_ssh,
name='machine-cred1',
inputs={'username': 'test_user', 'password': 'pas4word'})
cred2 = Credential.objects.create(
credential_type=credentialtype_ssh,
name='machine-cred2',
inputs={'username': 'test_user', 'password': 'pas4word'})
node.credentials.add(cred1)
assert node.credential == cred1.pk
url = node.get_absolute_url()
patch(
url,
data = {'credential': cred2.pk},
user = admin_user,
expect = 200
)
assert node.credential == cred2.pk