Mercurial > hg
comparison mercurial/bundlecaches.py @ 50542:7b723217d368 stable
clonebundles: filter out invalid schemes instead of failing on them
Previously, an invalid clonebundle scheme would result in a failed
clone. By specifying a list of schemes we support,
we can make sure adding a new scheme (like the one for inline clonebundles)
does not result in clones failing for older clients.
author | Mathias De Mare <mathias.de_mare@nokia.com> |
---|---|
date | Thu, 20 Apr 2023 11:23:45 +0200 |
parents | 9be765b82a90 |
children | a41eeb877d07 |
comparison
equal
deleted
inserted
replaced
50541:ef7f943ebabf | 50542:7b723217d368 |
---|---|
22 from .utils import stringutil | 22 from .utils import stringutil |
23 | 23 |
24 urlreq = util.urlreq | 24 urlreq = util.urlreq |
25 | 25 |
26 CB_MANIFEST_FILE = b'clonebundles.manifest' | 26 CB_MANIFEST_FILE = b'clonebundles.manifest' |
27 SUPPORTED_CLONEBUNDLE_SCHEMES = [ | |
28 b"http://", | |
29 b"https://", | |
30 b"largefile://", | |
31 ] | |
27 | 32 |
28 | 33 |
29 @attr.s | 34 @attr.s |
30 class bundlespec: | 35 class bundlespec: |
31 compression = attr.ib() | 36 compression = attr.ib() |
335 return True | 340 return True |
336 | 341 |
337 return False | 342 return False |
338 | 343 |
339 | 344 |
340 def filterclonebundleentries(repo, entries, streamclonerequested=False): | 345 def filterclonebundleentries( |
346 repo, entries, streamclonerequested=False, pullbundles=False | |
347 ): | |
341 """Remove incompatible clone bundle manifest entries. | 348 """Remove incompatible clone bundle manifest entries. |
342 | 349 |
343 Accepts a list of entries parsed with ``parseclonebundlesmanifest`` | 350 Accepts a list of entries parsed with ``parseclonebundlesmanifest`` |
344 and returns a new list consisting of only the entries that this client | 351 and returns a new list consisting of only the entries that this client |
345 should be able to apply. | 352 should be able to apply. |
347 There is no guarantee we'll be able to apply all returned entries because | 354 There is no guarantee we'll be able to apply all returned entries because |
348 the metadata we use to filter on may be missing or wrong. | 355 the metadata we use to filter on may be missing or wrong. |
349 """ | 356 """ |
350 newentries = [] | 357 newentries = [] |
351 for entry in entries: | 358 for entry in entries: |
359 url = entry.get(b'URL') | |
360 if not pullbundles and not any( | |
361 [url.startswith(scheme) for scheme in SUPPORTED_CLONEBUNDLE_SCHEMES] | |
362 ): | |
363 repo.ui.debug( | |
364 b'filtering %s because not a supported clonebundle scheme\n' | |
365 % url | |
366 ) | |
367 continue | |
368 | |
352 spec = entry.get(b'BUNDLESPEC') | 369 spec = entry.get(b'BUNDLESPEC') |
353 if spec: | 370 if spec: |
354 try: | 371 try: |
355 bundlespec = parsebundlespec(repo, spec, strict=True) | 372 bundlespec = parsebundlespec(repo, spec, strict=True) |
356 | 373 |
357 # If a stream clone was requested, filter out non-streamclone | 374 # If a stream clone was requested, filter out non-streamclone |
358 # entries. | 375 # entries. |
359 if streamclonerequested and not isstreamclonespec(bundlespec): | 376 if streamclonerequested and not isstreamclonespec(bundlespec): |
360 repo.ui.debug( | 377 repo.ui.debug( |
361 b'filtering %s because not a stream clone\n' | 378 b'filtering %s because not a stream clone\n' % url |
362 % entry[b'URL'] | |
363 ) | 379 ) |
364 continue | 380 continue |
365 | 381 |
366 except error.InvalidBundleSpecification as e: | 382 except error.InvalidBundleSpecification as e: |
367 repo.ui.debug(stringutil.forcebytestr(e) + b'\n') | 383 repo.ui.debug(stringutil.forcebytestr(e) + b'\n') |
368 continue | 384 continue |
369 except error.UnsupportedBundleSpecification as e: | 385 except error.UnsupportedBundleSpecification as e: |
370 repo.ui.debug( | 386 repo.ui.debug( |
371 b'filtering %s because unsupported bundle ' | 387 b'filtering %s because unsupported bundle ' |
372 b'spec: %s\n' % (entry[b'URL'], stringutil.forcebytestr(e)) | 388 b'spec: %s\n' % (url, stringutil.forcebytestr(e)) |
373 ) | 389 ) |
374 continue | 390 continue |
375 # If we don't have a spec and requested a stream clone, we don't know | 391 # If we don't have a spec and requested a stream clone, we don't know |
376 # what the entry is so don't attempt to apply it. | 392 # what the entry is so don't attempt to apply it. |
377 elif streamclonerequested: | 393 elif streamclonerequested: |
378 repo.ui.debug( | 394 repo.ui.debug( |
379 b'filtering %s because cannot determine if a stream ' | 395 b'filtering %s because cannot determine if a stream ' |
380 b'clone bundle\n' % entry[b'URL'] | 396 b'clone bundle\n' % url |
381 ) | 397 ) |
382 continue | 398 continue |
383 | 399 |
384 if b'REQUIRESNI' in entry and not sslutil.hassni: | 400 if b'REQUIRESNI' in entry and not sslutil.hassni: |
385 repo.ui.debug( | 401 repo.ui.debug(b'filtering %s because SNI not supported\n' % url) |
386 b'filtering %s because SNI not supported\n' % entry[b'URL'] | |
387 ) | |
388 continue | 402 continue |
389 | 403 |
390 if b'REQUIREDRAM' in entry: | 404 if b'REQUIREDRAM' in entry: |
391 try: | 405 try: |
392 requiredram = util.sizetoint(entry[b'REQUIREDRAM']) | 406 requiredram = util.sizetoint(entry[b'REQUIREDRAM']) |
393 except error.ParseError: | 407 except error.ParseError: |
394 repo.ui.debug( | 408 repo.ui.debug( |
395 b'filtering %s due to a bad REQUIREDRAM attribute\n' | 409 b'filtering %s due to a bad REQUIREDRAM attribute\n' % url |
396 % entry[b'URL'] | |
397 ) | 410 ) |
398 continue | 411 continue |
399 actualram = repo.ui.estimatememory() | 412 actualram = repo.ui.estimatememory() |
400 if actualram is not None and actualram * 0.66 < requiredram: | 413 if actualram is not None and actualram * 0.66 < requiredram: |
401 repo.ui.debug( | 414 repo.ui.debug( |
402 b'filtering %s as it needs more than 2/3 of system memory\n' | 415 b'filtering %s as it needs more than 2/3 of system memory\n' |
403 % entry[b'URL'] | 416 % url |
404 ) | 417 ) |
405 continue | 418 continue |
406 | 419 |
407 newentries.append(entry) | 420 newentries.append(entry) |
408 | 421 |