pulpcore 3.85.0__py3-none-any.whl → 3.86.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_file/app/__init__.py +1 -1
- pulpcore/app/apps.py +1 -1
- pulpcore/app/management/commands/optimizemigration.py +84 -0
- pulpcore/app/migrations/0139_task_app_lock.py +19 -0
- pulpcore/app/models/status.py +19 -1
- pulpcore/app/models/task.py +6 -1
- pulpcore/app/settings.py +12 -0
- pulpcore/middleware.py +37 -10
- pulpcore/migrations.py +4 -5
- pulpcore/plugin/repo_version_utils.py +8 -2
- pulpcore/pytest_plugin.py +6 -4
- pulpcore/tasking/tasks.py +16 -15
- pulpcore/tasking/worker.py +48 -29
- pulpcore/tests/functional/api/test_tasking.py +10 -11
- pulpcore/tests/functional/api/test_workers.py +1 -2
- pulpcore/tests/unit/content/test_heartbeat.py +2 -1
- pulpcore/tests/unit/models/test_repository.py +84 -1
- {pulpcore-3.85.0.dist-info → pulpcore-3.86.0.dist-info}/METADATA +2 -2
- {pulpcore-3.85.0.dist-info → pulpcore-3.86.0.dist-info}/RECORD +24 -22
- {pulpcore-3.85.0.dist-info → pulpcore-3.86.0.dist-info}/WHEEL +0 -0
- {pulpcore-3.85.0.dist-info → pulpcore-3.86.0.dist-info}/entry_points.txt +0 -0
- {pulpcore-3.85.0.dist-info → pulpcore-3.86.0.dist-info}/licenses/LICENSE +0 -0
- {pulpcore-3.85.0.dist-info → pulpcore-3.86.0.dist-info}/top_level.txt +0 -0
pulp_certguard/app/__init__.py
CHANGED
pulp_file/app/__init__.py
CHANGED
pulpcore/app/apps.py
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from gettext import gettext as _
|
|
3
|
+
|
|
4
|
+
from django.db import connection
|
|
5
|
+
from django.db.migrations.loader import MigrationLoader
|
|
6
|
+
from django.db.migrations.migration import SwappableTuple
|
|
7
|
+
from django.db.migrations.optimizer import MigrationOptimizer
|
|
8
|
+
from django.db.migrations.writer import MigrationWriter
|
|
9
|
+
from django.conf import settings
|
|
10
|
+
from django.core.management import BaseCommand
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def print_stats(migration):
|
|
14
|
+
operations = migration.operations
|
|
15
|
+
migration_types = defaultdict(int)
|
|
16
|
+
for operation in operations:
|
|
17
|
+
migration_types[operation.__class__.__name__] += 1
|
|
18
|
+
for key, value in migration_types.items():
|
|
19
|
+
print(f"{value: 4} {key}")
|
|
20
|
+
print("---")
|
|
21
|
+
print(_("Total: {count}").format(count=len(operations)))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Command(BaseCommand):
|
|
25
|
+
"""
|
|
26
|
+
Django management command to optimize a migration.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
help = _("Optimize a migration.")
|
|
30
|
+
|
|
31
|
+
def add_arguments(self, parser):
|
|
32
|
+
parser.add_argument("--dry-run", action="store_true", help=_("Don't change anything."))
|
|
33
|
+
parser.add_argument(
|
|
34
|
+
"--stat", action="store_true", help=_("Print statistics about operations.")
|
|
35
|
+
)
|
|
36
|
+
parser.add_argument("app-label", help=_("App label of the migrations to optimize."))
|
|
37
|
+
parser.add_argument("migration", help=_("Prefix of the migration to optimize."))
|
|
38
|
+
|
|
39
|
+
def handle(self, *args, **options):
|
|
40
|
+
dry_run = options.get("dry_run", False)
|
|
41
|
+
stat = options.get("stat", False)
|
|
42
|
+
app_label = options["app-label"]
|
|
43
|
+
migration_prefix = options["migration"]
|
|
44
|
+
|
|
45
|
+
loader = MigrationLoader(connection)
|
|
46
|
+
|
|
47
|
+
migration = loader.get_migration_by_prefix(app_label, migration_prefix)
|
|
48
|
+
|
|
49
|
+
print(_("Optimizing migration {}").format((migration.app_label, migration.name)))
|
|
50
|
+
if stat:
|
|
51
|
+
print(_("=== Old Migration Summary ==="))
|
|
52
|
+
print_stats(migration)
|
|
53
|
+
|
|
54
|
+
new_dependencies = []
|
|
55
|
+
for dependency in migration.dependencies:
|
|
56
|
+
if (
|
|
57
|
+
isinstance(dependency, SwappableTuple)
|
|
58
|
+
and settings.AUTH_USER_MODEL == dependency.setting
|
|
59
|
+
):
|
|
60
|
+
new_dependencies.append(("__setting__", "AUTH_USER_MODEL"))
|
|
61
|
+
else:
|
|
62
|
+
new_dependencies.append(dependency)
|
|
63
|
+
|
|
64
|
+
optimizer = MigrationOptimizer()
|
|
65
|
+
new_operations = optimizer.optimize(migration.operations, app_label)
|
|
66
|
+
|
|
67
|
+
if new_operations != migration.operations:
|
|
68
|
+
print(
|
|
69
|
+
_("Changed from {old_count} to {new_count} operations.").format(
|
|
70
|
+
old_count=len(migration.operations), new_count=len(new_operations)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
if stat:
|
|
74
|
+
print(_("=== New Migration Summary ==="))
|
|
75
|
+
print_stats(migration)
|
|
76
|
+
|
|
77
|
+
migration.operations = new_operations
|
|
78
|
+
migration.dependencies = new_dependencies
|
|
79
|
+
if not dry_run:
|
|
80
|
+
writer = MigrationWriter(migration)
|
|
81
|
+
with open(writer.path, "w") as output_file:
|
|
82
|
+
output_file.write(writer.as_string())
|
|
83
|
+
else:
|
|
84
|
+
print(_("No optimizations found."))
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Generated by Django 4.2.23 on 2025-08-07 11:43
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
import django.db.models.deletion
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Migration(migrations.Migration):
|
|
8
|
+
|
|
9
|
+
dependencies = [
|
|
10
|
+
('core', '0138_vulnerabilityreport'),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
operations = [
|
|
14
|
+
migrations.AddField(
|
|
15
|
+
model_name='task',
|
|
16
|
+
name='app_lock',
|
|
17
|
+
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='tasks', to='core.appstatus'),
|
|
18
|
+
),
|
|
19
|
+
]
|
pulpcore/app/models/status.py
CHANGED
|
@@ -15,6 +15,7 @@ from pulpcore.app.models import BaseModel
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class AppStatusManager(models.Manager):
|
|
18
|
+
# This should be replaced with 3.87.
|
|
18
19
|
def online(self):
|
|
19
20
|
"""
|
|
20
21
|
Returns a queryset of objects that are online.
|
|
@@ -51,8 +52,15 @@ class AppStatusManager(models.Manager):
|
|
|
51
52
|
|
|
52
53
|
class _AppStatusManager(AppStatusManager):
|
|
53
54
|
# This is an intermediate class in order to allow a ZDU.
|
|
54
|
-
# It should be
|
|
55
|
+
# It should be made the real thing with 3.87.
|
|
56
|
+
def __init__(self):
|
|
57
|
+
super().__init__()
|
|
58
|
+
self._current_app_status = None
|
|
59
|
+
|
|
55
60
|
def create(self, app_type, **kwargs):
|
|
61
|
+
if self._current_app_status is not None:
|
|
62
|
+
raise RuntimeError("There is already an app status in this process.")
|
|
63
|
+
|
|
56
64
|
if app_type == "api":
|
|
57
65
|
old_obj = ApiAppStatus.objects.create(**kwargs)
|
|
58
66
|
elif app_type == "worker":
|
|
@@ -63,17 +71,27 @@ class _AppStatusManager(AppStatusManager):
|
|
|
63
71
|
raise NotImplementedError(f"Invalid app_type: {app_type}")
|
|
64
72
|
obj = super().create(app_type=app_type, **kwargs)
|
|
65
73
|
obj._old_status = old_obj
|
|
74
|
+
self._current_app_status = obj
|
|
66
75
|
return obj
|
|
67
76
|
|
|
68
77
|
async def acreate(self, app_type, **kwargs):
|
|
78
|
+
if self._current_app_status is not None:
|
|
79
|
+
raise RuntimeError("There is already an app status in this process.")
|
|
80
|
+
|
|
69
81
|
if app_type == "content":
|
|
70
82
|
old_obj = await ContentAppStatus.objects.acreate(**kwargs)
|
|
71
83
|
else:
|
|
72
84
|
raise NotImplementedError(f"Invalid app_type: {app_type}")
|
|
73
85
|
obj = await super().acreate(app_type=app_type, **kwargs)
|
|
74
86
|
obj._old_status = old_obj
|
|
87
|
+
self._current_app_status = obj
|
|
75
88
|
return obj
|
|
76
89
|
|
|
90
|
+
def current(self):
|
|
91
|
+
if self._current_app_status is None:
|
|
92
|
+
raise RuntimeError("There is no current app status.")
|
|
93
|
+
return self._current_app_status
|
|
94
|
+
|
|
77
95
|
def online(self):
|
|
78
96
|
"""
|
|
79
97
|
Returns a queryset of objects that are online.
|
pulpcore/app/models/task.py
CHANGED
|
@@ -116,7 +116,8 @@ class Task(BaseModel, AutoAddObjPermsMixin):
|
|
|
116
116
|
Defaults to `True`.
|
|
117
117
|
|
|
118
118
|
Relations:
|
|
119
|
-
|
|
119
|
+
app_lock (AppStatus): The app holding the lock on this task.
|
|
120
|
+
Warning: This is not yet implemented/enforced.
|
|
120
121
|
parent (models.ForeignKey): Task that spawned this task (if any)
|
|
121
122
|
worker (models.ForeignKey): The worker that this task is in
|
|
122
123
|
pulp_domain (models.ForeignKey): The domain the Task is a part of
|
|
@@ -138,6 +139,10 @@ class Task(BaseModel, AutoAddObjPermsMixin):
|
|
|
138
139
|
enc_kwargs = EncryptedJSONField(null=True, encoder=DjangoJSONEncoder)
|
|
139
140
|
|
|
140
141
|
worker = models.ForeignKey("Worker", null=True, related_name="tasks", on_delete=models.SET_NULL)
|
|
142
|
+
# This field is supposed to replace the session advisory locks to protect tasks.
|
|
143
|
+
app_lock = models.ForeignKey(
|
|
144
|
+
"AppStatus", null=True, related_name="tasks", on_delete=models.SET_NULL
|
|
145
|
+
)
|
|
141
146
|
|
|
142
147
|
parent_task = models.ForeignKey(
|
|
143
148
|
"Task", null=True, related_name="child_tasks", on_delete=models.SET_NULL
|
pulpcore/app/settings.py
CHANGED
|
@@ -405,6 +405,7 @@ KAFKA_SASL_PASSWORD = None
|
|
|
405
405
|
|
|
406
406
|
# opentelemetry settings
|
|
407
407
|
OTEL_ENABLED = False
|
|
408
|
+
OTEL_PULP_API_HISTOGRAM_BUCKETS = []
|
|
408
409
|
|
|
409
410
|
# VulnerabilityReport settings
|
|
410
411
|
VULN_REPORT_TASK_LIMITER = 10
|
|
@@ -504,6 +505,16 @@ authentication_json_header_openapi_security_scheme_validator = Validator(
|
|
|
504
505
|
messages={"is_type_of": "{name} must be a dictionary."},
|
|
505
506
|
)
|
|
506
507
|
|
|
508
|
+
otel_pulp_api_histogram_buckets_validator = Validator(
|
|
509
|
+
"OTEL_PULP_API_HISTOGRAM_BUCKETS",
|
|
510
|
+
is_type_of=list,
|
|
511
|
+
condition=lambda v: all([isinstance(value, float) for value in v]),
|
|
512
|
+
messages={
|
|
513
|
+
"is_type_of": "{name} must be a list.",
|
|
514
|
+
"condition": "All buckets must be declared as a float value",
|
|
515
|
+
},
|
|
516
|
+
)
|
|
517
|
+
|
|
507
518
|
|
|
508
519
|
def otel_middleware_hook(settings):
|
|
509
520
|
data = {"dynaconf_merge": True}
|
|
@@ -530,6 +541,7 @@ settings = DjangoDynaconf(
|
|
|
530
541
|
unknown_algs_validator,
|
|
531
542
|
json_header_auth_validator,
|
|
532
543
|
authentication_json_header_openapi_security_scheme_validator,
|
|
544
|
+
otel_pulp_api_histogram_buckets_validator,
|
|
533
545
|
],
|
|
534
546
|
post_hooks=(otel_middleware_hook,),
|
|
535
547
|
)
|
pulpcore/middleware.py
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import time
|
|
2
|
+
import re
|
|
3
|
+
|
|
2
4
|
from contextvars import ContextVar
|
|
5
|
+
from os import environ
|
|
3
6
|
|
|
4
7
|
from django.http.response import Http404
|
|
5
8
|
from django.conf import settings
|
|
@@ -101,15 +104,32 @@ class APIRootRewriteMiddleware:
|
|
|
101
104
|
class DjangoMetricsMiddleware:
|
|
102
105
|
def __init__(self, get_response):
|
|
103
106
|
self.meter = init_otel_meter("pulp-api")
|
|
104
|
-
self.
|
|
105
|
-
name="api.request_duration",
|
|
106
|
-
description="Tracks the duration of HTTP requests",
|
|
107
|
-
unit="ms",
|
|
108
|
-
)
|
|
107
|
+
self._set_histogram(self.meter)
|
|
109
108
|
|
|
110
109
|
self.get_response = get_response
|
|
111
110
|
|
|
111
|
+
def _excluded_urls(self, url):
|
|
112
|
+
|
|
113
|
+
excluded_urls = environ.get(
|
|
114
|
+
"OTEL_PYTHON_EXCLUDED_URLS", environ.get("OTEL_PYTHON_DJANGO_EXCLUDED_URLS", "")
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if excluded_urls:
|
|
118
|
+
excluded_urls_list = [excluded_url.strip() for excluded_url in excluded_urls.split(",")]
|
|
119
|
+
else:
|
|
120
|
+
return False
|
|
121
|
+
|
|
122
|
+
exclusion_pattern = "|".join(excluded_urls_list)
|
|
123
|
+
|
|
124
|
+
if re.search(exclusion_pattern, url):
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
return False
|
|
128
|
+
|
|
112
129
|
def __call__(self, request):
|
|
130
|
+
if self._excluded_urls(request.build_absolute_uri("?")):
|
|
131
|
+
return self.get_response(request)
|
|
132
|
+
|
|
113
133
|
start_time = time.time()
|
|
114
134
|
response = self.get_response(request)
|
|
115
135
|
end_time = time.time()
|
|
@@ -122,11 +142,18 @@ class DjangoMetricsMiddleware:
|
|
|
122
142
|
return response
|
|
123
143
|
|
|
124
144
|
def _set_histogram(self, meter):
|
|
125
|
-
|
|
126
|
-
name
|
|
127
|
-
description
|
|
128
|
-
unit
|
|
129
|
-
|
|
145
|
+
create_histogram_kwargs = {
|
|
146
|
+
"name": "api.request_duration",
|
|
147
|
+
"description": "Tracks the duration of HTTP requests",
|
|
148
|
+
"unit": "ms",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if settings.OTEL_PULP_API_HISTOGRAM_BUCKETS:
|
|
152
|
+
create_histogram_kwargs["explicit_bucket_boundaries_advisory"] = (
|
|
153
|
+
settings.OTEL_PULP_API_HISTOGRAM_BUCKETS
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
self.request_duration_histogram = meter.create_histogram(**create_histogram_kwargs)
|
|
130
157
|
|
|
131
158
|
def _process_attributes(self, request, response):
|
|
132
159
|
return {
|
pulpcore/migrations.py
CHANGED
|
@@ -3,8 +3,6 @@ from packaging.version import parse as parse_version
|
|
|
3
3
|
from django.conf import settings
|
|
4
4
|
from django.utils import timezone
|
|
5
5
|
from django.db.migrations.operations.base import Operation
|
|
6
|
-
from django.db.models import F
|
|
7
|
-
from django.db.models.functions import Now
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
class RequireVersion(Operation):
|
|
@@ -58,7 +56,10 @@ class RequireVersion(Operation):
|
|
|
58
56
|
|
|
59
57
|
try:
|
|
60
58
|
AppStatus = from_state.apps.get_model("core", "AppStatus")
|
|
61
|
-
|
|
59
|
+
except LookupError:
|
|
60
|
+
pass
|
|
61
|
+
else:
|
|
62
|
+
for worker in AppStatus.objects.all():
|
|
62
63
|
present_version = worker.versions.get(self.plugin)
|
|
63
64
|
if present_version is not None and parse_version(present_version) < needed_version:
|
|
64
65
|
errors.append(
|
|
@@ -67,8 +68,6 @@ class RequireVersion(Operation):
|
|
|
67
68
|
)
|
|
68
69
|
|
|
69
70
|
found_either_table = True
|
|
70
|
-
except LookupError:
|
|
71
|
-
pass
|
|
72
71
|
|
|
73
72
|
assert found_either_table
|
|
74
73
|
if errors:
|
|
@@ -116,9 +116,15 @@ def validate_version_paths(version):
|
|
|
116
116
|
Raises:
|
|
117
117
|
ValueError: If two artifact relative paths overlap
|
|
118
118
|
"""
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
# Get unique (path, artifact) pairs to allow artifacts shared across content
|
|
120
|
+
content_artifacts = (
|
|
121
|
+
ContentArtifact.objects.filter(content__pk__in=version.content)
|
|
122
|
+
.values_list("relative_path", "artifact")
|
|
123
|
+
.distinct()
|
|
121
124
|
)
|
|
125
|
+
|
|
126
|
+
paths = [path for path, artifact_id in content_artifacts]
|
|
127
|
+
|
|
122
128
|
try:
|
|
123
129
|
validate_file_paths(paths)
|
|
124
130
|
except ValueError as e:
|
pulpcore/pytest_plugin.py
CHANGED
|
@@ -1013,23 +1013,25 @@ def dispatch_task(pulpcore_bindings):
|
|
|
1013
1013
|
commands = (
|
|
1014
1014
|
"from django_guid import set_guid; "
|
|
1015
1015
|
"from pulpcore.tasking.tasks import dispatch; "
|
|
1016
|
-
"from pulpcore.app.models import TaskGroup; "
|
|
1016
|
+
"from pulpcore.app.models import TaskGroup, AppStatus; "
|
|
1017
1017
|
"from pulpcore.app.util import get_url, set_current_user; "
|
|
1018
1018
|
"from django.contrib.auth import get_user_model; "
|
|
1019
|
+
f"app_status=AppStatus.objects.create(name='test-' + {cid!r},app_type='worker'); "
|
|
1019
1020
|
"User = get_user_model(); "
|
|
1020
1021
|
f"user = User.objects.filter(username='{username}').first(); "
|
|
1021
1022
|
"set_current_user(user); "
|
|
1022
1023
|
f"set_guid({cid!r}); "
|
|
1023
1024
|
f"tg = {task_group_id!r} and TaskGroup.objects.filter(pk={task_group_id!r}).first(); "
|
|
1024
1025
|
f"task = dispatch(*{args!r}, task_group=tg, **{kwargs!r}); "
|
|
1026
|
+
"app_status.delete(); "
|
|
1025
1027
|
"print(get_url(task))"
|
|
1026
1028
|
)
|
|
1027
1029
|
|
|
1028
1030
|
process = subprocess.run(["pulpcore-manager", "shell", "-c", commands], capture_output=True)
|
|
1029
|
-
|
|
1030
|
-
assert process.returncode == 0
|
|
1031
|
+
err_log = process.stderr.decode()
|
|
1032
|
+
assert process.returncode == 0, err_log
|
|
1031
1033
|
task_href = process.stdout.decode().strip()
|
|
1032
|
-
print(
|
|
1034
|
+
print(err_log, file=sys.stderr)
|
|
1033
1035
|
return task_href
|
|
1034
1036
|
|
|
1035
1037
|
return _dispatch_task
|
pulpcore/tasking/tasks.py
CHANGED
|
@@ -8,7 +8,6 @@ import sys
|
|
|
8
8
|
import traceback
|
|
9
9
|
import tempfile
|
|
10
10
|
import threading
|
|
11
|
-
from asgiref.sync import sync_to_async
|
|
12
11
|
from gettext import gettext as _
|
|
13
12
|
|
|
14
13
|
from django.conf import settings
|
|
@@ -16,8 +15,12 @@ from django.db import connection, transaction
|
|
|
16
15
|
from django.db.models import Model
|
|
17
16
|
from django_guid import get_guid
|
|
18
17
|
from pulpcore.app.apps import MODULE_PLUGIN_VERSIONS
|
|
19
|
-
from pulpcore.app.models import Task, TaskGroup
|
|
20
|
-
from pulpcore.app.util import
|
|
18
|
+
from pulpcore.app.models import Task, TaskGroup, AppStatus
|
|
19
|
+
from pulpcore.app.util import (
|
|
20
|
+
current_task,
|
|
21
|
+
get_domain,
|
|
22
|
+
get_prn,
|
|
23
|
+
)
|
|
21
24
|
from pulpcore.constants import (
|
|
22
25
|
TASK_FINAL_STATES,
|
|
23
26
|
TASK_INCOMPLETE_STATES,
|
|
@@ -81,19 +84,11 @@ def _execute_task(task):
|
|
|
81
84
|
immediate = task.immediate
|
|
82
85
|
is_coroutine_fn = asyncio.iscoroutinefunction(func)
|
|
83
86
|
|
|
84
|
-
if not is_coroutine_fn:
|
|
85
|
-
|
|
86
|
-
deprecation_logger.warning(
|
|
87
|
-
"Immediate tasks must be coroutine functions. "
|
|
88
|
-
"Support for non-coroutine immediate tasks will be dropped "
|
|
89
|
-
"in pulpcore 3.85."
|
|
90
|
-
)
|
|
91
|
-
func = sync_to_async(func)
|
|
92
|
-
is_coroutine_fn = True
|
|
93
|
-
else:
|
|
94
|
-
func(*args, **kwargs)
|
|
87
|
+
if immediate and not is_coroutine_fn:
|
|
88
|
+
raise ValueError("Immediate tasks must be async functions.")
|
|
95
89
|
|
|
96
90
|
if is_coroutine_fn:
|
|
91
|
+
# both regular and immediate tasks can be coroutines, but only immediate must timeout
|
|
97
92
|
_logger.debug("Task is coroutine %s", task.pk)
|
|
98
93
|
coro = func(*args, **kwargs)
|
|
99
94
|
if immediate:
|
|
@@ -110,6 +105,8 @@ def _execute_task(task):
|
|
|
110
105
|
timeout=IMMEDIATE_TIMEOUT,
|
|
111
106
|
)
|
|
112
107
|
)
|
|
108
|
+
else:
|
|
109
|
+
func(*args, **kwargs)
|
|
113
110
|
|
|
114
111
|
except Exception:
|
|
115
112
|
exc_type, exc, tb = sys.exc_info()
|
|
@@ -240,6 +237,7 @@ def dispatch(
|
|
|
240
237
|
immediate=immediate,
|
|
241
238
|
deferred=deferred,
|
|
242
239
|
profile_options=x_task_diagnostics_var.get(None),
|
|
240
|
+
app_lock=(immediate and AppStatus.objects.current()) or None,
|
|
243
241
|
)
|
|
244
242
|
task.refresh_from_db() # The database may have assigned a timestamp for us.
|
|
245
243
|
if immediate:
|
|
@@ -272,13 +270,16 @@ def dispatch(
|
|
|
272
270
|
try:
|
|
273
271
|
execute_task(task)
|
|
274
272
|
finally:
|
|
275
|
-
#
|
|
273
|
+
# Whether the task fails or not, we should always restore the workdir.
|
|
276
274
|
os.chdir(cur_dir)
|
|
277
275
|
|
|
278
276
|
if resources:
|
|
279
277
|
notify_workers = True
|
|
280
278
|
elif deferred:
|
|
279
|
+
# Resources are blocked. Let the others handle it.
|
|
281
280
|
notify_workers = True
|
|
281
|
+
task.app_lock = None
|
|
282
|
+
task.save()
|
|
282
283
|
else:
|
|
283
284
|
task.set_canceling()
|
|
284
285
|
task.set_canceled(TASK_STATES.CANCELED, "Resources temporarily unavailable.")
|
pulpcore/tasking/worker.py
CHANGED
|
@@ -363,41 +363,60 @@ class PulpcoreWorker:
|
|
|
363
363
|
def iter_tasks(self):
|
|
364
364
|
"""Iterate over ready tasks and yield each task while holding the lock."""
|
|
365
365
|
while not self.shutdown_requested:
|
|
366
|
-
# When batching this query, be sure to use "pulp_created" as a cursor
|
|
366
|
+
# When batching this query, be sure to use "pulp_created" as a cursor.
|
|
367
367
|
for task in Task.objects.filter(
|
|
368
368
|
state__in=TASK_INCOMPLETE_STATES,
|
|
369
369
|
unblocked_at__isnull=False,
|
|
370
370
|
).order_by("-immediate", F("pulp_created") + Value(timedelta(seconds=8)) * Random()):
|
|
371
|
-
# This code will only be called if we acquired the lock successfully
|
|
372
|
-
# The lock will be automatically be released at the end of the block
|
|
371
|
+
# This code will only be called if we acquired the lock successfully.
|
|
372
|
+
# The lock will be automatically be released at the end of the block.
|
|
373
373
|
with contextlib.suppress(AdvisoryLockError), task:
|
|
374
|
-
#
|
|
375
|
-
task.
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
if
|
|
386
|
-
|
|
374
|
+
# We got the advisory lock (OLD) now try to get the app_lock (NEW).
|
|
375
|
+
rows = Task.objects.filter(pk=task.pk, app_lock=None).update(
|
|
376
|
+
app_lock=AppStatus.objects.current()
|
|
377
|
+
)
|
|
378
|
+
if rows == 0:
|
|
379
|
+
_logger.error(
|
|
380
|
+
"Acquired advisory lock but missed the app_lock for the task. "
|
|
381
|
+
"This should only happen during the upgrade phase to the new app_lock."
|
|
382
|
+
)
|
|
383
|
+
continue
|
|
384
|
+
try:
|
|
385
|
+
# Check if someone else changed the task before we got the lock.
|
|
386
|
+
task.refresh_from_db()
|
|
387
|
+
|
|
388
|
+
if task.state == TASK_STATES.CANCELING and task.worker is None:
|
|
389
|
+
# No worker picked this task up before being canceled.
|
|
390
|
+
if self.cancel_abandoned_task(task, TASK_STATES.CANCELED):
|
|
391
|
+
# Continue looking for the next task without considering this
|
|
392
|
+
# tasks resources, as we just released them.
|
|
393
|
+
continue
|
|
394
|
+
if task.state in [TASK_STATES.RUNNING, TASK_STATES.CANCELING]:
|
|
395
|
+
# A running task without a lock must be abandoned.
|
|
396
|
+
if self.cancel_abandoned_task(
|
|
397
|
+
task, TASK_STATES.FAILED, "Worker has gone missing."
|
|
398
|
+
):
|
|
399
|
+
# Continue looking for the next task without considering this
|
|
400
|
+
# tasks resources, as we just released them.
|
|
401
|
+
continue
|
|
402
|
+
|
|
403
|
+
# This statement is using lazy evaluation.
|
|
404
|
+
if (
|
|
405
|
+
task.state == TASK_STATES.WAITING
|
|
406
|
+
and task.unblocked_at is not None
|
|
407
|
+
and self.is_compatible(task)
|
|
387
408
|
):
|
|
388
|
-
|
|
389
|
-
#
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
# Start from the top of the Task list
|
|
400
|
-
break
|
|
409
|
+
yield task
|
|
410
|
+
# Start from the top of the Task list.
|
|
411
|
+
break
|
|
412
|
+
finally:
|
|
413
|
+
rows = Task.objects.filter(
|
|
414
|
+
pk=task.pk, app_lock=AppStatus.objects.current()
|
|
415
|
+
).update(app_lock=None)
|
|
416
|
+
if rows != 1:
|
|
417
|
+
raise RuntimeError(
|
|
418
|
+
"Something other than us is messing around with locks."
|
|
419
|
+
)
|
|
401
420
|
else:
|
|
402
421
|
# No task found in the for-loop
|
|
403
422
|
break
|
|
@@ -490,22 +490,21 @@ class TestImmediateTaskWithNoResource:
|
|
|
490
490
|
assert task.worker is None
|
|
491
491
|
|
|
492
492
|
@pytest.mark.parallel
|
|
493
|
-
def test_executes_on_api_worker_when_no_async(
|
|
493
|
+
def test_executes_on_api_worker_when_no_async(
|
|
494
|
+
self, pulpcore_bindings, dispatch_task, monitor_task
|
|
495
|
+
):
|
|
494
496
|
"""
|
|
495
497
|
GIVEN a task with no resource requirements
|
|
496
498
|
AND the task IS NOT an async function
|
|
497
499
|
WHEN dispatching a task as immediate
|
|
498
|
-
THEN the
|
|
500
|
+
THEN the dispatch should throw an error
|
|
499
501
|
"""
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
assert task.state == "completed"
|
|
507
|
-
assert task.worker is None
|
|
508
|
-
assert "Support for non-coroutine immediate tasks will be dropped" in stderr_content
|
|
502
|
+
with pytest.raises(PulpTaskError) as ctx:
|
|
503
|
+
task_href = dispatch_task(
|
|
504
|
+
"pulpcore.app.tasks.test.sleep", args=(LT_TIMEOUT,), immediate=True
|
|
505
|
+
)
|
|
506
|
+
monitor_task(task_href)
|
|
507
|
+
assert "Immediate tasks must be async functions" in ctx.value.task.error["description"]
|
|
509
508
|
|
|
510
509
|
@pytest.mark.parallel
|
|
511
510
|
def test_timeouts_on_api_worker(self, pulpcore_bindings, dispatch_task):
|
|
@@ -4,7 +4,6 @@ import pytest
|
|
|
4
4
|
import subprocess
|
|
5
5
|
import uuid
|
|
6
6
|
from datetime import datetime, timedelta
|
|
7
|
-
from random import choice
|
|
8
7
|
from time import sleep
|
|
9
8
|
|
|
10
9
|
|
|
@@ -23,7 +22,7 @@ def test_worker_actions(pulpcore_bindings):
|
|
|
23
22
|
assert val is not None
|
|
24
23
|
|
|
25
24
|
# Pick a random worker to be used for the next assertions.
|
|
26
|
-
chosen_worker =
|
|
25
|
+
chosen_worker = next(worker for worker in workers if not worker.name.startswith("test-"))
|
|
27
26
|
|
|
28
27
|
# Read a worker by its pulp_href.
|
|
29
28
|
read_worker = pulpcore_bindings.WorkersApi.read(chosen_worker.pulp_href)
|
|
@@ -5,7 +5,7 @@ from django.db.utils import InterfaceError, OperationalError
|
|
|
5
5
|
|
|
6
6
|
from pulpcore.content import _heartbeat
|
|
7
7
|
from pulpcore.content.handler import Handler
|
|
8
|
-
from pulpcore.app.models.status import AppStatusManager
|
|
8
|
+
from pulpcore.app.models.status import AppStatus, AppStatusManager
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class MockException(Exception):
|
|
@@ -25,6 +25,7 @@ async def test_db_connection_interface_error(monkeypatch, settings, error_class)
|
|
|
25
25
|
mock_acreate = AsyncMock()
|
|
26
26
|
mock_acreate.return_value = mock_app_status
|
|
27
27
|
monkeypatch.setattr(AppStatusManager, "acreate", mock_acreate)
|
|
28
|
+
monkeypatch.setattr(AppStatus, "objects", AppStatusManager())
|
|
28
29
|
mock_reset_db = Mock()
|
|
29
30
|
monkeypatch.setattr(Handler, "_reset_db_connection", mock_reset_db)
|
|
30
31
|
settings.CONTENT_APP_TTL = 1
|
|
@@ -3,7 +3,8 @@ from uuid import uuid4
|
|
|
3
3
|
|
|
4
4
|
from itertools import compress
|
|
5
5
|
|
|
6
|
-
from pulpcore.plugin.models import Content, Repository
|
|
6
|
+
from pulpcore.plugin.models import Artifact, Content, ContentArtifact, Repository
|
|
7
|
+
from pulpcore.plugin.repo_version_utils import validate_version_paths
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def pks_of_next_qs(qs_generator):
|
|
@@ -258,3 +259,85 @@ def test_next_version_with_multiple_versions():
|
|
|
258
259
|
|
|
259
260
|
assert repository.next_version == 4
|
|
260
261
|
assert repository.latest_version().number == 1
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@pytest.mark.django_db
|
|
265
|
+
def test_shared_artifact_same_path_validation(tmp_path):
|
|
266
|
+
"""
|
|
267
|
+
Test that multiple content units can reference the same artifact with the same
|
|
268
|
+
relative path without causing validation errors.
|
|
269
|
+
|
|
270
|
+
This reproduces scenarios where different content units legitimately share
|
|
271
|
+
the same artifact (e.g. upstream source files).
|
|
272
|
+
"""
|
|
273
|
+
# Create a repository
|
|
274
|
+
repository = Repository.objects.create(name=uuid4())
|
|
275
|
+
repository.CONTENT_TYPES = [Content]
|
|
276
|
+
|
|
277
|
+
# Create a shared artifact using proper test pattern
|
|
278
|
+
artifact_path = tmp_path / "shared_file.txt"
|
|
279
|
+
artifact_path.write_text("Shared content data")
|
|
280
|
+
shared_artifact = Artifact.init_and_validate(str(artifact_path))
|
|
281
|
+
shared_artifact.save()
|
|
282
|
+
|
|
283
|
+
# Create two content units (simulates any content that shares artifacts)
|
|
284
|
+
content1 = Content.objects.create(pulp_type="core.content")
|
|
285
|
+
content2 = Content.objects.create(pulp_type="core.content")
|
|
286
|
+
|
|
287
|
+
# Both content units reference the same artifact with same path
|
|
288
|
+
ContentArtifact.objects.create(
|
|
289
|
+
content=content1, artifact=shared_artifact, relative_path="shared/common_file.txt"
|
|
290
|
+
)
|
|
291
|
+
ContentArtifact.objects.create(
|
|
292
|
+
content=content2, artifact=shared_artifact, relative_path="shared/common_file.txt"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Create a repository version with both content units
|
|
296
|
+
with repository.new_version() as new_version:
|
|
297
|
+
new_version.add_content(Content.objects.filter(pk__in=[content1.pk, content2.pk]))
|
|
298
|
+
|
|
299
|
+
# This should not raise validation errors with our fix
|
|
300
|
+
validate_version_paths(new_version)
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
@pytest.mark.django_db
|
|
304
|
+
def test_different_artifacts_same_path_validation_fails(tmp_path):
|
|
305
|
+
"""
|
|
306
|
+
Test that different artifacts trying to use the same relative path
|
|
307
|
+
still fail validation (this is a real conflict that should be caught).
|
|
308
|
+
"""
|
|
309
|
+
# Create a repository
|
|
310
|
+
repository = Repository.objects.create(name=uuid4())
|
|
311
|
+
repository.CONTENT_TYPES = [Content]
|
|
312
|
+
|
|
313
|
+
# Create two different artifacts using proper test pattern
|
|
314
|
+
artifact1_path = tmp_path / "artifact1.txt"
|
|
315
|
+
artifact1_path.write_text("Content of first artifact")
|
|
316
|
+
artifact1 = Artifact.init_and_validate(str(artifact1_path))
|
|
317
|
+
artifact1.save()
|
|
318
|
+
|
|
319
|
+
artifact2_path = tmp_path / "artifact2.txt"
|
|
320
|
+
artifact2_path.write_text("Content of second artifact") # Different content
|
|
321
|
+
artifact2 = Artifact.init_and_validate(str(artifact2_path))
|
|
322
|
+
artifact2.save()
|
|
323
|
+
|
|
324
|
+
# Create two content units with different artifacts but same path
|
|
325
|
+
content1 = Content.objects.create(pulp_type="core.content")
|
|
326
|
+
content2 = Content.objects.create(pulp_type="core.content")
|
|
327
|
+
|
|
328
|
+
ContentArtifact.objects.create(
|
|
329
|
+
content=content1, artifact=artifact1, relative_path="conflicting/file.txt"
|
|
330
|
+
)
|
|
331
|
+
ContentArtifact.objects.create(
|
|
332
|
+
content=content2,
|
|
333
|
+
artifact=artifact2,
|
|
334
|
+
relative_path="conflicting/file.txt", # Same path, different artifact
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
# Create a repository version with both content units
|
|
338
|
+
with repository.new_version() as new_version:
|
|
339
|
+
new_version.add_content(Content.objects.filter(pk__in=[content1.pk, content2.pk]))
|
|
340
|
+
|
|
341
|
+
# This should raise a validation error due to path conflict
|
|
342
|
+
with pytest.raises(ValueError, match="Repository version errors"):
|
|
343
|
+
validate_version_paths(new_version)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pulpcore
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.86.0
|
|
4
4
|
Summary: Pulp Django Application and Related Modules
|
|
5
5
|
Author-email: Pulp Team <pulp-list@redhat.com>
|
|
6
6
|
Project-URL: Homepage, https://pulpproject.org
|
|
@@ -45,7 +45,7 @@ Requires-Dist: PyOpenSSL<26.0
|
|
|
45
45
|
Requires-Dist: opentelemetry-api<1.37,>=1.27.0
|
|
46
46
|
Requires-Dist: opentelemetry-sdk<1.37,>=1.27.0
|
|
47
47
|
Requires-Dist: opentelemetry-exporter-otlp-proto-http<1.37,>=1.27.0
|
|
48
|
-
Requires-Dist: protobuf<
|
|
48
|
+
Requires-Dist: protobuf<7.0,>=4.21.1
|
|
49
49
|
Requires-Dist: pulp-glue<0.36,>=0.28.0
|
|
50
50
|
Requires-Dist: pygtrie<=2.5.0,>=2.5
|
|
51
51
|
Requires-Dist: psycopg[binary]<3.3,>=3.1.8
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
pulp_certguard/__init__.py,sha256=llnEd00PrsAretsgAOHiNKFbmvIdXe3iDVPmSaKz7gU,71
|
|
2
2
|
pulp_certguard/pytest_plugin.py,sha256=qhRbChzqN2PROtD-65KuoTfKr5k9T3GPsz9daFgpqpM,852
|
|
3
|
-
pulp_certguard/app/__init__.py,sha256=
|
|
3
|
+
pulp_certguard/app/__init__.py,sha256=uZZw9MWtJ_10aWLtmfluDTNp95Ys1lJil_PXTdA1_W4,297
|
|
4
4
|
pulp_certguard/app/models.py,sha256=YLEhBtZM4hetekVZ_GTnbLlWD6CkIQw2B3ILwXRcq-s,7483
|
|
5
5
|
pulp_certguard/app/serializers.py,sha256=9IxlQiy783RdKF9oI1mrYS4haG5Boy2DOjfP_eJtMLY,1726
|
|
6
6
|
pulp_certguard/app/viewsets.py,sha256=1_gNmsWyOT8kcOiGVkn4-wrtAjZO4wC8q0-aoEsCpjI,697
|
|
@@ -51,7 +51,7 @@ pulp_certguard/tests/unit/test_rhsm_check_path.py,sha256=Q1CsXnUgD7ELvtolPeumyNr
|
|
|
51
51
|
pulp_file/__init__.py,sha256=0vOCXofR6Eyxkg4y66esnOGPeESCe23C1cNBHj56w44,61
|
|
52
52
|
pulp_file/manifest.py,sha256=1WwIOJrPSkFcmkRm7CkWifVOCoZvo_nnANgce6uuG7U,3796
|
|
53
53
|
pulp_file/pytest_plugin.py,sha256=l1PvTxUi5D3uJy4SnHWNhr-otWEYNcm-kc5nSqVJg0Y,10646
|
|
54
|
-
pulp_file/app/__init__.py,sha256=
|
|
54
|
+
pulp_file/app/__init__.py,sha256=jD3E48tyhIAmkz0RW7ThsAoxuXVR5KpUsS68u6x5W4E,292
|
|
55
55
|
pulp_file/app/modelresource.py,sha256=v-m-_bBEsfr8wG0TI5ffx1TuKUy2-PsirhuQz4XXF-0,1063
|
|
56
56
|
pulp_file/app/models.py,sha256=QsrVg_2uKqnR89sLN2Y7Zy260_nLIcUfa94uZowlmFw,4571
|
|
57
57
|
pulp_file/app/replica.py,sha256=OtNWVmdFUgNTYhPttftVNQnSrnvx2_hnrJgtW_G0Vrg,1894
|
|
@@ -90,13 +90,13 @@ pulpcore/backends.py,sha256=Ax_MJpbvtNDg_rhkHaiQRm39DBSS2dH8UpMRJN2T0oE,4482
|
|
|
90
90
|
pulpcore/constants.py,sha256=ym3LV5TqFtUE_klh6yEogUb4fg4NZACF213VztnZ6jw,4882
|
|
91
91
|
pulpcore/filters.py,sha256=dD5oRRkWg65s3LoObr-ipRvRsxZK_3Zr0lKMNr9Sg5o,16682
|
|
92
92
|
pulpcore/metrics.py,sha256=Mfq-nnRjRf3vBHFO-ux-4d1I3yE7TgeptwgiSgGz4rA,2230
|
|
93
|
-
pulpcore/middleware.py,sha256=
|
|
94
|
-
pulpcore/migrations.py,sha256=
|
|
95
|
-
pulpcore/pytest_plugin.py,sha256=
|
|
93
|
+
pulpcore/middleware.py,sha256=YTmIW41bpIu3rw_zVRFz-1A4nko4zMte7EiYXBJkwXg,6721
|
|
94
|
+
pulpcore/migrations.py,sha256=YhvuCIqyTr9AwBGwOpawCEuuZpHHo50xt6VHqUGraeE,3383
|
|
95
|
+
pulpcore/pytest_plugin.py,sha256=fy9vz5-bw30T7f4jxDtNIgF7L_0MJ_q7KIAzpvizvnY,38215
|
|
96
96
|
pulpcore/responses.py,sha256=mIGKmdCfTSoZxbFu4yIH1xbdLx1u5gqt3D99LTamcJg,6125
|
|
97
97
|
pulpcore/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
98
|
pulpcore/app/access_policy.py,sha256=5vCKy6WoHtIt1_-eS5vMaZ7CmR4G-CIpsrB8yT-d88Q,6079
|
|
99
|
-
pulpcore/app/apps.py,sha256=
|
|
99
|
+
pulpcore/app/apps.py,sha256=1X52djxH5kP1k2Tktti7NJttzmNqI2rSKuJ_JKti7Zg,17412
|
|
100
100
|
pulpcore/app/authentication.py,sha256=1LIJW6HIQQlZrliHy__jdzkDEh6Oj7xKgd0V-vRcDus,2855
|
|
101
101
|
pulpcore/app/checks.py,sha256=jbfTF7nmftBbky4AQXHigpyCaGydKasvRUXsd72JZVg,1946
|
|
102
102
|
pulpcore/app/entrypoint.py,sha256=GYEq4GjglQZhFlU3865AT_H0nPypDKJAsf8qdyR4tPY,4985
|
|
@@ -115,7 +115,7 @@ pulpcore/app/redis_connection.py,sha256=VTdG0ulXuyESjYV6SJdG_jLzkLZH-MlLcD6pielw
|
|
|
115
115
|
pulpcore/app/replica.py,sha256=rGE14OBaR_FKxmHL7NMxf_OizMyS-90IPsMRo_j9YRI,11474
|
|
116
116
|
pulpcore/app/response.py,sha256=hYH_jSBrxmRsBr2bknmXE1qfs2g8JjDTXYcQ5ZWlF_c,1950
|
|
117
117
|
pulpcore/app/role_util.py,sha256=84HSt8_9fxB--dtfSyg_TumVgOdyBbyP6rBaiAfTpOU,22393
|
|
118
|
-
pulpcore/app/settings.py,sha256=
|
|
118
|
+
pulpcore/app/settings.py,sha256=k1MEoM25PXe1K8p9swB_lsNvg5Rd8X2mctuLTL7-nBw,21893
|
|
119
119
|
pulpcore/app/urls.py,sha256=0gdI74CAdycJStXSw1gknviDGe3J3k0UhS4J8RYa5dg,8120
|
|
120
120
|
pulpcore/app/util.py,sha256=nYF6nZXgqVk4U1QeZEpWYX-wqitGSGAJip6W78IfXUk,24432
|
|
121
121
|
pulpcore/app/wsgi.py,sha256=7rpZ_1NHEN_UfeNZCj8206bas1WeqRkHnGdxpd7rdDI,492
|
|
@@ -130,6 +130,7 @@ pulpcore/app/management/commands/dump-publications-to-fs.py,sha256=lkLwxPi4GXzcL
|
|
|
130
130
|
pulpcore/app/management/commands/handle-artifact-checksums.py,sha256=gblm6CkuyXrf9TsiTtts6iIgk4nyZnsJShozGxyALV8,8728
|
|
131
131
|
pulpcore/app/management/commands/migrationstat.py,sha256=Gy19UMSyUeXYT13ERQ-P1PdgnmNX9veJteEOgMMG6QY,1517
|
|
132
132
|
pulpcore/app/management/commands/openapi.py,sha256=p-aPuVfbnFQYIU7BMnipxe9nId-f2agNiviSIy43y9Q,3634
|
|
133
|
+
pulpcore/app/management/commands/optimizemigration.py,sha256=jt_V2PxvA7TSLz730hm6Yq8QwlcRIE5cllmve9KJo9M,3155
|
|
133
134
|
pulpcore/app/management/commands/rebasemigrations.py,sha256=xpn8Gg-L3SfBGG0dD782sBmhLMgVHrveBg7xrGII3f8,3356
|
|
134
135
|
pulpcore/app/management/commands/remove-plugin.py,sha256=bgcCm4y_Cl-C_8DL8wqTveOV4Jvw3cMwashauOhDD2E,7949
|
|
135
136
|
pulpcore/app/management/commands/remove-signing-service.py,sha256=OF0IeGoldEhFAK3QvDaxMpsmoCu-q6Kcs8SbZJutLgU,2199
|
|
@@ -186,6 +187,7 @@ pulpcore/app/migrations/0135_task_pulp_task_resources_index.py,sha256=X2cJRbuuKx
|
|
|
186
187
|
pulpcore/app/migrations/0136_delete_basedistribution.py,sha256=4oKidAU27jLLWRkboPstNF75kq3mTZ-QUxrKbMIczKo,315
|
|
187
188
|
pulpcore/app/migrations/0137_appstatus.py,sha256=WMjQ8Tb-jsaCtyYBVZAmFemGyIzNDABVPfTKsEYvxjI,1332
|
|
188
189
|
pulpcore/app/migrations/0138_vulnerabilityreport.py,sha256=rYmdIXfTCSZeH5sHLCCFMc2wrKhSuVE334A4kmFkJ6w,1372
|
|
190
|
+
pulpcore/app/migrations/0139_task_app_lock.py,sha256=Dtu_om_zFplrPr8DageoiXOWUiOS5aHgOy99S0bMXH0,502
|
|
189
191
|
pulpcore/app/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
190
192
|
pulpcore/app/models/__init__.py,sha256=P_2UnLmtQYbASWrm8elO2Zm_od-LXVqQKnjCwYFlZW0,3552
|
|
191
193
|
pulpcore/app/models/access_policy.py,sha256=o4L41RGoZ5UMmh5UeeenmadD5MJgShguphgd4eAVxQA,6071
|
|
@@ -204,9 +206,9 @@ pulpcore/app/models/publication.py,sha256=75uUnm_sU5V_QraD6kPvEXVKxYyA1ikIsoFD51
|
|
|
204
206
|
pulpcore/app/models/replica.py,sha256=i_wPxyPaVWpEVTJNVjJsBarxFauqeagtuwLadsmVz-g,2067
|
|
205
207
|
pulpcore/app/models/repository.py,sha256=SIc21Gex6okxI7OCfHEGIpXpGlypG3z9IgMt5-mkNy0,58056
|
|
206
208
|
pulpcore/app/models/role.py,sha256=dZklNd2VeAw4cT6dyJ7SyTBt9sZvdqakY86wXGAY3vU,3287
|
|
207
|
-
pulpcore/app/models/status.py,sha256=
|
|
209
|
+
pulpcore/app/models/status.py,sha256=2Fac5z8quv0cG-8CTxIzdT_uzPea6c0i5sEaHp4V82A,9268
|
|
208
210
|
pulpcore/app/models/storage.py,sha256=2b-DQWaO31NqjV6FiISALegND-sQZAU7BVAsduUvm3o,6780
|
|
209
|
-
pulpcore/app/models/task.py,sha256=
|
|
211
|
+
pulpcore/app/models/task.py,sha256=fHubp1YO6CjSxfXVtgdZsQyn7NJ7apBHKz6-YEqnuDM,15653
|
|
210
212
|
pulpcore/app/models/upload.py,sha256=3njXT2rrVJwBjEDegvqcLD9_7cPnnl974lhbAhikEp8,3004
|
|
211
213
|
pulpcore/app/models/vulnerability_report.py,sha256=DDAUjDaW3Kn9KPBkBl94u4EuQy8UIu5wKbmE5kMkhWE,1238
|
|
212
214
|
pulpcore/app/protobuf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -308,7 +310,7 @@ pulpcore/plugin/modelresources.py,sha256=E7w0ajI4Vp3hndkoLYqP0ICVjuj-D44aDF_OyGS
|
|
|
308
310
|
pulpcore/plugin/publication_utils.py,sha256=QOpOdVC6iotaBnDBUaj2VLlgcRKPx11CTNMdd-GjEyY,802
|
|
309
311
|
pulpcore/plugin/pulp_hashlib.py,sha256=imCPNxDcUEb7t-WMGlyZQwjuGQK-kwUaESNkiU5nYJE,157
|
|
310
312
|
pulpcore/plugin/replica.py,sha256=7TzKxW98ju7rXslugyBL4RUW-zHsUu_Nbd9cE-43sNQ,58
|
|
311
|
-
pulpcore/plugin/repo_version_utils.py,sha256=
|
|
313
|
+
pulpcore/plugin/repo_version_utils.py,sha256=aFLnE04-Azd0Qq3qBnHKcw2MOLBMGpF_ncwjTginswI,5270
|
|
312
314
|
pulpcore/plugin/responses.py,sha256=VcgJtVaTzCPjdIGz_AS4nQwO4ooh4O5T9KBEa1FGA1w,62
|
|
313
315
|
pulpcore/plugin/storage.py,sha256=CMoWFPfZG82-SOmuuaf8aPDYerK7mvxlER_cgWdJzQA,82
|
|
314
316
|
pulpcore/plugin/sync.py,sha256=IwAUZ_7vVYuba2Uhm0ndMyEnNZTkOhX0kOVWtb8SgxE,1002
|
|
@@ -334,8 +336,8 @@ pulpcore/tasking/_util.py,sha256=fPW4k1nUa_NZ0ywy_A15Fuiejo5stY58abPbZTXw5t8,990
|
|
|
334
336
|
pulpcore/tasking/entrypoint.py,sha256=eAypZD4ORoNOrmBeMdbwO9p6GSQ59bMvZ3TrbnE0czw,1305
|
|
335
337
|
pulpcore/tasking/kafka.py,sha256=76z4DzeXM1WL5uu1HlKnduWeLO3-b-czvGBXdWR6054,3845
|
|
336
338
|
pulpcore/tasking/storage.py,sha256=zQkwlpC_FDQtmZGZ8vKwHqxvD6CLO_gAS4Q7wijZE-k,3106
|
|
337
|
-
pulpcore/tasking/tasks.py,sha256=
|
|
338
|
-
pulpcore/tasking/worker.py,sha256=
|
|
339
|
+
pulpcore/tasking/tasks.py,sha256=CTlWLCmxP5-HZjo5_KLYIJQu-VKJnzQ5cyL7IFNRMWw,12944
|
|
340
|
+
pulpcore/tasking/worker.py,sha256=pp_9e8fTUgmghiUZI6NDQlVVxv8hpNes4SnFnqVvawQ,27195
|
|
339
341
|
pulpcore/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
340
342
|
pulpcore/tests/functional/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
341
343
|
pulpcore/tests/functional/content_with_coverage.py,sha256=gQK8himy32s9O9vpXdgoM6-_z2KySaXm5rTga9z0jGI,260
|
|
@@ -361,10 +363,10 @@ pulpcore/tests/functional/api/test_scoping.py,sha256=uiLOsx5_7puRMcvrpPKEYQziqlu
|
|
|
361
363
|
pulpcore/tests/functional/api/test_signing_service.py,sha256=yr1HXBrNoliBHJNAGAN4PAN0eBKPIvAQP-uMoMSrO_I,222
|
|
362
364
|
pulpcore/tests/functional/api/test_status.py,sha256=Vmmj-ueGxJw1JFSQtr5feTM8vjgyyROvxsRm-OgzLUQ,5370
|
|
363
365
|
pulpcore/tests/functional/api/test_task_purge.py,sha256=Av4DrUdCqf-JegfoP1pkY4B-teoUzYd1LBZKAhDa-08,7273
|
|
364
|
-
pulpcore/tests/functional/api/test_tasking.py,sha256=
|
|
366
|
+
pulpcore/tests/functional/api/test_tasking.py,sha256=UELvclncYdiqRBEC_oZZ_HoNLV4SW9isMaPQHjxw2Q4,22388
|
|
365
367
|
pulpcore/tests/functional/api/test_upload.py,sha256=oLP1ZmQgPzgK5jAQwGeXS8uLFHgAzVeLW0GfANMWexI,6794
|
|
366
368
|
pulpcore/tests/functional/api/test_users_groups.py,sha256=YFG0xtyJuIRraczR7ERl_UNS7dlJfKd2eUmXgD1lLBU,2926
|
|
367
|
-
pulpcore/tests/functional/api/test_workers.py,sha256=
|
|
369
|
+
pulpcore/tests/functional/api/test_workers.py,sha256=XJrQdxt0BpMeMVOdTyzcTEMk5bB8XC4rA8U580HnzBc,4691
|
|
368
370
|
pulpcore/tests/functional/api/using_plugin/__init__.py,sha256=QyyfzgjLOi4n32G3o9aGH5eQDNjjD_qUpHLOZpPPZa4,80
|
|
369
371
|
pulpcore/tests/functional/api/using_plugin/test_checkpoint.py,sha256=l1dE-CieRU_9wheQu-8Y7aAnGSKUztOJ6gaa4a0yoA8,8562
|
|
370
372
|
pulpcore/tests/functional/api/using_plugin/test_content_access.py,sha256=Ym800bU-M48RCDfQMkVa1UQt_sfgy5ciU0FxorCk9Ds,2551
|
|
@@ -406,7 +408,7 @@ pulpcore/tests/unit/test_viewsets.py,sha256=6rek28Rr0kEuYjQZ0_kTSnKsTvmMmD3l-WV_
|
|
|
406
408
|
pulpcore/tests/unit/test_vulnerability_report.py,sha256=KFehXFns2gIkGQ-zWsXyK--d8CqVfHgijlWbMI8QmF0,2986
|
|
407
409
|
pulpcore/tests/unit/content/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
408
410
|
pulpcore/tests/unit/content/test_handler.py,sha256=f4F9RAqCP60PUanYdeLW_A955UjRt8eCTrRuh0mChDU,19774
|
|
409
|
-
pulpcore/tests/unit/content/test_heartbeat.py,sha256=
|
|
411
|
+
pulpcore/tests/unit/content/test_heartbeat.py,sha256=Xtj4cvyI0jBsFZskcypwxruKgMh5M9cgNXQGDWhXMP4,1250
|
|
410
412
|
pulpcore/tests/unit/download/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
411
413
|
pulpcore/tests/unit/download/test_downloader_base.py,sha256=TYG_OPuyj-_N5L-zLTW1qJD_29nKFMA_PC-fPfLKLOo,1281
|
|
412
414
|
pulpcore/tests/unit/download/test_downloader_factory.py,sha256=mumtIAtRg_dS2uQvOH3J5NXb9XuvQ53iWlBP5koZ_nM,1177
|
|
@@ -419,7 +421,7 @@ pulpcore/tests/unit/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
419
421
|
pulpcore/tests/unit/models/test_base.py,sha256=77hnxOFBJYMNbI1YGEaR5yj8VCapNGmEgoD0m8Kr3GY,2618
|
|
420
422
|
pulpcore/tests/unit/models/test_content.py,sha256=heU0vJKucPIp6py2Ww-eXLvhFopvmK8QjFgzt1jGnYQ,5599
|
|
421
423
|
pulpcore/tests/unit/models/test_remote.py,sha256=KxXwHdA-wj7D-ZpuVi33cLX43wkEeIzeqF9uMsJGt-k,2354
|
|
422
|
-
pulpcore/tests/unit/models/test_repository.py,sha256=
|
|
424
|
+
pulpcore/tests/unit/models/test_repository.py,sha256=ciwyo7dMl-dxlzHb55eQ-ohEEBPF3-GjbO23mMSccqQ,13791
|
|
423
425
|
pulpcore/tests/unit/models/test_task.py,sha256=rjxeYe383Zsjk8Ck4inMBBTzR4osCrgTeZNWwmHfbjk,1457
|
|
424
426
|
pulpcore/tests/unit/roles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
425
427
|
pulpcore/tests/unit/roles/test_roles.py,sha256=TkPPCLEHMaxfafsRf_3pc4Z3w8BPTyteY7rFkVo65GM,4973
|
|
@@ -436,9 +438,9 @@ pulpcore/tests/unit/stages/test_artifactdownloader.py,sha256=qB1ANdFmNtUnljg8fCd
|
|
|
436
438
|
pulpcore/tests/unit/stages/test_stages.py,sha256=H1a2BQLjdZlZvcb_qULp62huZ1xy6ItTcthktVyGU0w,4735
|
|
437
439
|
pulpcore/tests/unit/viewsets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
438
440
|
pulpcore/tests/unit/viewsets/test_viewset_base.py,sha256=W9o3V6758bZctR6krMPPQytb0xJuF-jb4uBWTNDoD_U,4837
|
|
439
|
-
pulpcore-3.
|
|
440
|
-
pulpcore-3.
|
|
441
|
-
pulpcore-3.
|
|
442
|
-
pulpcore-3.
|
|
443
|
-
pulpcore-3.
|
|
444
|
-
pulpcore-3.
|
|
441
|
+
pulpcore-3.86.0.dist-info/licenses/LICENSE,sha256=dhnHU8rJXUdAIgIjveSKAyYG_KzN5eVG-bxETIGrNW0,17988
|
|
442
|
+
pulpcore-3.86.0.dist-info/METADATA,sha256=1JVEkZojL684SzjlPGm99WNzSTnuSeXODPsyYJg9UY0,4105
|
|
443
|
+
pulpcore-3.86.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
444
|
+
pulpcore-3.86.0.dist-info/entry_points.txt,sha256=OZven4wzXzQA5b5q9MpP4HUpIPPQCSvIOvkKtNInrK0,452
|
|
445
|
+
pulpcore-3.86.0.dist-info/top_level.txt,sha256=6h-Lm3FKQSaT_nL1KSxu_hBnzKE15bcvf_BoU-ea4CI,34
|
|
446
|
+
pulpcore-3.86.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|