Skip to content

Commit 26cdeff

Browse files
committed
Fix pull-through failing to check repository when package was not in remote
fixes: #1004
1 parent 001147e commit 26cdeff

File tree

3 files changed

+62
-50
lines changed

3 files changed

+62
-50
lines changed

CHANGES/1004.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed pull-through caching not checking the repository if package was not present on remote.

pulp_python/app/pypi/views.py

Lines changed: 31 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from django.db.utils import DatabaseError
1616
from django.http.response import (
1717
Http404,
18+
HttpResponseNotFound,
1819
HttpResponseForbidden,
1920
HttpResponseBadRequest,
2021
StreamingHttpResponse,
@@ -287,7 +288,7 @@ def list(self, request, path):
287288
kwargs = {"content_type": media_type, "headers": headers}
288289
return StreamingHttpResponse(index_data, **kwargs)
289290

290-
def pull_through_package_simple(self, package, path, remote, media_type):
291+
def pull_through_package_simple(self, package, path, remote):
291292
"""Gets the package's simple page from remote."""
292293

293294
def parse_package(release_package):
@@ -305,35 +306,27 @@ def parse_package(release_package):
305306

306307
rfilter = get_remote_package_filter(remote)
307308
if not rfilter.filter_project(package):
308-
raise Http404(f"{package} does not exist.")
309+
return {}
309310

310311
url = remote.get_remote_artifact_url(f"simple/{package}/")
311312
remote.headers = remote.headers or []
312313
remote.headers.append({"Accept": ACCEPT_JSON_PREFERRED})
313314
downloader = remote.get_downloader(url=url, max_retries=1)
314315
try:
315316
d = downloader.fetch()
316-
except ClientError:
317-
return HttpResponse(f"Failed to fetch {package} from {remote.url}.", status=502)
318-
except TimeoutException:
319-
return HttpResponse(f"{remote.url} timed out while fetching {package}.", status=504)
317+
except (ClientError, TimeoutException):
318+
log.info(f"Failed to fetch {package} simple page from {remote.url}")
319+
return {}
320320

321321
if d.headers["content-type"] == PYPI_SIMPLE_V1_JSON:
322322
page = ProjectPage.from_json_data(json.load(open(d.path, "rb")), base_url=url)
323323
else:
324324
page = ProjectPage.from_html(package, open(d.path, "rb").read(), base_url=url)
325-
packages = [
326-
parse_package(p) for p in page.packages if rfilter.filter_release(package, p.version)
327-
]
328-
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
329-
330-
if media_type == PYPI_SIMPLE_V1_JSON:
331-
detail_data = write_simple_detail_json(package, packages)
332-
return Response(detail_data, headers=headers)
333-
else:
334-
detail_data = write_simple_detail(package, packages)
335-
kwargs = {"content_type": media_type, "headers": headers}
336-
return HttpResponse(detail_data, **kwargs)
325+
return {
326+
p.filename: parse_package(p)
327+
for p in page.packages
328+
if rfilter.filter_release(package, p.version)
329+
}
337330

338331
@extend_schema(operation_id="pypi_simple_package_read", summary="Get package simple page")
339332
def retrieve(self, request, path, package):
@@ -343,44 +336,36 @@ def retrieve(self, request, path, package):
343336
repo_ver, content = self.get_rvc()
344337
# Should I redirect if the normalized name is different?
345338
normalized = canonicalize_name(package)
339+
releases = {}
346340
if self.distribution.remote:
347-
return self.pull_through_package_simple(
348-
normalized, path, self.distribution.remote, media_type
349-
)
350-
if self.should_redirect(repo_version=repo_ver):
341+
releases = self.pull_through_package_simple(normalized, path, self.distribution.remote)
342+
elif self.should_redirect(repo_version=repo_ver):
351343
return redirect(urljoin(self.base_content_url, f"{path}/simple/{normalized}/"))
352-
packages = (
353-
content.filter(name__normalize=normalized)
354-
.values_list("filename", "sha256", "name", "metadata_sha256", "requires_python")
355-
.iterator()
356-
)
357-
try:
358-
present = next(packages)
359-
except StopIteration:
360-
raise Http404(f"{normalized} does not exist.")
361-
else:
362-
packages = chain([present], packages)
363-
name = present[2]
364-
releases = (
365-
{
366-
"filename": filename,
367-
"url": urljoin(self.base_content_url, f"{path}/{filename}"),
368-
"sha256": sha256,
369-
"metadata_sha256": metadata_sha256,
370-
"requires_python": requires_python,
344+
if content:
345+
packages = content.filter(name__normalize=normalized).values(
346+
"filename", "sha256", "metadata_sha256", "requires_python"
347+
)
348+
local_releases = {
349+
p["filename"]: {
350+
**p,
351+
"url": urljoin(self.base_content_url, f"{path}/{p['filename']}"),
352+
}
353+
for p in packages
371354
}
372-
for filename, sha256, _, metadata_sha256, requires_python in packages
373-
)
355+
releases.update(local_releases)
356+
if not releases:
357+
return HttpResponseNotFound(f"{normalized} does not exist.")
358+
374359
media_type = request.accepted_renderer.media_type
375360
headers = {"X-PyPI-Last-Serial": str(PYPI_SERIAL_CONSTANT)}
376361

377362
if media_type == PYPI_SIMPLE_V1_JSON:
378-
detail_data = write_simple_detail_json(name, releases)
363+
detail_data = write_simple_detail_json(normalized, releases.values())
379364
return Response(detail_data, headers=headers)
380365
else:
381-
detail_data = write_simple_detail(name, releases, streamed=True)
366+
detail_data = write_simple_detail(normalized, releases.values())
382367
kwargs = {"content_type": media_type, "headers": headers}
383-
return StreamingHttpResponse(detail_data, **kwargs)
368+
return HttpResponse(detail_data, **kwargs)
384369

385370
@extend_schema(
386371
request=PackageUploadSerializer,

pulp_python/tests/functional/api/test_full_mirror.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test_pull_through_filter(python_remote_factory, python_distribution_factory)
8484

8585
r = requests.get(f"{distro.base_url}simple/pulpcore/")
8686
assert r.status_code == 404
87-
assert r.text == "404 Not Found"
87+
assert r.text == "pulpcore does not exist."
8888

8989
r = requests.get(f"{distro.base_url}simple/shelf-reader/")
9090
assert r.status_code == 200
@@ -104,11 +104,11 @@ def test_pull_through_filter(python_remote_factory, python_distribution_factory)
104104

105105
r = requests.get(f"{distro.base_url}simple/django/")
106106
assert r.status_code == 404
107-
assert r.text == "404 Not Found"
107+
assert r.text == "django does not exist."
108108

109109
r = requests.get(f"{distro.base_url}simple/pulpcore/")
110-
assert r.status_code == 502
111-
assert r.text == f"Failed to fetch pulpcore from {remote.url}."
110+
assert r.status_code == 404
111+
assert r.text == "pulpcore does not exist."
112112

113113
r = requests.get(f"{distro.base_url}simple/shelf-reader/")
114114
assert r.status_code == 200
@@ -156,3 +156,29 @@ def test_pull_through_with_repo(
156156
assert r.status_code == 200
157157
tasks = pulpcore_bindings.TasksApi.list(reserved_resources=repo.prn)
158158
assert tasks.count == 3
159+
160+
161+
@pytest.mark.parallel
162+
def test_pull_through_local_only(
163+
python_remote_factory, python_distribution_factory, python_repo_with_sync
164+
):
165+
"""Tests that pull-through checks the repository if the package is not present on the remote."""
166+
remote = python_remote_factory(url=PYPI_URL, includes=["pulpcore"])
167+
repo = python_repo_with_sync(remote=remote)
168+
remote2 = python_remote_factory(includes=[]) # Fixtures does not have pulpcore
169+
distro = python_distribution_factory(repository=repo.pulp_href, remote=remote2.pulp_href)
170+
171+
url = f"{distro.base_url}simple/pulpcore/"
172+
r = requests.get(url)
173+
assert r.status_code == 200
174+
assert "?redirect=" not in r.text
175+
176+
url = f"{distro.base_url}simple/shelf-reader/"
177+
r = requests.get(url)
178+
assert r.status_code == 200
179+
assert "?redirect=" in r.text
180+
181+
url = f"{distro.base_url}simple/pulp_python/"
182+
r = requests.get(url)
183+
assert r.status_code == 404
184+
assert r.text == "pulp-python does not exist."

0 commit comments

Comments
 (0)