pulp-container 2.29.1__tar.gz → 2.30.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. {pulp_container-2.29.1 → pulp_container-2.30.0}/CHANGES.md +104 -0
  2. {pulp_container-2.29.1 → pulp_container-2.30.0}/PKG-INFO +3 -3
  3. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/__init__.py +1 -1
  4. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/cache.py +2 -2
  5. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/downloaders.py +26 -14
  6. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/models.py +3 -4
  7. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/registry_api.py +30 -6
  8. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/viewsets.py +12 -12
  9. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container.egg-info/PKG-INFO +3 -3
  10. pulp_container-2.30.0/pulp_container.egg-info/requires.txt +4 -0
  11. {pulp_container-2.29.1 → pulp_container-2.30.0}/pyproject.toml +4 -4
  12. pulp_container-2.29.1/pulp_container.egg-info/requires.txt +0 -4
  13. {pulp_container-2.29.1 → pulp_container-2.30.0}/COMMITMENT +0 -0
  14. {pulp_container-2.29.1 → pulp_container-2.30.0}/COPYRIGHT +0 -0
  15. {pulp_container-2.29.1 → pulp_container-2.30.0}/LICENSE +0 -0
  16. {pulp_container-2.29.1 → pulp_container-2.30.0}/MANIFEST.in +0 -0
  17. {pulp_container-2.29.1 → pulp_container-2.30.0}/README.rst +0 -0
  18. {pulp_container-2.29.1 → pulp_container-2.30.0}/functest_requirements.txt +0 -0
  19. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/__init__.py +0 -0
  20. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/access_policy.py +0 -0
  21. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/authorization.py +0 -0
  22. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/checks.py +0 -0
  23. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/content.py +0 -0
  24. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/dynaconf_hooks.py +0 -0
  25. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/exceptions.py +0 -0
  26. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/fields.py +0 -0
  27. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/global_access_conditions.py +0 -0
  28. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/json_schemas.py +0 -0
  29. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/management/__init__.py +0 -0
  30. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/management/commands/__init__.py +0 -0
  31. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/management/commands/container-handle-image-data.py +0 -0
  32. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/management/commands/container-repair-media-type.py +0 -0
  33. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/management/commands/container-repair-pull-through-distributions.py +0 -0
  34. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0001_initial.py +0 -0
  35. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0001_squashed_0032_upload_artifact.py +0 -0
  36. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0002_containerrepository.py +0 -0
  37. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0003_oci_mediatype.py +0 -0
  38. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0004_upload.py +0 -0
  39. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0005_contentredirectcontentguard.py +0 -0
  40. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0006_containerpushrepository.py +0 -0
  41. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0007_clear_tags_artifacts_refs.py +0 -0
  42. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0008_include_exclude_tags.py +0 -0
  43. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0009_container_namespace.py +0 -0
  44. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0010_remove_uploadchunk.py +0 -0
  45. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0011_add_container_repository_permissions.py +0 -0
  46. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0012_add_container_namespace_permissions.py +0 -0
  47. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0013_add_pull_push_permissions.py +0 -0
  48. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0014_containerdistribution_private.py +0 -0
  49. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0015_manage_tags_push_repo.py +0 -0
  50. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0016_add_delete_versions_permission.py +0 -0
  51. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0017_add_granular_perms.py +0 -0
  52. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0018_containerdistribution_description.py +0 -0
  53. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0019_DATA_distribution_model_swap.py +0 -0
  54. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0020_update_push_repo_perms.py +0 -0
  55. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0021_data_move_redirect_content_guard_to_core.py +0 -0
  56. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0022_delete_contentredirectcontentguard.py +0 -0
  57. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0023_manifestsignature.py +0 -0
  58. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0024_containerremote_sigstore.py +0 -0
  59. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0025_signature_stored_in_textfield.py +0 -0
  60. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0026_manifest_signing_service.py +0 -0
  61. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0027_data_translate_perms_to_roles.py +0 -0
  62. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0028_add_role_manage_permissions.py +0 -0
  63. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0029_remove_blob_media_type.py +0 -0
  64. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0030_enforce_tagged_manifest_reference.py +0 -0
  65. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0031_replace_charf_with_textf.py +0 -0
  66. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0032_upload_artifact.py +0 -0
  67. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0033_raise_warning_for_repair.py +0 -0
  68. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0034_translate_signed_schema.py +0 -0
  69. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0035_alter_blob_content_ptr_and_more.py +0 -0
  70. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0036_containerpushrepository_pending_blobs_manifests.py +0 -0
  71. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0037_create_pull_through_cache_models.py +0 -0
  72. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0038_add_manifest_metadata_fields.py +0 -0
  73. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0039_manifest_data.py +0 -0
  74. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0040_add_remote_repo_filter.py +0 -0
  75. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0041_add_pull_through_pull_permissions.py +0 -0
  76. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0042_add_manifest_nature_field.py +0 -0
  77. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0043_add_os_arch_image_size_manifest_fields.py +0 -0
  78. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0044_add_domain.py +0 -0
  79. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0045_alter_manifest_compressed_image_size.py +0 -0
  80. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0046_alter_manifest_listed_manifests.py +0 -0
  81. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0047_containernamespace_pulp_labels.py +0 -0
  82. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0048_containerremote_includes_excludes.py +0 -0
  83. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0049_manifestsignature_fingerprint.py +0 -0
  84. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/0050_alter_containernamespace_options.py +0 -0
  85. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/migrations/__init__.py +0 -0
  86. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/modelresource.py +0 -0
  87. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/pull_through.py +0 -0
  88. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/redirects.py +0 -0
  89. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/registry.py +0 -0
  90. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/replica.py +0 -0
  91. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/serializers.py +0 -0
  92. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/settings.py +0 -0
  93. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/__init__.py +0 -0
  94. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/builder.py +0 -0
  95. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/download_image_data.py +0 -0
  96. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/migrate_push_repository.py +0 -0
  97. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/recursive_add.py +0 -0
  98. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/recursive_remove.py +0 -0
  99. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/sign.py +0 -0
  100. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/sync_stages.py +0 -0
  101. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/synchronize.py +0 -0
  102. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/tag.py +0 -0
  103. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/tasks/untag.py +0 -0
  104. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/token_verification.py +0 -0
  105. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/urls.py +0 -0
  106. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/utils.py +0 -0
  107. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/webserver_snippets/__init__.py +0 -0
  108. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/webserver_snippets/apache.conf +0 -0
  109. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/app/webserver_snippets/nginx.conf +0 -0
  110. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/constants.py +0 -0
  111. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/__init__.py +0 -0
  112. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/__init__.py +0 -0
  113. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/__init__.py +0 -0
  114. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_build_images.py +0 -0
  115. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_cancel_blob_upload.py +0 -0
  116. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_content_cache.py +0 -0
  117. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_crud_distributions.py +0 -0
  118. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_crud_remotes.py +0 -0
  119. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_delete_blob.py +0 -0
  120. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_delete_manifest.py +0 -0
  121. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_domains.py +0 -0
  122. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_flatpak.py +0 -0
  123. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_migrate_push_repository.py +0 -0
  124. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_pull_content.py +0 -0
  125. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_pull_through_cache.py +0 -0
  126. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_pulpimportexport.py +0 -0
  127. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_push_content.py +0 -0
  128. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_push_signatures.py +0 -0
  129. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_rbac_push_repositories.py +0 -0
  130. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_rbac_remotes.py +0 -0
  131. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_rbac_repo_content.py +0 -0
  132. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_rbac_repo_versions.py +0 -0
  133. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_rbac_sync_repositories.py +0 -0
  134. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_recursive_add.py +0 -0
  135. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_recursive_remove.py +0 -0
  136. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_remote_filter_pull_through.py +0 -0
  137. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_repositories_list.py +0 -0
  138. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_sign_manifests.py +0 -0
  139. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_sync.py +0 -0
  140. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_sync_signatures.py +0 -0
  141. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_tagging_images.py +0 -0
  142. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/api/test_token_authentication.py +0 -0
  143. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/conftest.py +0 -0
  144. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/constants.py +0 -0
  145. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/functional/utils.py +0 -0
  146. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/__init__.py +0 -0
  147. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/fixtures/ryuk_0.13.0_manifest_list.json +0 -0
  148. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/test_json_schemas.py +0 -0
  149. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/test_models.py +0 -0
  150. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/test_pull_through_repair.py +0 -0
  151. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/test_serializers.py +0 -0
  152. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container/tests/unit/test_sync_stages.py +0 -0
  153. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container.egg-info/SOURCES.txt +0 -0
  154. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container.egg-info/dependency_links.txt +0 -0
  155. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container.egg-info/entry_points.txt +0 -0
  156. {pulp_container-2.29.1 → pulp_container-2.30.0}/pulp_container.egg-info/top_level.txt +0 -0
  157. {pulp_container-2.29.1 → pulp_container-2.30.0}/setup.cfg +0 -0
  158. {pulp_container-2.29.1 → pulp_container-2.30.0}/test_requirements.txt +0 -0
  159. {pulp_container-2.29.1 → pulp_container-2.30.0}/unittest_requirements.txt +0 -0
