document config option "url_fetch_method" (#24638)

- Change config from the undocumented `use_curl: true/false` to `url_fetch_method: urllib/curl`.
- Documentation of `url_fetch_method` in `defaults/config.yaml`
- Default fetch option explicitly set to `urllib` for users who may not have curl on their system

To upgrade from `use_curl` to `url_fetch_method`, run `spack config update config`
This commit is contained in:
loulawrence 2021-08-02 04:30:25 -04:00 committed by GitHub
parent cd8f7d844d
commit be3e6a0e9b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 58 additions and 44 deletions

View file

@ -134,6 +134,10 @@ config:
# enabling locks. # enabling locks.
locks: true locks: true
# The default url fetch method to use.
# If set to 'curl', Spack will require curl on the user's system
# If set to 'urllib', Spack will use python built-in libs to fetch
url_fetch_method: urllib
# The maximum number of jobs to use for the build system (e.g. `make`), when # The maximum number of jobs to use for the build system (e.g. `make`), when
# the -j flag is not given on the command line. Defaults to 16 when not set. # the -j flag is not given on the command line. Defaults to 16 when not set.

View file

@ -338,7 +338,7 @@ def fetch(self):
def _existing_url(self, url): def _existing_url(self, url):
tty.debug('Checking existence of {0}'.format(url)) tty.debug('Checking existence of {0}'.format(url))
if spack.config.get('config:use_curl'): if spack.config.get('config:url_fetch_method') == 'curl':
curl = self.curl curl = self.curl
# Telling curl to fetch the first byte (-r 0-0) is supposed to be # Telling curl to fetch the first byte (-r 0-0) is supposed to be
# portable. # portable.
@ -357,7 +357,7 @@ def _existing_url(self, url):
return (response.getcode() is None or response.getcode() == 200) return (response.getcode() is None or response.getcode() == 200)
def _fetch_from_url(self, url): def _fetch_from_url(self, url):
if spack.config.get('config:use_curl'): if spack.config.get('config:url_fetch_method') == 'curl':
return self._fetch_curl(url) return self._fetch_curl(url)
else: else:
return self._fetch_urllib(url) return self._fetch_urllib(url)

View file

@ -99,6 +99,10 @@
}, },
'allow_sgid': {'type': 'boolean'}, 'allow_sgid': {'type': 'boolean'},
'binary_index_root': {'type': 'string'}, 'binary_index_root': {'type': 'string'},
'url_fetch_method': {
'type': 'string',
'enum': ['urllib', 'curl']
},
}, },
}, },
} }
@ -153,4 +157,10 @@ def update(data):
update_data = spack.config.merge_yaml(update_data, projections_data) update_data = spack.config.merge_yaml(update_data, projections_data)
data['install_tree'] = update_data data['install_tree'] = update_data
changed = True changed = True
use_curl = data.pop('use_curl', None)
if use_curl is not None:
data['url_fetch_method'] = 'curl' if use_curl else 'urllib'
changed = True
return changed return changed

View file

@ -14,25 +14,25 @@
from spack.stage import Stage from spack.stage import Stage
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_fetch_missing_cache(tmpdir, use_curl): def test_fetch_missing_cache(tmpdir, _fetch_method):
"""Ensure raise a missing cache file.""" """Ensure raise a missing cache file."""
testpath = str(tmpdir) testpath = str(tmpdir)
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
fetcher = CacheURLFetchStrategy(url='file:///not-a-real-cache-file') fetcher = CacheURLFetchStrategy(url='file:///not-a-real-cache-file')
with Stage(fetcher, path=testpath): with Stage(fetcher, path=testpath):
with pytest.raises(NoCacheError, match=r'No cache'): with pytest.raises(NoCacheError, match=r'No cache'):
fetcher.fetch() fetcher.fetch()
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_fetch(tmpdir, use_curl): def test_fetch(tmpdir, _fetch_method):
"""Ensure a fetch after expanding is effectively a no-op.""" """Ensure a fetch after expanding is effectively a no-op."""
testpath = str(tmpdir) testpath = str(tmpdir)
cache = os.path.join(testpath, 'cache.tar.gz') cache = os.path.join(testpath, 'cache.tar.gz')
touch(cache) touch(cache)
url = 'file:///{0}'.format(cache) url = 'file:///{0}'.format(cache)
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
fetcher = CacheURLFetchStrategy(url=url) fetcher = CacheURLFetchStrategy(url=url)
with Stage(fetcher, path=testpath) as stage: with Stage(fetcher, path=testpath) as stage:
source_path = stage.source_path source_path = stage.source_path

