Skip to content

Commit d3d471c

Browse files
karajan1001efiop
andauthored
remote config validation (#3628)
* remote config validation Fixes #3552 * solving config test fail * Validation problem in config merging sloving validation problem in config merging from different levels. Only those config in lower levels can affect the validation process. * reduce Cognitive Complexity * Two request changes 1. Duplicate load current level of config 2. rewrite test to pytest style * black it * config: don't use all_levels Co-authored-by: karajan1001 <[email protected]> Co-authored-by: Ruslan Kuprieiev <[email protected]>
1 parent 8455b7b commit d3d471c

File tree

3 files changed

+37
-4
lines changed

3 files changed

+37
-4
lines changed

dvc/config.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,7 @@ def load(self, validate=True):
278278
Raises:
279279
ConfigError: thrown if config has an invalid format.
280280
"""
281-
conf = {}
282-
for level in self.LEVELS:
283-
if level in self.files:
284-
_merge(conf, self.load_one(level))
281+
conf = self._load_config_to_level()
285282

286283
if validate:
287284
conf = self.validate(conf)
@@ -333,6 +330,15 @@ def _map_dirs(conf, func):
333330
dirs_schema = {"cache": {"dir": func}, "remote": {str: {"url": func}}}
334331
return Schema(dirs_schema, extra=ALLOW_EXTRA)(conf)
335332

333+
def _load_config_to_level(self, level=None):
334+
merged_conf = {}
335+
for merge_level in self.LEVELS:
336+
if merge_level == level:
337+
break
338+
if merge_level in self.files:
339+
_merge(merged_conf, self.load_one(merge_level))
340+
return merged_conf
341+
336342
@contextmanager
337343
def edit(self, level="repo"):
338344
if level in {"repo", "local"} and self.dvc_dir is None:
@@ -342,6 +348,11 @@ def edit(self, level="repo"):
342348
yield conf
343349

344350
conf = self._save_paths(conf, self.files[level])
351+
352+
merged_conf = self._load_config_to_level(level)
353+
_merge(merged_conf, conf)
354+
self.validate(merged_conf)
355+
345356
_save_config(self.files[level], conf)
346357
self.load()
347358

tests/func/test_config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ def test_merging_two_levels(dvc):
9494
with dvc.config.edit() as conf:
9595
conf["remote"]["test"] = {"url": "ssh://example.com"}
9696

97+
with pytest.raises(
98+
ConfigError, match=r"expected 'url' for dictionary value"
99+
):
100+
with dvc.config.edit("global") as conf:
101+
conf["remote"]["test"] = {"password": "1"}
102+
97103
with dvc.config.edit("local") as conf:
98104
conf["remote"]["test"] = {"password": "1"}
99105

tests/func/test_remote.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,3 +254,19 @@ def test_push_order(tmp_dir, dvc, tmp_path_factory, mocker):
254254
dvc.push()
255255
# last uploaded file should be dir checksum
256256
assert mocked_upload.call_args[0][0].endswith(".dir")
257+
258+
259+
def test_remote_modify_validation(dvc):
260+
remote_name = "drive"
261+
unsupported_config = "unsupported_config"
262+
assert (
263+
main(["remote", "add", "-d", remote_name, "gdrive://test/test"]) == 0
264+
)
265+
assert (
266+
main(
267+
["remote", "modify", remote_name, unsupported_config, "something"]
268+
)
269+
== 251
270+
)
271+
config = configobj.ConfigObj(dvc.config.files["repo"])
272+
assert unsupported_config not in config['remote "{}"'.format(remote_name)]

0 commit comments

Comments
 (0)