mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 01:47:35 -02:30
AAP-59874: Update to Python 3.12 (#16208)
* update to Python 3.12 * remove use of utcnow * switch to timezone.utc datetime.UTC is an alias of datetime.timezone.utc. if we're doing the double import for datetime it's more straightforward to just import timezone as well and get it directly * debug python env version issue * change python version * pin to SHA and remove debug portion
This commit is contained in:
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@@ -183,14 +183,19 @@ jobs:
|
|||||||
path: awx-operator
|
path: awx-operator
|
||||||
|
|
||||||
- name: Setup python, referencing action at awx relative path
|
- name: Setup python, referencing action at awx relative path
|
||||||
uses: ./awx/.github/actions/setup-python
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
|
||||||
with:
|
with:
|
||||||
python-version: '3.13'
|
python-version: '3.12'
|
||||||
|
|
||||||
- name: Install playbook dependencies
|
- name: Install playbook dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install docker
|
python -m pip install docker
|
||||||
|
|
||||||
|
- name: Check Python version
|
||||||
|
working-directory: awx
|
||||||
|
run: |
|
||||||
|
make print-PYTHON
|
||||||
|
|
||||||
- name: Build AWX image
|
- name: Build AWX image
|
||||||
working-directory: awx
|
working-directory: awx
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ build:
|
|||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
tools:
|
tools:
|
||||||
python: >-
|
python: >-
|
||||||
3.11
|
3.12
|
||||||
commands:
|
commands:
|
||||||
- pip install --user tox
|
- pip install --user tox
|
||||||
- python3 -m tox -e docs --notest -v
|
- python3 -m tox -e docs --notest -v
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -1,6 +1,6 @@
|
|||||||
-include awx/ui/Makefile
|
-include awx/ui/Makefile
|
||||||
|
|
||||||
PYTHON := $(notdir $(shell for i in python3.11 python3; do command -v $$i; done|sed 1q))
|
PYTHON := $(notdir $(shell for i in python3.12 python3; do command -v $$i; done|sed 1q))
|
||||||
SHELL := bash
|
SHELL := bash
|
||||||
DOCKER_COMPOSE ?= docker compose
|
DOCKER_COMPOSE ?= docker compose
|
||||||
OFFICIAL ?= no
|
OFFICIAL ?= no
|
||||||
@@ -79,7 +79,7 @@ RECEPTOR_IMAGE ?= quay.io/ansible/receptor:devel
|
|||||||
SRC_ONLY_PKGS ?= cffi,pycparser,psycopg,twilio
|
SRC_ONLY_PKGS ?= cffi,pycparser,psycopg,twilio
|
||||||
# These should be upgraded in the AWX and Ansible venv before attempting
|
# These should be upgraded in the AWX and Ansible venv before attempting
|
||||||
# to install the actual requirements
|
# to install the actual requirements
|
||||||
VENV_BOOTSTRAP ?= pip==21.2.4 setuptools==80.9.0 setuptools_scm[toml]==8.0.4 wheel==0.42.0 cython==3.1.3
|
VENV_BOOTSTRAP ?= pip==25.3 setuptools==80.9.0 setuptools_scm[toml]==9.2.2 wheel==0.45.1 cython==3.1.3
|
||||||
|
|
||||||
NAME ?= awx
|
NAME ?= awx
|
||||||
|
|
||||||
|
|||||||
@@ -198,8 +198,8 @@ def generate_receptor_tls(instance_obj):
|
|||||||
.issuer_name(ca_cert.issuer)
|
.issuer_name(ca_cert.issuer)
|
||||||
.public_key(csr.public_key())
|
.public_key(csr.public_key())
|
||||||
.serial_number(x509.random_serial_number())
|
.serial_number(x509.random_serial_number())
|
||||||
.not_valid_before(datetime.datetime.utcnow())
|
.not_valid_before(datetime.datetime.now(datetime.UTC))
|
||||||
.not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=3650))
|
.not_valid_after(datetime.datetime.now(datetime.UTC) + datetime.timedelta(days=3650))
|
||||||
.add_extension(
|
.add_extension(
|
||||||
csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).value,
|
csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).value,
|
||||||
critical=csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).critical,
|
critical=csr.extensions.get_extension_for_class(x509.SubjectAlternativeName).critical,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@@ -301,7 +301,7 @@ class WorkerPool(object):
|
|||||||
'\n'
|
'\n'
|
||||||
'{% endfor %}'
|
'{% endfor %}'
|
||||||
)
|
)
|
||||||
now = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S UTC')
|
now = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S UTC')
|
||||||
return tmpl.render(pool=self, workers=self.workers, meta=self.debug_meta, dt=now)
|
return tmpl.render(pool=self, workers=self.workers, meta=self.debug_meta, dt=now)
|
||||||
|
|
||||||
def write(self, preferred_queue, body):
|
def write(self, preferred_queue, body):
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
# Copyright (c) 2015 Ansible, Inc.
|
# Copyright (c) 2015 Ansible, Inc.
|
||||||
# All Rights Reserved
|
# All Rights Reserved
|
||||||
import sys
|
import sys
|
||||||
from distutils.util import strtobool
|
|
||||||
from argparse import RawTextHelpFormatter
|
from argparse import RawTextHelpFormatter
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
@@ -9,6 +8,24 @@ from django.conf import settings
|
|||||||
from awx.main.models import CredentialType, Credential, ExecutionEnvironment
|
from awx.main.models import CredentialType, Credential, ExecutionEnvironment
|
||||||
|
|
||||||
|
|
||||||
|
def strtobool(val):
|
||||||
|
"""Convert a string representation of truth to true (1) or false (0).
|
||||||
|
|
||||||
|
True values are 'y', 'yes', 't', 'true', 'on', and '1'.
|
||||||
|
False values are 'n', 'no', 'f', 'false', 'off', and '0'.
|
||||||
|
Raises ValueError if 'val' is anything else.
|
||||||
|
|
||||||
|
This replaces the deprecated distutils.util.strtobool removed in Python 3.12.
|
||||||
|
"""
|
||||||
|
val = val.lower()
|
||||||
|
if val in ('y', 'yes', 't', 'true', 'on', '1'):
|
||||||
|
return 1
|
||||||
|
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise ValueError(f"invalid truth value {val!r}")
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
"""Create default execution environments, intended for new installs"""
|
"""Create default execution environments, intended for new installs"""
|
||||||
|
|
||||||
|
|||||||
@@ -422,7 +422,7 @@ class BaseTask(object):
|
|||||||
except IOError as e:
|
except IOError as e:
|
||||||
if e.errno not in (errno.EAGAIN, errno.EACCES):
|
if e.errno not in (errno.EAGAIN, errno.EACCES):
|
||||||
os.close(self.lock_fd)
|
os.close(self.lock_fd)
|
||||||
logger.error("I/O error({0}) while trying to aquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror))
|
logger.error("I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(e.errno, lock_path, e.strerror))
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
if not emitted_lockfile_log:
|
if not emitted_lockfile_log:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import time
|
|||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import redirect_stdout
|
from contextlib import redirect_stdout
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from distutils.version import LooseVersion as Version
|
from packaging.version import Version
|
||||||
from io import StringIO
|
from io import StringIO
|
||||||
|
|
||||||
# dispatcherd
|
# dispatcherd
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ class TestSwaggerGeneration:
|
|||||||
JSON['info']['version'] = release
|
JSON['info']['version'] = release
|
||||||
|
|
||||||
if not request.config.getoption('--genschema'):
|
if not request.config.getoption('--genschema'):
|
||||||
JSON['modified'] = datetime.datetime.utcnow().isoformat()
|
JSON['modified'] = datetime.datetime.now(datetime.UTC).isoformat()
|
||||||
|
|
||||||
# Make some basic assertions about the rendered JSON so we can
|
# Make some basic assertions about the rendered JSON so we can
|
||||||
# be sure it doesn't break across DRF upgrades and view/serializer
|
# be sure it doesn't break across DRF upgrades and view/serializer
|
||||||
|
|||||||
@@ -139,7 +139,7 @@ def test_send_notifications_job_id(mocker):
|
|||||||
mocker.patch('awx.main.models.UnifiedJob.objects.get')
|
mocker.patch('awx.main.models.UnifiedJob.objects.get')
|
||||||
system.send_notifications([], job_id=1)
|
system.send_notifications([], job_id=1)
|
||||||
assert UnifiedJob.objects.get.called
|
assert UnifiedJob.objects.get.called
|
||||||
assert UnifiedJob.objects.get.called_with(id=1)
|
UnifiedJob.objects.get.assert_called_with(id=1)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('awx.main.models.UnifiedJob.objects.get')
|
@mock.patch('awx.main.models.UnifiedJob.objects.get')
|
||||||
@@ -156,7 +156,7 @@ def test_send_notifications_list(mock_notifications_filter, mock_job_get, mocker
|
|||||||
assert mock_notifications[0].save.called
|
assert mock_notifications[0].save.called
|
||||||
|
|
||||||
assert mock_job.notifications.add.called
|
assert mock_job.notifications.add.called
|
||||||
assert mock_job.notifications.add.called_with(*mock_notifications)
|
mock_job.notifications.add.assert_called_with(*mock_notifications)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
@@ -1508,8 +1508,8 @@ def test_fcntl_ioerror():
|
|||||||
|
|
||||||
|
|
||||||
@mock.patch('os.open')
|
@mock.patch('os.open')
|
||||||
@mock.patch('logging.getLogger')
|
@mock.patch('awx.main.tasks.jobs.logger')
|
||||||
def test_acquire_lock_open_fail_logged(logging_getLogger, os_open, mock_me):
|
def test_acquire_lock_open_fail_logged(logger_mock, os_open, mock_me):
|
||||||
err = OSError()
|
err = OSError()
|
||||||
err.errno = 3
|
err.errno = 3
|
||||||
err.strerror = 'dummy message'
|
err.strerror = 'dummy message'
|
||||||
@@ -1519,21 +1519,18 @@ def test_acquire_lock_open_fail_logged(logging_getLogger, os_open, mock_me):
|
|||||||
|
|
||||||
os_open.side_effect = err
|
os_open.side_effect = err
|
||||||
|
|
||||||
logger = mock.Mock()
|
|
||||||
logging_getLogger.return_value = logger
|
|
||||||
|
|
||||||
ProjectUpdate = jobs.RunProjectUpdate()
|
ProjectUpdate = jobs.RunProjectUpdate()
|
||||||
|
|
||||||
with pytest.raises(OSError):
|
with pytest.raises(OSError):
|
||||||
ProjectUpdate.acquire_lock(instance)
|
ProjectUpdate.acquire_lock(instance)
|
||||||
assert logger.err.called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
|
logger_mock.error.assert_called_with("I/O error({0}) while trying to open lock file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
|
||||||
|
|
||||||
|
|
||||||
@mock.patch('os.open')
|
@mock.patch('os.open')
|
||||||
@mock.patch('os.close')
|
@mock.patch('os.close')
|
||||||
@mock.patch('logging.getLogger')
|
@mock.patch('awx.main.tasks.jobs.logger')
|
||||||
@mock.patch('fcntl.lockf')
|
@mock.patch('fcntl.lockf')
|
||||||
def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logging_getLogger, os_close, os_open, mock_me):
|
def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logger_mock, os_close, os_open, mock_me):
|
||||||
err = IOError()
|
err = IOError()
|
||||||
err.errno = 3
|
err.errno = 3
|
||||||
err.strerror = 'dummy message'
|
err.strerror = 'dummy message'
|
||||||
@@ -1544,16 +1541,15 @@ def test_acquire_lock_acquisition_fail_logged(fcntl_lockf, logging_getLogger, os
|
|||||||
|
|
||||||
os_open.return_value = 3
|
os_open.return_value = 3
|
||||||
|
|
||||||
logger = mock.Mock()
|
|
||||||
logging_getLogger.return_value = logger
|
|
||||||
|
|
||||||
fcntl_lockf.side_effect = err
|
fcntl_lockf.side_effect = err
|
||||||
|
|
||||||
ProjectUpdate = jobs.RunProjectUpdate()
|
ProjectUpdate = jobs.RunProjectUpdate()
|
||||||
with pytest.raises(IOError):
|
with pytest.raises(IOError):
|
||||||
ProjectUpdate.acquire_lock(instance)
|
ProjectUpdate.acquire_lock(instance)
|
||||||
os_close.assert_called_with(3)
|
os_close.assert_called_with(3)
|
||||||
assert logger.err.called_with("I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message'))
|
logger_mock.error.assert_called_with(
|
||||||
|
"I/O error({0}) while trying to acquire lock on file [{1}]: {2}".format(3, 'this_file_does_not_exist', 'dummy message')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('injector_cls', [cls for cls in ManagedCredentialType.registry.values() if cls.injectors])
|
@pytest.mark.parametrize('injector_cls', [cls for cls in ManagedCredentialType.registry.values() if cls.injectors])
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import logging.handlers
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import os
|
import os
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -50,7 +50,7 @@ class RSysLogHandler(logging.handlers.SysLogHandler):
|
|||||||
# because the alternative is blocking the
|
# because the alternative is blocking the
|
||||||
# socket.send() in the Python process, which we definitely don't
|
# socket.send() in the Python process, which we definitely don't
|
||||||
# want to do)
|
# want to do)
|
||||||
dt = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
|
dt = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')
|
||||||
msg = f'{dt} ERROR rsyslogd was unresponsive: '
|
msg = f'{dt} ERROR rsyslogd was unresponsive: '
|
||||||
exc = traceback.format_exc()
|
exc = traceback.format_exc()
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -393,7 +393,7 @@ class Licenser(object):
|
|||||||
end_date = parse(sub['subscriptions']['endDate'])
|
end_date = parse(sub['subscriptions']['endDate'])
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
now = datetime.utcnow()
|
now = datetime.now(timezone.utc)
|
||||||
now = now.replace(tzinfo=end_date.tzinfo)
|
now = now.replace(tzinfo=end_date.tzinfo)
|
||||||
if end_date < now:
|
if end_date < now:
|
||||||
# If the sub has a past end date, skip it
|
# If the sub has a past end date, skip it
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ try:
|
|||||||
from ansible.module_utils.compat.version import LooseVersion as Version
|
from ansible.module_utils.compat.version import LooseVersion as Version
|
||||||
except ImportError:
|
except ImportError:
|
||||||
try:
|
try:
|
||||||
from distutils.version import LooseVersion as Version
|
from packaging.version import Version
|
||||||
except ImportError:
|
except ImportError:
|
||||||
raise AssertionError('To use this plugin or module with ansible-core 2.11, you need to use Python < 3.12 with distutils.version present')
|
raise AssertionError('To use this plugin or module you need to use Python >= 3.12')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import yaml
|
import yaml
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
import json
|
import json
|
||||||
|
|
||||||
from awxkit.utils import poll_until
|
from awxkit.utils import poll_until
|
||||||
@@ -35,10 +35,10 @@ class HasStatus(object):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def wait_until_completed(self, interval=5, timeout=60, **kwargs):
|
def wait_until_completed(self, interval=5, timeout=60, **kwargs):
|
||||||
start_time = datetime.utcnow()
|
start_time = datetime.now(timezone.utc)
|
||||||
HasStatus.wait_until_status(self, self.completed_statuses, interval=interval, timeout=timeout, **kwargs)
|
HasStatus.wait_until_status(self, self.completed_statuses, interval=interval, timeout=timeout, **kwargs)
|
||||||
if not getattr(self, 'event_processing_finished', True):
|
if not getattr(self, 'event_processing_finished', True):
|
||||||
elapsed = datetime.utcnow() - start_time
|
elapsed = datetime.now(timezone.utc) - start_time
|
||||||
time_left = timeout - elapsed.total_seconds()
|
time_left = timeout - elapsed.total_seconds()
|
||||||
poll_until(lambda: getattr(self.get(), 'event_processing_finished', True), interval=interval, timeout=time_left, **kwargs)
|
poll_until(lambda: getattr(self.get(), 'event_processing_finished', True), interval=interval, timeout=time_left, **kwargs)
|
||||||
return self
|
return self
|
||||||
@@ -92,7 +92,7 @@ class HasStatus(object):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
msg += '\nFailed to obtain dependency stdout: {}'.format(e)
|
msg += '\nFailed to obtain dependency stdout: {}'.format(e)
|
||||||
|
|
||||||
msg += '\nTIME WHEN STATUS WAS FOUND: {} (UTC)\n'.format(datetime.utcnow())
|
msg += '\nTIME WHEN STATUS WAS FOUND: {} (UTC)\n'.format(datetime.now(timezone.utc))
|
||||||
|
|
||||||
raise AssertionError(msg)
|
raise AssertionError(msg)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,15 @@
|
|||||||
from distutils.version import LooseVersion
|
from packaging.version import Version
|
||||||
|
|
||||||
|
|
||||||
def version_cmp(x, y):
|
def version_cmp(x, y):
|
||||||
return LooseVersion(x)._cmp(y)
|
"""Compare two version strings.
|
||||||
|
Returns -1 if x < y, 0 if x == y, 1 if x > y
|
||||||
|
"""
|
||||||
|
vx = Version(x)
|
||||||
|
vy = Version(y)
|
||||||
|
if vx < vy:
|
||||||
|
return -1
|
||||||
|
elif vx > vy:
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|||||||
@@ -1,13 +1,29 @@
|
|||||||
import locale
|
import locale
|
||||||
import json
|
import json
|
||||||
from distutils.util import strtobool
|
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from awxkit.cli.utils import colored
|
from awxkit.cli.utils import colored
|
||||||
from awxkit import config
|
from awxkit import config
|
||||||
|
|
||||||
|
|
||||||
|
def strtobool(val):
|
||||||
|
"""Convert a string representation of truth to true (1) or false (0).
|
||||||
|
|
||||||
|
True values are 'y', 'yes', 't', 'true', 'on', and '1'.
|
||||||
|
False values are 'n', 'no', 'f', 'false', 'off', and '0'.
|
||||||
|
Raises ValueError if 'val' is anything else.
|
||||||
|
|
||||||
|
This replaces the deprecated distutils.util.strtobool removed in Python 3.12.
|
||||||
|
"""
|
||||||
|
val = val.lower()
|
||||||
|
if val in ('y', 'yes', 't', 'true', 'on', '1'):
|
||||||
|
return 1
|
||||||
|
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
raise ValueError(f"invalid truth value {val!r}")
|
||||||
|
|
||||||
|
|
||||||
def get_config_credentials():
|
def get_config_credentials():
|
||||||
"""Load username and password from config.credentials.default.
|
"""Load username and password from config.credentials.default.
|
||||||
|
|
||||||
|
|||||||
@@ -6,10 +6,8 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
from distutils.util import strtobool
|
|
||||||
|
|
||||||
from .custom import CustomAction
|
from .custom import CustomAction
|
||||||
from .format import add_output_formatting_arguments
|
from .format import add_output_formatting_arguments, strtobool
|
||||||
from .resource import DEPRECATED_RESOURCES_REVERSE
|
from .resource import DEPRECATED_RESOURCES_REVERSE
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from datetime import datetime, timedelta, tzinfo
|
from datetime import datetime, timedelta, tzinfo, timezone
|
||||||
import inspect
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
@@ -364,7 +364,7 @@ def are_same_endpoint(first, second):
|
|||||||
|
|
||||||
def utcnow():
|
def utcnow():
|
||||||
"""Provide a wrapped copy of the built-in utcnow that can be easily mocked."""
|
"""Provide a wrapped copy of the built-in utcnow that can be easily mocked."""
|
||||||
return datetime.utcnow()
|
return datetime.now(timezone.utc)
|
||||||
|
|
||||||
|
|
||||||
class UTC(tzinfo):
|
class UTC(tzinfo):
|
||||||
|
|||||||
@@ -205,7 +205,7 @@ class WSClient(object):
|
|||||||
message = json.loads(message)
|
message = json.loads(message)
|
||||||
log.debug('received message: {}'.format(message))
|
log.debug('received message: {}'.format(message))
|
||||||
if self._add_received_time:
|
if self._add_received_time:
|
||||||
message['received_time'] = datetime.datetime.utcnow()
|
message['received_time'] = datetime.datetime.now(datetime.UTC)
|
||||||
|
|
||||||
if all([message.get('group_name') == 'jobs', message.get('status') == 'pending', message.get('unified_job_id'), self._should_subscribe_to_pending_job]):
|
if all([message.get('group_name') == 'jobs', message.get('status') == 'pending', message.get('unified_job_id'), self._should_subscribe_to_pending_job]):
|
||||||
if bool(message.get('project_id')) == (self._should_subscribe_to_pending_job['events'] == 'project_update_events'):
|
if bool(message.get('project_id')) == (self._should_subscribe_to_pending_job['events'] == 'project_update_events'):
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ setup(
|
|||||||
'requests',
|
'requests',
|
||||||
'setuptools',
|
'setuptools',
|
||||||
],
|
],
|
||||||
python_requires=">=3.8",
|
python_requires=">=3.12",
|
||||||
extras_require={'formatting': ['jq'], 'websockets': ['websocket-client==0.57.0'], 'crypto': ['cryptography']},
|
extras_require={'formatting': ['jq'], 'websockets': ['websocket-client==0.57.0'], 'crypto': ['cryptography']},
|
||||||
license='Apache 2.0',
|
license='Apache 2.0',
|
||||||
classifiers=[
|
classifiers=[
|
||||||
@@ -104,7 +104,7 @@ setup(
|
|||||||
'Operating System :: MacOS :: MacOS X',
|
'Operating System :: MacOS :: MacOS X',
|
||||||
'Operating System :: POSIX :: Linux',
|
'Operating System :: POSIX :: Linux',
|
||||||
'Programming Language :: Python',
|
'Programming Language :: Python',
|
||||||
'Programming Language :: Python :: 3.8',
|
'Programming Language :: Python :: 3.12',
|
||||||
'Topic :: System :: Software Distribution',
|
'Topic :: System :: Software Distribution',
|
||||||
'Topic :: System :: Systems Administration',
|
'Topic :: System :: Systems Administration',
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from datetime import datetime
|
from datetime import datetime, timezone
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
@@ -379,7 +379,7 @@ class TestUpdatePayload(object):
|
|||||||
|
|
||||||
|
|
||||||
def test_to_ical():
|
def test_to_ical():
|
||||||
now = datetime.utcnow()
|
now = datetime.now(timezone.utc)
|
||||||
ical_datetime = utils.to_ical(now)
|
ical_datetime = utils.to_ical(now)
|
||||||
date = str(now.date()).replace('-', '')
|
date = str(now.date()).replace('-', '')
|
||||||
time = str(now.time()).split('.')[0].replace(':', '')
|
time = str(now.time()).split('.')[0].replace(':', '')
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ skip_missing_interpreters = true
|
|||||||
# skipsdist = true
|
# skipsdist = true
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
basepython = python3.11
|
basepython = python3.12
|
||||||
setenv =
|
setenv =
|
||||||
PYTHONPATH = {toxinidir}:{env:PYTHONPATH:}:.
|
PYTHONPATH = {toxinidir}:{env:PYTHONPATH:}:.
|
||||||
deps =
|
deps =
|
||||||
|
|||||||
@@ -129,14 +129,14 @@ the following dates are saved in the database:
|
|||||||
- `main_schedule.rrule` - the original `RRULE` string provided by the user
|
- `main_schedule.rrule` - the original `RRULE` string provided by the user
|
||||||
- `main_schedule.dtstart` - the _first_ datetime in the list of all occurrences (coerced to UTC)
|
- `main_schedule.dtstart` - the _first_ datetime in the list of all occurrences (coerced to UTC)
|
||||||
- `main_schedule.dtend` - the _last_ datetime in the list of all occurrences (coerced to UTC)
|
- `main_schedule.dtend` - the _last_ datetime in the list of all occurrences (coerced to UTC)
|
||||||
- `main_schedule.next_run` - the _next_ datetime in list after `utcnow()` (coerced to UTC)
|
- `main_schedule.next_run` - the _next_ datetime in list after `now(datetime.UTC)` (coerced to UTC)
|
||||||
|
|
||||||
AWX makes use of [Celery Periodic Tasks
|
AWX makes use of [Celery Periodic Tasks
|
||||||
(celerybeat)](http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html)
|
(celerybeat)](http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html)
|
||||||
to run a periodic task that discovers new jobs that need to run at a regular
|
to run a periodic task that discovers new jobs that need to run at a regular
|
||||||
interval (by default, every 30 seconds). When this task starts, it queries the
|
interval (by default, every 30 seconds). When this task starts, it queries the
|
||||||
database for Schedules where `Schedule.next_run` is between
|
database for Schedules where `Schedule.next_run` is between
|
||||||
`scheduler_last_runtime()` and `utcnow()`. For each of these, a new job is
|
`scheduler_last_runtime()` and `now(datetime.UTC)`. For each of these, a new job is
|
||||||
launched, and `Schedule.next_run` is changed to the next chronological datetime
|
launched, and `Schedule.next_run` is changed to the next chronological datetime
|
||||||
in the list of all occurrences.
|
in the list of all occurrences.
|
||||||
|
|
||||||
|
|||||||
@@ -35,9 +35,6 @@ filterwarnings =
|
|||||||
# FIXME: Delete this entry once `zope` is updated.
|
# FIXME: Delete this entry once `zope` is updated.
|
||||||
once:Deprecated call to `pkg_resources.declare_namespace.'zope'.`.\nImplementing implicit namespace packages .as specified in PEP 420. is preferred to `pkg_resources.declare_namespace`. See https.//setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages:DeprecationWarning:
|
once:Deprecated call to `pkg_resources.declare_namespace.'zope'.`.\nImplementing implicit namespace packages .as specified in PEP 420. is preferred to `pkg_resources.declare_namespace`. See https.//setuptools.pypa.io/en/latest/references/keywords.html#keyword-namespace-packages:DeprecationWarning:
|
||||||
|
|
||||||
# FIXME: Delete this entry once the use of `distutils` is exterminated from the repo.
|
|
||||||
once:The distutils package is deprecated and slated for removal in Python 3.12. Use setuptools or check PEP 632 for potential alternatives:DeprecationWarning:_pytest.assertion.rewrite
|
|
||||||
|
|
||||||
# FIXME: Delete this entry once `coreapi` is deleted from the dependencies
|
# FIXME: Delete this entry once `coreapi` is deleted from the dependencies
|
||||||
# FIXME: and is no longer imported at runtime.
|
# FIXME: and is no longer imported at runtime.
|
||||||
once:CoreAPI compatibility is deprecated and will be removed in DRF 3.17:rest_framework.RemovedInDRF317Warning:rest_framework.schemas.coreapi
|
once:CoreAPI compatibility is deprecated and will be removed in DRF 3.17:rest_framework.RemovedInDRF317Warning:rest_framework.schemas.coreapi
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
aiohttp>=3.9.4 # CVE-2024-30251
|
aiohttp>=3.12.14 # CVE-2024-30251
|
||||||
ansi2html # Used to format the stdout from jobs into html for display
|
ansi2html # Used to format the stdout from jobs into html for display
|
||||||
jq # used for indirect host counting feature
|
jq # used for indirect host counting feature
|
||||||
asciichartpy
|
asciichartpy
|
||||||
@@ -65,7 +65,7 @@ urllib3<2.4.0, >=1.26.19 # CVE-2024-37891. capped by kubernetes 34.1.0 reqs
|
|||||||
uWSGI>=2.0.28
|
uWSGI>=2.0.28
|
||||||
uwsgitop
|
uwsgitop
|
||||||
wheel>=0.38.1 # CVE-2022-40898
|
wheel>=0.38.1 # CVE-2022-40898
|
||||||
pip==21.2.4 # see UPGRADE BLOCKERs
|
pip==25.3 # see UPGRADE BLOCKERs
|
||||||
setuptools==80.9.0 # see UPGRADE BLOCKERs
|
setuptools==80.9.0 # see UPGRADE BLOCKERs
|
||||||
setuptools_scm[toml]
|
setuptools_scm[toml]
|
||||||
setuptools-rust>=0.11.4 # cryptography build dep
|
setuptools-rust>=0.11.4 # cryptography build dep
|
||||||
|
|||||||
@@ -475,7 +475,7 @@ service-identity==24.2.0
|
|||||||
# via twisted
|
# via twisted
|
||||||
setuptools-rust==1.10.2
|
setuptools-rust==1.10.2
|
||||||
# via -r /awx_devel/requirements/requirements.in
|
# via -r /awx_devel/requirements/requirements.in
|
||||||
setuptools-scm[toml]==8.1.0
|
setuptools-scm[toml]==9.2.2
|
||||||
# via -r /awx_devel/requirements/requirements.in
|
# via -r /awx_devel/requirements/requirements.in
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
# via
|
# via
|
||||||
@@ -542,7 +542,7 @@ uwsgitop==0.12
|
|||||||
# via -r /awx_devel/requirements/requirements.in
|
# via -r /awx_devel/requirements/requirements.in
|
||||||
websocket-client==1.8.0
|
websocket-client==1.8.0
|
||||||
# via kubernetes
|
# via kubernetes
|
||||||
wheel==0.42.0
|
wheel==0.45.1
|
||||||
# via -r /awx_devel/requirements/requirements.in
|
# via -r /awx_devel/requirements/requirements.in
|
||||||
wrapt==1.17.3
|
wrapt==1.17.3
|
||||||
# via opentelemetry-instrumentation
|
# via opentelemetry-instrumentation
|
||||||
@@ -556,7 +556,7 @@ zstandard==0.25.0
|
|||||||
# via aiohttp
|
# via aiohttp
|
||||||
|
|
||||||
# The following packages are considered to be unsafe in a requirements file:
|
# The following packages are considered to be unsafe in a requirements file:
|
||||||
pip==21.2.4
|
pip==25.3
|
||||||
# via -r /awx_devel/requirements/requirements.in
|
# via -r /awx_devel/requirements/requirements.in
|
||||||
setuptools==80.9.0
|
setuptools==80.9.0
|
||||||
# via
|
# via
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ _cleanup() {
|
|||||||
generate_requirements() {
|
generate_requirements() {
|
||||||
venv="`pwd`/venv"
|
venv="`pwd`/venv"
|
||||||
echo $venv
|
echo $venv
|
||||||
/usr/bin/python3.11 -m venv "${venv}"
|
/usr/bin/python3.12 -m venv "${venv}"
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source ${venv}/bin/activate
|
source ${venv}/bin/activate
|
||||||
|
|
||||||
|
|||||||
@@ -48,12 +48,12 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \
|
|||||||
patch \
|
patch \
|
||||||
postgresql \
|
postgresql \
|
||||||
postgresql-devel \
|
postgresql-devel \
|
||||||
python3.11 \
|
python3.12 \
|
||||||
"python3.11-devel" \
|
"python3.12-devel" \
|
||||||
"python3.11-pip" \
|
"python3.12-pip" \
|
||||||
"python3.11-setuptools" \
|
"python3.12-setuptools" \
|
||||||
"python3.11-packaging" \
|
"python3.12-packaging" \
|
||||||
"python3.11-psycopg2" \
|
"python3.12-psycopg2" \
|
||||||
swig \
|
swig \
|
||||||
unzip \
|
unzip \
|
||||||
xmlsec1-devel \
|
xmlsec1-devel \
|
||||||
@@ -63,7 +63,7 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \
|
|||||||
RUN mkdir -p ~/.ssh && chmod 0700 ~/.ssh
|
RUN mkdir -p ~/.ssh && chmod 0700 ~/.ssh
|
||||||
RUN ssh-keyscan github.com > ~/.ssh/known_hosts
|
RUN ssh-keyscan github.com > ~/.ssh/known_hosts
|
||||||
|
|
||||||
RUN pip3.11 install -vv build
|
RUN pip3.12 install -vv build
|
||||||
|
|
||||||
{% if image_architecture == 'ppc64le' %}
|
{% if image_architecture == 'ppc64le' %}
|
||||||
RUN dnf -y update && dnf install -y wget && \
|
RUN dnf -y update && dnf install -y wget && \
|
||||||
@@ -133,12 +133,12 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \
|
|||||||
krb5-workstation \
|
krb5-workstation \
|
||||||
nginx \
|
nginx \
|
||||||
postgresql \
|
postgresql \
|
||||||
python3.11 \
|
python3.12 \
|
||||||
"python3.11-devel" \
|
"python3.12-devel" \
|
||||||
"python3.11-pip*" \
|
"python3.12-pip*" \
|
||||||
"python3.11-setuptools" \
|
"python3.12-setuptools" \
|
||||||
"python3.11-packaging" \
|
"python3.12-packaging" \
|
||||||
"python3.11-psycopg2" \
|
"python3.12-psycopg2" \
|
||||||
rsync \
|
rsync \
|
||||||
rsyslog \
|
rsyslog \
|
||||||
subversion \
|
subversion \
|
||||||
@@ -149,7 +149,7 @@ RUN dnf -y update && dnf install -y 'dnf-command(config-manager)' && \
|
|||||||
xmlsec1-openssl && \
|
xmlsec1-openssl && \
|
||||||
dnf -y clean all
|
dnf -y clean all
|
||||||
|
|
||||||
RUN pip3.11 install -vv virtualenv supervisor dumb-init build
|
RUN pip3.12 install -vv virtualenv supervisor dumb-init build
|
||||||
|
|
||||||
RUN rm -rf /root/.cache && rm -rf /tmp/*
|
RUN rm -rf /root/.cache && rm -rf /tmp/*
|
||||||
|
|
||||||
@@ -181,8 +181,8 @@ RUN dnf -y install \
|
|||||||
unzip && \
|
unzip && \
|
||||||
npm install -g n && n 16.13.1 && npm install -g npm@8.5.0 && dnf remove -y nodejs
|
npm install -g n && n 16.13.1 && npm install -g npm@8.5.0 && dnf remove -y nodejs
|
||||||
|
|
||||||
RUN pip3.11 install -vv git+https://github.com/coderanger/supervisor-stdout.git@973ba19967cdaf46d9c1634d1675fc65b9574f6e
|
RUN pip3.12 install -vv git+https://github.com/coderanger/supervisor-stdout.git@973ba19967cdaf46d9c1634d1675fc65b9574f6e
|
||||||
RUN pip3.11 install -vv black setuptools-scm build
|
RUN pip3.12 install -vv black setuptools-scm build
|
||||||
|
|
||||||
# This package randomly fails to download.
|
# This package randomly fails to download.
|
||||||
# It is nice to have in the dev env, but not necessary.
|
# It is nice to have in the dev env, but not necessary.
|
||||||
@@ -251,8 +251,8 @@ ADD tools/scripts/awx-python /usr/bin/awx-python
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if (build_dev|bool) or (kube_dev|bool) %}
|
{% if (build_dev|bool) or (kube_dev|bool) %}
|
||||||
RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.11/site-packages/awx.egg-link
|
RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.12/site-packages/awx.egg-link
|
||||||
RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.11/site-packages/awx.pth
|
RUN echo /awx_devel > /var/lib/awx/venv/awx/lib/python3.12/site-packages/awx.pth
|
||||||
RUN ln -sf /awx_devel/tools/docker-compose/awx-manage /usr/local/bin/awx-manage
|
RUN ln -sf /awx_devel/tools/docker-compose/awx-manage /usr/local/bin/awx-manage
|
||||||
RUN ln -sf /awx_devel/tools/scripts/awx-python /usr/bin/awx-python
|
RUN ln -sf /awx_devel/tools/scripts/awx-python /usr/bin/awx-python
|
||||||
RUN ln -sf /awx_devel/tools/scripts/rsyslog-4xx-recovery /usr/bin/rsyslog-4xx-recovery
|
RUN ln -sf /awx_devel/tools/scripts/rsyslog-4xx-recovery /usr/bin/rsyslog-4xx-recovery
|
||||||
@@ -284,8 +284,8 @@ RUN for dir in \
|
|||||||
/var/lib/awx/.local \
|
/var/lib/awx/.local \
|
||||||
/var/lib/awx/venv \
|
/var/lib/awx/venv \
|
||||||
/var/lib/awx/venv/awx/bin \
|
/var/lib/awx/venv/awx/bin \
|
||||||
/var/lib/awx/venv/awx/lib/python3.11 \
|
/var/lib/awx/venv/awx/lib/python3.12 \
|
||||||
/var/lib/awx/venv/awx/lib/python3.11/site-packages \
|
/var/lib/awx/venv/awx/lib/python3.12/site-packages \
|
||||||
/var/lib/awx/projects \
|
/var/lib/awx/projects \
|
||||||
/var/lib/awx/rsyslog \
|
/var/lib/awx/rsyslog \
|
||||||
/var/run/awx-rsyslog \
|
/var/run/awx-rsyslog \
|
||||||
|
|||||||
@@ -310,12 +310,12 @@ if __name__ == '__main__':
|
|||||||
if events > 0:
|
if events > 0:
|
||||||
for k_id in created_job_ids:
|
for k_id in created_job_ids:
|
||||||
generate_events(events, str(k_id), time_delta)
|
generate_events(events, str(k_id), time_delta)
|
||||||
print(datetime.datetime.utcnow().isoformat())
|
print(datetime.datetime.now(datetime.UTC).isoformat())
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
# restore all indexes
|
# restore all indexes
|
||||||
print(datetime.datetime.utcnow().isoformat())
|
print(datetime.datetime.now(datetime.UTC).isoformat())
|
||||||
print('restoring indexes and constraints (this may take awhile)')
|
print('restoring indexes and constraints (this may take awhile)')
|
||||||
|
|
||||||
workers = []
|
workers = []
|
||||||
@@ -343,4 +343,4 @@ if __name__ == '__main__':
|
|||||||
sql = f'ALTER TABLE main_jobevent ADD CONSTRAINT {conname} {condef}'
|
sql = f'ALTER TABLE main_jobevent ADD CONSTRAINT {conname} {condef}'
|
||||||
cleanup(sql)
|
cleanup(sql)
|
||||||
|
|
||||||
print(datetime.datetime.utcnow().isoformat())
|
print(datetime.datetime.now(datetime.UTC).isoformat())
|
||||||
|
|||||||
Reference in New Issue
Block a user