Skip to content

Commit 3b1e161

Browse files
committed
Added new PulpException replacing existing errors
1 parent af9625b commit 3b1e161

13 files changed

Lines changed: 236 additions & 83 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add more PulpExceptions. Treat DRF APIExceptions as safe in the task runner.

pulp_file/app/tasks/synchronizing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def synchronize(remote_pk, repository_pk, mirror, optimize=False, url=None, **kw
117117
optimize = optimize or settings.FILE_SYNC_OPTIMIZATION
118118

119119
if not remote.url:
120-
raise ValueError(_("A remote must have a url specified to synchronize."))
120+
raise SyncError(_("A remote must have a url specified to synchronize."))
121121

122122
if isinstance(remote, FileGitRemote):
123123
first_stage = GitFirstStage(remote)

pulpcore/app/tasks/replica.py

Lines changed: 48 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@
77
from django.db.models import Min
88
from pulp_glue.common import __version__ as pulp_glue_version
99
from pulp_glue.common.context import PluginRequirement
10+
from pulp_glue.common.exceptions import PulpException as GluePulpException
1011

1112
from pulpcore.app.apps import PulpAppConfig, pulp_plugin_configs
1213
from pulpcore.app.models import Distribution, Repository, Task, TaskGroup, UpstreamPulp
1314
from pulpcore.app.replica import ReplicaContext
1415
from pulpcore.constants import TASK_STATES
16+
from pulpcore.exceptions import ExternalServiceError
1517
from pulpcore.tasking.tasks import dispatch
1618

1719

@@ -66,47 +68,52 @@ def replicate_distributions(server_pk):
6668
"client_key": server.client_key,
6769
}
6870

69-
task_group = TaskGroup.current()
70-
supported_replicators = []
71-
# Load all the available replicators
72-
for config in pulp_plugin_configs():
73-
if config.replicator_classes:
74-
for replicator_class in config.replicator_classes:
75-
req = PluginRequirement(config.label, specifier=replicator_class.required_version)
76-
if ctx.has_plugin(req):
77-
replicator = replicator_class(ctx, task_group, tls_settings, server)
78-
supported_replicators.append(replicator)
79-
80-
distro_repo_pairs = []
81-
for replicator in supported_replicators:
82-
distros = replicator.upstream_distributions(q=server.q_select)
83-
distro_names = []
84-
for distro in distros:
85-
# Create remote
86-
remote = replicator.create_or_update_remote(upstream_distribution=distro)
87-
if not remote:
88-
# The upstream distribution is not serving any content,
89-
# let if fall through the cracks and be cleanup below.
90-
continue
91-
# Check if there is already a repository
92-
repository = replicator.create_or_update_repository(remote=remote)
93-
if not repository:
94-
# No update occurred because server.policy==LABELED and there was
95-
# an already existing local repository with the same name
96-
continue
97-
98-
# Dispatch a sync task if needed
99-
if replicator.requires_syncing(distro):
100-
replicator.sync(repository, remote)
101-
102-
# Get or create a distribution
103-
replicator.create_or_update_distribution(repository, distro)
104-
105-
# Add name to the list of known distribution names
106-
distro_names.append(distro["name"])
107-
distro_repo_pairs.append((distro["name"], str(repository.pk)))
108-
109-
replicator.remove_missing(distro_names)
71+
try:
72+
task_group = TaskGroup.current()
73+
supported_replicators = []
74+
# Load all the available replicators
75+
for config in pulp_plugin_configs():
76+
if config.replicator_classes:
77+
for replicator_class in config.replicator_classes:
78+
req = PluginRequirement(
79+
config.label, specifier=replicator_class.required_version
80+
)
81+
if ctx.has_plugin(req):
82+
replicator = replicator_class(ctx, task_group, tls_settings, server)
83+
supported_replicators.append(replicator)
84+
85+
distro_repo_pairs = []
86+
for replicator in supported_replicators:
87+
distro_names = []
88+
distros = replicator.upstream_distributions(q=server.q_select)
89+
for distro in distros:
90+
# Create remote
91+
remote = replicator.create_or_update_remote(upstream_distribution=distro)
92+
if not remote:
93+
# The upstream distribution is not serving any content,
94+
# let if fall through the cracks and be cleanup below.
95+
continue
96+
# Check if there is already a repository
97+
repository = replicator.create_or_update_repository(remote=remote)
98+
if not repository:
99+
# No update occurred because server.policy==LABELED and there was
100+
# an already existing local repository with the same name
101+
continue
102+
103+
# Dispatch a sync task if needed
104+
if replicator.requires_syncing(distro):
105+
replicator.sync(repository, remote)
106+
107+
# Get or create a distribution
108+
replicator.create_or_update_distribution(repository, distro)
109+
110+
# Add name to the list of known distribution names
111+
distro_names.append(distro["name"])
112+
distro_repo_pairs.append((distro["name"], str(repository.pk)))
113+
114+
replicator.remove_missing(distro_names)
115+
except GluePulpException as e:
116+
raise ExternalServiceError(service_name=server.base_url, details=str(e))
110117

