From e10030b73d7e0c901bb485dd1d7e1d314f0bd849 Mon Sep 17 00:00:00 2001 From: Elijah DeLee Date: Mon, 29 Nov 2021 14:30:19 -0500 Subject: [PATCH] Allow setting default execution group pod spec This will allow us to control the default container group created via settings, meaning we could set this in the operator and the default container group would get created with it applied. We need this for https://github.com/ansible/awx-operator/issues/242 Deepmerge the default podspec and the override With out this, providing the `spec` for the podspec would override everything contained, which ends up including the container used, which is not desired Also, use the same deepmerge function def, as the code seems to be copypasted from the utils --- .../management/commands/register_queue.py | 7 ++++++- awx/main/managers.py | 4 +++- awx/main/scheduler/kubernetes.py | 21 ++----------------- awx/main/tasks.py | 6 +++++- awx/settings/defaults.py | 2 ++ 5 files changed, 18 insertions(+), 22 deletions(-) diff --git a/awx/main/management/commands/register_queue.py b/awx/main/management/commands/register_queue.py index 2fa931c88b..8f4c564279 100644 --- a/awx/main/management/commands/register_queue.py +++ b/awx/main/management/commands/register_queue.py @@ -17,13 +17,14 @@ class InstanceNotFound(Exception): class RegisterQueue: - def __init__(self, queuename, instance_percent, inst_min, hostname_list, is_container_group=None): + def __init__(self, queuename, instance_percent, inst_min, hostname_list, is_container_group=None, pod_spec_override=None): self.instance_not_found_err = None self.queuename = queuename self.instance_percent = instance_percent self.instance_min = inst_min self.hostname_list = hostname_list self.is_container_group = is_container_group + self.pod_spec_override = pod_spec_override def get_create_update_instance_group(self): created = False @@ -40,6 +41,10 @@ class RegisterQueue: ig.is_container_group = self.is_container_group changed = True + if self.pod_spec_override and (ig.pod_spec_override != self.pod_spec_override): + ig.pod_spec_override = self.pod_spec_override + changed = True + if changed: ig.save() diff --git a/awx/main/managers.py b/awx/main/managers.py index 7b9164ef32..2091c36562 100644 --- a/awx/main/managers.py +++ b/awx/main/managers.py @@ -179,7 +179,9 @@ class InstanceManager(models.Manager): else: registered = self.register(ip_address=pod_ip, uuid=settings.SYSTEM_UUID) RegisterQueue(settings.DEFAULT_CONTROL_PLANE_QUEUE_NAME, 100, 0, [], is_container_group=False).register() - RegisterQueue(settings.DEFAULT_EXECUTION_QUEUE_NAME, 100, 0, [], is_container_group=True).register() + RegisterQueue( + settings.DEFAULT_EXECUTION_QUEUE_NAME, 100, 0, [], is_container_group=True, pod_spec_override=settings.DEFAULT_EXECUTION_QUEUE_POD_SPEC_OVERRIDE + ).register() return registered else: return (False, self.me()) diff --git a/awx/main/scheduler/kubernetes.py b/awx/main/scheduler/kubernetes.py index 17b098a77b..6e36226df5 100644 --- a/awx/main/scheduler/kubernetes.py +++ b/awx/main/scheduler/kubernetes.py @@ -9,29 +9,12 @@ from kubernetes import client, config from django.utils.functional import cached_property from django.utils.translation import ugettext_lazy as _ -from awx.main.utils.common import parse_yaml_or_json +from awx.main.utils.common import parse_yaml_or_json, deepmerge from awx.main.utils.execution_environments import get_default_pod_spec logger = logging.getLogger('awx.main.scheduler') -def deepmerge(a, b): - """ - Merge dict structures and return the result. - - >>> a = {'first': {'all_rows': {'pass': 'dog', 'number': '1'}}} - >>> b = {'first': {'all_rows': {'fail': 'cat', 'number': '5'}}} - >>> import pprint; pprint.pprint(deepmerge(a, b)) - {'first': {'all_rows': {'fail': 'cat', 'number': '5', 'pass': 'dog'}}} - """ - if isinstance(a, dict) and isinstance(b, dict): - return dict([(k, deepmerge(a.get(k), b.get(k))) for k in set(a.keys()).union(b.keys())]) - elif b is None: - return a - else: - return b - - class PodManager(object): def __init__(self, task=None): self.task = task @@ -183,7 +166,7 @@ class PodManager(object): pod_spec_override = {} if self.task and self.task.instance_group.pod_spec_override: pod_spec_override = parse_yaml_or_json(self.task.instance_group.pod_spec_override) - pod_spec = {**default_pod_spec, **pod_spec_override} + pod_spec = deepmerge(default_pod_spec, pod_spec_override) if self.task: pod_spec['metadata'] = deepmerge( diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 9b155123de..38855ae191 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -3286,7 +3286,11 @@ class AWXReceptorJob: pod_spec_override = {} if self.task and self.task.instance.instance_group.pod_spec_override: pod_spec_override = parse_yaml_or_json(self.task.instance.instance_group.pod_spec_override) - pod_spec = {**default_pod_spec, **pod_spec_override} + # According to the deepmerge docstring, the second dictionary will override when + # they share keys, which is the desired behavior. + # This allows user to only provide elements they want to override, and for us to still provide any + # defaults they don't want to change + pod_spec = deepmerge(default_pod_spec, pod_spec_override) pod_spec['spec']['containers'][0]['image'] = ee.image pod_spec['spec']['containers'][0]['args'] = ['ansible-runner', 'worker', '--private-data-dir=/runner'] diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index 49ba50efff..5fbc0dfd32 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -986,5 +986,7 @@ DJANGO_GUID = {'GUID_HEADER_NAME': 'X-API-Request-Id'} # Name of the default task queue DEFAULT_EXECUTION_QUEUE_NAME = 'default' +# pod spec used when the default execution queue is a container group, e.g. when deploying on k8s/ocp with the operator +DEFAULT_EXECUTION_QUEUE_POD_SPEC_OVERRIDE = '' # Name of the default controlplane queue DEFAULT_CONTROL_PLANE_QUEUE_NAME = 'controlplane'