mirror of
https://github.com/ansible/awx.git
synced 2026-05-13 04:17:36 -02:30
Add in more model changes around execution environments
- a new unique name 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
This commit is contained in:
committed by
Shane McDonald
parent
c0faa39b53
commit
c1133b3f6d
@@ -107,8 +107,8 @@ SUMMARIZABLE_FK_FIELDS = {
|
|||||||
'insights_credential_id',),
|
'insights_credential_id',),
|
||||||
'host': DEFAULT_SUMMARY_FIELDS,
|
'host': DEFAULT_SUMMARY_FIELDS,
|
||||||
'group': DEFAULT_SUMMARY_FIELDS,
|
'group': DEFAULT_SUMMARY_FIELDS,
|
||||||
'default_environment': ('id', 'organization_id', 'image', 'description'),
|
'default_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
|
||||||
'execution_environment': ('id', 'organization_id', 'image', 'description'),
|
'execution_environment': DEFAULT_SUMMARY_FIELDS + ('image',),
|
||||||
'project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
'project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
||||||
'source_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
'source_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'),
|
||||||
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
|
'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',),
|
||||||
@@ -1365,7 +1365,7 @@ class ExecutionEnvironmentSerializer(BaseSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ExecutionEnvironment
|
model = ExecutionEnvironment
|
||||||
fields = ('*', '-name', 'organization', 'image', 'managed_by_tower', 'credential')
|
fields = ('*', 'organization', 'image', 'managed_by_tower', 'credential')
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(ExecutionEnvironmentSerializer, self).get_related(obj)
|
res = super(ExecutionEnvironmentSerializer, self).get_related(obj)
|
||||||
@@ -1395,7 +1395,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Project
|
model = Project
|
||||||
fields = ('*', 'organization', 'scm_update_on_launch',
|
fields = ('*', 'organization', 'scm_update_on_launch',
|
||||||
'scm_update_cache_timeout', 'allow_override', 'custom_virtualenv',) + \
|
'scm_update_cache_timeout', 'allow_override', 'custom_virtualenv', 'default_environment') + \
|
||||||
('last_update_failed', 'last_updated') # Backwards compatibility
|
('last_update_failed', 'last_updated') # Backwards compatibility
|
||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
@@ -1420,6 +1420,9 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer):
|
|||||||
if obj.organization:
|
if obj.organization:
|
||||||
res['organization'] = self.reverse('api:organization_detail',
|
res['organization'] = self.reverse('api:organization_detail',
|
||||||
kwargs={'pk': obj.organization.pk})
|
kwargs={'pk': obj.organization.pk})
|
||||||
|
if obj.default_environment:
|
||||||
|
res['default_environment'] = self.reverse('api:execution_environment_detail',
|
||||||
|
kwargs={'pk': obj.default_environment_id})
|
||||||
# Backwards compatibility.
|
# Backwards compatibility.
|
||||||
if obj.current_update:
|
if obj.current_update:
|
||||||
res['current_update'] = self.reverse('api:project_update_detail',
|
res['current_update'] = self.reverse('api:project_update_detail',
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from rest_framework.fields import ( # noqa
|
|||||||
BooleanField, CharField, ChoiceField, DictField, DateTimeField, EmailField,
|
BooleanField, CharField, ChoiceField, DictField, DateTimeField, EmailField,
|
||||||
IntegerField, ListField, NullBooleanField
|
IntegerField, ListField, NullBooleanField
|
||||||
)
|
)
|
||||||
|
from rest_framework.serializers import PrimaryKeyRelatedField
|
||||||
|
|
||||||
logger = logging.getLogger('awx.conf.fields')
|
logger = logging.getLogger('awx.conf.fields')
|
||||||
|
|
||||||
|
|||||||
@@ -1320,6 +1320,8 @@ class ExecutionEnvironmentAccess(BaseAccess):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model = ExecutionEnvironment
|
model = ExecutionEnvironment
|
||||||
|
select_related = ('organization',)
|
||||||
|
prefetch_related = ('organization__admin_role',)
|
||||||
|
|
||||||
def filtered_queryset(self):
|
def filtered_queryset(self):
|
||||||
return ExecutionEnvironment.objects.filter(
|
return ExecutionEnvironment.objects.filter(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from rest_framework.fields import FloatField
|
|||||||
|
|
||||||
# Tower
|
# Tower
|
||||||
from awx.conf import fields, register, register_validate
|
from awx.conf import fields, register, register_validate
|
||||||
|
from awx.main.models import ExecutionEnvironment
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger('awx.main.conf')
|
logger = logging.getLogger('awx.main.conf')
|
||||||
@@ -176,6 +177,18 @@ register(
|
|||||||
read_only=True,
|
read_only=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
register(
|
||||||
|
'DEFAULT_EXECUTION_ENVIRONMENT',
|
||||||
|
field_class=fields.PrimaryKeyRelatedField,
|
||||||
|
allow_null=True,
|
||||||
|
default=None,
|
||||||
|
queryset=ExecutionEnvironment.objects.all(),
|
||||||
|
label=_('Global default execution environment'),
|
||||||
|
help_text=_('.'),
|
||||||
|
category=_('System'),
|
||||||
|
category_slug='system',
|
||||||
|
)
|
||||||
|
|
||||||
register(
|
register(
|
||||||
'CUSTOM_VENV_PATHS',
|
'CUSTOM_VENV_PATHS',
|
||||||
field_class=fields.StringListPathField,
|
field_class=fields.StringListPathField,
|
||||||
|
|||||||
41
awx/main/migrations/0125_more_ee_modeling_changes.py
Normal file
41
awx/main/migrations/0125_more_ee_modeling_changes.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Generated by Django 2.2.16 on 2020-11-19 16:20
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
import awx.main.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('main', '0124_execution_environments'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='executionenvironment',
|
||||||
|
options={'ordering': ('-created',)},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='executionenvironment',
|
||||||
|
name='name',
|
||||||
|
field=models.CharField(default=uuid.uuid4, max_length=512, unique=True),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='organization',
|
||||||
|
name='execution_environment_admin_role',
|
||||||
|
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'),
|
||||||
|
preserve_default='True',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='project',
|
||||||
|
name='default_environment',
|
||||||
|
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'),
|
||||||
|
),
|
||||||
|
migrations.AlterUniqueTogether(
|
||||||
|
name='executionenvironment',
|
||||||
|
unique_together=set(),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1133,7 +1133,6 @@ ManagedCredentialType(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
ManagedCredentialType(
|
ManagedCredentialType(
|
||||||
namespace='kubernetes_bearer_token',
|
namespace='kubernetes_bearer_token',
|
||||||
kind='kubernetes',
|
kind='kubernetes',
|
||||||
@@ -1165,6 +1164,37 @@ ManagedCredentialType(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
ManagedCredentialType(
|
||||||
|
namespace='registry',
|
||||||
|
kind='registry',
|
||||||
|
name=ugettext_noop('Container Registry'),
|
||||||
|
inputs={
|
||||||
|
'fields': [{
|
||||||
|
'id': 'host',
|
||||||
|
'label': ugettext_noop('Authentication URL'),
|
||||||
|
'type': 'string',
|
||||||
|
'help_text': ugettext_noop('Authentication endpoint for the container registry.'),
|
||||||
|
}, {
|
||||||
|
'id': 'username',
|
||||||
|
'label': ugettext_noop('Username'),
|
||||||
|
'type': 'string',
|
||||||
|
}, {
|
||||||
|
'id': 'password',
|
||||||
|
'label': ugettext_noop('Password'),
|
||||||
|
'type': 'string',
|
||||||
|
'secret': True,
|
||||||
|
}, {
|
||||||
|
'id': 'token',
|
||||||
|
'label': ugettext_noop('Access Token'),
|
||||||
|
'type': 'string',
|
||||||
|
'secret': True,
|
||||||
|
'help_text': ugettext_noop('A token to use to authenticate with. '
|
||||||
|
'This should not be set if username/password are being used.'),
|
||||||
|
}],
|
||||||
|
'required': ['host'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
ManagedCredentialType(
|
ManagedCredentialType(
|
||||||
namespace='galaxy_api_token',
|
namespace='galaxy_api_token',
|
||||||
|
|||||||
@@ -2,16 +2,15 @@ from django.db import models
|
|||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from awx.api.versioning import reverse
|
from awx.api.versioning import reverse
|
||||||
from awx.main.models.base import PrimordialModel
|
from awx.main.models.base import CommonModel
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['ExecutionEnvironment']
|
__all__ = ['ExecutionEnvironment']
|
||||||
|
|
||||||
|
|
||||||
class ExecutionEnvironment(PrimordialModel):
|
class ExecutionEnvironment(CommonModel):
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('organization', 'image')
|
ordering = ('-created',)
|
||||||
ordering = (models.F('organization_id').asc(nulls_first=True), 'image')
|
|
||||||
|
|
||||||
organization = models.ForeignKey(
|
organization = models.ForeignKey(
|
||||||
'Organization',
|
'Organization',
|
||||||
|
|||||||
@@ -95,6 +95,9 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin, CustomVi
|
|||||||
job_template_admin_role = ImplicitRoleField(
|
job_template_admin_role = ImplicitRoleField(
|
||||||
parent_role='admin_role',
|
parent_role='admin_role',
|
||||||
)
|
)
|
||||||
|
execution_environment_admin_role = ImplicitRoleField(
|
||||||
|
parent_role='admin_role',
|
||||||
|
)
|
||||||
auditor_role = ImplicitRoleField(
|
auditor_role = ImplicitRoleField(
|
||||||
parent_role='singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
parent_role='singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -259,6 +259,15 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin, CustomVirtualEn
|
|||||||
app_label = 'main'
|
app_label = 'main'
|
||||||
ordering = ('id',)
|
ordering = ('id',)
|
||||||
|
|
||||||
|
default_environment = models.ForeignKey(
|
||||||
|
'ExecutionEnvironment',
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
default=None,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
related_name='+',
|
||||||
|
help_text=_('The default execution environment for jobs run using this project.'),
|
||||||
|
)
|
||||||
scm_update_on_launch = models.BooleanField(
|
scm_update_on_launch = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
help_text=_('Update the project when a job is launched that uses the project.'),
|
help_text=_('Update the project when a job is launched that uses the project.'),
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ role_names = {
|
|||||||
'inventory_admin_role': _('Inventory Admin'),
|
'inventory_admin_role': _('Inventory Admin'),
|
||||||
'credential_admin_role': _('Credential Admin'),
|
'credential_admin_role': _('Credential Admin'),
|
||||||
'job_template_admin_role': _('Job Template Admin'),
|
'job_template_admin_role': _('Job Template Admin'),
|
||||||
|
'execution_environment_admin_role': _('Execution Environment Admin'),
|
||||||
'workflow_admin_role': _('Workflow Admin'),
|
'workflow_admin_role': _('Workflow Admin'),
|
||||||
'notification_admin_role': _('Notification Admin'),
|
'notification_admin_role': _('Notification Admin'),
|
||||||
'auditor_role': _('Auditor'),
|
'auditor_role': _('Auditor'),
|
||||||
@@ -60,6 +61,7 @@ role_descriptions = {
|
|||||||
'inventory_admin_role': _('Can manage all inventories of the %s'),
|
'inventory_admin_role': _('Can manage all inventories of the %s'),
|
||||||
'credential_admin_role': _('Can manage all credentials of the %s'),
|
'credential_admin_role': _('Can manage all credentials of the %s'),
|
||||||
'job_template_admin_role': _('Can manage all job templates of the %s'),
|
'job_template_admin_role': _('Can manage all job templates of the %s'),
|
||||||
|
'execution_environment_admin_role': _('Can manage all execution environments of the %s'),
|
||||||
'workflow_admin_role': _('Can manage all workflows of the %s'),
|
'workflow_admin_role': _('Can manage all workflows of the %s'),
|
||||||
'notification_admin_role': _('Can manage all notifications of the %s'),
|
'notification_admin_role': _('Can manage all notifications of the %s'),
|
||||||
'auditor_role': _('Can view all aspects of the %s'),
|
'auditor_role': _('Can view all aspects of the %s'),
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ from awx.main.dispatch import get_local_queuename
|
|||||||
from awx.main.dispatch.control import Control as ControlDispatcher
|
from awx.main.dispatch.control import Control as ControlDispatcher
|
||||||
from awx.main.registrar import activity_stream_registrar
|
from awx.main.registrar import activity_stream_registrar
|
||||||
from awx.main.models.mixins import ResourceMixin, TaskManagerUnifiedJobMixin, ExecutionEnvironmentMixin
|
from awx.main.models.mixins import ResourceMixin, TaskManagerUnifiedJobMixin, ExecutionEnvironmentMixin
|
||||||
|
from awx.main.models.execution_environments import ExecutionEnvironment
|
||||||
from awx.main.utils import (
|
from awx.main.utils import (
|
||||||
camelcase_to_underscore, get_model_for_type,
|
camelcase_to_underscore, get_model_for_type,
|
||||||
encrypt_dict, decrypt_field, _inventory_updates,
|
encrypt_dict, decrypt_field, _inventory_updates,
|
||||||
@@ -338,6 +339,23 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, ExecutionEn
|
|||||||
from awx.main.models.notifications import NotificationTemplate
|
from awx.main.models.notifications import NotificationTemplate
|
||||||
return NotificationTemplate.objects.none()
|
return NotificationTemplate.objects.none()
|
||||||
|
|
||||||
|
def resolve_execution_environment(self):
|
||||||
|
"""
|
||||||
|
Return the execution environment that should be used when creating a new job.
|
||||||
|
"""
|
||||||
|
if self.execution_environment is not None:
|
||||||
|
return self.execution_environment
|
||||||
|
if getattr(self, 'project_id', None) and self.project.default_environment is not None:
|
||||||
|
return self.project.default_environment
|
||||||
|
if getattr(self, 'organization', None) and self.organization.default_environment is not None:
|
||||||
|
return self.organization.default_environment
|
||||||
|
if getattr(self, 'inventory', None) and self.inventory.organization is not None:
|
||||||
|
if self.inventory.organization.default_environment is not None:
|
||||||
|
return self.inventory.organization.default_environment
|
||||||
|
if settings.DEFAULT_EXECUTION_ENVIRONMENT is not None:
|
||||||
|
return settings.DEFAULT_EXECUTION_ENVIRONMENT
|
||||||
|
return ExecutionEnvironment.objects.filter(organization=None, managed_by_tower=True).first()
|
||||||
|
|
||||||
def create_unified_job(self, **kwargs):
|
def create_unified_job(self, **kwargs):
|
||||||
'''
|
'''
|
||||||
Create a new unified job based on this unified job template.
|
Create a new unified job based on this unified job template.
|
||||||
@@ -376,6 +394,8 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, ExecutionEn
|
|||||||
for fd, val in eager_fields.items():
|
for fd, val in eager_fields.items():
|
||||||
setattr(unified_job, fd, val)
|
setattr(unified_job, fd, val)
|
||||||
|
|
||||||
|
unified_job.execution_environment = self.resolve_execution_environment()
|
||||||
|
|
||||||
# NOTE: slice workflow jobs _get_parent_field_name method
|
# NOTE: slice workflow jobs _get_parent_field_name method
|
||||||
# is not correct until this is set
|
# is not correct until this is set
|
||||||
if not parent_field_name:
|
if not parent_field_name:
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ def test_default_cred_types():
|
|||||||
'kubernetes_bearer_token',
|
'kubernetes_bearer_token',
|
||||||
'net',
|
'net',
|
||||||
'openstack',
|
'openstack',
|
||||||
|
'registry',
|
||||||
'rhv',
|
'rhv',
|
||||||
'satellite6',
|
'satellite6',
|
||||||
'scm',
|
'scm',
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ REMOTE_HOST_HEADERS = ['REMOTE_ADDR', 'REMOTE_HOST']
|
|||||||
PROXY_IP_ALLOWED_LIST = []
|
PROXY_IP_ALLOWED_LIST = []
|
||||||
|
|
||||||
CUSTOM_VENV_PATHS = []
|
CUSTOM_VENV_PATHS = []
|
||||||
|
DEFAULT_EXECUTION_ENVIRONMENT = None
|
||||||
|
|
||||||
# Note: This setting may be overridden by database settings.
|
# Note: This setting may be overridden by database settings.
|
||||||
STDOUT_MAX_BYTES_DISPLAY = 1048576
|
STDOUT_MAX_BYTES_DISPLAY = 1048576
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ log = logging.getLogger(__name__)
|
|||||||
class ExecutionEnvironment(HasCreate, base.Base):
|
class ExecutionEnvironment(HasCreate, base.Base):
|
||||||
|
|
||||||
dependencies = [Organization, Credential]
|
dependencies = [Organization, Credential]
|
||||||
NATURAL_KEY = ('organization', 'image')
|
NATURAL_KEY = ('name',)
|
||||||
|
|
||||||
# fields are image, organization, managed_by_tower, credential
|
# fields are image, organization, managed_by_tower, credential
|
||||||
def create(self, image='quay.io/ansible/ansible-runner:devel', credential=None, **kwargs):
|
def create(self, image='quay.io/ansible/ansible-runner:devel', credential=None, **kwargs):
|
||||||
|
|||||||
Reference in New Issue
Block a user