111118
dispatch(
112119
finalize_replication,

pulpcore/app/tasks/repository.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77

88
from asgiref.sync import sync_to_async
99
from django.db import transaction
10-
from rest_framework.serializers import ValidationError
1110

1211
from pulpcore.app import models
1312
from pulpcore.app.models import ProgressReport
1413
from pulpcore.app.util import get_domain
14+
from pulpcore.exceptions.base import RepositoryVersionDeleteError
1515

1616
log = getLogger(__name__)
1717

@@ -44,12 +44,7 @@ def delete_version(pk):
4444
return
4545

4646
if version.repository.versions.complete().count() <= 1:
47-
raise ValidationError(
48-
_(
49-
"Cannot delete repository version. Repositories must have at least one "
50-
"repository version."
51-
)
52-
)
47+
raise RepositoryVersionDeleteError()
5348

5449
log.info(
5550
"Deleting and squashing version {num} of repository '{repo}'".format(

pulpcore/content/handler.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@
6161
from pulpcore.cache import AsyncContentCache # noqa: E402
6262
from pulpcore.exceptions import ( # noqa: E402
6363
DigestValidationError,
64+
HttpResponseError,
65+
RemoteConnectionError,
66+
SslConnectionError,
6467
UnsupportedDigestValidationError,
6568
)
6669
from pulpcore.metrics import artifacts_size_counter # noqa: E402
@@ -915,13 +918,13 @@ async def _match_and_stream(self, path, request):
915918
save_artifact=save_artifact,
916919
repository=repository,
917920
)
918-
except ClientResponseError as ce:
921+
except (ClientResponseError, HttpResponseError) as e:
919922

920923
class Error(HTTPError):
921-
status_code = ce.status
924+
status_code = e.status
922925

923926
reason = _("Error while fetching from upstream remote({url}): {r}").format(
924-
url=url, r=ce.message
927+
url=url, r=e.message
925928
)
926929
raise Error(reason=reason)
927930

@@ -962,6 +965,9 @@ async def _stream_content_artifact(self, request, response, content_artifact):
962965
ClientResponseError,
963966
UnsupportedDigestValidationError,
964967
ClientConnectionError,
968+
HttpResponseError,
969+
SslConnectionError,
970+
RemoteConnectionError,
965971
)
966972

967973
protection_time = settings.REMOTE_CONTENT_FETCH_FAILURE_COOLDOWN

pulpcore/download/http.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@
66

77
from pulpcore.exceptions import (
88
DigestValidationError,
9+
DnsDomainNameException,
10+
HttpResponseError,
11+
ProxyAuthenticationError,
12+
RemoteConnectionError,
913
SizeValidationError,
14+
SslConnectionError,
1015
TimeoutException,
1116
)
1217

@@ -236,6 +241,7 @@ async def run(self, extra_data=None):
236241
aiohttp.ClientPayloadError,
237242
aiohttp.ClientResponseError,
238243
aiohttp.ServerDisconnectedError,
244+
DnsDomainNameException,
239245
TimeoutError,
240246
TimeoutException,
241247
DigestValidationError,
@@ -269,9 +275,21 @@ async def download_wrapper():
269275
e.message,
270276
)
271277
)
272-
raise e
278+
raise ProxyAuthenticationError(self.proxy)
273279

274-
return await download_wrapper()
280+
try:
281+
return await download_wrapper()
282+
except aiohttp.ClientResponseError as e:
283+
raise HttpResponseError(url=self.url, status=e.status, message=e.message)
284+
except aiohttp.ClientConnectorSSLError as e:
285+
raise SslConnectionError(url=self.url, details=str(e))
286+
except (
287+
aiohttp.ClientConnectorError,
288+
aiohttp.ClientOSError,
289+
aiohttp.ClientPayloadError,
290+
aiohttp.ServerDisconnectedError,
291+
) as e:
292+
raise RemoteConnectionError(url=self.url, details=str(e))
275293

