Skip to content

Commit 6cf7e0e

Browse files
authored
Support registration in Consul and created a driver to connect other Services Discovery (#221)
* feat: added basic consul register * refactor!: init actions in services, not in create_app * chore: increment coverage tests * feat: Added py-ms-consulate package, a fork from https://github.com/gmr/consulate * tests: updated service discovery
1 parent 1fa9d99 commit 6cf7e0e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1034
-675
lines changed

.github/workflows/python-package.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
- name: Lint with flake8
3737
run: |
3838
# stop the build if there are Python syntax errors or undefined names
39-
flake8 pyms --show-source --statistics --statistics
39+
flake8 pyms --show-source --statistics
4040
- name: Lint with pylint
4141
run: |
4242
pylint --rcfile=pylintrc pyms

.pre-commit-config.yaml

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
repos:
2+
- repo: https://github.com/ambv/black
3+
rev: stable
4+
hooks:
5+
- id: black
6+
files: .
7+

MANIFEST.in

+1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ include requirements-tests.txt
44
recursive-include pyms *
55
recursive-exclude tests *
66
recursive-exclude examples *
7+
recursive-exclude docker *
78
prune tests
89
prune examples

Pipfile

+5-2
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ url = "https://pypi.org/simple"
44
verify_ssl = true
55

66
[dev-packages]
7-
py-ms = {editable = true,extras = ["tests"], path = "."}
7+
py-ms = {editable = true,extras = ["tests"],path = "."}
88

99
[packages]
10-
py-ms = {editable = true,extras = ["all"], path = "."}
10+
py-ms = {editable = true,extras = ["all"],path = "."}
11+
12+
[pipenv]
13+
allow_prereleases = true

Pipfile.lock

+200-186
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from examples.microservice_configuration import ms
2+
23
app = ms.create_app()
34

4-
if __name__ == '__main__':
5+
if __name__ == "__main__":
56
app.run()

examples/microservice_configuration/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@ def example():
99
"GLOBAL_VARIABLE": GLOBAL_VARIABLE,
1010
"GLOBAL_VARIABLE2": GLOBAL_VARIABLE2,
1111
"test1": config().test1,
12-
"test2": config().test2
12+
"test2": config().test2,
1313
}

examples/microservice_crypt_aws_kms/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ def example():
1111
return jsonify({"main": app.ms.config.encrypted_key})
1212

1313

14-
if __name__ == '__main__':
14+
if __name__ == "__main__":
1515
app.run()

examples/microservice_distribued_tracing/ms1/main.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from flask import jsonify, current_app, request
1+
from flask import current_app, jsonify, request
22

33
from pyms.flask.app import Microservice
44

@@ -12,5 +12,5 @@ def index():
1212
return jsonify({"main": "hello world {}".format(current_app.config["APP_NAME"])})
1313

1414

15-
if __name__ == '__main__':
15+
if __name__ == "__main__":
1616
app.run()

examples/microservice_distribued_tracing/ms2/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ def index():
1313
return jsonify({"response": response.json()})
1414

1515

16-
if __name__ == '__main__':
16+
if __name__ == "__main__":
1717
app.run(port=5001)
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from pyms.flask.app import Microservice
22

3-
ms = Microservice()
3+
ms = Microservice()

examples/microservice_metrics/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
app = ms.create_app()
44

5-
if __name__ == '__main__':
5+
if __name__ == "__main__":
66
app.run()

examples/microservice_metrics/views.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ def example():
77
current_app.logger.info("start request")
88
result = ms.requests.get_for_object("https://ghibliapi.herokuapp.com/films/2baf70d1-42bb-4437-b551-e5fed5a87abe")
99
current_app.logger.info("end request")
10-
return result
10+
return result

examples/microservice_requests/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
app = ms.create_app()
44

5-
if __name__ == '__main__':
5+
if __name__ == "__main__":
66
app.run()

