Skip to content

Commit

Permalink
Fix sync failure on unreachable namespace avatar (#1544)
Browse files Browse the repository at this point in the history
fixes: #1543
  • Loading branch information
gerrod3 authored Aug 9, 2023
1 parent 6f7f087 commit f56e097
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGES/1543.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Stopped collection sync from failing if a namespace's avatar url was unreachable.
8 changes: 7 additions & 1 deletion pulp_ansible/app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,13 @@ class AnsibleNamespaceMetadata(Content):
@property
def avatar_artifact(self):
if self.avatar_sha256:
return self._artifacts.get(sha256=self.avatar_sha256)
avatar = self._artifacts.filter(sha256=self.avatar_sha256).first()
if avatar is None:
log.debug(
f"Artifact({self.avatar_sha256}) is missing for namespace avatar "
f"{self.name}:{self.metadata_sha256}"
)
return avatar

return None

Expand Down
38 changes: 33 additions & 5 deletions pulp_ansible/app/tasks/collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from uuid import uuid4

import yaml
from aiohttp.client_exceptions import ClientResponseError
from aiohttp.client_exceptions import ClientError, ClientResponseError
from asgiref.sync import sync_to_async
from async_lru import alru_cache
from django.conf import settings
Expand Down Expand Up @@ -503,7 +503,7 @@ def pipeline_stages(self, new_version):
ArtifactSaver(),
QueryExistingContents(),
DocsBlobDownloader(),
CollectionContentSaver(new_version),
AnsibleContentSaver(new_version),
RemoteArtifactSaver(),
ResolveContentFutures(),
]
Expand Down Expand Up @@ -733,8 +733,8 @@ async def _add_namespace(self, name, namespace_sha):

da = (
[
DeclarativeArtifact(
Artifact(sha256=namespace["avatar_sha256"]),
DeclarativeFailsafeArtifact(
Artifact(sha256=namespace.get("avatar_sha256")),
url=url,
remote=self.remote,
relative_path=f"{name}-avatar",
Expand Down Expand Up @@ -1081,6 +1081,25 @@ async def run(self):
pb.total = pb.done


class DeclarativeFailsafeArtifact(DeclarativeArtifact):
"""
Special handling for downloading Namespace Avatar Artifacts.
"""

async def download(self):
"""Allow download to fail, but not stop the sync."""
artifact_copy = self.artifact
try:
return await super().download()
except ClientError as e:
# Reset DA so that future stages can properly handle it
self.artifact = artifact_copy
self.deferred_download = True
name = self.extra_data.get("namespace")
log.info(f"Failed to download namespace avatar: {name} - {e}, Skipping")
return None


class DocsBlobDownloader(ArtifactDownloader):
"""
Stage for downloading docs_blob.
Expand Down Expand Up @@ -1121,7 +1140,7 @@ async def _handle_content_unit(self, d_content):
return downloaded


class CollectionContentSaver(ContentSaver):
class AnsibleContentSaver(ContentSaver):
"""
A modification of ContentSaver stage that additionally saves Ansible plugin specific items.
Expand Down Expand Up @@ -1156,6 +1175,15 @@ def _pre_save(self, batch):
name = d_content.content.name
namespace, created = AnsibleNamespace.objects.get_or_create(name=name)
d_content.content.namespace = namespace
if d_content.d_artifacts:
da = d_content.d_artifacts[0]
# Check to see if avatar failed to download, update metadata if so
if da.deferred_download:
d_content.d_artifacts = None
d_content.content.avatar_sha256 = None
# Check to see if upstream didn't have avatar_sha256 set
elif d_content.content.avatar_sha256 is None:
d_content.content.avatar_sha256 = da.artifact.sha256

def _post_save(self, batch):
"""
Expand Down
5 changes: 2 additions & 3 deletions pulp_ansible/app/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,11 +408,10 @@ class AnsibleNamespaceViewSet(ReadOnlyContentViewSet):
@action(detail=True, methods=["get"], serializer_class=None)
def avatar(self, request, pk):
"""
Dispatches a collection version rebuild task.
Tries to find a redirect link to the Namespace's avatar
"""
ns = self.get_object()
artifact = ns.avatar_artifact
if artifact:
if artifact := ns.avatar_artifact:
return HttpResponseRedirect(get_artifact_url(artifact))

return HttpResponseNotFound()
Expand Down

0 comments on commit f56e097

Please sign in to comment.