# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['eventsourcing_grpc', 'eventsourcing_grpc.protos']

package_data = \
{'': ['*']}

install_requires = \
['eventsourcing>=9.2.11,<9.3.0',
 'grpcio>=1.44.0,<1.45.0',
 'protobuf>=3.20.0,<3.21.0']

entry_points = \
{'console_scripts': ['eventsourcing_grpc_runner = '
                     'eventsourcing_grpc.console:eventsourcing_grpc_runner',
                     'eventsourcing_grpc_server = '
                     'eventsourcing_grpc.console:eventsourcing_grpc_server']}

setup_kwargs = {
    'name': 'eventsourcing-grpc',
    'version': '0.1.2',
    'description': 'Python package for eventsourcing with gRPC',
    'long_description': '# Event Sourcing with gRPC\n\nThis is an extension for [the Python eventsourcing library](https://github.com/pyeventsourcing/eventsourcing).\n\nThis package provides a gRPC server class that runs event-sourced applications\nin a gRPC server. A gRPC client class can be used to interact with the application\nserver. Application command and query methods can be called, and\nnotifications selected, remotely using an application proxy object\nprovided by the client.\n\nThis package also provides a system runner class, with the same interface as the Runner\nclasses in the core eventsourcing library. It can run a system of event-sourced\napplications as a set of gRPC servers. Leader and follower connections, for prompting\nand pulling event notifications, are implemented with gRPC clients.\n\nThis package provides console commands that can be used to start servers and run systems\nfrom a command line interface.\n\nThis package also provides an example of running an event-sourced application in\ngRPC server in a Docker container.\n\nThis package also provides an example of running a system of applications\nin gRPC servers in Docker containers with docker-compose.\n\nThis package also provides an example of running a system of applications\nin gRPC servers in Docker containers with Kubernetes, with clients and\nservers authenticated with SSL/TLS PKI certificates.\n\n## Installation\n\nUse pip to install the [stable distribution](https://pypi.org/project/eventsourcing-grpc/)\nfrom the Python Package Index.\n\n    $ pip install eventsourcing_grpc\n\nIt is recommended to install Python packages into a Python virtual environment.\n\n\n## gRPC clients and servers\n\nThe `GrpcApplicationServer` class can be used to run an event-sourced\napplication as a gRPC server, and the `GrpcApplicationClient` class can\nbe used to connect to a server.\n\nDefine an event-sourced application.\n\n```python\nfrom eventsourcing_grpc.example import Orders\n```\n\nConfigure with environment variables.\n\n```python\nimport os\n\nos.environ["ORDERS_GRPC_SERVER_ADDRESS"] = "localhost:50051"\n```\n\nApplication persistence can be configured in the usual way (see core library docs).\n\nUse the `start_server()` function to create and start a gRPC application server.\n\n```python\nfrom eventsourcing_grpc.application_server import start_server\n\nserver = start_server(Orders)\n```\n\nUse the `connect()` function to create a gRPC client and connect to the server.\n\n```python\nfrom eventsourcing_grpc.application_client import connect\n\nclient = connect(Orders, max_attempts=10)\n```\n\nUse the application proxy to call command and query methods on the application server.\n\n```python\n# Create order.\norder_id = client.app.create_new_order()\n\n# Get order.\norder = client.app.get_order(order_id)\nassert order["id"] == order_id\nassert order["is_reserved"] == False\nassert order["is_paid"] == False\n```\n\nUse the application proxy to select event notifications.\n\n```python\nnotifications = client.app.notification_log.select(start=1, limit=10)\nassert len(notifications) == 1\nassert notifications[0].id == 1\nassert notifications[0].originator_id == order_id\nassert notifications[0].originator_version == 1\nassert notifications[0].topic == "eventsourcing_grpc.example:Order.Created"\n```\n\nClose client and stop server.\n\n```python\nclient.close()\nserver.stop(wait=True)\n```\n\n\n## gRPC runner\n\nThe `GrpcRunner` class can be used to run a system of event-sourced applications\nas an inter-connected set of gRPC servers.\n\n```python\nfrom eventsourcing_grpc.runner import GrpcRunner\n```\n\nDefine a system of event-sourced applications.\n\n```python\nfrom eventsourcing_grpc.example import system\n```\n\nConfigure with environment variables.\n\n```python\nimport os\n\nos.environ["ORDERS_GRPC_SERVER_ADDRESS"] = "localhost:50051"\nos.environ["RESERVATIONS_GRPC_SERVER_ADDRESS"] = "localhost:50052"\nos.environ["PAYMENTS_GRPC_SERVER_ADDRESS"] = "localhost:50053"\n```\n\nCreate and start a gRPC system runner (starts servers and connects clients).\n\n```python\n\nrunner = GrpcRunner(system)\nrunner.start(with_subprocesses=True)\n```\n\nGet an application proxy from the runner.\n\n```python\napp = runner.get(Orders)\n```\n\nCall command and query methods on application proxy.\n\n```python\norder1_id = app.create_new_order()\n\n# Wait for the processing to happen.\nfrom time import sleep\n\nfor _ in range(100):\n    sleep(0.1)\n    if app.is_order_paid(order1_id):\n        break\n    elif runner.has_errored.is_set():\n        raise AssertionError("Runner error")\nelse:\n    raise AssertionError("Timeout waiting for order to be paid")\n\n```\n\nGet application\'s event notifications.\n\n```python\nnotifications = app.notification_log.select(start=1, limit=10)\nassert len(notifications) == 3\nassert notifications[0].id == 1\nassert notifications[0].originator_id == order1_id\nassert notifications[0].originator_version == 1\nassert notifications[0].topic == "eventsourcing_grpc.example:Order.Created"\nassert notifications[1].id == 2\nassert notifications[1].originator_id == order1_id\nassert notifications[1].originator_version == 2\nassert notifications[1].topic == "eventsourcing_grpc.example:Order.Reserved"\nassert notifications[2].id == 3\nassert notifications[2].originator_id == order1_id\nassert notifications[2].originator_version == 3\nassert notifications[2].topic == "eventsourcing_grpc.example:Order.Paid"\n\n```\n\nStop runner (closes clients and stops servers).\n\n```python\nrunner.stop()\n```\n\n## Start server from command line\n\nThe console command `eventsourcing_grpc_server` can be used to start\na gRPC application server. The application topic can be given as a\ncommand line argument.\n\n    $ export ORDERS_GRPC_SERVER_ADDRESS=localhost:50051\n    $ eventsourcing_grpc_server eventsourcing_grpc.example:Orders\n\nA system of interconnected application servers can be started in this way,\nafter setting `SYSTEM_TOPIC` in the environment.\n\n    $ export SYSTEM_TOPIC=eventsourcing_grpc.example:system\n    $ export ORDERS_GRPC_SERVER_ADDRESS=localhost:50051\n    $ export RESERVATIONS_GRPC_SERVER_ADDRESS=localhost:50052\n    $ export PAYMENTS_GRPC_SERVER_ADDRESS=localhost:50053\n\n    $ eventsourcing_grpc_server eventsourcing_grpc.example:Orders &\n    $ eventsourcing_grpc_server eventsourcing_grpc.example:Reservations &\n    $ eventsourcing_grpc_server eventsourcing_grpc.example:Payments &\n\nIn the example above, the servers are run as background processes.\n\n## Start runner from command line\n\nThe console command `eventsourcing_grpc_runner` can be used to run a system\nof event-sourced applications. Only the system topic must be configured.\n\nThe system topic can be given as a command line argument.\n\n    $ eventsourcing_grpc_runner eventsourcing_grpc.example:system\n\nAlternatively, the system topic can be set using the `SYSTEM_TOPIC` environment variable.\n\n    $ export SYSTEM_TOPIC=eventsourcing_grpc.example:system\n    $ eventsourcing_grpc_runner\n\nThe server addresses will be automatically configured, unless they are set as\nenvironment variables.\n\n## Docker container\n\nA Docker file can be defined to run the `eventsourcing_grpc_server` command\nmentioned above, after installing an event-sourced system and the `eventsourcing_grpc`\npackage.\n\n```dockerfile\nFROM python:3.10\n\nWORKDIR /app\n\n# Install package(s).\nRUN pip install eventsourcing_grpc\n\n# Run application server.\nENV PYTHONUNBUFFERED = 1\nCMD ["eventsourcing_grpc_server"]\n```\n\nA container image can then be built with Docker.\n\n    $ docker build -t eventsourcing-grpc -f Dockerfile .\n\n## Docker Compose\n\nA system of gRPC application servers can be run with Docker Compose.\nThe following Docker Compose file assumes SSL/TLS PKI certificates\nhave been generated in a local `./ssl` folder.\n\n```\nversion: \'2\'\n\nservices:\n  orders:\n    image: "eventsourcing-grpc:v1"\n    environment:\n      APPLICATION_TOPIC: "eventsourcing_grpc.example:Orders"\n      SYSTEM_TOPIC: "eventsourcing_grpc.example:system"\n      ORDERS_GRPC_SERVER_ADDRESS: "orders:50051"\n      RESERVATIONS_GRPC_SERVER_ADDRESS: "reservations:50052"\n      PAYMENTS_GRPC_SERVER_ADDRESS: "payments:50053"\n      MAX_PULL_INTERVAL: "10"\n      GRPC_SSL_PRIVATE_KEY_PATH: /app/ssl/orders.key\n      GRPC_SSL_CERTIFICATE_PATH: /app/ssl/orders.crt\n      GRPC_SSL_ROOT_CERTIFICATE_PATH: /app/ssl/root.crt\n    volumes:\n      - ./ssl/orders:/app/ssl\n    ports:\n      - "50051:50051"\n\n  reservations:\n    image: "eventsourcing-grpc:v1"\n    environment:\n      APPLICATION_TOPIC: "eventsourcing_grpc.example:Reservations"\n      SYSTEM_TOPIC: "eventsourcing_grpc.example:system"\n      ORDERS_GRPC_SERVER_ADDRESS: "orders:50051"\n      RESERVATIONS_GRPC_SERVER_ADDRESS: "reservations:50052"\n      PAYMENTS_GRPC_SERVER_ADDRESS: "payments:50053"\n      MAX_PULL_INTERVAL: "10"\n      GRPC_SSL_PRIVATE_KEY_PATH: /app/ssl/reservations.key\n      GRPC_SSL_CERTIFICATE_PATH: /app/ssl/reservations.crt\n      GRPC_SSL_ROOT_CERTIFICATE_PATH: /app/ssl/root.crt\n    volumes:\n      - ./ssl/reservations:/app/ssl\n    ports:\n      - "50052:50052"\n\n  payments:\n    image: "eventsourcing-grpc:v1"\n    environment:\n      APPLICATION_TOPIC: "eventsourcing_grpc.example:Payments"\n      SYSTEM_TOPIC: "eventsourcing_grpc.example:system"\n      ORDERS_GRPC_SERVER_ADDRESS: "orders:50051"\n      RESERVATIONS_GRPC_SERVER_ADDRESS: "reservations:50052"\n      PAYMENTS_GRPC_SERVER_ADDRESS: "payments:50053"\n      MAX_PULL_INTERVAL: "10"\n      GRPC_SSL_PRIVATE_KEY_PATH: /app/ssl/payments.key\n      GRPC_SSL_CERTIFICATE_PATH: /app/ssl/payments.crt\n      GRPC_SSL_ROOT_CERTIFICATE_PATH: /app/ssl/root.crt\n    volumes:\n      - ./ssl/payments:/app/ssl\n    ports:\n      - "50053:50053"\n```\n\nThe containers can then be started.\n\n    $ docker-compose up -d\n\n## Kubernetes\n\nA system of gRPC application servers can be run with Kubernetes.\n\nCreate Kubernetes Secrets from the SSL/TLS PKI certificates.\n\n\t$ kubectl create secret generic root-ssl-secret \\\n    --from-file=root.crt=ssl/root/root.crt\n\n\t$ kubectl create secret tls orders-ssl-secret \\\n\t--key ./ssl/orders/orders.key \\\n    --cert ./ssl/orders/orders.crt\n\n\t$ kubectl create secret tls reservations-ssl-secret \\\n\t--key ./ssl/reservations/reservations.key \\\n    --cert ./ssl/reservations/reservations.crt\n\n\t$ kubectl create secret tls payments-ssl-secret \\\n\t--key ./ssl/payments/payments.key \\\n    --cert ./ssl/payments/payments.crt\n\n\nCreate a Kubernetes Deployment for each application.\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: orders\n  labels:\n    system: orders\nspec:\n  selector:\n    matchLabels:\n      app: orders\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        app: orders\n    spec:\n      volumes:\n      - name: ssl-secret-volume\n        secret:\n          secretName: orders-ssl-secret\n      - name: root-ssl-secret-volume\n        secret:\n          secretName: root-ssl-secret\n      containers:\n      - name: orders\n        image: "eventsourcing-grpc:latest"\n        imagePullPolicy: Never\n        ports:\n        - containerPort: 50051\n        volumeMounts:\n          - mountPath: /app/ssl\n            name: ssl-secret-volume\n          - mountPath: /app/root-ssl\n            name: root-ssl-secret-volume\n        env:\n          - name: APPLICATION_TOPIC\n            value: "eventsourcing_grpc.example:Orders"\n          - name: SYSTEM_TOPIC\n            value: "eventsourcing_grpc.example:system"\n          - name: ORDERS_GRPC_SERVER_ADDRESS\n            value: "0.0.0.0:50051"\n          - name: RESERVATIONS_GRPC_SERVER_ADDRESS\n            value: "reservations:50052"\n          - name: PAYMENTS_GRPC_SERVER_ADDRESS\n            value: "payments:50053"\n          - name: GRPC_SSL_PRIVATE_KEY_PATH\n            value: /app/ssl/tls.key\n          - name: GRPC_SSL_CERTIFICATE_PATH\n            value: /app/ssl/tls.crt\n          - name: GRPC_SSL_ROOT_CERTIFICATE_PATH\n            value: /app/root-ssl/root.crt\n```\n\nCreate a Kubernetes Service for each Deployment.\n\n```yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: orders\n  labels:\n    system: orders\nspec:\n  selector:\n    app: orders\n  type: LoadBalancer\n  ports:\n  - port: 50051\n    targetPort: 50051\n    protocol: TCP\n```\n\nThe above configuration will run each application on a separate Pod. Alternatively,\nyou can run all the applications in the same Pod, by defining a multi-container\nDeployment.\n\nPlease refer to the project repo (and its GitHub actions workflow and Makefile)\nto see a fully working example of running a system of applications with minikube.\n\n## Developers\n\nAfter cloning the eventsourcing-grpc repository, set up a virtual\nenvironment and install dependencies by running the following command in the\nroot folder.\n\n    $ make install\n\nThe ``make install`` command uses the build tool Poetry to create a dedicated\nPython virtual environment for this project, and installs popular development\ndependencies such as Black, isort and pytest.\n\nAdd tests in `./tests`. Add code in `./eventsourcing_grpc`.\n\nGenerate SSL/TLS certificates and private keys for testing.\n\n    $ make ssl\n\nRun tests.\n\n    $ make test\n\nCheck the formatting of the code.\n\n    $ make lint\n\nReformat the code.\n\n    $ make fmt\n\nAdd dependencies in `pyproject.toml` and then update installed packages.\n\n    $ make update-packages\n\nIf you edit the .proto files, regenerate the project\'s protos package.\n\n    $ make grpc-stubs\n',
    'author': 'John Bywater',
    'author_email': 'john.bywater@appropriatesoftware.net',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/pyeventsourcing/eventsourcing-grpc',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'entry_points': entry_points,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
