Skip to content

Commit 703889a

Browse files
jbradberryshanemcd
authored andcommitted
Add in more model changes around execution environments
- a new unique name field to EE - a pull field to EE - a new configure-Tower-in-Tower setting DEFAULT_EXECUTION_ENVIRONMENT - an Org-level execution_environment_admin_role - a default_environment field on Project - a new Container Registry credential type - order EEs by reverse of the created timestamp - a method to resolve which EE to use on jobs
1 parent a1ee5b6 commit 703889a

14 files changed

+141
-10
lines changed

awx/api/serializers.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@
107107
'insights_credential_id',),
108108
'host': DEFAULT_SUMMARY_FIELDS,
109109
'group': DEFAULT_SUMMARY_FIELDS,
110-
'default_environment': ('id', 'organization_id', 'image', 'description'),
111-
'execution_environment': ('id', 'organization_id', 'image', 'description'),
110+
'default_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
111+
'execution_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
112112
'project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
113113
'source_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
114114
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
@@ -1365,7 +1365,7 @@ class ExecutionEnvironmentSerializer(BaseSerializer):
13651365

13661366
class Meta:
13671367
model = ExecutionEnvironment
1368-
fields = ('*', '-name', 'organization', 'image', 'managed_by_tower', 'credential')
1368+
fields = ('*', 'organization', 'image', 'managed_by_tower', 'pull', 'credential')
13691369

13701370
def get_related(self, obj):
13711371
res = super(ExecutionEnvironmentSerializer, self).get_related(obj)
@@ -1395,7 +1395,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
13951395
class Meta:
13961396
model = Project
13971397
fields = ('*', 'organization', 'scm_update_on_launch',
1398-
'scm_update_cache_timeout', 'allow_override', 'custom_virtualenv',) + \
1398+
'scm_update_cache_timeout', 'allow_override', 'custom_virtualenv', 'default_environment') + \
13991399
('last_update_failed', 'last_updated') # Backwards compatibility
14001400

14011401
def get_related(self, obj):
@@ -1420,6 +1420,9 @@ def get_related(self, obj):
14201420
if obj.organization:
14211421
res['organization'] = self.reverse('api:organization_detail',
14221422
kwargs={'pk': obj.organization.pk})
1423+
if obj.default_environment:
1424+
res['default_environment'] = self.reverse('api:execution_environment_detail',
1425+
kwargs={'pk': obj.default_environment_id})
14231426
# Backwards compatibility.
14241427
if obj.current_update:
14251428
res['current_update'] = self.reverse('api:project_update_detail',

awx/conf/fields.py

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
BooleanField, CharField, ChoiceField, DictField, DateTimeField, EmailField,
1515
IntegerField, ListField, NullBooleanField
1616
)
17+
from rest_framework.serializers import PrimaryKeyRelatedField
1718

1819
logger = logging.getLogger('awx.conf.fields')
1920

awx/main/access.py