276294
async def _run(self, extra_data=None):
277295
"""
@@ -296,10 +314,13 @@ async def _run(self, extra_data=None):
296314
}
297315
if extra_data and extra_data.get("request_kwargs"):
298316
request_kwargs.update(extra_data["request_kwargs"])
299-
async with self.session.get(self.url, **request_kwargs) as response:
300-
self.raise_for_status(response)
301-
to_return = await self._handle_response(response)
302-
await response.release()
317+
try:
318+
async with self.session.get(self.url, **request_kwargs) as response:
319+
self.raise_for_status(response)
320+
to_return = await self._handle_response(response)
321+
await response.release()
322+
except aiohttp.ClientConnectorDNSError:
323+
raise DnsDomainNameException(self.url)
303324
if self._close_session_on_finalize:
304325
await self.session.close()
305326
return to_return

pulpcore/exceptions/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
ReplicateError,
1919
SyncError,
2020
PublishError,
21+
TaskConfigurationError,
22+
TaskTimeoutException,
23+
HttpResponseError,
24+
SslConnectionError,
25+
RemoteConnectionError,
2126
)
2227
from .validation import (
2328
DigestValidationError,

pulpcore/exceptions/base.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,107 @@ class ReplicateError(PulpException):
326326

327327
def __str__(self):
328328
return f"[{self.error_code}] " + _("Replication failed")
329+
330+
331+
class TaskConfigurationError(PulpException):
332+
"""
333+
Raised when a task is incorrectly configured.
334+
"""
335+
336+
error_code = "PLP0023"
337+
338+
def __init__(self, task_name, message):
339+
"""
340+
:param task_name: the fully qualified name of the task function
341+
:type task_name: str
342+
:param message: description of the configuration error
343+
:type message: str
344+
"""
345+
self.task_name = task_name
346+
self.message = message
347+
348+
def __str__(self):
349+
return f"[{self.error_code}] " + _(
350+
"Task type '{task_name}' is misconfigured: {message}"
351+
).format(task_name=self.task_name, message=self.message)
352+
353+
354+
class TaskTimeoutException(PulpException):
355+
"""
356+
Raised when an immediate task exceeds its execution timeout.
357+
"""
358+
359+
error_code = "PLP0024"
360+
361+
def __init__(self, task_name, task_pk, timeout_seconds):
362+
"""
363+
:param task_name: the fully qualified name of the task function
364+
:type task_name: str
365+
:param task_pk: the unique task identifier
366+
:type task_pk: str
367+
:param timeout_seconds: the timeout value that was exceeded
368+
:type timeout_seconds: int
369+
"""
370+
self.task_name = task_name
371+
self.task_pk = task_pk
372+
self.timeout_seconds = timeout_seconds
373+
374+
def __str__(self):
375+
return f"[{self.error_code}] " + _(
376+
"Immediate task {task_pk} (type: {task_name}) timed out after {timeout} seconds."
377+
).format(task_pk=self.task_pk, task_name=self.task_name, timeout=self.timeout_seconds)
378+
379+
380+
class HttpResponseError(PulpException):
381+
"""
382+
Raised when a remote server returns an HTTP error response after retries are exhausted.
383+
"""
384+
385+
error_code = "PLP0025"
386+
387+
def __init__(self, url, status, message):
388+
super().__init__()
389+
self.url = url
390+
self.status = status
391+
self.message = message
392+
393+
def __str__(self):
394+
return f"[{self.error_code}] " + _(
395+
"HTTP error {status} when downloading {url}: {message}"
396+
).format(url=self.url, status=self.status, message=self.message)
397+
398+
399+
class SslConnectionError(PulpException):
400+
"""
401+
Raised when an SSL/TLS connection fails after retries are exhausted.
402+
"""
403+
404+
error_code = "PLP0026"
405+
406+
def __init__(self, url, details):
407+
super().__init__()
408+
self.url = url
409+
self.details = details
410+
411+
def __str__(self):
412+
return f"[{self.error_code}] " + _("SSL connection failed for {url}: {details}").format(
413+
url=self.url, details=self.details
414+
)
415+
416+
417+
class RemoteConnectionError(PulpException):
418+
"""
419+
Raised when a connection to a remote server fails after retries are exhausted.
420+
"""
421+
422+
error_code = "PLP0027"
423+
424+
def __init__(self, url, details):
425+
super().__init__()
426+
self.url = url
427+
self.details = details
428+
429+
def __str__(self):
430+
return f"[{self.error_code}] " + _("Connection failed for {url}: {details}").format(
431+
url=self.url, details=self.details
432+
)

pulpcore/exceptions/validation.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,12 @@ class UnsupportedDigestValidationError(ValidationError):
9696

9797
error_code = "PLP0020"
9898

99-
def __init__(self, digest_name=None):
100-
self.digest_name = digest_name
99+
def __init__(self, message=None):
100+
self.message = message
101101

102102
def __str__(self):
103-
if self.digest_name:
104-
return f"[{self.error_code}] " + _(
105-
"Checksum type '{digest}' is not supported or enabled."
106-
).format(digest=self.digest_name)
103+
if self.message:
104+
return f"[{self.error_code}] {self.message}"
107105
return f"[{self.error_code}] " + _("Unsupported checksum type.")
108106

109107

0 commit comments

Comments
 (0)