examples/microservice_service_discovery/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pyms:
2+
services:
3+
service_discovery:
4+
service: "examples.microservice_service_discovery.service.ServiceDiscoveryConsulBasic"
5+
host: "localhost"
6+
autoregister: true
7+
config:
8+
DEBUG: true
9+
TESTING: false
10+
APP_NAME: "Python Microservice My personal Service Discovery"
11+
APPLICATION_ROOT: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from flask import jsonify
2+
3+
from pyms.flask.app import Microservice
4+
5+
ms = Microservice(path=__file__)
6+
app = ms.create_app()
7+
8+
9+
@app.route("/")
10+
def example():
11+
return jsonify({"main": "hello world"})
12+
13+
14+
if __name__ == "__main__":
15+
app.run()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import json
2+
import uuid
3+
4+
import requests
5+
6+
from pyms.flask.services.service_discovery import ServiceDiscoveryBase
7+
8+
9+
class ServiceDiscoveryConsulBasic(ServiceDiscoveryBase):
10+
id_app = str(uuid.uuid1())
11+
12+
def __init__(self, config):
13+
super().__init__(config)
14+
self.host = config.host
15+
self.port = config.port
16+
17+
def register_service(self, *args, **kwargs):
18+
app_name = kwargs["app_name"]
19+
healtcheck_url = kwargs["healtcheck_url"]
20+
interval = kwargs["interval"]
21+
headers = {"Content-Type": "application/json; charset=utf-8"}
22+
data = {
23+
"id": app_name + "-" + self.id_app,
24+
"name": app_name,
25+
"check": {"name": "ping check", "http": healtcheck_url, "interval": interval, "status": "passing"},
26+
}
27+
response = requests.put(
28+
"http://{host}:{port}/v1/agent/service/register".format(host=self.host, port=self.port),
29+
data=json.dumps(data),
30+
headers=headers,
31+
)
32+
if response.status_code != 200:
33+
raise Exception(response.content)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
pyms:
2+
services:
3+
service_discovery:
4+
service: "consul"
5+
host: "localhost"
6+
autoregister: true
7+
config:
8+
DEBUG: true
9+
TESTING: false
10+
APP_NAME: "Python Microservice"
11+
APPLICATION_ROOT: ""
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from flask import jsonify
2+
3+
from pyms.flask.app import Microservice
4+
5+
ms = Microservice(path=__file__)
6+
app = ms.create_app()
7+
8+
9+
@app.route("/")
10+
def example():
11+
checks = ms.service_discovery.client.agent.checks()
12+
return jsonify({"main": checks})
13+
14+
15+
if __name__ == "__main__":
16+
app.run()

examples/microservice_swagger/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@
33
ms = Microservice()
44
app = ms.create_app()
55

6-
if __name__ == '__main__':
6+
if __name__ == "__main__":
77
app.run()

examples/microservice_tracer/main.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from examples.microservice_requests import ms
2+
23
app = ms.create_app()
34

4-
if __name__ == '__main__':
5+
if __name__ == "__main__":
56
app.run()

examples/mininum_microservice/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ def example():
1111
return jsonify({"main": "hello world"})
1212

1313

14-
if __name__ == '__main__':
14+
if __name__ == "__main__":
1515
app.run()

examples/mininum_microservice_docker/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,5 @@ def example():
1111
return jsonify({"main": ms.config.environment})
1212

1313

14-
if __name__ == '__main__':
14+
if __name__ == "__main__":
1515
app.run()

pyms/cloud/aws/kms.py

+5-7
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def __init__(self, *args, **kwargs) -> None:
1212
self._init_boto()
1313
super().__init__(*args, **kwargs)
1414