+2
Original file line numberDiff line numberDiff line change
@@ -1320,6 +1320,8 @@ class ExecutionEnvironmentAccess(BaseAccess):
13201320
"""
13211321

13221322
model = ExecutionEnvironment
1323+
select_related = ('organization',)
1324+
prefetch_related = ('organization__admin_role',)
13231325

13241326
def filtered_queryset(self):
13251327
return ExecutionEnvironment.objects.filter(

awx/main/conf.py

+13
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
# Tower
1212
from awx.conf import fields, register, register_validate
13+
from awx.main.models import ExecutionEnvironment
1314

1415

1516
logger = logging.getLogger('awx.main.conf')
@@ -176,6 +177,18 @@
176177
read_only=True,
177178
)
178179

180+
register(
181+
'DEFAULT_EXECUTION_ENVIRONMENT',
182+
field_class=fields.PrimaryKeyRelatedField,
183+
allow_null=True,
184+
default=None,
185+
queryset=ExecutionEnvironment.objects.all(),
186+
label=_('Global default execution environment'),
187+
help_text=_('.'),
188+
category=_('System'),
189+
category_slug='system',
190+
)
191+
179192
register(
180193
'CUSTOM_VENV_PATHS',
181194
field_class=fields.StringListPathField,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Generated by Django 2.2.16 on 2020-11-19 16:20
2+
import uuid
3+
4+
import awx.main.fields
5+
from django.db import migrations, models
6+
import django.db.models.deletion
7+
8+
9+
class Migration(migrations.Migration):
10+
11+
dependencies = [
12+
('main', '0123_execution_environments'),
13+
]
14+
15+
operations = [
16+
migrations.AlterModelOptions(
17+
name='executionenvironment',
18+
options={'ordering': ('-created',)},
19+
),
20+
migrations.AddField(
21+
model_name='executionenvironment',
22+
name='name',
23+
field=models.CharField(default=uuid.uuid4, max_length=512, unique=True),
24+
preserve_default=False,
25+
),
26+
migrations.AddField(
27+
model_name='executionenvironment',
28+
name='pull',
29+
field=models.BooleanField(default=True),
30+
),
31+
migrations.AddField(
32+
model_name='organization',
33+
name='execution_environment_admin_role',
34+
field=awx.main.fields.ImplicitRoleField(editable=False, null='True', on_delete=django.db.models.deletion.CASCADE, parent_role='admin_role', related_name='+', to='main.Role'),
35+
preserve_default='True',
36+
),
37+
migrations.AddField(
38+
model_name='project',
39+
name='default_environment',
40+
field=models.ForeignKey(blank=True, default=None, help_text='The default execution environment for jobs run using this project.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='main.ExecutionEnvironment'),
41+
),
42+
migrations.AlterUniqueTogether(
43+
name='executionenvironment',
44+
unique_together=set(),
45+
),
46+
]

awx/main/models/credential/__init__.py

+31-1
Original file line numberDiff line numberDiff line change
@@ -1128,7 +1128,6 @@ def create(self):
11281128
},
11291129
)
11301130

1131-
11321131
ManagedCredentialType(
11331132
namespace='kubernetes_bearer_token',
11341133
kind='kubernetes',
@@ -1160,6 +1159,37 @@ def create(self):
11601159
}
11611160
)
11621161

1162+
ManagedCredentialType(
1163+
namespace='registry',
1164+
kind='registry',
1165+
name=ugettext_noop('Container Registry'),
1166+
inputs={
1167+
'fields': [{
1168+
'id': 'host',
1169+
'label': ugettext_noop('Authentication URL'),
1170+
'type': 'string',
1171+
'help_text': ugettext_noop('Authentication endpoint for the container registry.'),
1172+
}, {
1173+
'id': 'username',
1174+
'label': ugettext_noop('Username'),
1175+
'type': 'string',
1176+
}, {
1177+
'id': 'password',
1178+
'label': ugettext_noop('Password'),
1179+
'type': 'string',
1180+
'secret': True,
1181+
}, {
1182+
'id': 'token',
1183+
'label': ugettext_noop('Access Token'),
1184+
'type': 'string',
1185+
'secret': True,
1186+
'help_text': ugettext_noop('A token to use to authenticate with. '
1187+
'This should not be set if username/password are being used.'),
1188+
}],
1189+
'required': ['host'],
1190+
}
1191+
)
1192+
11631193

11641194
ManagedCredentialType(
11651195
namespace='galaxy_api_token',

awx/main/models/execution_environments.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@
22
from django.utils.translation import ugettext_lazy as _
33

44
from awx.api.versioning import reverse
5-
from awx.main.models.base import PrimordialModel
5+
from awx.main.models.base import CommonModel
66

77

88
__all__ = ['ExecutionEnvironment']
99

1010

11-
class ExecutionEnvironment(PrimordialModel):
11+
class ExecutionEnvironment(CommonModel):
1212
class Meta:
13-
unique_together = ('organization', 'image')
14-
ordering = (models.F('organization_id').asc(nulls_first=True), 'image')
13+
ordering = ('-created',)
1514

1615
organization = models.ForeignKey(
1716
'Organization',
@@ -27,6 +26,7 @@ class Meta:
2726
verbose_name=_('image location'),
2827
help_text=_("The registry location where the container is stored."),
2928
)
29+
pull = models.BooleanField(default=True)
3030
managed_by_tower = models.BooleanField(default=False, editable=False)
3131
credential = models.ForeignKey(
3232
'Credential',

awx/main/models/organization.py

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ class Meta:
9595
job_template_admin_role = ImplicitRoleField(
9696
parent_role='admin_role',
9797
)
98+
execution_environment_admin_role = ImplicitRoleField(
99+
parent_role='admin_role',
100+
)
98101
auditor_role = ImplicitRoleField(
99102
parent_role='singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
100103
)

awx/main/models/projects.py

+9
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,15 @@ class Meta:
260260
app_label = 'main'
261261
ordering = ('id',)
262262

263+
default_environment = models.ForeignKey(
264+
'ExecutionEnvironment',
265+
null=True,
266+
blank=True,
267+
default=None,
268+
on_delete=models.SET_NULL,
269+
related_name='+',
270+
help_text=_('The default execution environment for jobs run using this project.'),
271+
)
263272
scm_update_on_launch = models.BooleanField(
264273
default=False,
265274
help_text=_('Update the project when a job is launched that uses the project.'),

awx/main/models/rbac.py

+2
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
'inventory_admin_role': _('Inventory Admin'),
4141
'credential_admin_role': _('Credential Admin'),
4242
'job_template_admin_role': _('Job Template Admin'),
43+
'execution_environment_admin_role': _('Execution Environment Admin'),
4344
'workflow_admin_role': _('Workflow Admin'),
4445
'notification_admin_role': _('Notification Admin'),
4546
'auditor_role': _('Auditor'),
@@ -60,6 +61,7 @@
6061
'inventory_admin_role': _('Can manage all inventories of the %s'),
6162
'credential_admin_role': _('Can manage all credentials of the %s'),
6263
'job_template_admin_role': _('Can manage all job templates of the %s'),
64+
'execution_environment_admin_role': _('Can manage all execution environments of the %s'),
6365
'workflow_admin_role': _('Can manage all workflows of the %s'),
6466
'notification_admin_role': _('Can manage all notifications of the %s'),
6567
'auditor_role': _('Can view all aspects of the %s'),

awx/main/models/unified_jobs.py

+20
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from awx.main.dispatch.control import Control as ControlDispatcher
4141
from awx.main.registrar import activity_stream_registrar
4242
from awx.main.models.mixins import ResourceMixin, TaskManagerUnifiedJobMixin, ExecutionEnvironmentMixin
43+
from awx.main.models.execution_environments import ExecutionEnvironment
4344
from awx.main.utils import (
4445
camelcase_to_underscore, get_model_for_type,
4546
encrypt_dict, decrypt_field, _inventory_updates,
@@ -338,6 +339,23 @@ def notification_templates(self):
338339
from awx.main.models.notifications import NotificationTemplate
339340
return NotificationTemplate.objects.none()
340341

342+
def resolve_execution_environment(self):
343+
"""
344+
Return the execution environment that should be used when creating a new job.
345+
"""
346+
if self.execution_environment is not None:
347+
return self.execution_environment
348+
if getattr(self, 'project_id', None) and self.project.default_environment is not None:
349+
return self.project.default_environment
350+
if getattr(self, 'organization', None) and self.organization.default_environment is not None:
351+
return self.organization.default_environment
352+
if getattr(self, 'inventory', None) and self.inventory.organization is not None:
353+
if self.inventory.organization.default_environment is not None:
354+
return self.inventory.organization.default_environment
355+
if settings.DEFAULT_EXECUTION_ENVIRONMENT is not None:
356+
return settings.DEFAULT_EXECUTION_ENVIRONMENT
357+
return ExecutionEnvironment.objects.filter(organization=None, managed_by_tower=True).first()
358+
341359
def create_unified_job(self, **kwargs):
342360
'''
343361
Create a new unified job based on this unified job template.
@@ -376,6 +394,8 @@ def create_unified_job(self, **kwargs):
376394
for fd, val in eager_fields.items():
377395
setattr(unified_job, fd, val)
378396

397+
unified_job.execution_environment = self.resolve_execution_environment()
398+
379399
# NOTE: slice workflow jobs _get_parent_field_name method
380400
# is not correct until this is set
381401
if not parent_field_name:

awx/main/tests/functional/test_credential.py

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def test_default_cred_types():
9090
'kubernetes_bearer_token',
9191
'net',
9292
'openstack',
93+
'registry',
9394
'rhv',
9495
'satellite6',
9596
'scm',

awx/settings/defaults.py

+1
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ def IS_TESTING(argv=None):
172172
PROXY_IP_ALLOWED_LIST = []
173173

174174
CUSTOM_VENV_PATHS = []
175+
DEFAULT_EXECUTION_ENVIRONMENT = None
175176

176177
# Note: This setting may be overridden by database settings.
177178
STDOUT_MAX_BYTES_DISPLAY = 1048576

awxkit/awxkit/api/pages/execution_environments.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
class ExecutionEnvironment(HasCreate, base.Base):
1919

2020
dependencies = [Organization, Credential]
21-
NATURAL_KEY = ('organization', 'image')
21+
NATURAL_KEY = ('name',)
2222

2323
# fields are image, organization, managed_by_tower, credential
2424
def create(self, image='quay.io/ansible/ansible-runner:devel', credential=None, **kwargs):

0 commit comments

Comments
 (0)