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