View file

@ -12,20 +12,20 @@
import spack.stage as spack_stage import spack.stage as spack_stage
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_s3fetchstrategy_sans_url(use_curl): def test_s3fetchstrategy_sans_url(_fetch_method):
"""Ensure constructor with no URL fails.""" """Ensure constructor with no URL fails."""
with spack_config.override('config:use_curl', use_curl): with spack_config.override('config:url_fetch_method', _fetch_method):
with pytest.raises(ValueError): with pytest.raises(ValueError):
spack_fs.S3FetchStrategy(None) spack_fs.S3FetchStrategy(None)
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_s3fetchstrategy_bad_url(tmpdir, use_curl): def test_s3fetchstrategy_bad_url(tmpdir, _fetch_method):
"""Ensure fetch with bad URL fails as expected.""" """Ensure fetch with bad URL fails as expected."""
testpath = str(tmpdir) testpath = str(tmpdir)
with spack_config.override('config:use_curl', use_curl): with spack_config.override('config:url_fetch_method', _fetch_method):
fetcher = spack_fs.S3FetchStrategy(url='file:///does-not-exist') fetcher = spack_fs.S3FetchStrategy(url='file:///does-not-exist')
assert fetcher is not None assert fetcher is not None
@ -36,13 +36,13 @@ def test_s3fetchstrategy_bad_url(tmpdir, use_curl):
fetcher.fetch() fetcher.fetch()
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_s3fetchstrategy_downloaded(tmpdir, use_curl): def test_s3fetchstrategy_downloaded(tmpdir, _fetch_method):
"""Ensure fetch with archive file already downloaded is a noop.""" """Ensure fetch with archive file already downloaded is a noop."""
testpath = str(tmpdir) testpath = str(tmpdir)
archive = os.path.join(testpath, 's3.tar.gz') archive = os.path.join(testpath, 's3.tar.gz')
with spack_config.override('config:use_curl', use_curl): with spack_config.override('config:url_fetch_method', _fetch_method):
class Archived_S3FS(spack_fs.S3FetchStrategy): class Archived_S3FS(spack_fs.S3FetchStrategy):
@property @property
def archive_file(self): def archive_file(self):

View file

