Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a008c28
feat: added basic consul register
avara1986 Nov 2, 2020
3088274
refactor!: init actions in services, not in create_app
avara1986 Nov 2, 2020
1f9b19d
fix: linters errors
avara1986 Nov 2, 2020
90c33bd
fix: removed assert to testing
avara1986 Nov 2, 2020
9012f38
fix: linters errors
avara1986 Nov 2, 2020
60d4227
feature: addec consulate fork
avara1986 Nov 9, 2020
2ff6c1a
fix: session reload out of scope
avara1986 Nov 9, 2020
bc1a543
feat: added Black and fix pylint errors of consul
avara1986 Nov 9, 2020
2d3fcd7
fix: removed python2 checks
avara1986 Nov 9, 2020
2b38505
fix: merge conflict, removed
avara1986 Nov 9, 2020
2d214e8
Merge remote-tracking branch 'origin/master' into feature/issue_190
avara1986 Nov 9, 2020
5f12cd2
feat: re-added pipfile lock
avara1986 Nov 9, 2020
7ca0d40
fix: flake8 errors
avara1986 Nov 9, 2020
0d9bd09
fix: bandit errors
avara1986 Nov 9, 2020
2fd7b63
feat: moved tests from consulate to pyms
avara1986 Nov 9, 2020
c96aed1
chore: increment coverage tests
avara1986 Nov 9, 2020
738dec3
fix: removed mock
avara1986 Nov 9, 2020
bbc1cde
fix: wait until docker is up
avara1986 Nov 9, 2020
fd89113
fix: assert raise depends of consul version
avara1986 Nov 9, 2020
fa17529
fix: tests, isort and linters
avara1986 Nov 9, 2020
6b06140
fix: bandit
avara1986 Nov 9, 2020
f947c45
fix: bandit
avara1986 Nov 9, 2020
d634a5b
fix: lint...
avara1986 Nov 9, 2020
00166e7
feat: moved consulate to py-ms-consulate package
avara1986 Nov 10, 2020
acbbf36
fix: removed unused code
avara1986 Nov 12, 2020
91c8e8f
docs: updated docstrings
avara1986 Nov 12, 2020
021b261
fix: updated example, added init in ServiceDiscoveryBase
avara1986 Nov 12, 2020
5ebfc24
tests: updated service discovery
avara1986 Nov 18, 2020
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
2 changes: 1 addition & 1 deletion .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 pyms --show-source --statistics --statistics
flake8 pyms --show-source --statistics
- name: Lint with pylint
run: |
pylint --rcfile=pylintrc pyms
Expand Down
10 changes: 10 additions & 0 deletions examples/microservice_service_discovery_consul/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pyms:
services:
service_discovery:
client: "consul"
host: "http://localhost:8500"
config:
DEBUG: true
TESTING: false
APP_NAME: "Python Microservice"
APPLICATION_ROOT: ""
15 changes: 15 additions & 0 deletions examples/microservice_service_discovery_consul/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from flask import jsonify

from pyms.flask.app import Microservice

ms = Microservice(path=__file__)
app = ms.create_app()


@app.route("/")
def example():
return jsonify({"main": "hello world"})


if __name__ == '__main__':
app.run()
4 changes: 4 additions & 0 deletions pyms/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,7 @@ class ConfigErrorException(Exception):

class PackageNotExists(Exception):
pass


class ServiceDiscoveryConnectionException(Exception):
pass
39 changes: 11 additions & 28 deletions pyms/flask/app/create_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
from pyms.constants import LOGGER_NAME, CONFIG_BASE
from pyms.crypt.driver import CryptResource
from pyms.flask.app.utils import SingletonMeta, ReverseProxied
from pyms.flask.healthcheck import healthcheck_blueprint
from pyms.flask.configreload import configreload_blueprint
from pyms.flask.healthcheck import healthcheck_blueprint
from pyms.flask.services.driver import ServicesResource, DriverService
from pyms.logger import CustomJsonFormatter
from pyms.utils import check_package_exists, import_from
from pyms.utils import check_package_exists

logger = logging.getLogger(LOGGER_NAME)

Expand Down Expand Up @@ -108,6 +108,14 @@ def init_services(self) -> None:
if service_name not in self.services or not getattr(self, service_name, False):
self.services.append(service_name)
setattr(self, service_name, service)
# if getattr(service, "init_action"):
# service.init_action(self)

def init_services_actions(self):
for service_name in self.services:
srv_action = getattr(getattr(self, service_name), "init_action")
if srv_action:
srv_action(self)

def init_crypt(self, *args, **kwargs) -> None:
"""
Expand Down Expand Up @@ -135,15 +143,6 @@ def init_libs(self) -> Flask:
"""
return self.application

def init_tracer(self) -> None:
"""Set attribute in flask `tracer`. See in `pyms.flask.services.tracer` how it works
:return: None
"""
if self.tracer:
FlaskTracing = import_from("flask_opentracing", "FlaskTracing")
client = self.tracer.get_client()
self.application.tracer = FlaskTracing(client, True, self.application)

