pulp-container 2.28.1__tar.gz → 2.29.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.
- {pulp_container-2.28.1 → pulp_container-2.29.0}/CHANGES.md +125 -2
- {pulp_container-2.28.1 → pulp_container-2.29.0}/MANIFEST.in +4 -3
- {pulp_container-2.28.1 → pulp_container-2.29.0}/PKG-INFO +1 -1
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/__init__.py +1 -1
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/authorization.py +0 -1
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/exceptions.py +33 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/global_access_conditions.py +3 -2
- pulp_container-2.29.0/pulp_container/app/migrations/0050_alter_containernamespace_options.py +17 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/models.py +3 -3
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/registry_api.py +115 -15
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/serializers.py +11 -4
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/__init__.py +1 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/builder.py +5 -4
- pulp_container-2.29.0/pulp_container/app/tasks/migrate_push_repository.py +133 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/utils.py +19 -3
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/viewsets.py +184 -122
- pulp_container-2.29.0/pulp_container/tests/functional/api/test_cancel_blob_upload.py +71 -0
- pulp_container-2.29.0/pulp_container/tests/functional/api/test_delete_blob.py +123 -0
- pulp_container-2.29.0/pulp_container/tests/functional/api/test_delete_manifest.py +97 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_domains.py +18 -3
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_flatpak.py +2 -2
- pulp_container-2.29.0/pulp_container/tests/functional/api/test_migrate_push_repository.py +181 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_pull_content.py +40 -1
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_push_content.py +83 -18
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_push_signatures.py +2 -2
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_rbac_push_repositories.py +86 -2
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_rbac_repo_content.py +32 -39
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_rbac_repo_versions.py +71 -3
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_recursive_remove.py +9 -2
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_sign_manifests.py +3 -3
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_tagging_images.py +2 -2
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/conftest.py +31 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container.egg-info/PKG-INFO +1 -1
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container.egg-info/SOURCES.txt +6 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pyproject.toml +3 -2
- {pulp_container-2.28.1 → pulp_container-2.29.0}/COMMITMENT +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/COPYRIGHT +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/LICENSE +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/README.rst +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/functest_requirements.txt +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/access_policy.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/cache.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/checks.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/content.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/downloaders.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/dynaconf_hooks.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/fields.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/json_schemas.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/management/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/management/commands/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/management/commands/container-handle-image-data.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/management/commands/container-repair-media-type.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0001_initial.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0001_squashed_0032_upload_artifact.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0002_containerrepository.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0003_oci_mediatype.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0004_upload.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0005_contentredirectcontentguard.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0006_containerpushrepository.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0007_clear_tags_artifacts_refs.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0008_include_exclude_tags.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0009_container_namespace.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0010_remove_uploadchunk.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0011_add_container_repository_permissions.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0012_add_container_namespace_permissions.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0013_add_pull_push_permissions.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0014_containerdistribution_private.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0015_manage_tags_push_repo.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0016_add_delete_versions_permission.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0017_add_granular_perms.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0018_containerdistribution_description.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0019_DATA_distribution_model_swap.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0020_update_push_repo_perms.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0021_data_move_redirect_content_guard_to_core.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0022_delete_contentredirectcontentguard.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0023_manifestsignature.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0024_containerremote_sigstore.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0025_signature_stored_in_textfield.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0026_manifest_signing_service.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0027_data_translate_perms_to_roles.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0028_add_role_manage_permissions.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0029_remove_blob_media_type.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0030_enforce_tagged_manifest_reference.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0031_replace_charf_with_textf.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0032_upload_artifact.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0033_raise_warning_for_repair.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0034_translate_signed_schema.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0035_alter_blob_content_ptr_and_more.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0036_containerpushrepository_pending_blobs_manifests.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0037_create_pull_through_cache_models.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0038_add_manifest_metadata_fields.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0039_manifest_data.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0040_add_remote_repo_filter.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0041_add_pull_through_pull_permissions.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0042_add_manifest_nature_field.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0043_add_os_arch_image_size_manifest_fields.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0044_add_domain.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0045_alter_manifest_compressed_image_size.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0046_alter_manifest_listed_manifests.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0047_containernamespace_pulp_labels.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0048_containerremote_includes_excludes.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/0049_manifestsignature_fingerprint.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/migrations/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/modelresource.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/redirects.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/registry.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/replica.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/settings.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/download_image_data.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/recursive_add.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/recursive_remove.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/sign.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/sync_stages.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/synchronize.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/tag.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/tasks/untag.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/token_verification.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/urls.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/webserver_snippets/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/webserver_snippets/apache.conf +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/webserver_snippets/nginx.conf +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/constants.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_build_images.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_content_cache.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_crud_distributions.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_crud_remotes.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_pull_through_cache.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_pulpimportexport.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_rbac_remotes.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_rbac_sync_repositories.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_recursive_add.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_remote_filter_pull_through.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_repositories_list.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_sync.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_sync_signatures.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/api/test_token_authentication.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/constants.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/functional/utils.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/unit/__init__.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/unit/fixtures/ryuk_0.13.0_manifest_list.json +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/unit/test_json_schemas.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/unit/test_models.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/unit/test_serializers.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/tests/unit/test_sync_stages.py +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container.egg-info/dependency_links.txt +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container.egg-info/entry_points.txt +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container.egg-info/requires.txt +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container.egg-info/top_level.txt +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/setup.cfg +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/test_requirements.txt +0 -0
- {pulp_container-2.28.1 → pulp_container-2.29.0}/unittest_requirements.txt +0 -0
|
@@ -8,6 +8,36 @@
|
|
|
8
8
|
|
|
9
9
|
[//]: # (towncrier release notes start)
|
|
10
10
|
|
|
11
|
+
## 2.29.0 (2026-08-10) {: #2.29.0 }
|
|
12
|
+
|
|
13
|
+
#### Features {: #2.29.0-feature }
|
|
14
|
+
|
|
15
|
+
- Enable DELETE on the Docker v2 manifest endpoint so users can delete manifests by digest.
|
|
16
|
+
[#480](https://github.com/pulp/pulp_container/issues/480)
|
|
17
|
+
- Enable DELETE on the Docker v2 blob endpoint so users can delete blobs by digest.
|
|
18
|
+
[#481](https://github.com/pulp/pulp_container/issues/481)
|
|
19
|
+
- Added support for retrieving blob upload status via `GET /v2/<name>/blobs/uploads/<uuid>`.
|
|
20
|
+
[#483](https://github.com/pulp/pulp_container/issues/483)
|
|
21
|
+
- Added a `migrate` endpoint on push repositories that converts a legacy `ContainerPushRepository`
|
|
22
|
+
into a `ContainerRepository` in place. The repository primary key and version history are
|
|
23
|
+
preserved; only the repository type changes.
|
|
24
|
+
- Added support for cancelling blob uploads via `DELETE /v2/<name>/blobs/uploads/<uuid>`.
|
|
25
|
+
- Changed the default created repository on push to be a ContainerRepository instead of a ContainerPushRepository. ContainerPushRepository will eventually be phased out in future releases.
|
|
26
|
+
|
|
27
|
+
#### Bugfixes {: #2.29.0-bugfix }
|
|
28
|
+
|
|
29
|
+
- Fixed tag-based manifest pulls returning 404 when the client's `Accept` header listed the tagged manifest's media type together with parameters such as q-values, since the media type comparison did not strip them and had no support for the `*/*` wildcard.
|
|
30
|
+
[#2417](https://github.com/pulp/pulp_container/issues/2417)
|
|
31
|
+
- Add transparent repair code during Manifest sync to redownload the raw data in case it was lost during an upgrade.
|
|
32
|
+
- Added OCI manifest as an allowed media type in the Docker manifest list schema.
|
|
33
|
+
- Raised a proper `PulpException` subclass instead of a bare `Exception` when building or pushing an OCI image fails, so the error is not sanitized away by pulpcore in a future release.
|
|
34
|
+
|
|
35
|
+
#### Improved Documentation {: #2.29.0-doc }
|
|
36
|
+
|
|
37
|
+
- Rewrote the RBAC documentation to be more comprehensive and detailed.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
11
41
|
## 2.28.1 (2026-07-22) {: #2.28.1 }
|
|
12
42
|
|
|
13
43
|
#### Bugfixes {: #2.28.1-bugfix }
|
|
@@ -68,6 +98,21 @@
|
|
|
68
98
|
|
|
69
99
|
---
|
|
70
100
|
|
|
101
|
+
## 2.27.11 (2026-07-29) {: #2.27.11 }
|
|
102
|
+
|
|
103
|
+
No significant changes.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 2.27.10 (2026-06-08) {: #2.27.10 }
|
|
108
|
+
|
|
109
|
+
#### Bugfixes {: #2.27.10-bugfix }
|
|
110
|
+
|
|
111
|
+
- Add transparent repair code during Manifest sync to redownload the raw data in case it was lost during an upgrade.
|
|
112
|
+
- Added OCI manifest as an allowed media type in the Docker manifest list schema.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
71
116
|
## 2.27.9 (2026-05-14) {: #2.27.9 }
|
|
72
117
|
|
|
73
118
|
#### Bugfixes {: #2.27.9-bugfix }
|
|
@@ -172,7 +217,22 @@ No significant changes.
|
|
|
172
217
|
|
|
173
218
|
#### Misc {: #2.27.0-misc }
|
|
174
219
|
|
|
175
|
-
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
223
|
+
## 2.26.16 (2026-06-08) {: #2.26.16 }
|
|
224
|
+
|
|
225
|
+
#### Bugfixes {: #2.26.16-bugfix }
|
|
226
|
+
|
|
227
|
+
- Added OCI manifest as an allowed media type in the Docker manifest list schema.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 2.26.15 (2026-06-02) {: #2.26.15 }
|
|
232
|
+
|
|
233
|
+
#### Bugfixes {: #2.26.15-bugfix }
|
|
234
|
+
|
|
235
|
+
- Readded backwards compatibility for manifests that haven't been migrated to new data field. Sync will try to update them to the new format and repair manifests that are missing their artifact, but it is recommended that you run the `pulpcore-manager container-handle-image-data` command to fix all manifests at once. The command has been updated to report broken manifests by repository. Broken manifests in pushed repositories need to be deleted and re-pushed.
|
|
176
236
|
|
|
177
237
|
---
|
|
178
238
|
|
|
@@ -301,6 +361,8 @@ No significant changes.
|
|
|
301
361
|
|
|
302
362
|
## 2.26.0 (2025-08-14) {: #2.26.0 }
|
|
303
363
|
|
|
364
|
+
**WARNING:** If your installation pre-dates pulp_container 2.20, make sure that you run `pulpcore-manager container-handle-image-data` before upgrading to (or past) this release. If your installation began after pulp_container 2.20, or if you have already run this command at some point, this is not necessary.
|
|
365
|
+
|
|
304
366
|
#### Features {: #2.26.0-feature }
|
|
305
367
|
|
|
306
368
|
- Bump pulpcore upperbound to <3.100. pulp_container is now a Python >=3.11 project.
|
|
@@ -338,6 +400,8 @@ No significant changes.
|
|
|
338
400
|
|
|
339
401
|
## 2.25.0 (2025-04-15) {: #2.25.0 }
|
|
340
402
|
|
|
403
|
+
**WARNING:** If your installation pre-dates pulp_container 2.20, make sure that you run `pulpcore-manager container-handle-image-data` before upgrading to (or past) this release. If your installation began after pulp_container 2.20, or if you have already run this command at some point, this is not necessary.
|
|
404
|
+
|
|
341
405
|
#### Features {: #2.25.0-feature }
|
|
342
406
|
|
|
343
407
|
- Added full support for domains.
|
|
@@ -362,6 +426,14 @@ No significant changes.
|
|
|
362
426
|
|
|
363
427
|
---
|
|
364
428
|
|
|
429
|
+
## 2.24.12 (2026-07-21) {: #2.24.12 }
|
|
430
|
+
|
|
431
|
+
#### Bugfixes {: #2.24.12-bugfix }
|
|
432
|
+
|
|
433
|
+
- Added OCI manifest as an allowed media type in the Docker manifest list schema.
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
365
437
|
## 2.24.11 (2026-04-29) {: #2.24.11 }
|
|
366
438
|
|
|
367
439
|
#### Bugfixes {: #2.24.11-bugfix }
|
|
@@ -492,6 +564,23 @@ No significant changes.
|
|
|
492
564
|
|
|
493
565
|
---
|
|
494
566
|
|
|
567
|
+
## 2.22.10 (2026-07-23) {: #2.22.10 }
|
|
568
|
+
|
|
569
|
+
#### Bugfixes {: #2.22.10-bugfix }
|
|
570
|
+
|
|
571
|
+
- Fixed OS and architecture filters being silently ignored when filtering manifest lists in the flatpak index.
|
|
572
|
+
- Fixed a serialization issue with the FlatpakIndex cache.
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## 2.22.9 (2026-07-21) {: #2.22.9 }
|
|
577
|
+
|
|
578
|
+
#### Bugfixes {: #2.22.9-bugfix }
|
|
579
|
+
|
|
580
|
+
- Added OCI manifest as an allowed media type in the Docker manifest list schema.
|
|
581
|
+
|
|
582
|
+
---
|
|
583
|
+
|
|
495
584
|
## 2.22.8 (2026-04-24) {: #2.22.8 }
|
|
496
585
|
|
|
497
586
|
No significant changes.
|
|
@@ -668,6 +757,23 @@ No significant changes.
|
|
|
668
757
|
|
|
669
758
|
---
|
|
670
759
|
|
|
760
|
+
## 2.20.11 (2026-07-27) {: #2.20.11 }
|
|
761
|
+
|
|
762
|
+
#### Bugfixes {: #2.20.11-bugfix }
|
|
763
|
+
|
|
764
|
+
- Fixed OS and architecture filters being silently ignored when filtering manifest lists in the flatpak index.
|
|
765
|
+
- Fixed a serialization issue with the FlatpakIndex cache.
|
|
766
|
+
|
|
767
|
+
---
|
|
768
|
+
|
|
769
|
+
## 2.20.10 (2026-07-21) {: #2.20.10 }
|
|
770
|
+
|
|
771
|
+
#### Bugfixes {: #2.20.10-bugfix }
|
|
772
|
+
|
|
773
|
+
- Added OCI manifest as an allowed media type in the Docker manifest list schema.
|
|
774
|
+
|
|
775
|
+
---
|
|
776
|
+
|
|
671
777
|
## 2.20.9 (2026-04-24) {: #2.20.9 }
|
|
672
778
|
|
|
673
779
|
No significant changes.
|
|
@@ -758,6 +864,8 @@ No significant changes.
|
|
|
758
864
|
|
|
759
865
|
## 2.20.0 (2024-05-06) {: #2.20.0 }
|
|
760
866
|
|
|
867
|
+
**NOTICE:** If you are upgrading from a previous version of pulp_container, make sure that you run `pulpcore-manager container-handle-image-data` at some point before or shortly after upgrading. This is required in order to complete the transition away from storing Manifest data as artifacts. This must be done to avoid the potential for issues with later releases of pulp_container.
|
|
868
|
+
|
|
761
869
|
### Features
|
|
762
870
|
|
|
763
871
|
- Updated the Manifest model to no longer rely on artifacts, storing all manifest data internally
|
|
@@ -786,7 +894,22 @@ No significant changes.
|
|
|
786
894
|
|
|
787
895
|
### Misc
|
|
788
896
|
|
|
789
|
-
-
|
|
897
|
+
-
|
|
898
|
+
|
|
899
|
+
---
|
|
900
|
+
|
|
901
|
+
## 2.19.13 (2026-07-27) {: #2.19.13 }
|
|
902
|
+
|
|
903
|
+
#### Bugfixes {: #2.19.13-bugfix }
|
|
904
|
+
|
|
905
|
+
- Fixed OS and architecture filters being silently ignored when filtering manifest lists in the flatpak index.
|
|
906
|
+
- Fixed a serialization issue with the FlatpakIndex cache.
|
|
907
|
+
|
|
908
|
+
---
|
|
909
|
+
|
|
910
|
+
## 2.19.12 (2026-06-08) {: #2.19.12 }
|
|
911
|
+
|
|
912
|
+
No significant changes.
|
|
790
913
|
|
|
791
914
|
---
|
|
792
915
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
include LICENSE
|
|
2
1
|
include pulp_container/app/webserver_snippets/*
|
|
3
|
-
include
|
|
2
|
+
include pulp_container/tests/unit/fixtures/*
|
|
3
|
+
include LICENSE
|
|
4
4
|
include CHANGES.md
|
|
5
5
|
include COMMITMENT
|
|
6
6
|
include COPYRIGHT
|
|
7
|
+
include pyproject.toml
|
|
7
8
|
include functest_requirements.txt
|
|
8
9
|
include test_requirements.txt
|
|
9
10
|
include unittest_requirements.txt
|
|
10
11
|
exclude CONTRIBUTING.md
|
|
11
12
|
exclude releasing.md
|
|
12
|
-
|
|
13
|
+
exclude Makefile
|
|
@@ -295,7 +295,6 @@ class PermissionChecker:
|
|
|
295
295
|
if not domain:
|
|
296
296
|
return False
|
|
297
297
|
|
|
298
|
-
print("Checking push permissions for path ", path, "and domain ", domain.name)
|
|
299
298
|
try:
|
|
300
299
|
distribution = ContainerDistribution.objects.get(base_path=path, pulp_domain=domain)
|
|
301
300
|
except ContainerDistribution.DoesNotExist:
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from rest_framework.exceptions import APIException, NotFound, ParseError
|
|
2
2
|
|
|
3
|
+
from pulpcore.plugin.exceptions import PulpException
|
|
4
|
+
|
|
3
5
|
|
|
4
6
|
class BadGateway(APIException):
|
|
5
7
|
status_code = 502
|
|
@@ -90,6 +92,24 @@ class BlobInvalid(ParseError):
|
|
|
90
92
|
)
|
|
91
93
|
|
|
92
94
|
|
|
95
|
+
class BlobUploadUnknown(NotFound):
|
|
96
|
+
"""Exception to render a 404 with the code 'BLOB_UPLOAD_UNKNOWN'"""
|
|
97
|
+
|
|
98
|
+
def __init__(self, uuid):
|
|
99
|
+
"""Initialize the exception with the upload uuid."""
|
|
100
|
+
super().__init__(
|
|
101
|
+
detail={
|
|
102
|
+
"errors": [
|
|
103
|
+
{
|
|
104
|
+
"code": "BLOB_UPLOAD_UNKNOWN",
|
|
105
|
+
"message": "blob upload unknown to registry",
|
|
106
|
+
"detail": {"uuid": uuid},
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
|
|
93
113
|
class ManifestNotFound(NotFound):
|
|
94
114
|
"""Exception to render a 404 with the code 'MANIFEST_UNKNOWN'"""
|
|
95
115
|
|
|
@@ -144,6 +164,19 @@ class ManifestSignatureInvalid(ParseError):
|
|
|
144
164
|
)
|
|
145
165
|
|
|
146
166
|
|
|
167
|
+
class ContainerBuildError(PulpException):
|
|
168
|
+
"""Exception to signal that building or pushing an OCI image failed."""
|
|
169
|
+
|
|
170
|
+
error_code = "CON0001"
|
|
171
|
+
|
|
172
|
+
def __init__(self, message):
|
|
173
|
+
"""Initialize the exception with the decoded stderr of the failed podman command."""
|
|
174
|
+
self.message = message
|
|
175
|
+
|
|
176
|
+
def __str__(self):
|
|
177
|
+
return self.message
|
|
178
|
+
|
|
179
|
+
|
|
147
180
|
class InvalidRequest(ParseError):
|
|
148
181
|
"""An exception to render an HTTP 400 response."""
|
|
149
182
|
|
{pulp_container-2.28.1 → pulp_container-2.29.0}/pulp_container/app/global_access_conditions.py
RENAMED
|
@@ -28,9 +28,10 @@ def has_namespace_obj_perms(request, view, action, permission):
|
|
|
28
28
|
if type(obj) is models.ContainerDistribution:
|
|
29
29
|
namespace = obj.namespace
|
|
30
30
|
return request.user.has_perm(permission, namespace)
|
|
31
|
-
elif type(obj) is models.ContainerPushRepository:
|
|
31
|
+
elif type(obj) is models.ContainerPushRepository or type(obj) is models.ContainerRepository:
|
|
32
32
|
for dist in obj.distributions.all():
|
|
33
|
-
|
|
33
|
+
namespace = dist.cast().namespace
|
|
34
|
+
if namespace and request.user.has_perm(permission, namespace):
|
|
34
35
|
return True
|
|
35
36
|
elif type(obj) is models.ContainerPullThroughDistribution:
|
|
36
37
|
namespace = obj.namespace
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Generated by Django 5.2.13 on 2026-06-03 19:16
|
|
2
|
+
|
|
3
|
+
from django.db import migrations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
|
|
8
|
+
dependencies = [
|
|
9
|
+
('container', '0049_manifestsignature_fingerprint'),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
operations = [
|
|
13
|
+
migrations.AlterModelOptions(
|
|
14
|
+
name='containernamespace',
|
|
15
|
+
options={'permissions': [('namespace_add_containerdistribution', 'Add any distribution to a namespace'), ('namespace_delete_containerdistribution', 'Delete any distribution from a namespace'), ('namespace_view_containerdistribution', 'View any distribution in a namespace'), ('namespace_pull_containerdistribution', 'Pull from any distribution in a namespace'), ('namespace_push_containerdistribution', 'Push to any distribution in a namespace'), ('namespace_change_containerdistribution', 'Change any distribution in a namespace'), ('namespace_view_containerpushrepository', 'View any push repository in a namespace'), ('namespace_view_containerrepository', 'View any repository in a namespace'), ('namespace_modify_content_containerpushrepository', 'Modify content in any push repository in a namespace'), ('namespace_modify_content_containerrepository', 'Modify content in any repository in a namespace'), ('namespace_change_containerpushrepository', 'Update any existing push repository in a namespace'), ('namespace_change_containerrepository', 'Change any repository in a namespace'), ('manage_roles_containernamespace', 'Can manage role assignments on container namespace')]},
|
|
16
|
+
),
|
|
17
|
+
]
|
|
@@ -2,7 +2,6 @@ import json
|
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
import tempfile
|
|
5
|
-
import time
|
|
6
5
|
from gettext import gettext as _
|
|
7
6
|
from logging import getLogger
|
|
8
7
|
|
|
@@ -458,6 +457,7 @@ class ContainerNamespace(BaseModel, AutoAddObjPermsMixin):
|
|
|
458
457
|
("namespace_push_containerdistribution", "Push to any distribution in a namespace"),
|
|
459
458
|
("namespace_change_containerdistribution", "Change any distribution in a namespace"),
|
|
460
459
|
("namespace_view_containerpushrepository", "View any push repository in a namespace"),
|
|
460
|
+
("namespace_view_containerrepository", "View any repository in a namespace"),
|
|
461
461
|
(
|
|
462
462
|
"namespace_modify_content_containerpushrepository",
|
|
463
463
|
"Modify content in any push repository in a namespace",
|
|
@@ -470,6 +470,7 @@ class ContainerNamespace(BaseModel, AutoAddObjPermsMixin):
|
|
|
470
470
|
"namespace_change_containerpushrepository",
|
|
471
471
|
"Update any existing push repository in a namespace",
|
|
472
472
|
),
|
|
473
|
+
("namespace_change_containerrepository", "Change any repository in a namespace"),
|
|
473
474
|
(
|
|
474
475
|
"manage_roles_containernamespace",
|
|
475
476
|
"Can manage role assignments on container namespace",
|
|
@@ -947,8 +948,7 @@ INCOMPLETE_EXT = ".part"
|
|
|
947
948
|
|
|
948
949
|
def generate_filename(instance, filename):
|
|
949
950
|
"""Method for generating upload file name"""
|
|
950
|
-
|
|
951
|
-
return time.strftime(filename)
|
|
951
|
+
return os.path.join(instance.upload_dir, str(instance.pk) + INCOMPLETE_EXT)
|
|
952
952
|
|
|
953
953
|
|
|
954
954
|
class Upload(CoreUpload):
|
|
@@ -45,7 +45,13 @@ from rest_framework.viewsets import ViewSet
|
|
|
45
45
|
from pulpcore.plugin import pulp_hashlib
|
|
46
46
|
from pulpcore.plugin.exceptions import TimeoutException
|
|
47
47
|
from pulpcore.plugin.files import PulpTemporaryUploadedFile
|
|
48
|
-
from pulpcore.plugin.models import
|
|
48
|
+
from pulpcore.plugin.models import (
|
|
49
|
+
Artifact,
|
|
50
|
+
ContentArtifact,
|
|
51
|
+
RemoteArtifact,
|
|
52
|
+
Repository,
|
|
53
|
+
UploadChunk,
|
|
54
|
+
)
|
|
49
55
|
from pulpcore.plugin.tasking import dispatch
|
|
50
56
|
from pulpcore.plugin.util import get_domain, get_objects_for_user, get_url
|
|
51
57
|
|
|
@@ -60,6 +66,7 @@ from pulp_container.app.exceptions import (
|
|
|
60
66
|
BadGateway,
|
|
61
67
|
BlobInvalid,
|
|
62
68
|
BlobNotFound,
|
|
69
|
+
BlobUploadUnknown,
|
|
63
70
|
GatewayTimeout,
|
|
64
71
|
InvalidRequest,
|
|
65
72
|
ManifestInvalid,
|
|
@@ -73,7 +80,7 @@ from pulp_container.app.redirects import (
|
|
|
73
80
|
FileStorageRedirects,
|
|
74
81
|
S3StorageRedirects,
|
|
75
82
|
)
|
|
76
|
-
from pulp_container.app.tasks import aadd_and_remove, download_image_data
|
|
83
|
+
from pulp_container.app.tasks import aadd_and_remove, download_image_data, recursive_remove_content
|
|
77
84
|
from pulp_container.app.token_verification import (
|
|
78
85
|
RegistryAuthentication,
|
|
79
86
|
RegistryPermission,
|
|
@@ -88,6 +95,7 @@ from pulp_container.app.utils import (
|
|
|
88
95
|
get_accepted_media_types,
|
|
89
96
|
get_full_path,
|
|
90
97
|
has_task_completed,
|
|
98
|
+
is_media_type_accepted,
|
|
91
99
|
validate_manifest,
|
|
92
100
|
)
|
|
93
101
|
from pulp_container.constants import (
|
|
@@ -159,11 +167,11 @@ class ManifestResponse(Response):
|
|
|
159
167
|
manifest_media_type = manifest.media_type
|
|
160
168
|
if request:
|
|
161
169
|
accepted_media_types = get_accepted_media_types(request.headers)
|
|
162
|
-
if (
|
|
163
|
-
manifest_media_type
|
|
164
|
-
and manifest_media_type != MEDIA_TYPE.MANIFEST_V1
|
|
170
|
+
if accepted_media_types and not is_media_type_accepted(
|
|
171
|
+
accepted_media_types, manifest_media_type
|
|
165
172
|
):
|
|
166
|
-
|
|
173
|
+
if manifest_media_type != MEDIA_TYPE.MANIFEST_V1:
|
|
174
|
+
raise ManifestNotFound(reference=tag.name)
|
|
167
175
|
|
|
168
176
|
# Schema v1 manifests are always returned with the signed content type
|
|
169
177
|
if manifest_media_type == MEDIA_TYPE.MANIFEST_V1:
|
|
@@ -436,11 +444,9 @@ class ContainerRegistryApiMixin:
|
|
|
436
444
|
repository = distribution.repository
|
|
437
445
|
if repository:
|
|
438
446
|
repository = repository.cast()
|
|
439
|
-
if not repository.PUSH_ENABLED:
|
|
440
|
-
raise RepositoryInvalid(name=path, message="Repository is read-only.")
|
|
441
447
|
elif create:
|
|
442
448
|
with transaction.atomic():
|
|
443
|
-
repository = serializers.
|
|
449
|
+
repository = serializers.ContainerRepositorySerializer.get_or_create(
|
|
444
450
|
{"name": path, "pulp_domain": domain}
|
|
445
451
|
)
|
|
446
452
|
distribution.repository = repository
|
|
@@ -451,11 +457,22 @@ class ContainerRegistryApiMixin:
|
|
|
451
457
|
|
|
452
458
|
def create_dr(self, path, request):
|
|
453
459
|
domain = get_domain()
|
|
460
|
+
repository_types = [
|
|
461
|
+
models.ContainerRepository.get_pulp_type(),
|
|
462
|
+
models.ContainerPushRepository.get_pulp_type(),
|
|
463
|
+
]
|
|
454
464
|
with transaction.atomic():
|
|
455
465
|
try:
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
466
|
+
# Handle new default of ContainerRepository and fallback old ContainerPushRepository
|
|
467
|
+
repository = Repository.objects.filter(name=path, pulp_domain=domain).first()
|
|
468
|
+
if repository:
|
|
469
|
+
if repository.pulp_type not in repository_types:
|
|
470
|
+
raise RepositoryInvalid(name=path, message="Repository is read-only.")
|
|
471
|
+
repository = repository.cast()
|
|
472
|
+
else:
|
|
473
|
+
repository = serializers.ContainerRepositorySerializer.get_or_create(
|
|
474
|
+
{"name": path, "pulp_domain": domain}
|
|
475
|
+
)
|
|
459
476
|
distribution = serializers.ContainerDistributionSerializer.get_or_create(
|
|
460
477
|
{"base_path": path, "name": path, "pulp_domain": domain},
|
|
461
478
|
{"repository": get_url(repository)},
|
|
@@ -464,9 +481,10 @@ class ContainerRegistryApiMixin:
|
|
|
464
481
|
raise RepositoryInvalid(name=path, message="Repository is read-only.")
|
|
465
482
|
|
|
466
483
|
if distribution.repository:
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
484
|
+
if repository.pk != distribution.repository.pk:
|
|
485
|
+
raise RepositoryInvalid(
|
|
486
|
+
name=path, message="Repository is not available for push."
|
|
487
|
+
)
|
|
470
488
|
else:
|
|
471
489
|
distribution.repository = repository
|
|
472
490
|
distribution.save()
|
|
@@ -1014,6 +1032,30 @@ class BlobUploads(ContainerRegistryApiMixin, ViewSet):
|
|
|
1014
1032
|
|
|
1015
1033
|
return UploadResponse(upload=upload, path=path, request=request)
|
|
1016
1034
|
|
|
1035
|
+
def get(self, request, path, pk=None):
|
|
1036
|
+
"""
|
|
1037
|
+
Retrieve the status of an upload.
|
|
1038
|
+
"""
|
|
1039
|
+
_, repository = self.get_dr_push(request, path)
|
|
1040
|
+
upload = get_object_or_404(models.Upload, repository=repository, pk=pk)
|
|
1041
|
+
return UploadResponse(upload=upload, path=path, request=request, status=204)
|
|
1042
|
+
|
|
1043
|
+
def head(self, request, path, pk=None):
|
|
1044
|
+
"""Respond to HEAD requests about blob uploads."""
|
|
1045
|
+
return self.get(request, path, pk=pk)
|
|
1046
|
+
|
|
1047
|
+
def destroy(self, request, path, pk=None):
|
|
1048
|
+
"""
|
|
1049
|
+
Cancel an outstanding blob upload.
|
|
1050
|
+
"""
|
|
1051
|
+
_, repository = self.get_dr_push(request, path)
|
|
1052
|
+
try:
|
|
1053
|
+
upload = models.Upload.objects.get(repository=repository, pk=pk)
|
|
1054
|
+
except models.Upload.DoesNotExist:
|
|
1055
|
+
raise BlobUploadUnknown(uuid=pk)
|
|
1056
|
+
upload.delete()
|
|
1057
|
+
return Response(status=204)
|
|
1058
|
+
|
|
1017
1059
|
def put(self, request, path, pk=None):
|
|
1018
1060
|
"""
|
|
1019
1061
|
Create a blob from uploaded chunks.
|
|
@@ -1185,6 +1227,34 @@ class Blobs(RedirectsMixin, ContainerRegistryApiMixin, ViewSet):
|
|
|
1185
1227
|
raise BlobNotFound(digest=pk)
|
|
1186
1228
|
return blob_url
|
|
1187
1229
|
|
|
1230
|
+
def destroy(self, request, path, pk=None):
|
|
1231
|
+
"""
|
|
1232
|
+
Delete a blob identified by digest.
|
|
1233
|
+
"""
|
|
1234
|
+
if not pk.startswith("sha256:"):
|
|
1235
|
+
raise InvalidRequest(message="A blob can only be deleted by digest.")
|
|
1236
|
+
|
|
1237
|
+
_, repository = self.get_dr_push(request, path)
|
|
1238
|
+
latest_version = repository.latest_version()
|
|
1239
|
+
|
|
1240
|
+
if blob := models.Blob.objects.filter(digest=pk, pk__in=latest_version.content).first():
|
|
1241
|
+
dispatch(
|
|
1242
|
+
aadd_and_remove,
|
|
1243
|
+
exclusive_resources=[repository],
|
|
1244
|
+
kwargs={
|
|
1245
|
+
"repository_pk": str(repository.pk),
|
|
1246
|
+
"add_content_units": [],
|
|
1247
|
+
"remove_content_units": [str(blob.pk)],
|
|
1248
|
+
},
|
|
1249
|
+
immediate=True,
|
|
1250
|
+
deferred=True,
|
|
1251
|
+
)
|
|
1252
|
+
elif pending_blob := repository.pending_blobs.filter(digest=pk).first():
|
|
1253
|
+
repository.pending_blobs.remove(pending_blob)
|
|
1254
|
+
else:
|
|
1255
|
+
raise BlobNotFound(digest=pk)
|
|
1256
|
+
return Response(status=202)
|
|
1257
|
+
|
|
1188
1258
|
|
|
1189
1259
|
class Manifests(RedirectsMixin, ContainerRegistryApiMixin, ViewSet):
|
|
1190
1260
|
"""
|
|
@@ -1329,6 +1399,36 @@ class Manifests(RedirectsMixin, ContainerRegistryApiMixin, ViewSet):
|
|
|
1329
1399
|
# Fallthrough catchall, no manifest or tag found
|
|
1330
1400
|
raise ManifestNotFound(reference=pk)
|
|
1331
1401
|
|
|
1402
|
+
def destroy(self, request, path, pk=None):
|
|
1403
|
+
"""
|
|
1404
|
+
Delete a manifest identified by digest.
|
|
1405
|
+
"""
|
|
1406
|
+
if not pk.startswith("sha256:"):
|
|
1407
|
+
raise InvalidRequest(message="A manifest can only be deleted by digest.")
|
|
1408
|
+
|
|
1409
|
+
_, repository = self.get_dr_push(request, path)
|
|
1410
|
+
latest_version = repository.latest_version()
|
|
1411
|
+
|
|
1412
|
+
manifest = models.Manifest.objects.filter(digest=pk, pk__in=latest_version.content).first()
|
|
1413
|
+
if not manifest:
|
|
1414
|
+
manifest = repository.pending_manifests.filter(digest=pk).first()
|
|
1415
|
+
if not manifest:
|
|
1416
|
+
raise ManifestNotFound(reference=pk)
|
|
1417
|
+
repository.pending_manifests.remove(manifest)
|
|
1418
|
+
|
|
1419
|
+
tags = models.Tag.objects.filter(tagged_manifest=manifest, pk__in=latest_version.content)
|
|
1420
|
+
content_units = [str(manifest.pk)] + [str(tag.pk) for tag in tags]
|
|
1421
|
+
|
|
1422
|
+
dispatch(
|
|
1423
|
+
recursive_remove_content,
|
|
1424
|
+
exclusive_resources=[repository],
|
|
1425
|
+
kwargs={
|
|
1426
|
+
"repository_pk": str(repository.pk),
|
|
1427
|
+
"content_units": content_units,
|
|
1428
|
+
},
|
|
1429
|
+
)
|
|
1430
|
+
return Response(status=202)
|
|
1431
|
+
|
|
1332
1432
|
def get_content_units_to_add(self, manifest, tag=None):
|
|
1333
1433
|
add_content_units = [str(manifest.pk)]
|
|
1334
1434
|
if tag:
|
|
@@ -250,7 +250,7 @@ class ContainerNamespaceSerializer(ModelSerializer, GetOrCreateSerializerMixin):
|
|
|
250
250
|
model = models.ContainerNamespace
|
|
251
251
|
|
|
252
252
|
|
|
253
|
-
class ContainerRepositorySerializer(RepositorySerializer):
|
|
253
|
+
class ContainerRepositorySerializer(RepositorySerializer, GetOrCreateSerializerMixin):
|
|
254
254
|
"""
|
|
255
255
|
Serializer for Container Repositories.
|
|
256
256
|
"""
|
|
@@ -290,6 +290,12 @@ class ContainerPushRepositorySerializer(RepositorySerializer, GetOrCreateSeriali
|
|
|
290
290
|
model = models.ContainerPushRepository
|
|
291
291
|
|
|
292
292
|
|
|
293
|
+
class MigratePushRepositorySerializer(ValidateFieldsMixin, serializers.Serializer):
|
|
294
|
+
"""
|
|
295
|
+
Serializer for migrating a push repository to a container repository in place.
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
|
|
293
299
|
class ContainerRemoteSerializer(RemoteSerializer):
|
|
294
300
|
"""
|
|
295
301
|
A Serializer for ContainerRemote.
|
|
@@ -984,12 +990,13 @@ class RepositorySignSerializer(ValidateFieldsMixin, serializers.Serializer):
|
|
|
984
990
|
}
|
|
985
991
|
)
|
|
986
992
|
|
|
987
|
-
|
|
993
|
+
distro_count = repository.distributions.count()
|
|
994
|
+
if distro_count == 1:
|
|
988
995
|
if "future_base_path" in data:
|
|
989
996
|
raise serializers.ValidationError(
|
|
990
997
|
{
|
|
991
998
|
"future_base_path": _(
|
|
992
|
-
"This field cannot be set since this
|
|
999
|
+
"This field cannot be set since this repo has a single distribution."
|
|
993
1000
|
)
|
|
994
1001
|
}
|
|
995
1002
|
)
|
|
@@ -999,7 +1006,7 @@ class RepositorySignSerializer(ValidateFieldsMixin, serializers.Serializer):
|
|
|
999
1006
|
raise serializers.ValidationError(
|
|
1000
1007
|
{
|
|
1001
1008
|
"future_base_path": _(
|
|
1002
|
-
"This field is required since this
|
|
1009
|
+
"This field is required since this repo has zero or multiple distributions."
|
|
1003
1010
|
)
|
|
1004
1011
|
}
|
|
1005
1012
|
)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from .download_image_data import aadd_and_remove, download_image_data # noqa
|
|
2
2
|
from .builder import build_image_from_containerfile, build_image # noqa
|
|
3
|
+
from .migrate_push_repository import migrate_push_repository # noqa
|
|
3
4
|
from .recursive_add import recursive_add_content # noqa
|
|
4
5
|
from .recursive_remove import recursive_remove_content # noqa
|
|
5
6
|
from .sign import sign # noqa
|
|
@@ -13,6 +13,7 @@ from pulpcore.plugin.models import (
|
|
|
13
13
|
)
|
|
14
14
|
from pulpcore.plugin.util import get_domain
|
|
15
15
|
|
|
16
|
+
from pulp_container.app.exceptions import ContainerBuildError
|
|
16
17
|
from pulp_container.app.models import (
|
|
17
18
|
Blob,
|
|
18
19
|
BlobManifest,
|
|
@@ -179,7 +180,7 @@ def build_image(
|
|
|
179
180
|
stderr=subprocess.PIPE,
|
|
180
181
|
)
|
|
181
182
|
if bud_cp.returncode != 0:
|
|
182
|
-
raise
|
|
183
|
+
raise ContainerBuildError(bud_cp.stderr.decode())
|
|
183
184
|
image_dir = os.path.join(working_directory, "image")
|
|
184
185
|
os.makedirs(image_dir, exist_ok=True)
|
|
185
186
|
push_cp = subprocess.run(
|
|
@@ -188,7 +189,7 @@ def build_image(
|
|
|
188
189
|
stderr=subprocess.PIPE,
|
|
189
190
|
)
|
|
190
191
|
if push_cp.returncode != 0:
|
|
191
|
-
raise
|
|
192
|
+
raise ContainerBuildError(push_cp.stderr.decode())
|
|
192
193
|
repository_version = add_image_from_directory_to_repository(image_dir, repository, tag)
|
|
193
194
|
if isinstance(containerfile_artifact, PulpTemporaryFile):
|
|
194
195
|
containerfile_artifact.delete()
|
|
@@ -265,7 +266,7 @@ def build_image_from_containerfile(
|
|
|
265
266
|
stderr=subprocess.PIPE,
|
|
266
267
|
)
|
|
267
268
|
if bud_cp.returncode != 0:
|
|
268
|
-
raise
|
|
269
|
+
raise ContainerBuildError(bud_cp.stderr.decode())
|
|
269
270
|
image_dir = os.path.join(working_directory, "image")
|
|
270
271
|
os.makedirs(image_dir, exist_ok=True)
|
|
271
272
|
push_cp = subprocess.run(
|
|
@@ -274,7 +275,7 @@ def build_image_from_containerfile(
|
|
|
274
275
|
stderr=subprocess.PIPE,
|
|
275
276
|
)
|
|
276
277
|
if push_cp.returncode != 0:
|
|
277
|
-
raise
|
|
278
|
+
raise ContainerBuildError(push_cp.stderr.decode())
|
|
278
279
|
repository_version = add_image_from_directory_to_repository(image_dir, repository, tag)
|
|
279
280
|
|
|
280
281
|
return repository_version
|