15-
def encrypt(self, message: str) -> str: # pragma: no cover
15+
def encrypt(self, message: str) -> str: # pragma: no cover
1616
ciphertext = self.client.encrypt(
1717
KeyId=self.config.key_id,
1818
Plaintext=bytes(message, encoding="UTF-8"),
@@ -22,16 +22,14 @@ def encrypt(self, message: str) -> str: # pragma: no cover
2222
def _init_boto(self) -> None: # pragma: no cover
2323
check_package_exists("boto3")
2424
boto3 = import_package("boto3")
25-
boto3.set_stream_logger(name='botocore')
26-
self.client = boto3.client('kms')
25+
boto3.set_stream_logger(name="botocore")
26+
self.client = boto3.client("kms")
2727

2828
def _aws_decrypt(self, blob_text: bytes) -> str: # pragma: no cover
2929
response = self.client.decrypt(
30-
CiphertextBlob=blob_text,
31-
KeyId=self.config.key_id,
32-
EncryptionAlgorithm=self.encryption_algorithm
30+
CiphertextBlob=blob_text, KeyId=self.config.key_id, EncryptionAlgorithm=self.encryption_algorithm
3331
)
34-
return str(response['Plaintext'], encoding="UTF-8")
32+
return str(response["Plaintext"], encoding="UTF-8")
3533

3634
def _parse_encrypted(self, encrypted: str) -> bytes:
3735
blob_text = base64.b64decode(encrypted)

pyms/cmd/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
from .main import Command
22

3-
__all__ = ['Command']
3+
__all__ = ["Command"]

pyms/cmd/main.py

+34-30
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
#!/usr/bin/env python
2-
# -*- coding: utf-8 -*-
3-
from __future__ import unicode_literals, print_function
2+
from __future__ import print_function, unicode_literals
43

54
import argparse
65
import os
76
import sys
87
from distutils.util import strtobool
98

9+
from pyms.config import create_conf_file
1010
from pyms.crypt.fernet import Crypt
1111
from pyms.flask.services.swagger import merge_swagger_file
1212
from pyms.utils import check_package_exists, import_from, utils
13-
from pyms.config import create_conf_file
1413

1514

1615
class Command:
@@ -27,37 +26,40 @@ def __init__(self, *args, **kwargs):
2726
if not arguments: # pragma: no cover
2827
arguments = sys.argv[1:]
2928

30-
parser = argparse.ArgumentParser(description='Python Microservices')
29+
parser = argparse.ArgumentParser(description="Python Microservices")
3130

32-
commands = parser.add_subparsers(title="Commands", description='Available commands', dest='command_name')
31+
commands = parser.add_subparsers(title="Commands", description="Available commands", dest="command_name")
3332

34-
parser_encrypt = commands.add_parser('encrypt', help='Encrypt a string')
35-
parser_encrypt.add_argument("encrypt", default='', type=str, help='Encrypt a string')
33+
parser_encrypt = commands.add_parser("encrypt", help="Encrypt a string")
34+
parser_encrypt.add_argument("encrypt", default="", type=str, help="Encrypt a string")
3635

37-
parser_create_key = commands.add_parser('create-key', help='Generate a Key to encrypt strings in config')
38-
parser_create_key.add_argument("create_key", action='store_true',
39-
help='Generate a Key to encrypt strings in config')
36+
parser_create_key = commands.add_parser("create-key", help="Generate a Key to encrypt strings in config")
37+
parser_create_key.add_argument(
38+
"create_key", action="store_true", help="Generate a Key to encrypt strings in config"
39+
)
4040

4141
parser_startproject = commands.add_parser(
42-
'startproject',
43-
help='Generate a project from https://github.com/python-microservices/microservices-template')
42+
"startproject",
43+
help="Generate a project from https://github.com/python-microservices/microservices-template",
44+
)
4445
parser_startproject.add_argument(
45-
"startproject", action='store_true',
46-
help='Generate a project from https://github.com/python-microservices/microservices-template')
46+
"startproject",
47+
action="store_true",
48+
help="Generate a project from https://github.com/python-microservices/microservices-template",
49+
)
4750

4851
parser_startproject.add_argument(
49-
"-b", "--branch",
50-
help='Select a branch from https://github.com/python-microservices/microservices-template')
52+
"-b", "--branch", help="Select a branch from https://github.com/python-microservices/microservices-template"
53+
)
5154

