Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Environments variables of PyMS:

**CONFIGMAP_FILE**: The path to the configuration file. By default, PyMS searches for the configuration file in your
**PYMS_CONFIGMAP_FILE**: The path to the configuration file. By default, PyMS searches for the configuration file in your
current folder with the name "config.yml"
**KEY_FILE**: The path to the key file to decrypt your configuration. By default, PyMS searches for the configuration file in your
**PYMS_KEY_FILE**: The path to the key file to decrypt your configuration. By default, PyMS searches for the configuration file in your
current folder with the name "key.key"

## Create configuration
Expand Down Expand Up @@ -136,7 +136,7 @@ API = Api(

## Looking for Configuration file with Kubernetes Configmaps
By default, the Microservice class searches for a config.yml in the same path. You can set a different route or set a json file.
To change this path, you must define an environment variable called `CONFIGMAP_FILE`.
To change this path, you must define an environment variable called `PYMS_CONFIGMAP_FILE`.

This way of looking for the configuration is useful when you work with Docker and Kubernetes. For example, you could integrate
a configmap of Kubernetes, with this microservice and a deployment with:
Expand All @@ -154,7 +154,7 @@ spec:
- name: my-microservice
image: ...
env:
- name: CONFIGMAP_FILE
- name: PYMS_CONFIGMAP_FILE
value: "/usr/share/microservice/config.yaml"

volumeMounts:
Expand Down
4 changes: 2 additions & 2 deletions docs/encrypt_decryt_configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Move your key, for example, to `mv key.key /home/my_user/keys/myproject.key`
then, store the key in a environment variable with:

```bash
export KEY_FILE=/home/my_user/keys/myproject.key
export PYMS_KEY_FILE=/home/my_user/keys/myproject.key
```

## 3. Encrypt your information and store it in config
Expand Down Expand Up @@ -89,7 +89,7 @@ can find the answer
## 4. Decrypt from your config file

Pyms knows if a variable is encrypted if this var start with the prefix `enc_` or `ENC_`. PyMS searches for your key file
in the `KEY_FILE` env variable and decrypts this value to store it in the same variable without the `enc_` prefix,
in the `PYMS_KEY_FILE` env variable and decrypts this value to store it in the same variable without the `enc_` prefix,
for example,

```yaml
Expand Down
2 changes: 1 addition & 1 deletion docs/ms_class.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pyms:
Check the [Configuration](configuration.md) section to learn how to create a configuration file.

The `Microservice` class searches for a `config.yml` in the directory you pass in `path` parameter or looks for the file in
`CONFIGMAP_FILE` env var.
`PYMS_CONFIGMAP_FILE` env var.

Each keyword in our configuration block, can be accessed in our Microservice object through the attribute `config`.

Expand Down
2 changes: 1 addition & 1 deletion examples/mininum_microservice_docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ RUN apk add --update curl gcc g++ git libffi-dev openssl-dev python3-dev build-b
&& rm -rf /var/cache/apk/*

ENV PYTHONUNBUFFERED=1 APP_HOME=/microservice/
ENV CONFIGMAP_FILE="$APP_HOME"config-docker.yml
ENV PYMS_CONFIGMAP_FILE="$APP_HOME"config-docker.yml

RUN mkdir $APP_HOME && adduser -S -D -H python
RUN chown -R python $APP_HOME
Expand Down
3 changes: 1 addition & 2 deletions pylintrc
Original file line number Diff line number Diff line change
Expand Up @@ -291,8 +291,7 @@ min-similarity-lines=4

# List of note tags to take in consideration, separated by a comma.
notes=FIXME,
XXX,
TODO
XXX


[TYPECHECK]
Expand Down
6 changes: 3 additions & 3 deletions pyms/cmd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from pyms.crypt.fernet import Crypt
from pyms.flask.services.swagger import merge_swagger_file
from pyms.utils import check_package_exists, import_from
from pyms.utils import check_package_exists, import_from, utils


class Command:
Expand Down Expand Up @@ -117,15 +117,15 @@ def run(self):

@staticmethod
def print_ok(msg=""):
print('\033[92m\033[1m ' + msg + ' \033[0m\033[0m')
print(utils.colored_text(msg, utils.Colors.BRIGHT_GREEN, True))

def print_verbose(self, msg=""): # pragma: no cover
if self.verbose:
print(msg)

@staticmethod
def print_error(msg=""): # pragma: no cover
print('\033[91m\033[1m ' + msg + ' \033[0m\033[0m')
print(utils.colored_text(msg, utils.Colors.BRIGHT_RED, True))

def exit_with_error(self, msg=""): # pragma: no cover
self.print_error(msg)
Expand Down
42 changes: 41 additions & 1 deletion pyms/config/conf.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import logging
import os

from pyms.config.confile import ConfFile
from pyms.constants import PYMS_CONFIG_WHITELIST_KEYWORDS
from pyms.constants import PYMS_CONFIG_WHITELIST_KEYWORDS, CONFIGMAP_FILE_ENVIRONMENT_LEGACY, \
CONFIGMAP_FILE_ENVIRONMENT, CRYPT_FILE_KEY_ENVIRONMENT, CRYPT_FILE_KEY_ENVIRONMENT_LEGACY, LOGGER_NAME
from pyms.exceptions import ServiceDoesNotExistException, ConfigErrorException, AttrDoesNotExistException
from pyms.utils import utils

logger = logging.getLogger(LOGGER_NAME)


def get_conf(*args, **kwargs):
Expand Down Expand Up @@ -42,6 +49,7 @@ def get_conf(*args, **kwargs):


def validate_conf(*args, **kwargs):

config = ConfFile(*args, **kwargs)
is_config_ok = True
try:
Expand Down Expand Up @@ -104,3 +112,35 @@ def validate_conf(*args, **kwargs):
config:
DEBUG: true
TESTING: true""".format(wrong_keywords))

# TODO Remove temporally deprecated warnings on future versions
__verify_deprecated_env_variables(config)


def __verify_deprecated_env_variables(config):
env_var_duplicated = "IMPORTANT: If you are using \"{}\" environment variable, \"{}\" value will be ignored."
env_var_deprecated = "IMPORTANT: \"{}\" environment variable is deprecated on this version, use \"{}\" instead."

if os.getenv(CONFIGMAP_FILE_ENVIRONMENT_LEGACY) is not None:
if os.getenv(CONFIGMAP_FILE_ENVIRONMENT) is not None:
msg = env_var_duplicated.format(CONFIGMAP_FILE_ENVIRONMENT, CONFIGMAP_FILE_ENVIRONMENT_LEGACY)
else:
msg = env_var_deprecated.format(CONFIGMAP_FILE_ENVIRONMENT_LEGACY, CONFIGMAP_FILE_ENVIRONMENT)
try:
if config.pyms.config.DEBUG:
msg = utils.colored_text(msg, utils.Colors.BRIGHT_YELLOW, True)
except AttrDoesNotExistException:
pass
logger.warning(msg)

if os.getenv(CRYPT_FILE_KEY_ENVIRONMENT_LEGACY) is not None:
if os.getenv(CRYPT_FILE_KEY_ENVIRONMENT) is not None:
msg = env_var_duplicated.format(CRYPT_FILE_KEY_ENVIRONMENT, CRYPT_FILE_KEY_ENVIRONMENT_LEGACY)
else:
msg = env_var_deprecated.format(CRYPT_FILE_KEY_ENVIRONMENT_LEGACY, CRYPT_FILE_KEY_ENVIRONMENT)
try:
if config.pyms.config.DEBUG:
msg = utils.colored_text(msg, utils.Colors.BRIGHT_YELLOW, True)
except AttrDoesNotExistException:
pass
logger.warning(msg)
20 changes: 16 additions & 4 deletions pyms/config/confile.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""Module to read yaml or json conf"""
import logging
import os
import re
from typing import Dict, Union, Text, Tuple, Iterable

import anyconfig

from pyms.constants import CONFIGMAP_FILE_ENVIRONMENT, LOGGER_NAME, DEFAULT_CONFIGMAP_FILENAME
from pyms.constants import CONFIGMAP_FILE_ENVIRONMENT, LOGGER_NAME, DEFAULT_CONFIGMAP_FILENAME, \
CONFIGMAP_FILE_ENVIRONMENT_LEGACY
from pyms.exceptions import AttrDoesNotExistException, ConfigDoesNotFoundException
from pyms.utils.files import LoadFile

Expand All @@ -14,7 +16,7 @@

class ConfFile(dict):
"""Recursive get configuration from dictionary, a config file in JSON or YAML format from a path or
`CONFIGMAP_FILE` environment variable.
`PYMS_CONFIGMAP_FILE` environment variable.
**Atributes:**
* path: Path to find the `DEFAULT_CONFIGMAP_FILENAME` and `DEFAULT_KEY_FILENAME` if use encrypted vars
* empty_init: Allow blank variables
Expand All @@ -26,15 +28,18 @@ class ConfFile(dict):
def __init__(self, *args, **kwargs):
"""
Get configuration from a dictionary(variable `config`), from path (variable `path`) or from
environment with the constant `CONFIGMAP_FILE`
environment with the constant `PYMS_CONFIGMAP_FILE`
Set the configuration as upper case to inject the keys in flask config. Flask search for uppercase keys in
`app.config.from_object`
```python
if key.isupper():
self[key] = getattr(obj, key)
```
"""
self._loader = LoadFile(kwargs.get("path"), CONFIGMAP_FILE_ENVIRONMENT, DEFAULT_CONFIGMAP_FILENAME)
# TODO Remove temporally backward compatibility on future versions
configmap_file_env = self.__get_updated_configmap_file_env() # Temporally backward compatibility

self._loader = LoadFile(kwargs.get("path"), configmap_file_env, DEFAULT_CONFIGMAP_FILENAME)
self._crypt_cls = kwargs.get("crypt")
if self._crypt_cls:
self._crypt = self._crypt_cls(path=kwargs.get("path"))
Expand Down Expand Up @@ -125,3 +130,10 @@ def reload(self):

def __setattr__(self, name, value, *args, **kwargs):
super().__setattr__(name, value)

@staticmethod
def __get_updated_configmap_file_env() -> str:
result = CONFIGMAP_FILE_ENVIRONMENT
if (os.getenv(CONFIGMAP_FILE_ENVIRONMENT_LEGACY) is not None) & (os.getenv(CONFIGMAP_FILE_ENVIRONMENT) is None):
return CONFIGMAP_FILE_ENVIRONMENT_LEGACY
return result
6 changes: 4 additions & 2 deletions pyms/constants.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
CONFIGMAP_FILE_ENVIRONMENT = "CONFIGMAP_FILE"
CONFIGMAP_FILE_ENVIRONMENT = "PYMS_CONFIGMAP_FILE"
CONFIGMAP_FILE_ENVIRONMENT_LEGACY = "CONFIGMAP_FILE"

DEFAULT_CONFIGMAP_FILENAME = "config.yml"

CRYPT_FILE_KEY_ENVIRONMENT = "KEY_FILE"
CRYPT_FILE_KEY_ENVIRONMENT = "PYMS_KEY_FILE"
CRYPT_FILE_KEY_ENVIRONMENT_LEGACY = "KEY_FILE"

DEFAULT_KEY_FILENAME = "key.key"

Expand Down
17 changes: 14 additions & 3 deletions pyms/crypt/fernet.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC

from pyms.constants import CRYPT_FILE_KEY_ENVIRONMENT, DEFAULT_KEY_FILENAME
from pyms.constants import CRYPT_FILE_KEY_ENVIRONMENT, DEFAULT_KEY_FILENAME, CRYPT_FILE_KEY_ENVIRONMENT_LEGACY
from pyms.crypt.driver import CryptAbstract
from pyms.exceptions import FileDoesNotExistException
from pyms.utils.files import LoadFile


class Crypt(CryptAbstract):
def __init__(self, *args, **kwargs):
self._loader = LoadFile(kwargs.get("path"), CRYPT_FILE_KEY_ENVIRONMENT, DEFAULT_KEY_FILENAME)
# TODO Remove temporally backward compatibility on future versions
crypt_file_key_env = self.__get_updated_crypt_file_key_env() # Temporally backward compatibility
self._loader = LoadFile(kwargs.get("path"), crypt_file_key_env, DEFAULT_KEY_FILENAME)
super().__init__(*args, **kwargs)

def generate_key(self, password: Text, write_to_file: bool = False) -> bytes:
Expand All @@ -36,9 +38,11 @@ def generate_key(self, password: Text, write_to_file: bool = False) -> bytes:
def read_key(self):
key = self._loader.get_file()
if not key:
# TODO Remove temporally backward compatibility on future versions
crypt_file_key_env = self.__get_updated_crypt_file_key_env() # Temporally backward compatibility
raise FileDoesNotExistException(
"Decrypt key {} not exists. You must set a correct env var {} "
"or run `pyms crypt create-key` command".format(self._loader.path, CRYPT_FILE_KEY_ENVIRONMENT))
"or run `pyms crypt create-key` command".format(self._loader.path, crypt_file_key_env))
return key

def encrypt(self, message):
Expand All @@ -57,3 +61,10 @@ def decrypt(self, encrypted):

def delete_key(self):
os.remove(self._loader.get_path_from_env())

@staticmethod
def __get_updated_crypt_file_key_env() -> str:
result = CRYPT_FILE_KEY_ENVIRONMENT
if (os.getenv(CRYPT_FILE_KEY_ENVIRONMENT_LEGACY) is not None) and (os.getenv(CRYPT_FILE_KEY_ENVIRONMENT) is None):
result = CRYPT_FILE_KEY_ENVIRONMENT_LEGACY
return result
2 changes: 1 addition & 1 deletion pyms/flask/app/create_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def example():
app.run()
```
Environments variables of PyMS:
**CONFIGMAP_FILE**: The path to the configuration file. By default, PyMS search the configuration file in your
**PYMS_CONFIGMAP_FILE**: The path to the configuration file. By default, PyMS search the configuration file in your
actual folder with the name "config.yml"

## Create configuration
Expand Down
20 changes: 20 additions & 0 deletions pyms/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,19 @@
from pyms.exceptions import PackageNotExists


class Colors:
BLUE = "\033[34m"
GREEN = "\033[32m"
MAGENTA = "\033[35m"
RED = "\033[31m"
YELLOW = "\033[33m"
BRIGHT_BLUE = "\033[94m"
BRIGHT_GREEN = "\033[92m"
BRIGHT_MAGENTA = "\033[95m"
BRIGHT_RED = "\033[91m"
BRIGHT_YELLOW = "\033[93m"


def import_from(module: Text, name: Text):
module = __import__(module, fromlist=[name])
return getattr(module, name)
Expand All @@ -20,3 +33,10 @@ def check_package_exists(package_name: Text) -> Union[Exception, bool]:
raise PackageNotExists(
"{package} is not installed. try with pip install -U {package}".format(package=package_name))
return True


def colored_text(msg, color: Colors, bold=False):
result = "{}{}{}".format(color, msg, "\033[0m")
if bold:
result = "{}{}".format("\033[1m", result)
return result
2 changes: 1 addition & 1 deletion tests/test_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test_crypt_file_error(self):
cmd = Command(arguments=arguments, autorun=False)
with pytest.raises(FileDoesNotExistException) as excinfo:
cmd.run()
assert ("Decrypt key None not exists. You must set a correct env var KEY_FILE or run "
assert ("Decrypt key None not exists. You must set a correct env var PYMS_KEY_FILE or run "
"`pyms crypt create-key` command") \
in str(excinfo.value)

Expand Down
2 changes: 1 addition & 1 deletion tests/test_crypt.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def test_crypt_file_error(self):
crypt = CryptFernet()
with pytest.raises(FileDoesNotExistException) as excinfo:
crypt.read_key()
assert ("Decrypt key None not exists. You must set a correct env var KEY_FILE or run "
assert ("Decrypt key None not exists. You must set a correct env var PYMS_KEY_FILE or run "
"`pyms crypt create-key` command") \
in str(excinfo.value)

Expand Down