|
1 | 1 | import asyncio |
| 2 | +import json |
2 | 3 | import logging |
3 | 4 | from functools import partial |
4 | | -from urllib.parse import urljoin, urlparse |
| 5 | +from urllib.parse import urlparse |
5 | 6 |
|
6 | | -from aiohttp import ClientError, ClientResponseError |
7 | 7 | from bandersnatch.configuration import BandersnatchConfig |
8 | 8 | from bandersnatch.master import Master |
9 | 9 | from bandersnatch.mirror import Mirror |
10 | | -from lxml.etree import LxmlError |
11 | 10 | from packaging.requirements import Requirement |
12 | | -from pypi_simple import IndexPage |
| 11 | +from pypi_simple import ACCEPT_ANY, IndexPage, UnsupportedContentTypeError |
13 | 12 |
|
14 | 13 | from pulpcore.plugin.download import HttpDownloader |
15 | 14 | from pulpcore.plugin.exceptions import SyncError |
@@ -169,41 +168,34 @@ def __init__(self, serial, master, workers, deferred_download, python_stage, pro |
169 | 168 |
|
170 | 169 | async def determine_packages_to_sync(self): |
171 | 170 | """ |
172 | | - Calling this means that includes wasn't specified, |
173 | | - so try to get all of the packages from Mirror (hopefully PyPi) |
| 171 | + Called when includes wasn't specified. List all projects from the remote's simple index. |
174 | 172 | """ |
175 | | - number_xmlrpc_attempts = 3 |
176 | | - for attempt in range(number_xmlrpc_attempts): |
177 | | - logger.info("Attempt {} to get package list from {}".format(attempt, self.master.url)) |
178 | | - try: |
179 | | - if not self.synced_serial: |
180 | | - logger.info("Syncing all packages.") |
181 | | - # First get the current serial, then start to sync. |
182 | | - all_packages = await self.master.all_packages() |
183 | | - self.packages_to_sync.update(all_packages) |
184 | | - self.target_serial = max( |
185 | | - [self.synced_serial] + [int(v) for v in self.packages_to_sync.values()] |
186 | | - ) |
187 | | - else: |
188 | | - logger.info("Syncing based on changelog.") |
189 | | - changed_packages = await self.master.changed_packages(self.synced_serial) |
190 | | - self.packages_to_sync.update(changed_packages) |
191 | | - self.target_serial = max( |
192 | | - [self.synced_serial] + [int(v) for v in self.packages_to_sync.values()] |
193 | | - ) |
194 | | - break |
195 | | - except (ClientError, ClientResponseError, LxmlError): |
196 | | - # Retry if XMLRPC endpoint failed, server might not support it. |
197 | | - continue |
| 173 | + logger.info("Syncing all packages from %s", self.master.url) |
| 174 | + url = f"{self.remote.url.rstrip('/')}/simple/" |
| 175 | + downloader = self.remote.get_downloader(url=url) |
| 176 | + result = await downloader.run( |
| 177 | + extra_data={"request_kwargs": {"headers": {"Accept": ACCEPT_ANY}}} |
| 178 | + ) |
| 179 | + |
| 180 | + content_type = result.headers.get("Content-Type", "text/html").partition(";")[0].lower() |
| 181 | + with open(result.path, "rb") as f: |
| 182 | + content = f.read() |
| 183 | + |
| 184 | + if content_type == "application/vnd.pypi.simple.v1+json": |
| 185 | + index = IndexPage.from_json_data(json.loads(content)) |
| 186 | + elif content_type in {"application/vnd.pypi.simple.v1+html", "text/html"}: |
| 187 | + index = IndexPage.from_html(content) |
| 188 | + else: |
| 189 | + raise UnsupportedContentTypeError(url, content_type) |
| 190 | + |
| 191 | + self.packages_to_sync = {project: 0 for project in index.projects} |
| 192 | + last_serial = index.last_serial |
| 193 | + if last_serial is None: |
| 194 | + last_serial = result.headers.get(PYPI_LAST_SERIAL) |
| 195 | + if last_serial is None: |
| 196 | + self.target_serial = self.synced_serial |
198 | 197 | else: |
199 | | - logger.info("Failed to get package list using XMLRPC, trying parse simple page.") |
200 | | - url = urljoin(self.remote.url, "simple/") |
201 | | - downloader = self.remote.get_downloader(url=url) |
202 | | - result = await downloader.run() |
203 | | - with open(result.path) as f: |
204 | | - index = IndexPage.from_html(f.read()) |
205 | | - self.packages_to_sync.update({p: 0 for p in index.projects}) |
206 | | - self.target_serial = result.headers.get(PYPI_LAST_SERIAL, 0) |
| 198 | + self.target_serial = max(self.synced_serial, int(last_serial)) |
207 | 199 |
|
208 | 200 | self._filter_packages() |
209 | 201 | if self.target_serial: |
|
0 commit comments