52-
parser_merge_swagger = commands.add_parser('merge-swagger', help='Merge swagger into a single file')
53-
parser_merge_swagger.add_argument("merge_swagger", action='store_true',
54-
help='Merge swagger into a single file')
55+
parser_merge_swagger = commands.add_parser("merge-swagger", help="Merge swagger into a single file")
56+
parser_merge_swagger.add_argument("merge_swagger", action="store_true", help="Merge swagger into a single file")
5557
parser_merge_swagger.add_argument(
56-
"-f", "--file", default=os.path.join('project', 'swagger', 'swagger.yaml'),
57-
help='Swagger file path')
58+
"-f", "--file", default=os.path.join("project", "swagger", "swagger.yaml"), help="Swagger file path"
59+
)
5860

59-
parser_create_config = commands.add_parser('create-config', help='Generate a config file')
60-
parser_create_config.add_argument("create_config", action='store_true', help='Generate a config file')
61+
parser_create_config = commands.add_parser("create-config", help="Generate a config file")
62+
parser_create_config.add_argument("create_config", action="store_true", help="Generate a config file")
6163

6264
parser.add_argument("-v", "--verbose", default="", type=str, help="Verbose ")
6365

@@ -100,10 +102,10 @@ def run(self):
100102
crypt = Crypt()
101103
if self.create_key:
102104
path = crypt._loader.get_path_from_env() # pylint: disable=protected-access
103-
pwd = self.get_input('Type a password to generate the key file: ')
105+
pwd = self.get_input("Type a password to generate the key file: ")
104106
# Should use yes_no_input insted of get input below
105107
# the result should be validated for Yes (Y|y) rather allowing anything other than 'n'
106-
generate_file = self.get_input('Do you want to generate a file in {}? [Y/n]'.format(path))
108+
generate_file = self.get_input("Do you want to generate a file in {}? [Y/n]".format(path))
107109
generate_file = generate_file.lower() != "n"
108110
key = crypt.generate_key(pwd, generate_file)
109111
if generate_file:
@@ -118,7 +120,7 @@ def run(self):
118120
if self.startproject:
119121
check_package_exists("cookiecutter")
120122
cookiecutter = import_from("cookiecutter.main", "cookiecutter")
121-
cookiecutter('gh:python-microservices/cookiecutter-pyms', checkout=self.branch)
123+
cookiecutter("gh:python-microservices/cookiecutter-pyms", checkout=self.branch)
122124
self.print_ok("Created project OK")
123125
if self.merge_swagger:
124126
try:
@@ -128,8 +130,8 @@ def run(self):
128130
self.print_error(ex.__str__())
129131
return False
130132
if self.create_config:
131-
use_requests = self.yes_no_input('Do you want to use request')
132-
use_swagger = self.yes_no_input('Do you want to use swagger')
133+
use_requests = self.yes_no_input("Do you want to use request")
134+
use_swagger = self.yes_no_input("Do you want to use swagger")
133135
try:
134136
conf_file_path = create_conf_file(use_requests, use_swagger)
135137
self.print_ok(f'Config file "{conf_file_path}" created')
@@ -140,7 +142,9 @@ def run(self):
140142
return True
141143

142144
def yes_no_input(self, msg=""): # pragma: no cover
143-
answer = input(utils.colored_text(f'{msg}{"?" if not msg.endswith("?") else ""} [Y/n] :', utils.Colors.BLUE, True)) # nosec
145+
answer = input( # nosec
146+
utils.colored_text(f'{msg}{"?" if not msg.endswith("?") else ""} [Y/n] :', utils.Colors.BLUE, True)
147+
)
144148
try:
145149
return strtobool(answer)
146150
except ValueError:
@@ -168,6 +172,6 @@ def exit_ok(self, msg=""): # pragma: no cover
168172
sys.exit(0)
169173

170174

171-
if __name__ == '__main__': # pragma: no cover
175+
if __name__ == "__main__": # pragma: no cover
172176
cmd = Command(arguments=sys.argv[1:], autorun=False)
173177
cmd.run()

0 commit comments

Comments
 (0)