diff --git a/Dockerfile b/Dockerfile index 54acdd1f..3fbc0d8d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -171,8 +171,6 @@ RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < COPY images/assets/patches/0044-Move-content-app-heartbeat-to-a-thread.patch /tmp/ RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0044-Move-content-app-heartbeat-to-a-thread.patch -COPY images/assets/patches/0045-Include-DRF-default-auth-classes-when-token-auth-is-disabled.patch /tmp/ -RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0045-Include-DRF-default-auth-classes-when-token-auth-is-disabled.patch COPY images/assets/keys/SIGSTORE-redhat-release3.pem /etc/pki/sigstore/SIGSTORE-redhat-release3 @@ -188,8 +186,6 @@ RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < COPY images/assets/patches/0053-python-agent-scan-task.patch /tmp/ RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0053-python-agent-scan-task.patch -COPY images/assets/patches/0054-defer-contentid-cleanup-old-versions.patch /tmp/ -RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0054-defer-contentid-cleanup-old-versions.patch COPY images/assets/patches/0055-decouple-livez-from-db.patch /tmp/ RUN patch -p1 -d /usr/local/lib/pulp/lib/python${PYTHON_VERSION}/site-packages < /tmp/0055-decouple-livez-from-db.patch diff --git a/images/assets/patches/0018-Re-root-the-registry-API-at-api-pulp-v2.patch b/images/assets/patches/0018-Re-root-the-registry-API-at-api-pulp-v2.patch index 1437aad2..228c5460 100644 --- a/images/assets/patches/0018-Re-root-the-registry-API-at-api-pulp-v2.patch +++ b/images/assets/patches/0018-Re-root-the-registry-API-at-api-pulp-v2.patch @@ -1,21 +1,8 @@ -From ad565cd57a1d1f643d1fc2757965f22bbb164624 Mon Sep 17 00:00:00 2001 -From: Dennis Kliban -Date: Tue, 4 Mar 2025 11:19:30 -0500 -Subject: [PATCH] Re-root the registry API at /api/pulp/v2/ - ---- - pulp_container/app/content.py | 2 +- - pulp_container/app/redirects.py | 4 ++-- - pulp_container/app/registry_api.py | 4 +++- - pulp_container/app/token_verification.py | 16 +--------------- - pulp_container/app/urls.py | 14 +++++++------- - 5 files changed, 15 insertions(+), 27 deletions(-) - diff --git a/pulp_container/app/content.py b/pulp_container/app/content.py -index dbe6418d..006e8afb 100644 +index 145533fc..000c97fc 100644 --- a/pulp_container/app/content.py +++ b/pulp_container/app/content.py -@@ -6,7 +6,7 @@ from pulp_container.app.registry import Registry +@@ -7,7 +7,7 @@ from pulp_container.app.registry import Registry registry = Registry() @@ -25,10 +12,10 @@ index dbe6418d..006e8afb 100644 app.add_routes( [ diff --git a/pulp_container/app/redirects.py b/pulp_container/app/redirects.py -index 0c7a5898..fa4808cb 100644 +index 2ac80050..c7694cd7 100644 --- a/pulp_container/app/redirects.py +++ b/pulp_container/app/redirects.py -@@ -21,7 +21,7 @@ class CommonRedirects: +@@ -22,7 +22,7 @@ class CommonRedirects: self.path = path self.request = request self.path_prefix = ( @@ -38,10 +25,10 @@ index 0c7a5898..fa4808cb 100644 else "pulp/container" ) diff --git a/pulp_container/app/registry_api.py b/pulp_container/app/registry_api.py -index e3b0766c..4e9114be 100644 +index 24b007ae..91a5e102 100644 --- a/pulp_container/app/registry_api.py +++ b/pulp_container/app/registry_api.py -@@ -98,6 +98,8 @@ from pulp_container.constants import ( +@@ -97,6 +97,8 @@ from pulp_container.constants import ( V2_ACCEPT_HEADERS, ) @@ -50,28 +37,35 @@ index e3b0766c..4e9114be 100644 log = logging.getLogger(__name__) IGNORED_PULL_THROUGH_REMOTE_ATTRIBUTES = [ -@@ -232,7 +234,7 @@ class ContainerRegistryApiMixin: +@@ -229,9 +231,14 @@ class ContainerRegistryApiMixin: + def authentication_classes(self): + """ List of authentication classes to check for this view. ++ ++ When token auth is disabled, includes both RegistryAuthentication (Basic auth) ++ and any authentication classes configured in DEFAULT_AUTHENTICATION_CLASSES. ++ This allows deployments to use custom authentication backends (e.g. remote ++ header-based auth) alongside standard Basic auth for container registry operations. """ if settings.get("TOKEN_AUTH_DISABLED", False): - return [RegistryAuthentication] -+ return [RHServiceAccountCertAuthentication] ++ return [RegistryAuthentication, *api_settings.DEFAULT_AUTHENTICATION_CLASSES] return [TokenAuthentication] @property diff --git a/pulp_container/app/token_verification.py b/pulp_container/app/token_verification.py -index d78738a4..faf05cd6 100644 +index 019310ac..9c685b68 100644 --- a/pulp_container/app/token_verification.py +++ b/pulp_container/app/token_verification.py -@@ -15,6 +15,7 @@ from rest_framework.authentication import ( - from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated - from rest_framework.permissions import BasePermission, SAFE_METHODS +@@ -14,6 +14,7 @@ from rest_framework.exceptions import AuthenticationFailed, NotAuthenticated + from rest_framework.permissions import SAFE_METHODS, BasePermission + from pulp_container.app.utils import get_full_path +from pulp_service.app.authorization import DomainBasedPermission Scope = namedtuple("Scope", "resource_type, name, action") User = get_user_model() -@@ -178,23 +179,11 @@ def get_scopes(request): +@@ -177,23 +178,11 @@ def get_scopes(request): return scopes @@ -97,10 +91,10 @@ index d78738a4..faf05cd6 100644 class TokenPermission(BasePermission): diff --git a/pulp_container/app/urls.py b/pulp_container/app/urls.py -index 47c4342a..6c93b927 100644 +index f1a54872..0b40d9a8 100644 --- a/pulp_container/app/urls.py +++ b/pulp_container/app/urls.py -@@ -32,16 +32,16 @@ head_route = Route( +@@ -33,16 +33,16 @@ head_route = Route( ) router.routes.append(head_route) @@ -124,6 +118,3 @@ index 47c4342a..6c93b927 100644 path("", include(router.urls)), ] # print(router.urls) --- -2.49.0 - diff --git a/images/assets/patches/0025-clamAV.patch b/images/assets/patches/0025-clamAV.patch index f72fed28..68d52fd4 100644 --- a/images/assets/patches/0025-clamAV.patch +++ b/images/assets/patches/0025-clamAV.patch @@ -1,24 +1,10 @@ -From eeab8cc111d03218b3f3b58200206b18aaa1f0b5 Mon Sep 17 00:00:00 2001 -From: Dennis Kliban -Date: Tue, 24 Mar 2026 12:12:45 -0400 -Subject: [PATCH] Extend dwloader to push data stream through clamAV - ---- - pulpcore/app/settings.py | 4 +++ - pulpcore/content/handler.py | 23 ++++++++++++++++- - pulpcore/download/base.py | 41 +++++++++++++++++++++++++++++++ - pulpcore/exceptions/__init__.py | 1 + - pulpcore/exceptions/validation.py | 19 ++++++++++++++ - pulpcore/plugin/exceptions.py | 2 ++ - 6 files changed, 89 insertions(+), 1 deletion(-) - diff --git a/pulpcore/app/settings.py b/pulpcore/app/settings.py -index 7d30421f2..ffbc2ab30 100644 +index a4411f50e..29646bd29 100644 --- a/pulpcore/app/settings.py +++ b/pulpcore/app/settings.py -@@ -425,6 +425,10 @@ VULN_REPORT_TASK_LIMITER = 10 - # Replaces asyncio event loop with uvloop - UVLOOP_ENABLED = False +@@ -439,6 +439,10 @@ UVLOOP_ENABLED = False + # Replaces every non PulpException error msg with "An internal error occured" + REDACT_UNSAFE_EXCEPTIONS = False +# ClamAV default config +CLAMAV_HOST = None @@ -28,7 +14,7 @@ index 7d30421f2..ffbc2ab30 100644 # Read more at https://www.dynaconf.com/django/ diff --git a/pulpcore/content/handler.py b/pulpcore/content/handler.py -index d00a53cba..51fffdc4d 100644 +index 56e9addf0..7787124a0 100644 --- a/pulpcore/content/handler.py +++ b/pulpcore/content/handler.py @@ -17,6 +17,7 @@ from aiohttp.web_exceptions import ( @@ -38,19 +24,16 @@ index d00a53cba..51fffdc4d 100644 + HTTPPreconditionFailed, HTTPRequestRangeNotSatisfiable, ) - from yarl import URL -@@ -60,8 +61,9 @@ from pulpcore.app.util import ( # noqa: E402: module level not at top of file - ) - + from asgiref.sync import sync_to_async +@@ -62,6 +63,7 @@ from pulpcore.cache import AsyncContentCache # noqa: E402 from pulpcore.exceptions import ( # noqa: E402 -- UnsupportedDigestValidationError, DigestValidationError, -+ UnsupportedDigestValidationError, + UnsupportedDigestValidationError, + MalwareError, ) from pulpcore.metrics import artifacts_size_counter # noqa: E402 -@@ -916,6 +918,11 @@ class Handler: +@@ -924,6 +926,11 @@ class Handler: url=url, r=ce.message ) raise Error(reason=reason) @@ -62,7 +45,7 @@ index d00a53cba..51fffdc4d 100644 if not any([repository, repo_version, publication, distro.remote]): reason = _( -@@ -1298,6 +1305,10 @@ class Handler: +@@ -1306,6 +1313,10 @@ class Handler: downloader.handle_data = handle_data original_finalize = downloader.finalize downloader.finalize = finalize @@ -73,7 +56,7 @@ index d00a53cba..51fffdc4d 100644 failed_download = True try: download_result = await downloader.run( -@@ -1321,6 +1332,16 @@ class Handler: +@@ -1329,6 +1340,16 @@ class Handler: "Learn more on " ) @@ -91,26 +74,25 @@ index d00a53cba..51fffdc4d 100644 if failed_download: # remove the temporary file diff --git a/pulpcore/download/base.py b/pulpcore/download/base.py -index b093ad9e4..ab8acff4b 100644 +index 8900ff81c..2c34efbe6 100644 --- a/pulpcore/download/base.py +++ b/pulpcore/download/base.py -@@ -1,6 +1,7 @@ - from gettext import gettext as _ - +@@ -1,4 +1,6 @@ import asyncio +from clamav_client import clamd - from collections import namedtuple ++from collections import namedtuple import concurrent.futures - from concurrent.futures import ThreadPoolExecutor, ALL_COMPLETED -@@ -8,6 +9,7 @@ import logging + import logging import os - import tempfile +@@ -7,6 +9,7 @@ from collections import namedtuple + from concurrent.futures import ALL_COMPLETED, ThreadPoolExecutor + from gettext import gettext as _ from pathlib import Path +import struct from urllib.parse import urlsplit from django.conf import settings -@@ -18,6 +20,7 @@ from pulpcore.exceptions import ( +@@ -18,6 +21,7 @@ from pulpcore.exceptions import ( SizeValidationError, TimeoutException, UnsupportedDigestValidationError, @@ -118,7 +100,7 @@ index b093ad9e4..ab8acff4b 100644 ) log = logging.getLogger(__name__) -@@ -114,6 +117,9 @@ class BaseDownloader: +@@ -114,6 +118,9 @@ class BaseDownloader: ).format(self.url, Artifact.DIGEST_FIELDS, set(self.expected_digests)) ) @@ -128,7 +110,7 @@ index b093ad9e4..ab8acff4b 100644 def _ensure_writer_has_open_file(self): """ Create a temporary file on demand. -@@ -158,6 +164,7 @@ class BaseDownloader: +@@ -158,6 +165,7 @@ class BaseDownloader: self._ensure_writer_has_open_file() self._writer.write(data) self._record_size_and_digests_for_data(data) @@ -136,7 +118,7 @@ index b093ad9e4..ab8acff4b 100644 async def finalize(self): """ -@@ -181,6 +188,7 @@ class BaseDownloader: +@@ -181,6 +189,7 @@ class BaseDownloader: self._writer = None self.validate_digests() self.validate_size() @@ -144,7 +126,7 @@ index b093ad9e4..ab8acff4b 100644 log.debug(f"Downloaded file from {self.url}") def fetch(self, extra_data=None): -@@ -210,6 +218,12 @@ class BaseDownloader: +@@ -210,6 +219,12 @@ class BaseDownloader: concurrent.futures.wait(futures, timeout=None, return_when=ALL_COMPLETED) self._size += len(data) @@ -157,7 +139,7 @@ index b093ad9e4..ab8acff4b 100644 @property def artifact_attributes(self): """ -@@ -251,6 +265,25 @@ class BaseDownloader: +@@ -251,6 +266,25 @@ class BaseDownloader: if actual_size != expected_size: raise SizeValidationError(actual_size, expected_size, url=self.url) @@ -183,7 +165,7 @@ index b093ad9e4..ab8acff4b 100644 async def run(self, extra_data=None): """ Run the downloader with concurrency restriction. -@@ -306,3 +339,11 @@ class BaseDownloader: +@@ -306,3 +340,11 @@ class BaseDownloader: :meth:`~pulpcore.plugin.download.BaseDownloader.finalize`. """ raise NotImplementedError("Subclasses must define a _run() method that returns a coroutine") @@ -196,10 +178,10 @@ index b093ad9e4..ab8acff4b 100644 + self.clamd_client._init_socket() + self.clamd_client._send_command("INSTREAM") diff --git a/pulpcore/exceptions/__init__.py b/pulpcore/exceptions/__init__.py -index 4c458e439..17e30978b 100644 +index e6b935de6..1887e9c61 100644 --- a/pulpcore/exceptions/__init__.py +++ b/pulpcore/exceptions/__init__.py -@@ -21,6 +21,7 @@ from .validation import ( +@@ -23,6 +23,7 @@ from .validation import ( DigestValidationError, InvalidSignatureError, SizeValidationError, @@ -208,10 +190,10 @@ index 4c458e439..17e30978b 100644 MissingDigestValidationError, UnsupportedDigestValidationError, diff --git a/pulpcore/exceptions/validation.py b/pulpcore/exceptions/validation.py -index 2131e5fce..94cd27ab1 100644 +index 985aad65d..65813d1bd 100644 --- a/pulpcore/exceptions/validation.py +++ b/pulpcore/exceptions/validation.py -@@ -74,6 +74,25 @@ class SizeValidationError(ValidationError): +@@ -75,6 +75,25 @@ class SizeValidationError(ValidationError): return f"[{self.error_code}] " + msg.format(expected=self.expected, actual=self.actual) @@ -238,18 +220,18 @@ index 2131e5fce..94cd27ab1 100644 """ Raised when attempting to save() an Artifact with an incomplete set of checksums. diff --git a/pulpcore/plugin/exceptions.py b/pulpcore/plugin/exceptions.py -index 0406964ec..d1ffedd6a 100644 +index 3e7e33852..ff36ef24d 100644 --- a/pulpcore/plugin/exceptions.py +++ b/pulpcore/plugin/exceptions.py -@@ -1,6 +1,7 @@ +@@ -1,6 +1,6 @@ from pulpcore.exceptions import ( - ValidationError, DigestValidationError, +- ExternalServiceError, + MalwareError, InvalidSignatureError, - PulpException, - SizeValidationError, -@@ -17,6 +18,7 @@ from pulpcore.exceptions import ( + MissingDigestValidationError, + PublishError, +@@ -17,6 +17,7 @@ from pulpcore.exceptions import ( __all__ = [ "ValidationError", "DigestValidationError", @@ -257,6 +239,3 @@ index 0406964ec..d1ffedd6a 100644 "InvalidSignatureError", "PulpException", "SizeValidationError", --- -2.53.0 - diff --git a/images/assets/patches/0028-OCIStorage-create-manifest.patch b/images/assets/patches/0028-OCIStorage-create-manifest.patch index d8f0a902..f7acaaf9 100644 --- a/images/assets/patches/0028-OCIStorage-create-manifest.patch +++ b/images/assets/patches/0028-OCIStorage-create-manifest.patch @@ -1,11 +1,12 @@ diff --git a/pulpcore/app/models/publication.py b/pulpcore/app/models/publication.py -index f354f0dd5..edc41cb99 100644 +index b953be30d..2a8804e8b 100644 --- a/pulpcore/app/models/publication.py +++ b/pulpcore/app/models/publication.py -@@ -12,6 +12,12 @@ from gettext import gettext as _ - from url_normalize import url_normalize - from urllib.parse import urlparse, urljoin +@@ -9,7 +9,12 @@ from datetime import timedelta + from gettext import gettext as _ + from urllib.parse import urljoin, urlparse +-import jq +import oras.client +import oras.oci +import oras.defaults @@ -13,9 +14,9 @@ index f354f0dd5..edc41cb99 100644 +import requests + from aiohttp.web_exceptions import HTTPNotFound - from django.conf import settings -@@ -198,6 +204,153 @@ class Publication(MasterModel): + from django.contrib.postgres.fields import HStoreField +@@ -196,6 +201,153 @@ class Publication(MasterModel): """ pass @@ -169,7 +170,7 @@ index f354f0dd5..edc41cb99 100644 def __enter__(self): """ Enter context. -@@ -230,6 +383,12 @@ class Publication(MasterModel): +@@ -228,6 +380,12 @@ class Publication(MasterModel): self.finalize_new_publication() self.complete = True self.save() @@ -181,4 +182,4 @@ index f354f0dd5..edc41cb99 100644 + except Exception: self.delete() - raise \ No newline at end of file + raise diff --git a/images/assets/patches/0034-Fix-profile-artifact-being-stored-in-default-domain.patch b/images/assets/patches/0034-Fix-profile-artifact-being-stored-in-default-domain.patch index 53ee2383..2d46d8f0 100644 --- a/images/assets/patches/0034-Fix-profile-artifact-being-stored-in-default-domain.patch +++ b/images/assets/patches/0034-Fix-profile-artifact-being-stored-in-default-domain.patch @@ -1,26 +1,19 @@ -From 29b7dc1a6537e6cc1bc93e0268421c856401ff52 Mon Sep 17 00:00:00 2001 -From: git-hyagi <45576767+git-hyagi@users.noreply.github.com> -Date: Fri, 12 Dec 2025 16:43:10 -0300 -Subject: [PATCH] Fix profile artifact being stored in default domain - -ref: https://issues.redhat.com/browse/PULP-1044 ---- - pulpcore/tasking/_util.py | 31 ++++++++++++++++++------------- - 1 file changed, 18 insertions(+), 13 deletions(-) - diff --git a/pulpcore/tasking/_util.py b/pulpcore/tasking/_util.py -index 7274d7cb8..37968200a 100644 +index abfd706cd..6f427784d 100644 --- a/pulpcore/tasking/_util.py +++ b/pulpcore/tasking/_util.py -@@ -16,6 +16,7 @@ from django.db.models import Q +@@ -16,8 +16,8 @@ from django.db.models import Q from django.utils import timezone from django_guid import set_guid from django_guid.utils import generate_guid +- +-from pulpcore.app.models import Artifact, Content, ProfileArtifact, Task, TaskSchedule +from pulpcore.app.contexts import with_domain - from pulpcore.app.models import Artifact, Content, Task, TaskSchedule, ProfileArtifact ++from pulpcore.app.models import Artifact, Content, Task, TaskSchedule, ProfileArtifact from pulpcore.app.util import ( configure_analytics, -@@ -167,18 +168,21 @@ def _pyinstrument_diagnostic_decorator(temp_dir, func): + configure_cleanup, +@@ -179,18 +179,21 @@ def _pyinstrument_diagnostic_decorator(temp_dir, func): f.write(profiler.output_html()) f.flush() @@ -54,7 +47,7 @@ index 7274d7cb8..37968200a 100644 _logger.info("Created pyinstrument profile data.") else: func(task) -@@ -200,13 +204,14 @@ def _memray_diagnostic_decorator(temp_dir, func): +@@ -212,13 +215,14 @@ def _memray_diagnostic_decorator(temp_dir, func): func(task) artifact = Artifact.init_and_validate(str(profile_file_path)) @@ -70,6 +63,3 @@ index 7274d7cb8..37968200a 100644 ProfileArtifact.objects.get_or_create( artifact=artifact, name="memray_profile", task=task --- -2.46.2 - diff --git a/images/assets/patches/0044-Move-content-app-heartbeat-to-a-thread.patch b/images/assets/patches/0044-Move-content-app-heartbeat-to-a-thread.patch index 405592d1..c30d8574 100644 --- a/images/assets/patches/0044-Move-content-app-heartbeat-to-a-thread.patch +++ b/images/assets/patches/0044-Move-content-app-heartbeat-to-a-thread.patch @@ -1,38 +1,36 @@ -From aa15b73667de977bf6f0caaab91d6909c431bb1e Mon Sep 17 00:00:00 2001 -From: Dennis Kliban -Date: Wed, 11 Feb 2026 21:19:28 -0500 -Subject: [PATCH] Move content app's heartbeat to a thread. - ---- - pulpcore/content/__init__.py | 33 +++++++++++++++++---------------- - 1 file changed, 17 insertions(+), 16 deletions(-) - diff --git a/pulpcore/content/__init__.py b/pulpcore/content/__init__.py -index 96e4f6a6d..9420c2abb 100644 +index decf0957b..d0775dbef 100644 --- a/pulpcore/content/__init__.py +++ b/pulpcore/content/__init__.py @@ -1,11 +1,10 @@ -import asyncio +-import logging +-import os from contextlib import suppress from importlib import import_module from importlib.util import find_spec - import logging - import os ++import logging ++import os +import threading --from asgiref.sync import sync_to_async +-import django from aiohttp import web + from asgiref.sync import sync_to_async from gunicorn.arbiter import Arbiter - import django -@@ -15,6 +14,7 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pulpcore.app.settings") +@@ -13,9 +12,9 @@ from gunicorn.arbiter import Arbiter + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pulpcore.app.settings") django.setup() - from django.conf import settings # noqa: E402: module level not at top of file +-from django.conf import settings # noqa: E402 +-from django.db.utils import ( # noqa: E402 +- DatabaseError, ++from django.conf import settings # noqa: E402: module level not at top of file +from django.db import connection # noqa: E402: module level not at top of file - from django.db.utils import ( # noqa: E402: module level not at top of file ++from django.db.utils import ( # noqa: E402: module level not at top of file IntegrityError, InterfaceError, -@@ -48,7 +48,7 @@ if settings.UVLOOP_ENABLED: + ) +@@ -46,7 +45,7 @@ if settings.UVLOOP_ENABLED: CONTENT_MODULE_NAME = "content" @@ -41,7 +39,7 @@ index 96e4f6a6d..9420c2abb 100644 name = get_worker_name() heartbeat_interval = settings.CONTENT_APP_TTL // 4 msg = "Content App '{name}' heartbeat written, sleeping for '{interarrival}' seconds".format( -@@ -60,39 +60,40 @@ async def _heartbeat(): +@@ -58,39 +57,40 @@ async def _heartbeat(): versions = {app.label: app.version for app in pulp_plugin_configs()} try: @@ -95,6 +93,3 @@ index 96e4f6a6d..9420c2abb 100644 async def server(*args, **kwargs): --- -2.52.0 - diff --git a/images/assets/patches/0045-Include-DRF-default-auth-classes-when-token-auth-is-disabled.patch b/images/assets/patches/0045-Include-DRF-default-auth-classes-when-token-auth-is-disabled.patch deleted file mode 100644 index 8889c5c2..00000000 --- a/images/assets/patches/0045-Include-DRF-default-auth-classes-when-token-auth-is-disabled.patch +++ /dev/null @@ -1,38 +0,0 @@ -From 0107bbe45858812529e95b77b5cb933ec2ee8aba Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Andr=C3=A9=20=22decko=22=20de=20Brito?= -Date: Wed, 4 Mar 2026 11:35:39 -0300 -Subject: [PATCH] Include DRF default auth classes when token auth is disabled - -When TOKEN_AUTH_DISABLED=true, ContainerRegistryApiMixin only used -a single authentication class. This prevented custom backends -configured in DEFAULT_AUTHENTICATION_CLASSES (e.g. remote header-based -auth) from being used for container registry operations. - -Now includes both RegistryAuthentication and all configured default -authentication classes, allowing deployments to use custom auth -backends alongside standard Basic auth. - -Co-Authored-By: Claude Opus 4.6 (1M context) ---- - pulp_container/app/registry_api.py | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/pulp_container/app/registry_api.py b/pulp_container/app/registry_api.py ---- a/pulp_container/app/registry_api.py -+++ b/pulp_container/app/registry_api.py -@@ -234,9 +234,14 @@ class ContainerRegistryApiMixin: - def authentication_classes(self): - """ - List of authentication classes to check for this view. -+ -+ When token auth is disabled, includes both RegistryAuthentication (Basic auth) -+ and any authentication classes configured in DEFAULT_AUTHENTICATION_CLASSES. -+ This allows deployments to use custom authentication backends (e.g. remote -+ header-based auth) alongside standard Basic auth for container registry operations. - """ - if settings.get("TOKEN_AUTH_DISABLED", False): -- return [RHServiceAccountCertAuthentication] -+ return [RegistryAuthentication, *api_settings.DEFAULT_AUTHENTICATION_CLASSES] - return [TokenAuthentication] - - @property diff --git a/images/assets/patches/0054-defer-contentid-cleanup-old-versions.patch b/images/assets/patches/0054-defer-contentid-cleanup-old-versions.patch deleted file mode 100644 index f8807642..00000000 --- a/images/assets/patches/0054-defer-contentid-cleanup-old-versions.patch +++ /dev/null @@ -1,69 +0,0 @@ -diff --git a/pulpcore/app/models/repository.py b/pulpcore/app/models/repository.py -index a2350072e..c07ba74ec 100644 ---- a/pulpcore/app/models/repository.py -+++ b/pulpcore/app/models/repository.py -@@ -323,20 +323,31 @@ class Repository(MasterModel): - """ - from .publication import Distribution, Publication - -+ protected_pks = set() -+ - # find all repo versions set on a distribution -- qs = self.versions.filter(pk__in=Distribution.objects.values_list("repository_version_id")) -+ protected_pks.update( -+ Distribution.objects.filter( -+ repository_version__repository=self, -+ ).values_list("repository_version_id", flat=True) -+ ) - - # find all repo versions with publications set on a distribution -- qs |= self.versions.filter( -- publication__pk__in=Distribution.objects.values_list("publication_id") -+ dist_pub_ids = Distribution.objects.values_list("publication_id", flat=True) -+ protected_pks.update( -+ Publication.objects.filter( -+ pk__in=dist_pub_ids, -+ repository_version__repository=self, -+ ).values_list("repository_version_id", flat=True) - ) - - # Protect repo versions of distributed checkpoint publications. - if Distribution.objects.filter(repository=self.pk, checkpoint=True).exists(): -- qs |= self.versions.filter( -- publication__pk__in=Publication.objects.filter(checkpoint=True).values_list( -- "pulp_id" -- ) -+ protected_pks.update( -+ Publication.objects.filter( -+ checkpoint=True, -+ repository_version__repository=self, -+ ).values_list("repository_version_id", flat=True) - ) - - if distro := Distribution.objects.filter(repository=self.pk, checkpoint=False).first(): -@@ -352,9 +363,12 @@ class Repository(MasterModel): - version = self.latest_version() - - if version: -- qs |= self.versions.filter(pk=version.pk) -+ protected_pks.add(version.pk) -+ -+ # Discard None values from distributions with no repository_version set -+ protected_pks.discard(None) - -- return qs.distinct() -+ return self.versions.filter(pk__in=protected_pks) - - def pull_through_add_content(self, content_artifact): - """ -@@ -416,7 +430,9 @@ class Repository(MasterModel): - if self.retain_repo_versions: - # Consider only completed versions that aren't protected for cleanup - versions = self.versions.complete().exclude(pk__in=self.protected_versions()) -- for version in versions.order_by("-number")[self.retain_repo_versions :]: -+ for version in versions.defer("content_ids").order_by("-number")[ -+ self.retain_repo_versions : -+ ]: - _logger.info( - "Deleting repository version {} due to version retention limit.".format(version) - ) diff --git a/images/assets/patches/CLAUDE.md b/images/assets/patches/CLAUDE.md index e344bcac..897c7325 100644 --- a/images/assets/patches/CLAUDE.md +++ b/images/assets/patches/CLAUDE.md @@ -7,12 +7,12 @@ Each patch modifies files installed into site-packages via the Dockerfile. | Patch prefix | GitHub repository | PyPI package | Current version tag | | ---------------- | ---------------------------------------------------------- | ---------------- | ------------------- | -| `pulpcore/` | [pulp/pulpcore](https://github.com/pulp/pulpcore) | pulpcore | 3.108.0 | -| `pulp_file/` | [pulp/pulpcore](https://github.com/pulp/pulpcore) | (bundled) | 3.108.0 | -| `pulp_container/`| [pulp/pulp_container](https://github.com/pulp/pulp_container) | pulp-container | 2.27.6 | +| `pulpcore/` | [pulp/pulpcore](https://github.com/pulp/pulpcore) | pulpcore | 3.110.2 | +| `pulp_file/` | [pulp/pulpcore](https://github.com/pulp/pulpcore) | (bundled) | 3.110.2 | +| `pulp_container/`| [pulp/pulp_container](https://github.com/pulp/pulp_container) | pulp-container | 2.27.8 | | `pulp_python/` | [pulp/pulp_python](https://github.com/pulp/pulp_python) | pulp-python | 3.29.0 | | `pulp_maven/` | [pulp/pulp_maven](https://github.com/pulp/pulp_maven) | pulp-maven | 0.12.0 | -| `oras/` | [oras-project/oras-py](https://github.com/oras-project/oras-py) | oras | 0.2.38 | +| `oras/` | [oras-project/oras-py](https://github.com/oras-project/oras-py) | oras | 0.2.42 | | `storages/` | [jschneier/django-storages](https://github.com/jschneier/django-storages) | django-storages | 1.14.6 | Versions are pinned in `pulp_service/requirements.txt`. Django-storages is a @@ -43,6 +43,7 @@ transitive dependency pinned in pulpcore's `pyproject.toml`. - **Package:** pulp_container - **Files:** `pulp_container/app/content.py`, `pulp_container/app/redirects.py`, `pulp_container/app/registry_api.py`, `pulp_container/app/token_verification.py`, `pulp_container/app/urls.py` - **Description:** Moves all container registry URL routes from `/v2/` to `/api/pulp/v2/` and the content app prefix from `/pulp/container/` to `/api/pulp-container/`. Replaces `RegistryPermission` with `DomainBasedPermission` and imports `RHServiceAccountCertAuthentication` for service-account auth. +- **Note:** Includes changes from: 0045-Include-DRF-default-auth-classes-when-token-auth-is-disabled.patch ### 0022 — Adds authentication to the mvn deploy api @@ -104,12 +105,6 @@ transitive dependency pinned in pulpcore's `pyproject.toml`. - **Files:** `pulpcore/content/__init__.py` - **Description:** Converts the content app heartbeat from an async coroutine to a synchronous thread with a shutdown event. Replaces `asyncio.sleep` with `threading.Event.wait` and async ORM calls with synchronous ones. -### 0045 — Include DRF default auth classes when token auth is disabled - -- **Package:** pulp_container -- **Files:** `pulp_container/app/registry_api.py` -- **Note:** Depends on patch 0018 (modifies the same authentication block). These two patches are automatically combined when upgrading pulp_container. -- **Description:** When `TOKEN_AUTH_DISABLED` is true, includes both `RegistryAuthentication` and all `DEFAULT_AUTHENTICATION_CLASSES` instead of a single auth class, allowing custom authentication backends (e.g., remote header-based auth) to work alongside basic auth for registry operations. ### 0047 — Improve repair_metadata log with repo and package names @@ -141,11 +136,6 @@ transitive dependency pinned in pulpcore's `pyproject.toml`. - **Files:** `pulp_python/app/models.py`, `pulp_python/app/tasks/__init__.py`, `pulp_python/app/tasks/scan_package.py` - **Description:** Adds an automatic package scanning task that dispatches on repository version creation. Downloads Python package artifacts to a temp directory, runs an external scanner, and collects results. -### 0054 — Defer content ID cleanup for old versions - -- **Package:** pulpcore -- **Files:** `pulpcore/app/tasks/repository.py` -- **Description:** Optimizes the `protected_versions()` query to avoid expensive JOINs by deferring content ID cleanup for old repository versions, reducing database load during version management. ### 0055 — Decouple /livez endpoint from the database diff --git a/pulp_service/requirements.txt b/pulp_service/requirements.txt index c7fbebb0..5d8de6ef 100644 --- a/pulp_service/requirements.txt +++ b/pulp_service/requirements.txt @@ -1,16 +1,16 @@ -pulpcore==3.108.0 +pulpcore==3.110.2 pulp-rpm==3.35.2 pulp-gem==0.7.5 pulp-python==3.29.0 pulp-npm==0.7.0 -pulp-container==2.27.6 +pulp-container==2.27.8 pulp-maven==0.12.0 pulp-hugging-face==0.3.0 pulp-cli pulp-cli-gem sentry-sdk app-common-python -oras==0.2.38 +oras==0.2.42 uvloop==0.21.0 jsonschema memray