@ -49,20 +49,20 @@ def fn(v):
return factory return factory
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_urlfetchstrategy_sans_url(use_curl): def test_urlfetchstrategy_sans_url(_fetch_method):
"""Ensure constructor with no URL fails.""" """Ensure constructor with no URL fails."""
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
with pytest.raises(ValueError): with pytest.raises(ValueError):
with fs.URLFetchStrategy(None): with fs.URLFetchStrategy(None):
pass pass
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_urlfetchstrategy_bad_url(tmpdir, use_curl): def test_urlfetchstrategy_bad_url(tmpdir, _fetch_method):
"""Ensure fetch with bad URL fails as expected.""" """Ensure fetch with bad URL fails as expected."""
testpath = str(tmpdir) testpath = str(tmpdir)
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
with pytest.raises(fs.FailedDownloadError): with pytest.raises(fs.FailedDownloadError):
fetcher = fs.URLFetchStrategy(url='file:///does-not-exist') fetcher = fs.URLFetchStrategy(url='file:///does-not-exist')
assert fetcher is not None assert fetcher is not None
@ -75,7 +75,7 @@ def test_urlfetchstrategy_bad_url(tmpdir, use_curl):
def test_fetch_options(tmpdir, mock_archive): def test_fetch_options(tmpdir, mock_archive):
testpath = str(tmpdir) testpath = str(tmpdir)
with spack.config.override('config:use_curl', True): with spack.config.override('config:url_fetch_method', 'curl'):
fetcher = fs.URLFetchStrategy(url=mock_archive.url, fetcher = fs.URLFetchStrategy(url=mock_archive.url,
fetch_options={'cookie': 'True', fetch_options={'cookie': 'True',
'timeout': 10}) 'timeout': 10})
@ -87,11 +87,11 @@ def test_fetch_options(tmpdir, mock_archive):
fetcher.fetch() fetcher.fetch()
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_archive_file_errors(tmpdir, mock_archive, use_curl): def test_archive_file_errors(tmpdir, mock_archive, _fetch_method):
"""Ensure FetchStrategy commands may only be used as intended""" """Ensure FetchStrategy commands may only be used as intended"""
testpath = str(tmpdir) testpath = str(tmpdir)
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
fetcher = fs.URLFetchStrategy(url=mock_archive.url) fetcher = fs.URLFetchStrategy(url=mock_archive.url)
assert fetcher is not None assert fetcher is not None
with pytest.raises(fs.FailedDownloadError): with pytest.raises(fs.FailedDownloadError):
@ -112,7 +112,7 @@ def test_archive_file_errors(tmpdir, mock_archive, use_curl):
@pytest.mark.parametrize('secure', [True, False]) @pytest.mark.parametrize('secure', [True, False])
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
@pytest.mark.parametrize('mock_archive', @pytest.mark.parametrize('mock_archive',
[('.tar.gz', 'z'), ('.tgz', 'z'), [('.tar.gz', 'z'), ('.tgz', 'z'),
('.tar.bz2', 'j'), ('.tbz2', 'j'), ('.tar.bz2', 'j'), ('.tbz2', 'j'),
@ -121,7 +121,7 @@ def test_archive_file_errors(tmpdir, mock_archive, use_curl):
def test_fetch( def test_fetch(
mock_archive, mock_archive,
secure, secure,
use_curl, _fetch_method,
checksum_type, checksum_type,
config, config,
mutable_mock_repo mutable_mock_repo
@ -147,7 +147,7 @@ def test_fetch(
# Enter the stage directory and check some properties # Enter the stage directory and check some properties
with pkg.stage: with pkg.stage:
with spack.config.override('config:verify_ssl', secure): with spack.config.override('config:verify_ssl', secure):
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
pkg.do_stage() pkg.do_stage()
with working_dir(pkg.stage.source_path): with working_dir(pkg.stage.source_path):
assert os.path.exists('configure') assert os.path.exists('configure')
@ -168,13 +168,13 @@ def test_fetch(
('url-list-test @3.0a1', 'foo-3.0a1.tar.gz', 'abc30a1'), ('url-list-test @3.0a1', 'foo-3.0a1.tar.gz', 'abc30a1'),
('url-list-test @4.5-rc5', 'foo-4.5-rc5.tar.gz', 'abc45rc5'), ('url-list-test @4.5-rc5', 'foo-4.5-rc5.tar.gz', 'abc45rc5'),
]) ])
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_from_list_url(mock_packages, config, spec, url, digest, use_curl): def test_from_list_url(mock_packages, config, spec, url, digest, _fetch_method):
""" """
Test URLs in the url-list-test package, which means they should Test URLs in the url-list-test package, which means they should
have checksums in the package. have checksums in the package.
""" """
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
specification = Spec(spec).concretized() specification = Spec(spec).concretized()
pkg = spack.repo.get(specification) pkg = spack.repo.get(specification)
fetch_strategy = fs.from_list_url(pkg) fetch_strategy = fs.from_list_url(pkg)
@ -187,10 +187,10 @@ def test_from_list_url(mock_packages, config, spec, url, digest, use_curl):
assert fetch_strategy.extra_options == {'timeout': 60} assert fetch_strategy.extra_options == {'timeout': 60}
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_from_list_url_unspecified(mock_packages, config, use_curl): def test_from_list_url_unspecified(mock_packages, config, _fetch_method):
"""Test non-specific URLs from the url-list-test package.""" """Test non-specific URLs from the url-list-test package."""
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
pkg = spack.repo.get('url-list-test') pkg = spack.repo.get('url-list-test')
spec = Spec('url-list-test @2.0.0').concretized() spec = Spec('url-list-test @2.0.0').concretized()
@ -236,7 +236,7 @@ def is_true():
monkeypatch.setattr(sys.stdout, 'isatty', is_true) monkeypatch.setattr(sys.stdout, 'isatty', is_true)
monkeypatch.setattr(tty, 'msg_enabled', is_true) monkeypatch.setattr(tty, 'msg_enabled', is_true)
with spack.config.override('config:use_curl', True): with spack.config.override('config:url_fetch_method', 'curl'):
fetcher = fs.URLFetchStrategy(mock_archive.url) fetcher = fs.URLFetchStrategy(mock_archive.url)
with Stage(fetcher, path=testpath) as stage: with Stage(fetcher, path=testpath) as stage:
assert fetcher.archive_file is None assert fetcher.archive_file is None
@ -246,10 +246,10 @@ def is_true():
assert '##### 100' in status assert '##### 100' in status
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_url_extra_fetch(tmpdir, mock_archive, use_curl): def test_url_extra_fetch(tmpdir, mock_archive, _fetch_method):
"""Ensure a fetch after downloading is effectively a no-op.""" """Ensure a fetch after downloading is effectively a no-op."""
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
testpath = str(tmpdir) testpath = str(tmpdir)
fetcher = fs.URLFetchStrategy(mock_archive.url) fetcher = fs.URLFetchStrategy(mock_archive.url)
with Stage(fetcher, path=testpath) as stage: with Stage(fetcher, path=testpath) as stage:
@ -267,12 +267,12 @@ def test_url_extra_fetch(tmpdir, mock_archive, use_curl):
['https://ftpmirror.gnu.org/autoconf/autoconf-2.62.tar.gz', ['https://ftpmirror.gnu.org/autoconf/autoconf-2.62.tar.gz',
'https://ftp.gnu.org/gnu/autoconf/autoconf-2.62.tar.gz']) 'https://ftp.gnu.org/gnu/autoconf/autoconf-2.62.tar.gz'])
]) ])
@pytest.mark.parametrize('use_curl', [True, False]) @pytest.mark.parametrize('_fetch_method', ['curl', 'urllib'])
def test_candidate_urls(pkg_factory, url, urls, version, expected, use_curl): def test_candidate_urls(pkg_factory, url, urls, version, expected, _fetch_method):
"""Tests that candidate urls include mirrors and that they go through """Tests that candidate urls include mirrors and that they go through
pattern matching and substitution for versions. pattern matching and substitution for versions.
""" """
with spack.config.override('config:use_curl', use_curl): with spack.config.override('config:url_fetch_method', _fetch_method):
pkg = pkg_factory(url, urls) pkg = pkg_factory(url, urls)
f = fs._from_merged_attrs(fs.URLFetchStrategy, pkg, version) f = fs._from_merged_attrs(fs.URLFetchStrategy, pkg, version)
assert f.candidate_urls == expected assert f.candidate_urls == expected
@ -298,7 +298,7 @@ def _which(*args, **kwargs):
testpath = str(tmpdir) testpath = str(tmpdir)
url = 'http://github.com/spack/spack' url = 'http://github.com/spack/spack'
with spack.config.override('config:use_curl', True): with spack.config.override('config:url_fetch_method', 'curl'):
fetcher = fs.URLFetchStrategy(url=url) fetcher = fs.URLFetchStrategy(url=url)
assert fetcher is not None assert fetcher is not None
with pytest.raises(TypeError, match='object is not callable'): with pytest.raises(TypeError, match='object is not callable'):