Skip to content

Commit 824cb5c

Browse files
authored
Human readable config errors (#85)
* Added validations to check the config file structure * Updated docs * Updated authors
1 parent e70f4be commit 824cb5c

File tree

14 files changed

+184
-41
lines changed

14 files changed

+184
-41
lines changed

AUTHORS

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Alberto Vara <a.vara.1986@gmail.com>
2+
Àlex Pérez <alexperezpujol@disroot.org>
23
Hugo Camino <hugo.camino.villacorta@gmail.com>
3-
José Manuel <jmrivas86@gmail.com>
44
Javier Luna molina <javierlunamolina@gmail.com>
5-
pilamb <perikopalotes@gmail.com>
5+
José Manuel <jmrivas86@gmail.com>
6+
Mike Rubin <miguelgr1988@gmail.com>

docs/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ microservices with Python which handles cross-cutting concerns:
77
- Externalized configuration
88
- Logging
99
- Health checks
10-
- Metrics (TODO)
10+
- Metrics
1111
- Distributed tracing
1212

1313
PyMS is powered by [Flask](https://flask.palletsprojects.com/en/1.1.x/), [Connexion](https://github.com/zalando/connexion) and [Opentracing](https://opentracing.io/).

docs/ms_class.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,19 @@ Each keyword in our configuration block, can be accessed in our Microservice obj
3737
```yaml
3838
# Config.yml
3939
pyms:
40-
config:
41-
app_name: "Python Microservice"
42-
foo: "var"
43-
multiplevars:
44-
config1: "test1"
45-
config2: "test2"
40+
config:
41+
app_name: "Python Microservice"
42+
foo: "var"
43+
multiplevars:
44+
config1: "test1"
45+
config2: "test2"
4646
4747
```
4848
```python
4949
#app.py
5050
from pyms.flask.app import Microservice
5151
52-
ms = Microservice(service="example-config", path=__file__)
52+
ms = Microservice(path=__file__)
5353
print(ms.config.APP_NAME)
5454
# >> "Python Microservice"
5555
print(ms.config.app_name)

docs/quickstart.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ if __name__ == '__main__':
2626
config.yml
2727
```yaml
2828
pyms:
29-
services: # 1.2
30-
requests:
31-
data: {}
32-
config: # 1.3
33-
DEBUG: true
34-
APP_NAME: business-glossary
35-
APPLICATION_ROOT : ""
36-
SECRET_KEY: "gjr39dkjn344_!67#"
29+
services: # 1.2
30+
requests:
31+
data: {}
32+
config: # 1.3
33+
DEBUG: true
34+
APP_NAME: business-glossary
35+
APPLICATION_ROOT : ""
36+
SECRET_KEY: "gjr39dkjn344_!67#"
3737
```
3838
3939
## So what did that code do?

pyms/config/conf.py

Lines changed: 83 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from pyms.config.confile import ConfFile
2-
from pyms.exceptions import ServiceDoesNotExistException
2+
from pyms.constants import PYMS_CONFIG_WHITELIST_KEYWORDS
3+
from pyms.exceptions import ServiceDoesNotExistException, ConfigErrorException, AttrDoesNotExistException
34

45

56
def get_conf(*args, **kwargs):
@@ -8,26 +9,26 @@ def get_conf(*args, **kwargs):
89
parent and this name, in example of the next yaml, tracer will be `pyms.tracer`. If we have got his config file:
910
```
1011
pyms:
11-
metrics: true
12-
requests:
13-
data: data
14-
swagger:
15-
path: ""
16-
file: "swagger.yaml"
17-
tracer:
18-
client: "jaeger"
19-
host: "localhost"
20-
component_name: "Python Microservice"
21-
my-ms:
22-
DEBUG: true
23-
TESTING: true
12+
services:
13+
metrics: true
14+
requests:
15+
data: data
16+
swagger:
17+
path: ""
18+
file: "swagger.yaml"
19+
tracer:
20+
client: "jaeger"
21+
host: "localhost"
22+
component_name: "Python Microservice"
23+
config:
24+
DEBUG: true
25+
TESTING: true
2426
```
25-
* `pyms` block is the default key to load in the pyms.flask.app.create_app.Microservice class.
27+
* `pyms.services`: block is the default key to load in the pyms.flask.app.create_app.Microservice class.
2628
* `metrics`: is set as the service `pyms.metrics`
2729
* `swagger`: is set as the service `pyms.swagger`
2830
* `tracer`: is set as the service `pyms.tracer`
29-
* `my-ms` block is defined by the env var `CONFIGMAP_SERVICE`. By default is `ms`. This block is the default flask
30-
block config
31+
* `pyms.config`: This block is the default flask block config
3132
:param args:
3233
:param kwargs:
3334
@@ -36,6 +37,70 @@ def get_conf(*args, **kwargs):
3637
service = kwargs.pop('service', None)
3738
if not service:
3839
raise ServiceDoesNotExistException("Service not defined")
39-
4040
config = ConfFile(*args, **kwargs)
4141
return getattr(config, service)
42+
43+
44+
def validate_conf(*args, **kwargs):
45+
config = ConfFile(*args, **kwargs)
46+
is_config_ok = True
47+
try:
48+
config.pyms
49+
except AttrDoesNotExistException:
50+
is_config_ok = False
51+
if not is_config_ok:
52+
raise ConfigErrorException("""Config file must start with `pyms` keyword, for example:
53+
pyms:
54+
services:
55+
metrics: true
56+
requests:
57+
data: data
58+
swagger:
59+
path: ""
60+
file: "swagger.yaml"
61+
tracer:
62+
client: "jaeger"
63+
host: "localhost"
64+
component_name: "Python Microservice"
65+
config:
66+
DEBUG: true
67+
TESTING: true""")
68+
try:
69+
config.pyms.config
70+
except AttrDoesNotExistException:
71+
is_config_ok = False
72+
if not is_config_ok:
73+
raise ConfigErrorException("""`pyms` block must contain a `config` keyword in your Config file, for example:
74+
pyms:
75+
services:
76+
metrics: true
77+
requests:
78+
data: data
79+
swagger:
80+
path: ""
81+
file: "swagger.yaml"
82+
tracer:
83+
client: "jaeger"
84+
host: "localhost"
85+
component_name: "Python Microservice"
86+
config:
87+
DEBUG: true
88+
TESTING: true""")
89+
wrong_keywords = [i for i in config.pyms if i not in PYMS_CONFIG_WHITELIST_KEYWORDS]
90+
if len(wrong_keywords) > 0:
91+
raise ConfigErrorException("""{} isn`t a valid keyword for pyms block, for example:
92+
pyms:
93+
services:
94+
metrics: true
95+
requests:
96+
data: data
97+
swagger:
98+
path: ""
99+
file: "swagger.yaml"
100+
tracer:
101+
client: "jaeger"
102+
host: "localhost"
103+
component_name: "Python Microservice"
104+
config:
105+
DEBUG: true
106+
TESTING: true""".format(wrong_keywords))

pyms/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@
55
CONFIG_BASE = "pyms.config"
66

77
SERVICE_BASE = "pyms.services"
8+
9+
PYMS_CONFIG_WHITELIST_KEYWORDS = ["config", "services"]

pyms/exceptions.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,9 @@ class ConfigDoesNotFoundException(Exception):
1717
pass
1818

1919

20+
class ConfigErrorException(Exception):
21+
pass
22+
23+
2024
class PackageNotExists(Exception):
2125
pass

pyms/flask/app/create_app.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from flask_opentracing import FlaskTracing
77

88
from pyms.config import get_conf
9+
from pyms.config.conf import validate_conf
910
from pyms.constants import LOGGER_NAME, CONFIG_BASE
1011
from pyms.flask.healthcheck import healthcheck_blueprint
1112
from pyms.flask.services.driver import ServicesManager
@@ -56,8 +57,6 @@ def example():
5657
Environments variables of PyMS:
5758
**CONFIGMAP_FILE**: The path to the configuration file. By default, PyMS search the configuration file in your
5859
actual folder with the name "config.yml"
59-
**CONFIGMAP_SERVICE**: the name of the keyword that define the block of key-value of [Flask Configuration Handling](http://flask.pocoo.org/docs/1.0/config/)
60-
and your own configuration (see the next section to more info)
6160
6261
## Create configuration
6362
Each microservice needs a config file in yaml or json format to work with it. This configuration contains
@@ -99,6 +98,7 @@ def example():
9998

10099
def __init__(self, *args, **kwargs):
101100
self.path = os.path.dirname(kwargs.get("path", __file__))
101+
validate_conf()
102102
self.config = get_conf(service=CONFIG_BASE)
103103
self.init_services()
104104

pyms/flask/app/create_config.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@
44
def config():
55
"""The behavior of this function is to access to the configuration outer the scope of flask context, to prevent to
66
raise a `'working outside of application context`
7-
**IMPORTANT:** If you use this method to get configuration out of context, you must set the `CONFIGMAP_SERVICE` or
8-
set the default key `ms` for your configuration block in your config.yml
97
:return:
108
"""
119
ms = Microservice()

pyms/flask/services/swagger.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313

1414

1515
class Service(DriverService):
16+
"""The parameters you can add to your config are:
17+
* **path:** The relative or absolute route to your swagger yaml file. The default value is the current directory
18+
* **file:** The name of you swagger yaml file. The default value is `swagger.yaml`
19+
* **url:** The url where swagger run in your server. The default value is `/ui/`.
20+
* **project_dir:** Relative path of the project folder to automatic routing,
21+
see [this link for more info](https://github.com/zalando/connexion#automatic-routing). The default value is `project`
22+
"""
1623
service = "swagger"
1724
default_values = {
1825
"path": SWAGGER_PATH,

0 commit comments

Comments
 (0)