Repository: elastic/rally

Issue:
Password with symbols can cause errors parsing rally.ini file
**Rally version**: 0.11.0

**OS version**: Ubuntu 16.04

**Description of the problem including expected versus actual behavior**:
I specified an Elastic Cloud cluster as the metrics store and picked a password that contained a `$` followed by a character. This resulted in the following error when I tried to run the eventdata track:

```
Traceback (most recent call last):
  File "/usr/local/bin/esrally", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python3.6/site-packages/esrally/rally.py", line 588, in main
    ensure_configuration_present(cfg, args, sub_command)
  File "/usr/local/lib/python3.6/site-packages/esrally/rally.py", line 428, in ensure_configuration_present
    cfg.load_config(auto_upgrade=True)
  File "/usr/local/lib/python3.6/site-packages/esrally/config.py", line 206, in load_config
    self.migrate_config()
  File "/usr/local/lib/python3.6/site-packages/esrally/config.py", line 239, in migrate_config
    migrate(self.config_file, self._stored_config_version(), Config.CURRENT_CONFIG_VERSION)
  File "/usr/local/lib/python3.6/site-packages/esrally/config.py", line 588, in migrate
    .format(config_file.location, PROGRAM_NAME))
esrally.config.ConfigError: ('The config file in /home/ubuntu/.rally/rally.ini is too old. Please delete it and reconfigure Rally from scratch with esrally configure.', None)
``` 


Discussion:

Patch:
diff --git a/esrally/config.py b/esrally/config.py
index a4a65a81..d9152ec7 100644
--- a/esrally/config.py
+++ b/esrally/config.py
@@ -38,8 +38,8 @@ class ConfigFile:
         """
         return os.path.isfile(self.location)
 
-    def load(self, interpolation=configparser.ExtendedInterpolation()):
-        config = configparser.ConfigParser(interpolation=interpolation)
+    def load(self):
+        config = configparser.ConfigParser()
         config.read(self.location, encoding="utf-8")
         return config
 
@@ -370,7 +370,7 @@ class ConfigFactory:
         config["source"]["elasticsearch.src.subdir"] = io.basename(source_dir)
 
         config["benchmarks"] = {}
-        config["benchmarks"]["local.dataset.cache"] = "${node:root.dir}/data"
+        config["benchmarks"]["local.dataset.cache"] = os.path.join(root_dir, "data")
 
         config["reporting"] = {}
         config["reporting"]["datastore.type"] = data_store_type
@@ -515,7 +515,7 @@ def migrate(config_file, current_version, target_version, out=print, i=input):
                           % (target_version, current_version))
     # but first a backup...
     config_file.backup()
-    config = config_file.load(interpolation=None)
+    config = config_file.load()
 
     if current_version == 12 and target_version > current_version:
         # the current configuration allows to benchmark from sources
@@ -633,6 +633,10 @@ def migrate(config_file, current_version, target_version, out=print, i=input):
 
     if current_version == 16 and target_version > current_version:
         config.pop("runtime", None)
+        if "benchmarks" in config and "local.dataset.cache" in config["benchmarks"]:
+            if config["benchmarks"]["local.dataset.cache"] == "${node:root.dir}/data":
+                root_dir = config["node"]["root.dir"]
+                config["benchmarks"]["local.dataset.cache"] = os.path.join(root_dir, "data")
         current_version = 17
         config["meta"]["config.version"] = str(current_version)
 


Test patch:
diff --git a/tests/config_test.py b/tests/config_test.py
index 9e42da3f..8ace3469 100644
--- a/tests/config_test.py
+++ b/tests/config_test.py
@@ -252,6 +252,7 @@ class ConfigFactoryTests(TestCase):
             for k, v in config_store.config[section].items():
                 print("%s::%s: %s" % (section, k, v))
 
+        root_dir = io.normalize_path(os.path.abspath("./in-memory/benchmarks"))
         self.assertTrue("meta" in config_store.config)
         self.assertEqual(str(config.Config.CURRENT_CONFIG_VERSION), config_store.config["meta"]["config.version"])
 
@@ -259,15 +260,16 @@ class ConfigFactoryTests(TestCase):
         self.assertEqual("local", config_store.config["system"]["env.name"])
 
         self.assertTrue("node" in config_store.config)
-        self.assertEqual(io.normalize_path(os.path.abspath("./in-memory/benchmarks")), config_store.config["node"]["root.dir"])
-        self.assertEqual(io.normalize_path(os.path.abspath("./in-memory/benchmarks/src")), config_store.config["node"]["src.root.dir"])
+
+        self.assertEqual(root_dir, config_store.config["node"]["root.dir"])
+        self.assertEqual(os.path.join(root_dir, "src"), config_store.config["node"]["src.root.dir"])
 
         self.assertTrue("source" in config_store.config)
         self.assertEqual("https://github.com/elastic/elasticsearch.git", config_store.config["source"]["remote.repo.url"])
         self.assertEqual("elasticsearch", config_store.config["source"]["elasticsearch.src.subdir"])
 
         self.assertTrue("benchmarks" in config_store.config)
-        self.assertEqual("${node:root.dir}/data", config_store.config["benchmarks"]["local.dataset.cache"])
+        self.assertEqual(os.path.join(root_dir, "data"), config_store.config["benchmarks"]["local.dataset.cache"])
 
         self.assertTrue("reporting" in config_store.config)
         self.assertEqual("in-memory", config_store.config["reporting"]["datastore.type"])
@@ -765,5 +767,29 @@ class ConfigMigrationTests(TestCase):
         self.assertIn("distributions", config_file.config)
         self.assertNotIn("release.url", config_file.config["distributions"])
 
+    def test_migrate_from_16_to_17(self):
+        config_file = InMemoryConfigStore("test")
+        sample_config = {
+            "meta": {
+                "config.version": 16
+            },
+            "node": {
+                "root.dir": "/home/user/.rally/benchmarks"
+            },
+            "runtime": {
+                "java.home": "/usr/local/javas/10"
+            },
+            "benchmarks": {
+                "local.dataset.cache": "${node:root.dir}/data"
+            }
+        }
+        config_file.store(sample_config)
+        config.migrate(config_file, 16, 17, out=null_output)
+
+        self.assertTrue(config_file.backup_created)
+        self.assertEqual("17", config_file.config["meta"]["config.version"])
+        self.assertNotIn("runtime", config_file.config)
+        self.assertEqual("/home/user/.rally/benchmarks/data", config_file.config["benchmarks"]["local.dataset.cache"])
+