pulpcore 3.83.2__py3-none-any.whl → 3.85.0__py3-none-any.whl
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.
Potentially problematic release.
This version of pulpcore might be problematic. Click here for more details.
- pulp_certguard/app/__init__.py +1 -1
- pulp_certguard/app/models.py +7 -26
- pulp_certguard/app/serializers.py +0 -2
- pulp_certguard/rhsm/__init__.py +4 -0
- pulp_certguard/rhsm/rhsm_check_path.py +198 -0
- pulp_certguard/tests/unit/certdata.py +249 -0
- pulp_certguard/tests/unit/test_rhsm_check_path.py +213 -0
- pulp_file/app/__init__.py +1 -1
- pulp_file/app/migrations/0001_initial_squashed_0016_add_domain.py +0 -20
- pulp_file/app/migrations/0017_alter_filealternatecontentsource_alternatecontentsource_ptr_and_more.py +1 -1
- pulpcore/app/apps.py +2 -12
- pulpcore/app/entrypoint.py +22 -22
- pulpcore/app/migrations/0001_squashed_0090_char_to_text_field.py +0 -95
- pulpcore/app/migrations/0091_systemid.py +1 -1
- pulpcore/app/migrations/0134_task_insert_trigger.py +81 -0
- pulpcore/app/migrations/0135_task_pulp_task_resources_index.py +25 -0
- pulp_file/app/migrations/0006_delete_filefilesystemexporter.py → pulpcore/app/migrations/0136_delete_basedistribution.py +3 -3
- pulpcore/app/migrations/0137_appstatus.py +33 -0
- pulpcore/app/migrations/0138_vulnerabilityreport.py +33 -0
- pulpcore/app/models/__init__.py +4 -1
- pulpcore/app/models/publication.py +0 -41
- pulpcore/app/models/status.py +145 -0
- pulpcore/app/models/task.py +8 -0
- pulpcore/app/models/vulnerability_report.py +34 -0
- pulpcore/app/serializers/__init__.py +1 -0
- pulpcore/app/serializers/content.py +13 -1
- pulpcore/app/serializers/repository.py +8 -1
- pulpcore/app/serializers/vulnerability_report.py +27 -0
- pulpcore/app/settings.py +13 -38
- pulpcore/app/tasks/__init__.py +2 -0
- pulpcore/app/tasks/purge.py +8 -5
- pulpcore/app/tasks/vulnerability_report.py +159 -0
- pulpcore/app/viewsets/__init__.py +1 -0
- pulpcore/app/viewsets/vulnerability_report.py +20 -0
- pulpcore/constants.py +8 -0
- pulpcore/content/__init__.py +23 -22
- pulpcore/content/handler.py +5 -2
- pulpcore/migrations.py +38 -11
- pulpcore/openapi/__init__.py +8 -0
- pulpcore/plugin/models/__init__.py +2 -0
- pulpcore/plugin/serializers/__init__.py +2 -0
- pulpcore/plugin/tasking.py +2 -0
- pulpcore/plugin/viewsets/__init__.py +2 -0
- pulpcore/pytest_plugin.py +21 -21
- pulpcore/tasking/entrypoint.py +12 -2
- pulpcore/tasking/tasks.py +5 -30
- pulpcore/tasking/worker.py +115 -74
- pulpcore/tests/functional/api/test_auth.py +18 -3
- pulpcore/tests/functional/api/test_login.py +62 -32
- pulpcore/tests/functional/api/test_openapi_schema.py +32 -15
- pulpcore/tests/functional/api/using_plugin/test_checkpoint.py +23 -1
- pulpcore/tests/functional/api/using_plugin/test_proxy.py +1 -1
- pulpcore/tests/unit/content/test_heartbeat.py +11 -8
- pulpcore/tests/unit/test_vulnerability_report.py +74 -0
- {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/METADATA +13 -18
- {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/RECORD +60 -156
- pulp_certguard/app/utils.py +0 -28
- pulp_certguard/tests/unit/test_models.py +0 -9
- pulp_file/app/migrations/0001_initial.py +0 -59
- pulp_file/app/migrations/0002_file_related_names.py +0 -55
- pulp_file/app/migrations/0003_auto_20191014_1721.py +0 -18
- pulp_file/app/migrations/0004_filefilesystemexporter.py +0 -21
- pulp_file/app/migrations/0005_filerepository.py +0 -24
- pulp_file/app/migrations/0007_filefilesystemexporter.py +0 -25
- pulp_file/app/migrations/0008_add_manifest_field.py +0 -19
- pulp_file/app/migrations/0009_move_data_to_new_master_distribution_model.py +0 -77
- pulp_file/app/migrations/0010_auto_publish.py +0 -23
- pulp_file/app/migrations/0011_fix_auto_publish.py +0 -36
- pulp_file/app/migrations/0012_delete_filefilesystemexporter.py +0 -28
- pulp_file/app/migrations/0013_file_acs.py +0 -24
- pulp_file/app/migrations/0014_new_rbac_permissions.py +0 -33
- pulp_file/app/migrations/0015_allow_null_manifest.py +0 -23
- pulp_file/app/migrations/0016_add_domain.py +0 -25
- pulpcore/app/migrations/0001_initial.py +0 -451
- pulpcore/app/migrations/0002_increase_artifact_size_field.py +0 -18
- pulpcore/app/migrations/0003_remove_upload_completed.py +0 -17
- pulpcore/app/migrations/0004_add_duplicated_reserved_resources.py +0 -45
- pulpcore/app/migrations/0005_progressreport_code.py +0 -19
- pulpcore/app/migrations/0006_repository_plugin_managed.py +0 -18
- pulpcore/app/migrations/0007_delete_progress_proxies.py +0 -19
- pulpcore/app/migrations/0008_published_metadata_as_content.py +0 -44
- pulpcore/app/migrations/0009_remove_task_non_fatal_errors.py +0 -17
- pulpcore/app/migrations/0010_pulp_fields.py +0 -570
- pulpcore/app/migrations/0011_relative_path.py +0 -28
- pulpcore/app/migrations/0012_auto_20191104_2000.py +0 -31
- pulpcore/app/migrations/0013_repository_pulp_type.py +0 -18
- pulpcore/app/migrations/0014_remove_repository_plugin_managed.py +0 -17
- pulpcore/app/migrations/0015_auto_20191112_1426.py +0 -33
- pulpcore/app/migrations/0016_charfield_to_textfield.py +0 -68
- pulpcore/app/migrations/0017_remove_task_parent.py +0 -17
- pulpcore/app/migrations/0018_auto_20191127_2350.py +0 -20
- pulpcore/app/migrations/0019_add_signing_service_model.py +0 -27
- pulpcore/app/migrations/0020_change_publishedartifact_constraints.py +0 -17
- pulpcore/app/migrations/0021_add_signing_service_foreign_key.py +0 -24
- pulpcore/app/migrations/0022_rename_last_version.py +0 -27
- pulpcore/app/migrations/0023_change_exporter_models.py +0 -82
- pulpcore/app/migrations/0024_use_local_storage_for_uploads.py +0 -19
- pulpcore/app/migrations/0025_task_parent_task.py +0 -19
- pulpcore/app/migrations/0026_task_group.py +0 -32
- pulpcore/app/migrations/0027_export_backend.py +0 -31
- pulpcore/app/migrations/0028_import_importer_pulpimporter_pulpimporterrepository.py +0 -85
- pulpcore/app/migrations/0029_export_delete.py +0 -19
- pulpcore/app/migrations/0030_taskgroup_all_tasks_dispatched.py +0 -24
- pulpcore/app/migrations/0031_import_export_validate_params.py +0 -19
- pulpcore/app/migrations/0032_export_to_chunks.py +0 -27
- pulpcore/app/migrations/0033_increase_remote_artifact_size_field.py +0 -18
- pulpcore/app/migrations/0034_groupprogressreport.py +0 -32
- pulpcore/app/migrations/0035_content_upstream_id.py +0 -18
- pulpcore/app/migrations/0036_unprotect_last_export.py +0 -19
- pulpcore/app/migrations/0037_pulptemporaryfile.py +0 -28
- pulpcore/app/migrations/0038_repository_remote.py +0 -19
- pulpcore/app/migrations/0039_change_download_concurrency.py +0 -25
- pulpcore/app/migrations/0040_set_admin_is_staff.py +0 -28
- pulpcore/app/migrations/0041_accesspolicy.py +0 -29
- pulpcore/app/migrations/0042_rbac_for_tasks.py +0 -56
- pulpcore/app/migrations/0043_toc_attribute.py +0 -19
- pulpcore/app/migrations/0044_temp_file_artifact_field.py +0 -20
- pulpcore/app/migrations/0045_accesspolicy_permissions_allow_null.py +0 -19
- pulpcore/app/migrations/0046_task__resource_job_id.py +0 -35
- pulpcore/app/migrations/0047_improve_orphan_cleanup.py +0 -59
- pulpcore/app/migrations/0048_fips_checksums.py +0 -38
- pulpcore/app/migrations/0049_add_file_field_to_uploadchunk.py +0 -24
- pulpcore/app/migrations/0050_namespace_access_policies.py +0 -28
- pulpcore/app/migrations/0051_timeoutfields.py +0 -34
- pulpcore/app/migrations/0052_tasking_logging_cid.py +0 -18
- pulpcore/app/migrations/0053_remote_headers.py +0 -19
- pulpcore/app/migrations/0054_add_public_key.py +0 -104
- pulpcore/app/migrations/0055_label.py +0 -31
- pulpcore/app/migrations/0056_remote_rate_limit.py +0 -18
- pulpcore/app/migrations/0057_add_label_indexes.py +0 -23
- pulpcore/app/migrations/0058_accesspolicy_customized.py +0 -18
- pulpcore/app/migrations/0059_proxy_creds.py +0 -23
- pulpcore/app/migrations/0060_data_migration_proxy_creds.py +0 -45
- pulpcore/app/migrations/0061_call_handle_artifact_checksums_command.py +0 -87
- pulpcore/app/migrations/0062_add_new_distribution_mastermodel.py +0 -36
- pulpcore/app/migrations/0063_repository_retained_versions.py +0 -18
- pulpcore/app/migrations/0064_add_new_style_task_columns.py +0 -109
- pulpcore/app/migrations/0064_repository_user_hidden.py +0 -18
- pulpcore/app/migrations/0065_merge_20210615_1211.py +0 -14
- pulpcore/app/migrations/0066_download_concurrency_and_retry_changes.py +0 -24
- pulpcore/app/migrations/0067_add_protect_to_task_reservation.py +0 -19
- pulpcore/app/migrations/0068_add_timestamp_of_interest.py +0 -23
- pulpcore/app/migrations/0069_update_json_fields.py +0 -63
- pulpcore/app/migrations/0070_rename_retained_versions.py +0 -18
- pulpcore/app/migrations/0071_filesystemexport_filesystemexporter.py +0 -35
- pulpcore/app/migrations/0072_add_method_to_filesystem_exporter.py +0 -18
- pulpcore/app/migrations/0073_encrypt_remote_fields.py +0 -139
- pulpcore/app/migrations/0074_acs.py +0 -47
- pulpcore/app/migrations/0075_rbaccontentguard.py +0 -25
- pulpcore/app/migrations/0076_remove_reserved_resource.py +0 -39
- pulpcore/app/migrations/0077_move_remote_url_credentials.py +0 -41
- pulpcore/app/migrations/0078_grouprole_role_userrole.py +0 -70
- pulpcore/app/migrations/0079_rename_permissions_assignment_accesspolicy_creation_hooks.py +0 -18
- pulpcore/app/migrations/0080_proxy_group_model.py +0 -37
- pulpcore/app/migrations/0081_reapplabel_group_permissions.py +0 -59
- pulpcore/app/migrations/0082_add_manage_roles_permissions.py +0 -17
- pulpcore/app/migrations/0083_alter_group_options.py +0 -17
- pulpcore/app/migrations/0084_alter_rbaccontentguard_options.py +0 -17
- pulpcore/app/migrations/0085_contentredirectcontentguard.py +0 -26
- pulpcore/app/migrations/0086_task_json_fields.py +0 -77
- pulpcore/app/migrations/0087_taskschedule.py +0 -34
- pulpcore/app/migrations/0088_accesspolicy_queryset_scoping.py +0 -18
- pulpcore/app/migrations/0089_alter_contentredirectcontentguard_options.py +0 -17
- pulpcore/app/migrations/0090_char_to_text_field.py +0 -79
- pulpcore/tests/unit/migration/test_0077_move_remote_url_credentials.py +0 -35
- {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/WHEEL +0 -0
- {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/entry_points.txt +0 -0
- {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/licenses/LICENSE +0 -0
- {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Generated by Django 4.2.19 on 2025-08-07 00:07
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
import django_lifecycle.mixins
|
|
6
|
+
import pulpcore.app.models.base
|
|
7
|
+
import pulpcore.app.util
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Migration(migrations.Migration):
|
|
11
|
+
|
|
12
|
+
dependencies = [
|
|
13
|
+
('core', '0137_appstatus'),
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
operations = [
|
|
17
|
+
migrations.CreateModel(
|
|
18
|
+
name='VulnerabilityReport',
|
|
19
|
+
fields=[
|
|
20
|
+
('pulp_id', models.UUIDField(default=pulpcore.app.models.base.pulp_uuid, editable=False, primary_key=True, serialize=False)),
|
|
21
|
+
('pulp_created', models.DateTimeField(auto_now_add=True)),
|
|
22
|
+
('pulp_last_updated', models.DateTimeField(auto_now=True, null=True)),
|
|
23
|
+
('vulns', models.JSONField()),
|
|
24
|
+
('content', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='core.content')),
|
|
25
|
+
('pulp_domain', models.ForeignKey(default=pulpcore.app.util.get_domain_pk, on_delete=django.db.models.deletion.CASCADE, to='core.domain')),
|
|
26
|
+
('repo_versions', models.ManyToManyField(blank=True, to='core.repositoryversion')),
|
|
27
|
+
],
|
|
28
|
+
options={
|
|
29
|
+
'default_related_name': '%(app_label)s_%(model_name)s',
|
|
30
|
+
},
|
|
31
|
+
bases=(django_lifecycle.mixins.LifecycleModelMixin, models.Model),
|
|
32
|
+
),
|
|
33
|
+
]
|
pulpcore/app/models/__init__.py
CHANGED
|
@@ -69,7 +69,7 @@ from .repository import (
|
|
|
69
69
|
RepositoryVersionContentDetails,
|
|
70
70
|
)
|
|
71
71
|
|
|
72
|
-
from .status import ApiAppStatus, ContentAppStatus
|
|
72
|
+
from .status import AppStatus, ApiAppStatus, ContentAppStatus
|
|
73
73
|
|
|
74
74
|
from .task import (
|
|
75
75
|
CreatedResource,
|
|
@@ -87,6 +87,8 @@ from .upload import (
|
|
|
87
87
|
UploadChunk,
|
|
88
88
|
)
|
|
89
89
|
|
|
90
|
+
from .vulnerability_report import VulnerabilityReport
|
|
91
|
+
|
|
90
92
|
# Moved here to avoid a circular import with Task
|
|
91
93
|
from .progress import GroupProgressReport, ProgressReport
|
|
92
94
|
|
|
@@ -170,4 +172,5 @@ __all__ = [
|
|
|
170
172
|
"OpenPGPSignature",
|
|
171
173
|
"OpenPGPUserAttribute",
|
|
172
174
|
"OpenPGPUserID",
|
|
175
|
+
"VulnerabilityReport",
|
|
173
176
|
]
|
|
@@ -576,47 +576,6 @@ class CompositeContentGuard(ContentGuard, AutoAddObjPermsMixin):
|
|
|
576
576
|
)
|
|
577
577
|
|
|
578
578
|
|
|
579
|
-
class BaseDistribution(MasterModel):
|
|
580
|
-
"""
|
|
581
|
-
A distribution defines how a publication is distributed by the Content App.
|
|
582
|
-
|
|
583
|
-
This abstract model can be used by plugin writers to create concrete distributions that are
|
|
584
|
-
stored in separate tables from the Distributions provided by pulpcore.
|
|
585
|
-
|
|
586
|
-
The `name` must be unique.
|
|
587
|
-
|
|
588
|
-
The ``base_path`` must have no overlapping components. So if a Distribution with ``base_path``
|
|
589
|
-
of ``a/path/foo`` existed, you could not make a second Distribution with a ``base_path`` of
|
|
590
|
-
``a/path`` or ``a`` because both are subpaths of ``a/path/foo``.
|
|
591
|
-
|
|
592
|
-
Note:
|
|
593
|
-
This class is no longer supported and cannot be removed from Pulp 3 due to possible
|
|
594
|
-
problems with old migrations for plugins. Until the migrations are squashed, this class
|
|
595
|
-
should be preserved.
|
|
596
|
-
|
|
597
|
-
Fields:
|
|
598
|
-
name (models.TextField): The name of the distribution. Examples: "rawhide" and "stable".
|
|
599
|
-
base_path (models.TextField): The base (relative) path component of the published url.
|
|
600
|
-
|
|
601
|
-
Relations:
|
|
602
|
-
content_guard (models.ForeignKey): An optional content-guard.
|
|
603
|
-
remote (models.ForeignKey): A remote that the content app can use to find content not
|
|
604
|
-
yet stored in Pulp.
|
|
605
|
-
"""
|
|
606
|
-
|
|
607
|
-
name = models.TextField(db_index=True, unique=True)
|
|
608
|
-
base_path = models.TextField(unique=True)
|
|
609
|
-
|
|
610
|
-
content_guard = models.ForeignKey(ContentGuard, null=True, on_delete=models.SET_NULL)
|
|
611
|
-
remote = models.ForeignKey(Remote, null=True, on_delete=models.SET_NULL)
|
|
612
|
-
|
|
613
|
-
def __init__(self, *args, **kwargs):
|
|
614
|
-
raise Exception(
|
|
615
|
-
"BaseDistribution is no longer supported. "
|
|
616
|
-
"Please use pulpcore.plugin.models.Distribution instead."
|
|
617
|
-
)
|
|
618
|
-
|
|
619
|
-
|
|
620
579
|
class Distribution(MasterModel):
|
|
621
580
|
"""
|
|
622
581
|
A Distribution defines how the Content App distributes a publication or repository_version.
|
pulpcore/app/models/status.py
CHANGED
|
@@ -6,7 +6,9 @@ from datetime import timedelta
|
|
|
6
6
|
|
|
7
7
|
from django.conf import settings
|
|
8
8
|
from django.contrib.postgres.fields import HStoreField
|
|
9
|
+
from django.contrib.postgres.functions import TransactionNow
|
|
9
10
|
from django.db import models
|
|
11
|
+
from django.db.models import F, Value
|
|
10
12
|
from django.utils import timezone
|
|
11
13
|
|
|
12
14
|
from pulpcore.app.models import BaseModel
|
|
@@ -47,9 +49,138 @@ class AppStatusManager(models.Manager):
|
|
|
47
49
|
return self.filter(last_heartbeat__lt=age_threshold)
|
|
48
50
|
|
|
49
51
|
|
|
52
|
+
class _AppStatusManager(AppStatusManager):
|
|
53
|
+
# This is an intermediate class in order to allow a ZDU.
|
|
54
|
+
# It should be removed from the chain with 3.87.
|
|
55
|
+
def create(self, app_type, **kwargs):
|
|
56
|
+
if app_type == "api":
|
|
57
|
+
old_obj = ApiAppStatus.objects.create(**kwargs)
|
|
58
|
+
elif app_type == "worker":
|
|
59
|
+
from pulpcore.app.models import Worker
|
|
60
|
+
|
|
61
|
+
old_obj = Worker.objects.create(**kwargs)
|
|
62
|
+
else:
|
|
63
|
+
raise NotImplementedError(f"Invalid app_type: {app_type}")
|
|
64
|
+
obj = super().create(app_type=app_type, **kwargs)
|
|
65
|
+
obj._old_status = old_obj
|
|
66
|
+
return obj
|
|
67
|
+
|
|
68
|
+
async def acreate(self, app_type, **kwargs):
|
|
69
|
+
if app_type == "content":
|
|
70
|
+
old_obj = await ContentAppStatus.objects.acreate(**kwargs)
|
|
71
|
+
else:
|
|
72
|
+
raise NotImplementedError(f"Invalid app_type: {app_type}")
|
|
73
|
+
obj = await super().acreate(app_type=app_type, **kwargs)
|
|
74
|
+
obj._old_status = old_obj
|
|
75
|
+
return obj
|
|
76
|
+
|
|
77
|
+
def online(self):
|
|
78
|
+
"""
|
|
79
|
+
Returns a queryset of objects that are online.
|
|
80
|
+
"""
|
|
81
|
+
return self.filter(last_heartbeat__gte=TransactionNow() - F("ttl"))
|
|
82
|
+
|
|
83
|
+
def missing(self):
|
|
84
|
+
"""
|
|
85
|
+
Returns a queryset of workers that are missing.
|
|
86
|
+
"""
|
|
87
|
+
return self.filter(last_heartbeat__lt=TransactionNow() - F("ttl"))
|
|
88
|
+
|
|
89
|
+
def older_than(self, age):
|
|
90
|
+
"""
|
|
91
|
+
Returns a queryset of workers that are older than age.
|
|
92
|
+
"""
|
|
93
|
+
return self.filter(last_heartbeat__lt=TransactionNow() - Value(age))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class AppStatus(BaseModel):
|
|
97
|
+
APP_TYPES = [
|
|
98
|
+
("api", "api"),
|
|
99
|
+
("content", "content"),
|
|
100
|
+
("worker", "worker"),
|
|
101
|
+
]
|
|
102
|
+
_APP_TTL = {
|
|
103
|
+
"api": settings.API_APP_TTL,
|
|
104
|
+
"content": settings.CONTENT_APP_TTL,
|
|
105
|
+
"worker": settings.WORKER_TTL,
|
|
106
|
+
}
|
|
107
|
+
objects = _AppStatusManager()
|
|
108
|
+
|
|
109
|
+
app_type = models.CharField(max_length=10, choices=APP_TYPES)
|
|
110
|
+
name = models.TextField(db_index=True, unique=True)
|
|
111
|
+
versions = HStoreField(default=dict)
|
|
112
|
+
ttl = models.DurationField(null=False)
|
|
113
|
+
last_heartbeat = models.DateTimeField(auto_now=True)
|
|
114
|
+
|
|
115
|
+
def __init__(self, *args, **kwargs):
|
|
116
|
+
super().__init__(*args, **kwargs)
|
|
117
|
+
self.ttl = timedelta(seconds=self._APP_TTL[self.app_type])
|
|
118
|
+
self._old_status = None
|
|
119
|
+
|
|
120
|
+
def delete(self, *args, **kwargs):
|
|
121
|
+
# adelete will call into this, so we should not replicate that one here.
|
|
122
|
+
if self._old_status is not None:
|
|
123
|
+
self._old_status.delete(*args, **kwargs)
|
|
124
|
+
super().delete(*args, **kwargs)
|
|
125
|
+
|
|
126
|
+
@property
|
|
127
|
+
def online(self) -> bool:
|
|
128
|
+
"""
|
|
129
|
+
To be considered 'online', an app must have a timestamp more recent than ``self.ttl``.
|
|
130
|
+
"""
|
|
131
|
+
age_threshold = timezone.now() - self.ttl
|
|
132
|
+
return self.last_heartbeat >= age_threshold
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def missing(self):
|
|
136
|
+
"""
|
|
137
|
+
Whether an app can be considered 'missing'
|
|
138
|
+
|
|
139
|
+
To be considered 'missing', an App must have a timestamp older than ``self.ttl``.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
bool: True if the app is considered missing, otherwise False
|
|
143
|
+
"""
|
|
144
|
+
return not self.online
|
|
145
|
+
|
|
146
|
+
def save_heartbeat(self):
|
|
147
|
+
"""
|
|
148
|
+
Update the last_heartbeat field to now and save it.
|
|
149
|
+
|
|
150
|
+
Only the last_heartbeat field will be saved. No other changes will be saved.
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
ValueError: When the model instance has never been saved before. This method can
|
|
154
|
+
only update an existing database record.
|
|
155
|
+
"""
|
|
156
|
+
self._old_status.save_heartbeat()
|
|
157
|
+
self.save(update_fields=["last_heartbeat"])
|
|
158
|
+
|
|
159
|
+
async def asave_heartbeat(self):
|
|
160
|
+
"""
|
|
161
|
+
Update the last_heartbeat field to now and save it.
|
|
162
|
+
|
|
163
|
+
Only the last_heartbeat field will be saved. No other changes will be saved.
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
ValueError: When the model instance has never been saved before. This method can
|
|
167
|
+
only update an existing database record.
|
|
168
|
+
"""
|
|
169
|
+
await self._old_status.asave_heartbeat()
|
|
170
|
+
await self.asave(update_fields=["last_heartbeat"])
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def current_task(self):
|
|
174
|
+
"""
|
|
175
|
+
The task this worker is currently executing, if any.
|
|
176
|
+
"""
|
|
177
|
+
return self.tasks.filter(state="running").first()
|
|
178
|
+
|
|
179
|
+
|
|
50
180
|
class BaseAppStatus(BaseModel):
|
|
51
181
|
"""
|
|
52
182
|
Represents an AppStatus.
|
|
183
|
+
Deprecated, to be removed with 3.87.
|
|
53
184
|
|
|
54
185
|
This class is abstract. Subclasses must define `APP_TTL` as a `timedelta`.
|
|
55
186
|
|
|
@@ -103,6 +234,18 @@ class BaseAppStatus(BaseModel):
|
|
|
103
234
|
"""
|
|
104
235
|
self.save(update_fields=["last_heartbeat"])
|
|
105
236
|
|
|
237
|
+
async def asave_heartbeat(self):
|
|
238
|
+
"""
|
|
239
|
+
Update the last_heartbeat field to now and save it.
|
|
240
|
+
|
|
241
|
+
Only the last_heartbeat field will be saved. No other changes will be saved.
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
ValueError: When the model instance has never been saved before. This method can
|
|
245
|
+
only update an existing database record.
|
|
246
|
+
"""
|
|
247
|
+
await self.asave(update_fields=["last_heartbeat"])
|
|
248
|
+
|
|
106
249
|
class Meta:
|
|
107
250
|
abstract = True
|
|
108
251
|
|
|
@@ -110,6 +253,7 @@ class BaseAppStatus(BaseModel):
|
|
|
110
253
|
class ApiAppStatus(BaseAppStatus):
|
|
111
254
|
"""
|
|
112
255
|
Represents a Api App Status
|
|
256
|
+
Deprecated, to be removed with 3.87.
|
|
113
257
|
"""
|
|
114
258
|
|
|
115
259
|
APP_TTL = timedelta(seconds=settings.API_APP_TTL)
|
|
@@ -118,6 +262,7 @@ class ApiAppStatus(BaseAppStatus):
|
|
|
118
262
|
class ContentAppStatus(BaseAppStatus):
|
|
119
263
|
"""
|
|
120
264
|
Represents a Content App Status
|
|
265
|
+
Deprecated, to be removed with 3.87.
|
|
121
266
|
"""
|
|
122
267
|
|
|
123
268
|
APP_TTL = timedelta(seconds=settings.CONTENT_APP_TTL)
|
pulpcore/app/models/task.py
CHANGED
|
@@ -9,6 +9,7 @@ from gettext import gettext as _
|
|
|
9
9
|
|
|
10
10
|
from django.conf import settings
|
|
11
11
|
from django.contrib.postgres.fields import ArrayField, HStoreField
|
|
12
|
+
from django.contrib.postgres.indexes import GinIndex
|
|
12
13
|
from django.core.serializers.json import DjangoJSONEncoder
|
|
13
14
|
from django.db import connection, models
|
|
14
15
|
from django.utils import timezone
|
|
@@ -32,6 +33,7 @@ _logger = logging.getLogger(__name__)
|
|
|
32
33
|
class Worker(BaseAppStatus):
|
|
33
34
|
"""
|
|
34
35
|
Represents a worker
|
|
36
|
+
Deprecated, to be removed with 3.87.
|
|
35
37
|
"""
|
|
36
38
|
|
|
37
39
|
APP_TTL = timedelta(seconds=settings.WORKER_TTL)
|
|
@@ -348,6 +350,12 @@ class Task(BaseModel, AutoAddObjPermsMixin):
|
|
|
348
350
|
models.Index(fields=["unblocked_at"]),
|
|
349
351
|
models.Index(fields=["state"]),
|
|
350
352
|
models.Index(fields=["state", "pulp_created"]),
|
|
353
|
+
GinIndex(
|
|
354
|
+
name="pulp_task_resources_index",
|
|
355
|
+
fields=["reserved_resources_record"],
|
|
356
|
+
condition=~models.Q(state__in=["completed", "failed", "canceled", "skipped"]),
|
|
357
|
+
opclasses=["array_ops"],
|
|
358
|
+
),
|
|
351
359
|
]
|
|
352
360
|
permissions = [
|
|
353
361
|
("manage_roles_task", "Can manage role assignments on task"),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
from pulpcore.app.models.base import BaseModel
|
|
4
|
+
from pulpcore.app.util import get_domain_pk
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class VulnerabilityReport(BaseModel):
|
|
8
|
+
"""
|
|
9
|
+
A model for storing vulnerability reports for Content/RepositoryVersion.
|
|
10
|
+
|
|
11
|
+
Fields:
|
|
12
|
+
vulns (models.JSONField): A JSON field containing the list of vulnerabilities found
|
|
13
|
+
in osv.dev.
|
|
14
|
+
|
|
15
|
+
Relations:
|
|
16
|
+
content (models.OneToOneField): The Content object that was scanned for vulnerabilities.
|
|
17
|
+
pulp_domain (models.ForeignKey): The Domain this vulnerability report belongs to,
|
|
18
|
+
providing multi-tenancy isolation.
|
|
19
|
+
repo_versions (models.ManyToManyField): The RepositoryVersion(s) where the scanned
|
|
20
|
+
Content appears. This allows tracking which repository versions contain
|
|
21
|
+
vulnerable content.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
content = models.OneToOneField(
|
|
25
|
+
"Content",
|
|
26
|
+
on_delete=models.CASCADE,
|
|
27
|
+
unique=True,
|
|
28
|
+
)
|
|
29
|
+
vulns = models.JSONField()
|
|
30
|
+
pulp_domain = models.ForeignKey("Domain", default=get_domain_pk, on_delete=models.CASCADE)
|
|
31
|
+
repo_versions = models.ManyToManyField("RepositoryVersion", blank=True)
|
|
32
|
+
|
|
33
|
+
class Meta:
|
|
34
|
+
default_related_name = "%(app_label)s_%(model_name)s"
|
|
@@ -5,7 +5,13 @@ from rest_framework import serializers
|
|
|
5
5
|
from rest_framework.validators import UniqueValidator
|
|
6
6
|
|
|
7
7
|
from pulpcore.app import models
|
|
8
|
-
from pulpcore.app.serializers import
|
|
8
|
+
from pulpcore.app.serializers import (
|
|
9
|
+
base,
|
|
10
|
+
fields,
|
|
11
|
+
pulp_labels_validator,
|
|
12
|
+
DetailRelatedField,
|
|
13
|
+
RelatedField,
|
|
14
|
+
)
|
|
9
15
|
from pulpcore.app.util import get_domain
|
|
10
16
|
|
|
11
17
|
|
|
@@ -27,6 +33,11 @@ class NoArtifactContentSerializer(base.ModelSerializer):
|
|
|
27
33
|
view_name_pattern=r"repositories(-.*/.*)-detail",
|
|
28
34
|
queryset=models.Repository.objects.all(),
|
|
29
35
|
)
|
|
36
|
+
vuln_report = RelatedField(
|
|
37
|
+
read_only=True,
|
|
38
|
+
view_name="vuln_report-detail",
|
|
39
|
+
source="core_vulnerabilityreport",
|
|
40
|
+
)
|
|
30
41
|
|
|
31
42
|
def get_artifacts(self, validated_data):
|
|
32
43
|
"""
|
|
@@ -116,6 +127,7 @@ class NoArtifactContentSerializer(base.ModelSerializer):
|
|
|
116
127
|
fields = base.ModelSerializer.Meta.fields + (
|
|
117
128
|
"repository",
|
|
118
129
|
"pulp_labels",
|
|
130
|
+
"vuln_report",
|
|
119
131
|
)
|
|
120
132
|
|
|
121
133
|
|
|
@@ -7,7 +7,7 @@ from rest_framework import fields, serializers
|
|
|
7
7
|
from rest_framework_nested.serializers import NestedHyperlinkedModelSerializer
|
|
8
8
|
|
|
9
9
|
from pulpcore.app import models, settings
|
|
10
|
-
from pulpcore.app.util import get_prn
|
|
10
|
+
from pulpcore.app.util import get_prn, reverse
|
|
11
11
|
from pulpcore.app.serializers import (
|
|
12
12
|
DetailIdentityField,
|
|
13
13
|
DetailRelatedField,
|
|
@@ -490,6 +490,12 @@ class RepositoryVersionSerializer(ModelSerializer, NestedHyperlinkedModelSeriali
|
|
|
490
490
|
source="*",
|
|
491
491
|
read_only=True,
|
|
492
492
|
)
|
|
493
|
+
vuln_report = serializers.SerializerMethodField(
|
|
494
|
+
read_only=True,
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
def get_vuln_report(self, object):
|
|
498
|
+
return f"{reverse('vuln_report-list')}?repository_version={get_prn(object)}"
|
|
493
499
|
|
|
494
500
|
class Meta:
|
|
495
501
|
model = models.RepositoryVersion
|
|
@@ -499,6 +505,7 @@ class RepositoryVersionSerializer(ModelSerializer, NestedHyperlinkedModelSeriali
|
|
|
499
505
|
"repository",
|
|
500
506
|
"base_version",
|
|
501
507
|
"content_summary",
|
|
508
|
+
"vuln_report",
|
|
502
509
|
)
|
|
503
510
|
|
|
504
511
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from rest_framework import serializers
|
|
2
|
+
|
|
3
|
+
from pulpcore.app.models import VulnerabilityReport
|
|
4
|
+
from pulpcore.app.serializers import (
|
|
5
|
+
DetailRelatedField,
|
|
6
|
+
IdentityField,
|
|
7
|
+
ModelSerializer,
|
|
8
|
+
RepositoryVersionRelatedField,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VulnerabilityReportSerializer(ModelSerializer):
|
|
13
|
+
"""
|
|
14
|
+
A serializer for the VulnerabilityReport Model.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
vulns = serializers.JSONField()
|
|
18
|
+
pulp_href = IdentityField(view_name="vuln_report-detail")
|
|
19
|
+
content = DetailRelatedField(
|
|
20
|
+
read_only=True,
|
|
21
|
+
view_name_pattern=r"content(-.*/.*)-detail",
|
|
22
|
+
)
|
|
23
|
+
repo_versions = RepositoryVersionRelatedField(many=True, required=False)
|
|
24
|
+
|
|
25
|
+
class Meta:
|
|
26
|
+
model = VulnerabilityReport
|
|
27
|
+
fields = ModelSerializer.Meta.fields + ("vulns", "repo_versions", "content")
|
pulpcore/app/settings.py
CHANGED
|
@@ -12,12 +12,11 @@ import sys
|
|
|
12
12
|
|
|
13
13
|
from contextlib import suppress
|
|
14
14
|
from importlib import import_module
|
|
15
|
+
from importlib.metadata import entry_points
|
|
15
16
|
from logging import getLogger
|
|
16
17
|
from pathlib import Path
|
|
17
18
|
|
|
18
19
|
from cryptography.fernet import Fernet
|
|
19
|
-
from django.core.files.storage import storages
|
|
20
|
-
from django.conf import global_settings
|
|
21
20
|
from django.core.exceptions import ImproperlyConfigured
|
|
22
21
|
from django.db import connection
|
|
23
22
|
from dynaconf import DjangoDynaconf, Dynaconf, Validator
|
|
@@ -32,12 +31,6 @@ try:
|
|
|
32
31
|
except ImportError:
|
|
33
32
|
pass
|
|
34
33
|
|
|
35
|
-
if sys.version_info < (3, 10):
|
|
36
|
-
# Python 3.9 has a rather different interface for `entry_points`.
|
|
37
|
-
# Let's use a compatibility version.
|
|
38
|
-
from importlib_metadata import entry_points
|
|
39
|
-
else:
|
|
40
|
-
from importlib.metadata import entry_points
|
|
41
34
|
|
|
42
35
|
# Load settings first pass before applying all the defaults to get a grip on ENABLED_PLUGINS.
|
|
43
36
|
enabled_plugins_validator = Validator(
|
|
@@ -77,16 +70,7 @@ MEDIA_ROOT = str(DEPLOY_ROOT / "media") # Django 3.1 adds support for pathlib.P
|
|
|
77
70
|
STATIC_URL = "/assets/"
|
|
78
71
|
STATIC_ROOT = DEPLOY_ROOT / STATIC_URL.strip("/")
|
|
79
72
|
|
|
80
|
-
|
|
81
|
-
# Remove on pulpcore=3.85 or pulpcore=4.0
|
|
82
|
-
|
|
83
|
-
# - What is this?
|
|
84
|
-
# We shouldn't use STORAGES or DEFAULT_FILE_STORAGE directly because those are
|
|
85
|
-
# mutually exclusive by django, which constraints users to use whatever we use.
|
|
86
|
-
# This is a hack/workaround to set Pulp's default while still enabling users to choose
|
|
87
|
-
# the legacy or the new storage setting.
|
|
88
|
-
_DEFAULT_FILE_STORAGE = "pulpcore.app.models.storage.FileSystem"
|
|
89
|
-
_STORAGES = {
|
|
73
|
+
STORAGES = {
|
|
90
74
|
"default": {
|
|
91
75
|
"BACKEND": "pulpcore.app.models.storage.FileSystem",
|
|
92
76
|
},
|
|
@@ -95,10 +79,6 @@ _STORAGES = {
|
|
|
95
79
|
},
|
|
96
80
|
}
|
|
97
81
|
|
|
98
|
-
setattr(global_settings, "DEFAULT_FILE_STORAGE", _DEFAULT_FILE_STORAGE)
|
|
99
|
-
setattr(global_settings, "STORAGES", _STORAGES)
|
|
100
|
-
# end DEFAULT_FILE_STORAGE deprecation layer
|
|
101
|
-
|
|
102
82
|
REDIRECT_TO_OBJECT_STORAGE = True
|
|
103
83
|
|
|
104
84
|
WORKING_DIRECTORY = DEPLOY_ROOT / "tmp"
|
|
@@ -328,6 +308,7 @@ TASK_PROTECTION_TIME = 0
|
|
|
328
308
|
TMPFILE_PROTECTION_TIME = 0
|
|
329
309
|
|
|
330
310
|
REMOTE_USER_ENVIRON_NAME = "REMOTE_USER"
|
|
311
|
+
REMOTE_USER_OPENAPI_SECURITY_SCHEME = {"type": "mutualTLS"}
|
|
331
312
|
|
|
332
313
|
AUTHENTICATION_JSON_HEADER = ""
|
|
333
314
|
AUTHENTICATION_JSON_HEADER_JQ_FILTER = ""
|
|
@@ -347,6 +328,7 @@ CACHE_SETTINGS = {
|
|
|
347
328
|
REMOTE_CONTENT_FETCH_FAILURE_COOLDOWN = 5 * 60 # 5 minutes
|
|
348
329
|
|
|
349
330
|
SPECTACULAR_SETTINGS = {
|
|
331
|
+
"OAS_VERSION": "3.1.1",
|
|
350
332
|
"SERVE_URLCONF": ROOT_URLCONF,
|
|
351
333
|
"DEFAULT_GENERATOR_CLASS": "pulpcore.openapi.PulpSchemaGenerator",
|
|
352
334
|
"DEFAULT_SCHEMA_CLASS": "pulpcore.openapi.PulpAutoSchema",
|
|
@@ -424,6 +406,9 @@ KAFKA_SASL_PASSWORD = None
|
|
|
424
406
|
# opentelemetry settings
|
|
425
407
|
OTEL_ENABLED = False
|
|
426
408
|
|
|
409
|
+
# VulnerabilityReport settings
|
|
410
|
+
VULN_REPORT_TASK_LIMITER = 10
|
|
411
|
+
|
|
427
412
|
# Replaces asyncio event loop with uvloop
|
|
428
413
|
UVLOOP_ENABLED = False
|
|
429
414
|
|
|
@@ -432,19 +417,17 @@ UVLOOP_ENABLED = False
|
|
|
432
417
|
|
|
433
418
|
# Validators
|
|
434
419
|
|
|
435
|
-
storage_keys = ("STORAGES.default.BACKEND", "DEFAULT_FILE_STORAGE")
|
|
436
420
|
storage_validator = (
|
|
437
421
|
Validator("REDIRECT_TO_OBJECT_STORAGE", eq=False)
|
|
438
|
-
| Validator(
|
|
439
|
-
| Validator(
|
|
440
|
-
| Validator(
|
|
441
|
-
| Validator(
|
|
442
|
-
| Validator(
|
|
422
|
+
| Validator("STORAGES.default.BACKEND", eq="pulpcore.app.models.storage.FileSystem")
|
|
423
|
+
| Validator("STORAGES.default.BACKEND", eq="storages.backends.azure_storage.AzureStorage")
|
|
424
|
+
| Validator("STORAGES.default.BACKEND", eq="storages.backends.s3.S3Storage")
|
|
425
|
+
| Validator("STORAGES.default.BACKEND", eq="storages.backends.s3boto3.S3Boto3Storage")
|
|
426
|
+
| Validator("STORAGES.default.BACKEND", eq="storages.backends.gcloud.GoogleCloudStorage")
|
|
443
427
|
)
|
|
444
428
|
storage_validator.messages["combined"] = (
|
|
445
429
|
"'REDIRECT_TO_OBJECT_STORAGE=True' is only supported with the local file, S3, GCP or Azure "
|
|
446
|
-
"storage backend configured in STORAGES['default']['BACKEND']
|
|
447
|
-
"(deprecated DEFAULT_FILE_STORAGE)."
|
|
430
|
+
"storage backend configured in STORAGES['default']['BACKEND']."
|
|
448
431
|
)
|
|
449
432
|
|
|
450
433
|
cache_enabled_validator = Validator("CACHE_ENABLED", eq=True)
|
|
@@ -551,14 +534,6 @@ settings = DjangoDynaconf(
|
|
|
551
534
|
post_hooks=(otel_middleware_hook,),
|
|
552
535
|
)
|
|
553
536
|
|
|
554
|
-
# begin compatibility layer for DEFAULT_FILE_STORAGE
|
|
555
|
-
# Remove on pulpcore=3.85 or pulpcore=4.0
|
|
556
|
-
|
|
557
|
-
# Ensures the cached property storage.backends uses the the right value
|
|
558
|
-
storages._backends = settings.STORAGES.copy()
|
|
559
|
-
storages.backends
|
|
560
|
-
# end compatibility layer
|
|
561
|
-
|
|
562
537
|
_logger = getLogger(__name__)
|
|
563
538
|
|
|
564
539
|
|
pulpcore/app/tasks/__init__.py
CHANGED
pulpcore/app/tasks/purge.py
CHANGED
|
@@ -82,17 +82,20 @@ def purge(finished_before=None, states=None, **kwargs):
|
|
|
82
82
|
states (Optional[List[str]]): List of task-states we want to purge.
|
|
83
83
|
|
|
84
84
|
"""
|
|
85
|
+
scheduled = current_task.get().taskschedule_set.exists()
|
|
86
|
+
|
|
85
87
|
if finished_before is None:
|
|
86
88
|
assert settings.TASK_PROTECTION_TIME > 0
|
|
87
89
|
finished_before = timezone.now() - timezone.timedelta(minutes=settings.TASK_PROTECTION_TIME)
|
|
88
90
|
if states is None:
|
|
89
91
|
states = TASK_FINAL_STATES
|
|
90
|
-
|
|
92
|
+
|
|
91
93
|
# Tasks, prior to the specified date, in the specified state, owned by the current-user, in the
|
|
92
94
|
# current domain
|
|
93
|
-
candidate_qs = Task.objects.filter(
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
candidate_qs = Task.objects.filter(finished_at__lt=finished_before, state__in=states)
|
|
96
|
+
|
|
97
|
+
if not scheduled:
|
|
98
|
+
candidate_qs = candidate_qs.filter(pulp_domain=get_domain())
|
|
96
99
|
if "user_pk" in kwargs:
|
|
97
100
|
if (user_pk := kwargs["user_pk"]) is not None:
|
|
98
101
|
current_user = User.objects.get(pk=user_pk)
|
|
@@ -101,7 +104,7 @@ def purge(finished_before=None, states=None, **kwargs):
|
|
|
101
104
|
# This is the old task signature (<= 3.74) without "user_pk".
|
|
102
105
|
# Has this task not been dispatched from a task schedule? Then we assume there was a user
|
|
103
106
|
# doing that.
|
|
104
|
-
if not
|
|
107
|
+
if not scheduled:
|
|
105
108
|
current_user = get_current_authenticated_user()
|
|
106
109
|
if current_user is None:
|
|
107
110
|
raise RuntimeError(
|