Allow to set configuration via config file
Change-Id: I84307abb7600918a88cf001fa2ec7292f47a9d50
diff --git a/Pipfile b/Pipfile
index 1985299..7bc8090 100644
--- a/Pipfile
+++ b/Pipfile
@@ -11,6 +11,7 @@
requests = "*"
gitpython = "*"
numpy = "*"
+pyyaml = "*"
[requires]
python_version = "3.7"
diff --git a/Pipfile.lock b/Pipfile.lock
index b982f0a..4b8c03b 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "c384a922e65387ba7e2e555f5364d272a8a3b49872417980879191dcb7719209"
+ "sha256": "0e63fa4f4d245859474870dc3f0dd71d014fffd259c30bcbe10989fbbe6bc4ad"
},
"pipfile-spec": 6,
"requires": {
@@ -74,6 +74,25 @@
"index": "pypi",
"version": "==1.17.2"
},
+ "pyyaml": {
+ "hashes": [
+ "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
+ "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
+ "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
+ "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
+ "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
+ "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
+ "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
+ "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
+ "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
+ "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
+ "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
+ "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
+ "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
+ ],
+ "index": "pypi",
+ "version": "==5.1.2"
+ },
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
diff --git a/README.md b/README.md
index e6a2db6..d74459b 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,20 @@
docker build -t gerrit/loadtester ./container
```
+## Configuration
+
+A configuration file in yaml-format can be used to configure the test run. The
+`config.sample.yaml`-file gives an example-configuration.
+
+The single configuration values are listed here:
+
+| key | description | default value |
+|--------------------|-------------------------------------|-------------------------|
+| `gerrit.url` | URL of the Gerrit test server | `http://localhost:8080` |
+| `gerrit.user` | Gerrit user used for tests | `admin` |
+| `gerrit.password` | Password of Gerrit user | `secret` |
+| `testrun.duration` | Duration for which to run the tests | `null` (indefinitely) |
+
## Run
### Docker
@@ -45,6 +59,7 @@
```sh
docker run -it gerrit/loadtester \
+ --config $CONFIG_FILE \
--duration $TEST_DURATION \
--password $GERRIT_PWD \
--url $GERRIT_URL \
@@ -53,6 +68,9 @@
The options are:
+- `--config` (default: `None`): Path to a config file (optional). The config file
+ has to be present in the container, either by building it in or by mounting it.
+ Parameters will overwrite configuration from file.
- `--duration` (default: `None`): Duration, for which to run the tests in
seconds (optional; if not set, test runs until stopped)
- `--password` (default: `secret`): Password of Gerrit user used for executing
diff --git a/config.sample.yaml b/config.sample.yaml
new file mode 100644
index 0000000..57f4650
--- /dev/null
+++ b/config.sample.yaml
@@ -0,0 +1,7 @@
+gerrit:
+ url: http://localhost:8080
+ user: admin
+ password: secret
+
+testrun:
+ duration: null
diff --git a/container/dependencies/Pipfile b/container/dependencies/Pipfile
index 84a8cec..d1e9ce9 100644
--- a/container/dependencies/Pipfile
+++ b/container/dependencies/Pipfile
@@ -9,6 +9,7 @@
requests = "*"
gitpython = "*"
numpy = "*"
+pyyaml = "*"
[requires]
python_version = "3.7"
diff --git a/container/dependencies/Pipfile.lock b/container/dependencies/Pipfile.lock
index 87b9d49..062e4d2 100644
--- a/container/dependencies/Pipfile.lock
+++ b/container/dependencies/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "ad115c17ea23b1608c7391cf91c7ea9f39bd2da751fd77ef53328db95223ead7"
+ "sha256": "faf79ab6897105c99765abc998976a84ef7eb8bf4756d835518b49eb6f017401"
},
"pipfile-spec": 6,
"requires": {
@@ -74,6 +74,25 @@
"index": "pypi",
"version": "==1.17.2"
},
+ "pyyaml": {
+ "hashes": [
+ "sha256:0113bc0ec2ad727182326b61326afa3d1d8280ae1122493553fd6f4397f33df9",
+ "sha256:01adf0b6c6f61bd11af6e10ca52b7d4057dd0be0343eb9283c878cf3af56aee4",
+ "sha256:5124373960b0b3f4aa7df1707e63e9f109b5263eca5976c66e08b1c552d4eaf8",
+ "sha256:5ca4f10adbddae56d824b2c09668e91219bb178a1eee1faa56af6f99f11bf696",
+ "sha256:7907be34ffa3c5a32b60b95f4d95ea25361c951383a894fec31be7252b2b6f34",
+ "sha256:7ec9b2a4ed5cad025c2278a1e6a19c011c80a3caaac804fd2d329e9cc2c287c9",
+ "sha256:87ae4c829bb25b9fe99cf71fbb2140c448f534e24c998cc60f39ae4f94396a73",
+ "sha256:9de9919becc9cc2ff03637872a440195ac4241c80536632fffeb6a1e25a74299",
+ "sha256:a5a85b10e450c66b49f98846937e8cfca1db3127a9d5d1e31ca45c3d0bef4c5b",
+ "sha256:b0997827b4f6a7c286c01c5f60384d218dca4ed7d9efa945c3e1aa623d5709ae",
+ "sha256:b631ef96d3222e62861443cc89d6563ba3eeb816eeb96b2629345ab795e53681",
+ "sha256:bf47c0607522fdbca6c9e817a6e81b08491de50f3766a7a0e6a5be7905961b41",
+ "sha256:f81025eddd0327c7d4cfe9b62cf33190e1e736cc6e97502b3ec425f574b3e7a8"
+ ],
+ "index": "pypi",
+ "version": "==5.1.2"
+ },
"requests": {
"hashes": [
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
diff --git a/container/tools/config/__init__.py b/container/tools/config/__init__.py
new file mode 100644
index 0000000..d383d50
--- /dev/null
+++ b/container/tools/config/__init__.py
@@ -0,0 +1,15 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from .parser import Parser
diff --git a/container/tools/config/parser.py b/container/tools/config/parser.py
new file mode 100644
index 0000000..2e2466a
--- /dev/null
+++ b/container/tools/config/parser.py
@@ -0,0 +1,62 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os.path
+
+import yaml
+
+DEFAULTS = {
+ "gerrit": {"url": None, "user": "admin", "password": "secret"},
+ "testrun": {"duration": None},
+}
+
+ARG_TO_CONFIG_MAPPING = {
+ "url": {"category": "gerrit", "option": "url"},
+ "user": {"category": "gerrit", "option": "user"},
+ "password": {"category": "gerrit", "option": "password"},
+ "duration": {"category": "testrun", "option": "duration"},
+}
+
+
+class Parser:
+ def __init__(self, args):
+ self.args = vars(args)
+
+ self.config = DEFAULTS
+
+ def parse(self):
+ if self.args["config_file"]:
+ for category, category_dict in self._parse_config_file().items():
+ for option, value in category_dict.items():
+ self.config[category][option] = value
+
+ self._apply_args()
+
+ return self.config
+
+ def _apply_args(self):
+ for arg, arg_mapping in ARG_TO_CONFIG_MAPPING.items():
+ if self.args[arg]:
+ self.config[arg_mapping["category"]][arg_mapping["option"]] = self.args[
+ arg
+ ]
+
+ def _parse_config_file(self):
+ if not os.path.exists(self.args["config_file"]):
+ raise FileNotFoundError(
+ "Could not find config file: %s" % self.args["config_file"]
+ )
+
+ with open(self.args["config_file"], "r") as f:
+ return yaml.load(f, Loader=yaml.SafeLoader)
diff --git a/container/tools/start_test.py b/container/tools/start_test.py
index 381aefa..e504586 100755
--- a/container/tools/start_test.py
+++ b/container/tools/start_test.py
@@ -23,17 +23,22 @@
import numpy as np
import actions
+import config
LOG_PATH = "/var/logs/loadtester.log"
class LoadTestInstance:
- def __init__(self, url, user, pwd, test_duration=None):
- self.url = url
- self.user = user
- self.pwd = pwd
+ def __init__(self, test_config):
+ self.url = test_config["gerrit"]["url"]
+ self.user = test_config["gerrit"]["user"]
+ self.pwd = test_config["gerrit"]["password"]
- self.timeout = time.time() + test_duration if test_duration else None
+ self.timeout = (
+ time.time() + test_config["testrun"]["duration"]
+ if test_config["testrun"]["duration"]
+ else None
+ )
self.owned_projects = set()
self.cloned_projects = set()
@@ -150,20 +155,13 @@
parser = argparse.ArgumentParser()
parser.add_argument(
- "-U", "--url", help="Gerrit base url", dest="url", action="store", required=True
+ "-U", "--url", help="Gerrit base url", dest="url", action="store"
)
- parser.add_argument(
- "-u", "--user", help="Gerrit user", dest="user", action="store", default="admin"
- )
+ parser.add_argument("-u", "--user", help="Gerrit user", dest="user", action="store")
parser.add_argument(
- "-p",
- "--password",
- help="Gerrit password",
- dest="pwd",
- action="store",
- default="secret",
+ "-p", "--password", help="Gerrit password", dest="password", action="store"
)
parser.add_argument(
@@ -173,10 +171,13 @@
dest="duration",
action="store",
type=int,
- default=None,
+ )
+
+ parser.add_argument(
+ "-c", "--config", help="Configuration file", dest="config_file", action="store"
)
args = parser.parse_args()
- test = LoadTestInstance(args.url, args.user, args.pwd, args.duration)
+ test = LoadTestInstance(config.Parser(args).parse())
test.run()