@@ -8,6 +8,20 @@
8
8
 
9
9
  [//]: # (towncrier release notes start)
10
10
 
11
+ ## 2.30.0 (2026-10-08) {: #2.30.0 }
12
+
13
+ #### Features {: #2.30.0-feature }
14
+
15
+ - Pull-through manifest resolution now uses HEAD checks, reducing Docker Hub 429s for repeated tag resolutions.
16
+
17
+ #### Bugfixes {: #2.30.0-bugfix }
18
+
19
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
20
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
21
+ - Fixed global auth token leakage. CVE-2026-103868
22
+
23
+ ---
24
+
11
25
  ## 2.29.1 (2026-09-23) {: #2.29.1 }
12
26
 
13
27
  #### Bugfixes {: #2.29.1-bugfix }
@@ -107,6 +121,21 @@
107
121
 
108
122
  ---
109
123
 
124
+ ## 2.27.13 (2026-10-07) {: #2.27.13 }
125
+
126
+ No significant changes.
127
+
128
+ ---
129
+
130
+ ## 2.27.12 (2026-09-23) {: #2.27.12 }
131
+
132
+ #### Bugfixes {: #2.27.12-bugfix }
133
+
134
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
135
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
136
+
137
+ ---
138
+
110
139
  ## 2.27.11 (2026-07-29) {: #2.27.11 }
111
140
 
112
141
  No significant changes.
@@ -227,6 +256,21 @@ No significant changes.
227
256
  #### Misc {: #2.27.0-misc }
228
257
 
229
258
 
259
+ ---
260
+
261
+ ## 2.26.18 (2026-10-07) {: #2.26.18 }
262
+
263
+ No significant changes.
264
+
265
+ ---
266
+
267
+ ## 2.26.17 (2026-09-23) {: #2.26.17 }
268
+
269
+ #### Bugfixes {: #2.26.17-bugfix }
270
+
271
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
272
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
273
+
230
274
  ---
231
275
 
232
276
  ## 2.26.16 (2026-06-08) {: #2.26.16 }
@@ -435,6 +479,21 @@ No significant changes.
435
479
 
436
480
  ---
437
481
 
482
+ ## 2.24.14 (2026-10-07) {: #2.24.14 }
483
+
484
+ No significant changes.
485
+
486
+ ---
487
+
488
+ ## 2.24.13 (2026-09-23) {: #2.24.13 }
489
+
490
+ #### Bugfixes {: #2.24.13-bugfix }
491
+
492
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
493
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
494
+
495
+ ---
496
+
438
497
  ## 2.24.12 (2026-07-21) {: #2.24.12 }
439
498
 
440
499
  #### Bugfixes {: #2.24.12-bugfix }
@@ -573,6 +632,21 @@ No significant changes.
573
632
 
574
633
  ---
575
634
 
635
+ ## 2.22.12 (2026-10-07) {: #2.22.12 }
636
+
637
+ No significant changes.
638
+
639
+ ---
640
+
641
+ ## 2.22.11 (2026-09-23) {: #2.22.11 }
642
+
643
+ #### Bugfixes {: #2.22.11-bugfix }
644
+
645
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
646
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
647
+
648
+ ---
649
+
576
650
  ## 2.22.10 (2026-07-23) {: #2.22.10 }
577
651
 
578
652
  #### Bugfixes {: #2.22.10-bugfix }
@@ -766,6 +840,21 @@ No significant changes.
766
840
 
767
841
  ---
768
842
 
843
+ ## 2.20.13 (2026-10-07) {: #2.20.13 }
844
+
845
+ No significant changes.
846
+
847
+ ---
848
+
849
+ ## 2.20.12 (2026-09-23) {: #2.20.12 }
850
+
851
+ #### Bugfixes {: #2.20.12-bugfix }
852
+
853
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
854
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
855
+
856
+ ---
857
+
769
858
  ## 2.20.11 (2026-07-27) {: #2.20.11 }
770
859
 
771
860
  #### Bugfixes {: #2.20.11-bugfix }
@@ -907,6 +996,21 @@ No significant changes.
907
996
 
908
997
  ---
909
998
 
999
+ ## 2.19.15 (2026-10-07) {: #2.19.15 }
1000
+
1001
+ No significant changes.
1002
+
1003
+ ---
1004
+
1005
+ ## 2.19.14 (2026-09-23) {: #2.19.14 }
1006
+
1007
+ #### Bugfixes {: #2.19.14-bugfix }
1008
+
1009
+ - Prevent container distributions from overlapping other distribution base paths. Pull-through distributions **NEED** to be fixed before upgrading pulpcore; run `pulpcore-manager container-repair-pull-through-distributions` to repair existing distributions.
1010
+ [#2494](https://github.com/pulp/pulp_container/issues/2494)
1011
+
1012
+ ---
1013
+
910
1014
  ## 2.19.13 (2026-07-27) {: #2.19.13 }
911
1015
 
912
1016
  #### Bugfixes {: #2.19.13-bugfix }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-container
3
- Version: 2.29.1
3
+ Version: 2.30.0
4
4
  Summary: Container plugin for the Pulp Project
5
5
  Author-email: Pulp Team <pulp-list@redhat.com>
6
6
  Project-URL: Homepage, https://pulpproject.org
@@ -18,7 +18,7 @@ Requires-Python: >=3.11
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
20
  Requires-Dist: jsonschema<4.27,>=4.4
21
- Requires-Dist: pulpcore<3.130,>=3.111.0
22
- Requires-Dist: pyjwt[crypto]<2.14,>=2.4
21
+ Requires-Dist: pulpcore<3.130,>=3.118.0
22
+ Requires-Dist: pyjwt[crypto]<2.16,>=2.4
23
23
  Requires-Dist: pysequoia<0.2.0,>=0.1.35
24
24
  Dynamic: license-file
@@ -24,7 +24,7 @@ class PulpContainerPluginAppConfig(PulpPluginAppConfig):
24
24
 
25
25
  name = "pulp_container.app"
26
26
  label = "container"
27
- version = "2.29.1"
27
+ version = "2.30.0"
28
28
  python_package_name = "pulp-container"
29
29
  domain_compatible = True
30
30
 
@@ -40,9 +40,9 @@ class RegistryContentCache(RegistryCache, AsyncContentCache):
40
40
  key = ":".join(all_keys[k] for k in self.keys)
41
41
  return key
42
42
 
43
- async def make_response(self, key, base_key):
43
+ async def make_response(self, key, base_key, *args, **kwargs):
44
44
  """Fix the Content-Type header to remove the charset."""
45
- response = await super().make_response(key, base_key)
45
+ response = await super().make_response(key, base_key, *args, **kwargs)
46
46
  if response is not None:
47
47
  if content_type := response.headers.get("Content-Type"):
48
48
  response.headers["Content-Type"] = content_type.split(";")[0]
@@ -25,15 +25,12 @@ class RegistryAuthHttpDownloader(HttpDownloader):
25
25
  Additionally, use custom headers from DeclarativeArtifact.extra_data['headers']
26
26
  """
27
27
 
28
- registry_auth = {"bearer": None, "basic": None}
29
- token_lock = asyncio.Lock()
30
-
31
28
  def __init__(self, *args, **kwargs):
32
29
  """
33
30
  Initialize the downloader.
34
31
  """
35
32
  self.remote = kwargs.pop("remote")
36
-
33
+ self.factory = kwargs.pop("factory")
37
34
  super().__init__(*args, **kwargs)
38
35
 
39
36
  async def _run(self, handle_401=True, extra_data=None):
@@ -57,8 +54,8 @@ class RegistryAuthHttpDownloader(HttpDownloader):
57
54
  headers = extra_data.get("headers", headers)
58
55
  repo_name = extra_data.get("repo_name", None)
59
56
  http_method = extra_data.get("http_method", "get") if extra_data is not None else "get"
60
- this_token = self.registry_auth["bearer"]
61
- basic_auth = self.registry_auth["basic"]
57
+ this_token = self.factory.registry_auth["bearer"]
58
+ basic_auth = self.factory.registry_auth["basic"]
62
59
  auth_headers = self.auth_header(this_token, basic_auth)
63
60
  headers.update(auth_headers)
64
61
  # aiohttps does not allow to send auth argument and auth header together
@@ -81,16 +78,16 @@ class RegistryAuthHttpDownloader(HttpDownloader):
81
78
  if "bearer" in response_auth_header.lower():
82
79
  # Token has not been updated during request
83
80
  if (
84
- self.registry_auth["bearer"] is None
85
- or self.registry_auth["bearer"] == this_token
81
+ self.factory.registry_auth["bearer"] is None
82
+ or self.factory.registry_auth["bearer"] == this_token
86
83
  ):
87
- self.registry_auth["bearer"] = None
84
+ self.factory.registry_auth["bearer"] = None
88
85
  await self.update_token(response_auth_header, this_token, repo_name)
89
86
  return await self._run(handle_401=False, extra_data=extra_data)
90
87
  elif "basic" in response_auth_header.lower():
91
88
  if self.remote.username:
92
89
  basic = aiohttp.BasicAuth(self.remote.username, self.remote.password)
93
- self.registry_auth["basic"] = basic.encode()
90
+ self.factory.registry_auth["basic"] = basic.encode()
94
91
  return await self._run(handle_401=False, extra_data=extra_data)
95
92
  else:
96
93
  raise
@@ -111,10 +108,10 @@ class RegistryAuthHttpDownloader(HttpDownloader):
111
108
  """
112
109
  Update the Bearer token to be used with all requests.
113
110
  """
114
- async with self.token_lock:
111
+ async with self.factory.token_lock:
115
112
  if (
116
- self.registry_auth["bearer"] is not None
117
- and self.registry_auth["bearer"] == used_token
113
+ self.factory.registry_auth["bearer"] is not None
114
+ and self.factory.registry_auth["bearer"] == used_token
118
115
  ):
119
116
  return
120
117
  log.info("Updating bearer token")
@@ -157,7 +154,7 @@ class RegistryAuthHttpDownloader(HttpDownloader):
157
154
 
158
155
  token_js = json.loads(token_data)
159
156
  token = token_js.get("token") or token_js.get("access_token")
160
- self.registry_auth["bearer"] = token
157
+ self.factory.registry_auth["bearer"] = token
161
158
 
162
159
  @staticmethod
163
160
  def auth_header(token, basic_auth):
@@ -187,6 +184,21 @@ class RegistryAuthHttpDownloader(HttpDownloader):
187
184
  )
188
185
 
189
186
 
187
+ class RegistryAuthDownloaderFactory(DownloaderFactory):
188
+ """
189
+ A downloader factory for registry access that can also share the authentication tokens.
190
+ """
191
+
192
+ def __init__(self, *args, **kwargs):
193
+ super().__init__(*args, **kwargs)
194
+ self.registry_auth = {"bearer": None, "basic": None}
195
+ self.token_lock = asyncio.Lock()
196
+
197
+ def _http_or_https(self, download_class, url, **kwargs):
198
+ kwargs.setdefault("factory", self)
199
+ return super()._http_or_https(download_class, url, **kwargs)
200
+
201
+
190
202
  class NoAuthSignatureDownloader(HttpDownloader):
191
203
  """A downloader class suited for signature downloads."""
192
204
 
@@ -13,7 +13,6 @@ from django.shortcuts import redirect
13
13
  from django_lifecycle import AFTER_CREATE, AFTER_DELETE, AFTER_UPDATE, hook
14
14
 
15
15
  from pulpcore.plugin.cache import SyncContentCache
16
- from pulpcore.plugin.download import DownloaderFactory
17
16
  from pulpcore.plugin.models import (
18
17
  Artifact,
19
18
  AutoAddObjPermsMixin,
@@ -510,17 +509,17 @@ class ContainerRemote(Remote, AutoAddObjPermsMixin):
510
509
  """
511
510
  Downloader Factory that maps to custom downloaders which support registry auth.
512
511
 
513
- Upon first access, the DownloaderFactory is instantiated and saved internally.
512
+ Upon first access, the RegistryAuthDownloaderFactory is instantiated and saved internally.
514
513
 
515
514
  Returns:
516
- DownloadFactory: The instantiated DownloaderFactory to be used by
515
+ DownloadFactory: The instantiated RegistryAuthDownloaderFactory to be used by
517
516
  get_downloader()
518
517
 
519
518
  """
520
519
  try:
521
520
  return self._download_factory
522
521
  except AttributeError:
523
- self._download_factory = DownloaderFactory(
522
+ self._download_factory = downloaders.RegistryAuthDownloaderFactory(
524
523
  self,
525
524
  downloader_overrides={
526
525
  "http": downloaders.RegistryAuthHttpDownloader,
@@ -1477,14 +1477,43 @@ class Manifests(RedirectsMixin, ContainerRegistryApiMixin, ViewSet):
1477
1477
  Fetch a manifest from the upstream remote.
1478
1478
  Returns the local manifest, if it exists in Pulp, and the full response from upstream.
1479
1479
  Raises response errors if manifest is not found or the download fails.
1480
+
1481
+ A HEAD request is issued first to check the manifest's digest against what is already
1482
+ stored in Pulp. Per Docker Hub's pull-rate accounting, a HEAD ("version check") does not
1483
+ count as a pull, unlike a GET. If the manifest is already stored locally, the HEAD
1484
+ response is returned as-is and the counted GET is skipped entirely.
1480
1485
  """
1481
1486
  relative_url = "/v2/{name}/manifests/{pk}".format(
1482
1487
  name=remote.namespaced_upstream_name, pk=pk
1483
1488
  )
1484
1489
  tag_url = urljoin(remote.url, relative_url)
1490
+
1491
+ head_response = self._fetch_manifest_response(remote, tag_url, pk, http_method="head")
1492
+ digest = head_response.headers.get("docker-content-digest")
1493
+ if digest:
1494
+ manifest = models.Manifest.objects.filter(
1495
+ digest=digest, pulp_domain=get_domain()
1496
+ ).first()
1497
+ if manifest is not None:
1498
+ return manifest, head_response
1499
+
1500
+ # The manifest is not stored locally yet, or the upstream did not report a digest on the
1501
+ # HEAD response; fall back to a full GET, identical to the previous behavior.
1502
+ response = self._fetch_manifest_response(remote, tag_url, pk, http_method="get")
1503
+ digest = response.headers.get("docker-content-digest")
1504
+ return models.Manifest.objects.filter(
1505
+ digest=digest, pulp_domain=get_domain()
1506
+ ).first(), response
1507
+
1508
+ def _fetch_manifest_response(self, remote, tag_url, pk, http_method):
1509
+ """
1510
+ Issue a HEAD or GET request for a manifest and map response errors consistently.
1511
+ """
1485
1512
  downloader = remote.get_downloader(url=tag_url)
1486
1513
  try:
1487
- response = downloader.fetch(extra_data={"headers": V2_ACCEPT_HEADERS})
1514
+ return downloader.fetch(
1515
+ extra_data={"headers": V2_ACCEPT_HEADERS, "http_method": http_method}
1516
+ )
1488
1517
  except ClientResponseError as response_error:
1489
1518
  if response_error.status == 429:
1490
1519
  # the client could request the manifest outside the docker hub pull limit;
@@ -1497,11 +1526,6 @@ class Manifests(RedirectsMixin, ContainerRegistryApiMixin, ViewSet):
1497
1526
  except (ClientConnectionError, TimeoutException):
1498
1527
  # The remote server is not available at the moment
1499
1528
  raise GatewayTimeout()
1500
- else:
1501
- digest = response.headers.get("docker-content-digest")
1502
- return models.Manifest.objects.filter(
1503
- digest=digest, pulp_domain=get_domain()
1504
- ).first(), response
1505
1529
 
1506
1530
  def put(self, request, path, pk=None):
1507
1531
  """
@@ -561,7 +561,7 @@ class TagOperationsMixin:
561
561
  responses={202: AsyncOperationResponseSerializer},
562
562
  )
563
563
  @action(detail=True, methods=["post"], serializer_class=serializers.TagImageSerializer)
564
- def tag(self, request, pk):
564
+ def tag(self, request, pk, **kwargs):
565
565
  """
566
566
  Create a task which is responsible for creating a new tag.
567
567
  """
@@ -592,7 +592,7 @@ class TagOperationsMixin:
592
592
  responses={202: AsyncOperationResponseSerializer},
593
593
  )
594
594
  @action(detail=True, methods=["post"], serializer_class=serializers.UnTagImageSerializer)
595
- def untag(self, request, pk):
595
+ def untag(self, request, pk, **kwargs):
596
596
  """
597
597
  Create a task which is responsible for untagging an image.
598
598
  """
@@ -622,7 +622,7 @@ class SignOperationsMixin:
622
622
  responses={202: AsyncOperationResponseSerializer},
623
623
  )
624
624
  @action(detail=True, methods=["post"], serializer_class=serializers.RepositorySignSerializer)
625
- def sign(self, request, pk):
625
+ def sign(self, request, pk, **kwargs):
626
626
  """
627
627
  Signs manifests by tag in the repository.
628
628
  """
@@ -827,7 +827,7 @@ class ContainerRepositoryViewSet(
827
827
  methods=["post"],
828
828
  serializer_class=serializers.ContainerRepositorySyncURLSerializer,
829
829
  )
830
- def sync(self, request, pk):
830
+ def sync(self, request, pk, **kwargs):
831
831
  """
832
832
  Synchronizes a repository. The ``repository`` field has to be provided.
833
833
  """
@@ -861,7 +861,7 @@ class ContainerRepositoryViewSet(
861
861
  responses={202: AsyncOperationResponseSerializer},
862
862
  )
863
863
  @action(detail=True, methods=["post"], serializer_class=serializers.RecursiveManageSerializer)
864
- def add(self, request, pk):
864
+ def add(self, request, pk, **kwargs):
865
865
  """
866
866
  Queues a task that creates a new RepositoryVersion by adding content units.
867
867
  """
@@ -900,7 +900,7 @@ class ContainerRepositoryViewSet(
900
900
  responses={202: AsyncOperationResponseSerializer},
901
901
  )
902
902
  @action(detail=True, methods=["post"], serializer_class=serializers.RecursiveManageSerializer)
903
- def remove(self, request, pk):
903
+ def remove(self, request, pk, **kwargs):
904
904
  """
905
905
  Queues a task that creates a new RepositoryVersion by removing content units.
906
906
  """
@@ -935,7 +935,7 @@ class ContainerRepositoryViewSet(
935
935
  responses={202: AsyncOperationResponseSerializer},
936
936
  )
937
937
  @action(detail=True, methods=["post"], serializer_class=serializers.TagCopySerializer)
938
- def copy_tags(self, request, pk):
938
+ def copy_tags(self, request, pk, **kwargs):
939
939
  """
940
940
  Queues a task that creates a new RepositoryVersion by adding Tags.
941
941
  """
@@ -968,7 +968,7 @@ class ContainerRepositoryViewSet(
968
968
  responses={202: AsyncOperationResponseSerializer},
969
969
  )
970
970
  @action(detail=True, methods=["post"], serializer_class=serializers.ManifestCopySerializer)
971
- def copy_manifests(self, request, pk):
971
+ def copy_manifests(self, request, pk, **kwargs):
972
972
  """
973
973
  Queues a task that creates a new RepositoryVersion by adding Manifests.
974
974
  """
@@ -1008,7 +1008,7 @@ class ContainerRepositoryViewSet(
1008
1008
  responses={202: AsyncOperationResponseSerializer},
1009
1009
  )
1010
1010
  @action(detail=True, methods=["post"], serializer_class=serializers.OCIBuildImageSerializer)
1011
- def build_image(self, request, pk):
1011
+ def build_image(self, request, pk, **kwargs):
1012
1012
  """
1013
1013
  Create a task which is responsible for creating a new image and tag.
1014
1014
  """
@@ -1169,7 +1169,7 @@ class ContainerPushRepositoryViewSet(
1169
1169
  @action(
1170
1170
  detail=True, methods=["post"], serializer_class=serializers.MigratePushRepositorySerializer
1171
1171
  )
1172
- def migrate(self, request, pk):
1172
+ def migrate(self, request, pk, **kwargs):
1173
1173
  """
1174
1174
  Create a task which converts a push repository into a container repository in place.
1175
1175
  """
@@ -1199,7 +1199,7 @@ class ContainerPushRepositoryViewSet(
1199
1199
  responses={202: AsyncOperationResponseSerializer},
1200
1200
  )
1201
1201
  @action(detail=True, methods=["post"], serializer_class=serializers.RemoveImageSerializer)
1202
- def remove_image(self, request, pk):
1202
+ def remove_image(self, request, pk, **kwargs):
1203
1203
  """
1204
1204
  Create a task which deletes an image by the passed digest.
1205
1205
  """
@@ -1225,7 +1225,7 @@ class ContainerPushRepositoryViewSet(
1225
1225
  return OperationPostponedResponse(result, request)
1226
1226
 
1227
1227
  @action(detail=True, methods=["post"], serializer_class=serializers.RemoveSignaturesSerializer)
1228
- def remove_signatures(self, request, pk):
1228
+ def remove_signatures(self, request, pk, **kwargs):
1229
1229
  """
1230
1230
  Create a task which deletes signatures by the passed key_id.
1231
1231
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pulp-container
3
- Version: 2.29.1
3
+ Version: 2.30.0
4
4
  Summary: Container plugin for the Pulp Project
5
5
  Author-email: Pulp Team <pulp-list@redhat.com>
6
6
  Project-URL: Homepage, https://pulpproject.org
@@ -18,7 +18,7 @@ Requires-Python: >=3.11
18
18
  Description-Content-Type: text/markdown
19
19
  License-File: LICENSE
20
20
  Requires-Dist: jsonschema<4.27,>=4.4
21
- Requires-Dist: pulpcore<3.130,>=3.111.0
22
- Requires-Dist: pyjwt[crypto]<2.14,>=2.4
21
+ Requires-Dist: pulpcore<3.130,>=3.118.0
22
+ Requires-Dist: pyjwt[crypto]<2.16,>=2.4
23
23
  Requires-Dist: pysequoia<0.2.0,>=0.1.35
24
24
  Dynamic: license-file
@@ -0,0 +1,4 @@
1
+ jsonschema<4.27,>=4.4
2
+ pulpcore<3.130,>=3.118.0
3
+ pyjwt[crypto]<2.16,>=2.4
4
+ pysequoia<0.2.0,>=0.1.35
@@ -7,7 +7,7 @@ build-backend = 'setuptools.build_meta'
7
7
 
8
8
  [project]
9
9
  name = "pulp-container"
10
- version = "2.29.1"
10
+ version = "2.30.0"
11
11
  description = "Container plugin for the Pulp Project"
12
12
  readme = "README.md"
13
13
  authors = [
@@ -24,8 +24,8 @@ classifiers=[
24
24
  requires-python = ">=3.11"
25
25
  dependencies = [
26
26
  "jsonschema>=4.4,<4.27",
27
- "pulpcore>=3.111.0,<3.130",
28
- "pyjwt[crypto]>=2.4,<2.14",
27
+ "pulpcore>=3.118.0,<3.130",
28
+ "pyjwt[crypto]>=2.4,<2.16",
29
29
  "pysequoia>=0.1.35,<0.2.0"
30
30
  ]
31
31
 
@@ -89,7 +89,7 @@ ignore = [
89
89
  [tool.bumpversion]
90
90
  # This section is managed by the plugin template. Do not edit manually.
91
91
 
92
- current_version = "2.29.1"
92
+ current_version = "2.30.0"
93
93
  commit = false
94
94
  tag = false
95
95
  parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<alpha>0a)?(?P<patch>\\d+)(\\.(?P<release>[a-z]+))?"
@@ -1,4 +0,0 @@
1
- jsonschema<4.27,>=4.4
2
- pulpcore<3.130,>=3.111.0
3
- pyjwt[crypto]<2.14,>=2.4
4
- pysequoia<0.2.0,>=0.1.35
File without changes