def init_logger(self) -> None:
"""
Set a logger and return in JSON format.
Expand Down Expand Up @@ -185,18 +184,6 @@ def init_app(self) -> Flask:

return application

def init_metrics(self) -> None:
"""Set attribute in flask `metrics`. See in `pyms.flask.services.metrics` how it works
:return: None
"""
if self.metrics:
self.application.register_blueprint(self.metrics.metrics_blueprint)
self.metrics.add_logger_handler(
self.application.logger,
self.application.config["APP_NAME"]
)
self.metrics.monitor(self.application.config["APP_NAME"], self.application)

def reload_conf(self):
self.delete_services()
self.config.reload()
Expand All @@ -214,7 +201,6 @@ def create_app(self) -> Flask:
"""
self.application = self.init_app()
self.application.config.from_object(self.config.to_flask())
self.application.tracer = None
self.application.ms = self

# Initialize Blueprints
Expand All @@ -223,12 +209,9 @@ def create_app(self) -> Flask:

self.init_libs()
self.add_error_handlers()

self.init_tracer()

self.init_logger()

self.init_metrics()
self.init_services_actions()

logger.debug("Started app with PyMS and this services: {}".format(self.services))

Expand Down
2 changes: 2 additions & 0 deletions pyms/flask/services/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class DriverService(ConfigResource):
"""
enabled = True

init_action = False

def __init__(self, *args, **kwargs):
self.config_resource = get_service_name(service=self.config_resource)
super().__init__(*args, **kwargs)
Expand Down
14 changes: 10 additions & 4 deletions pyms/flask/services/metrics.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import time
import logging

from typing import Text
import time

from flask import Blueprint, Response, request, Flask
from prometheus_client import multiprocess, Counter, Histogram, generate_latest, CollectorRegistry, REGISTRY

from pyms.flask.services.driver import DriverService

# Based on https://github.com/sbarratt/flask-prometheus
Expand Down Expand Up @@ -47,14 +46,21 @@ class Service(DriverService):
"""
Adds [Prometheus](https://prometheus.io/) metrics using the [Prometheus Client Library](https://github.com/prometheus/client_python).
"""
config_resource: Text = "metrics"

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.metrics_blueprint = Blueprint("metrics", __name__)
self.init_registry()
self.serve_metrics()

def init_action(self, microservice_instance):
microservice_instance.application.register_blueprint(microservice_instance.metrics.metrics_blueprint)
self.add_logger_handler(
microservice_instance.application.logger,
microservice_instance.application.config["APP_NAME"]
)
self.monitor(microservice_instance.application.config["APP_NAME"], microservice_instance.application)

def init_registry(self) -> None:
try:
multiprocess_registry = CollectorRegistry()
Expand Down
82 changes: 82 additions & 0 deletions pyms/flask/services/service_discovery.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import json
import logging
import uuid

import requests
from requests.exceptions import RequestException
from pyms.constants import LOGGER_NAME
from pyms.exceptions import ServiceDiscoveryConnectionException
from pyms.flask.services.driver import DriverService

logger = logging.getLogger(LOGGER_NAME)

CONSUL_SERVICE_DISCOVERY = "consul"

DEFAULT_SERVICE_DISCOVERY = CONSUL_SERVICE_DISCOVERY


class ServiceDiscoveryBase:
def register_service(self, id_app, host, port, healtcheck_url, app_name):
pass


class ServiceDiscoveryConsul(ServiceDiscoveryBase):
def register_service(self, id_app, host, port, healtcheck_url, app_name):
headers = {
'Content-Type': 'application/json; charset=utf-8'
}
data = {
"id": app_name + "-" + id_app,
"name": app_name,
"port": port,
"check": {
"name": "ping check",
"http": healtcheck_url,
"interval": "30s",
"status": "passing"
}
}
error = False
msg_error = "Failed to establish a new connection"
try:
response = requests.put("{host}/v1/agent/service/register".format(host=host), data=json.dumps(data),
headers=headers)
if response.status_code != 200:
msg_error = response.content
error = True
except RequestException:
error = True

if error:
raise ServiceDiscoveryConnectionException("Host %s raise an error: %s" % (host, msg_error))


class Service(DriverService):
id_app = str(uuid.uuid1())
config_resource = "service_discovery"
default_values = {
"client": DEFAULT_SERVICE_DISCOVERY,
"host": "http://localhost:8500",
"port": 5000,
"healtcheck_url": "http://127.0.0.1.nip.io:5000/healthcheck",
}

def init_action(self, microservice_instance):
self.client.register_service(
id_app=self.id_app,
host=self.host,
healtcheck_url=self.healtcheck_url,
port=self.port,
app_name=microservice_instance.application.config["APP_NAME"])

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.client = self.get_client()

def get_client(self) -> ServiceDiscoveryBase:
client = False
if self.config.client == CONSUL_SERVICE_DISCOVERY:
client = ServiceDiscoveryConsul()

logger.debug("Init %s as service discovery", client)
return client
5 changes: 5 additions & 0 deletions pyms/flask/services/tracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ class Service(DriverService):
"client": DEFAULT_CLIENT,
}

def init_action(self, microservice_instance):
FlaskTracing = import_from("flask_opentracing", "FlaskTracing")
client = self.get_client()
microservice_instance.application.tracer = FlaskTracing(client, True, microservice_instance.application)

def get_client(self) -> Union[bool, type]:
opentracing_tracer = False
if self.config.client == JAEGER_CLIENT:
Expand Down