elixir-cloud-aai / foca

Opinionated Flask microservice archetype for quick OpenAPI-based microservice development
Apache License 2.0
16 stars 12 forks source link

Using FOCA without Docker #209

Open paras41617 opened 8 months ago

paras41617 commented 8 months ago

Problem

When trying to use FOCA without using docker container, the below error is arising. image (1)

Steps to recreate

1) create a virtualenv using virtualenv venv and activate it using venv\scripts\activate. 2) Install foca package using pip install foca. 3) Create app.py file and add the following code in it.

    from foca import Foca
    if __name__ == '__main__':
    foca = Foca(
        config_file="config.yaml"
    )
    app = foca.create_app()
    app.run()

4) create a config.yaml file and add the following code in it.

    server:
    host: '0.0.0.0'
    port: 8080
    debug: True
    environment: development
    testing: False
    use_reloader: True```

5) Run python app.py

Workaround

1) Create a requirements.txt file in the folder where your app.py is present. 2) Copy all the packages from this file and paste it into your requirements.txt file (just copy all and paste). 3) Run python install -r requirements.txt. 4) Now start again python app.py and it will work.

uniqueg commented 6 months ago

Thanks for the detailed report, @paras41617 :pray: We need to do some maintenance work first because currently our tests are failing on the head commit. We will look into it afterwards.

uniqueg commented 6 months ago

Currently blocked by #188

JaeAeich commented 6 months ago

TL;DR: relative path causing issues, check this fix.

@uniqueg

The issue (well not really an issue), is that configs use relative paths.

api
  - app.py
  - models.py
  - ...
  - ...
  - config.yaml

Lets say you have the above str, if you run app.py from inside the api dir and outside with `python3 api/app.py both will yield diff results, as most probably config written wrt $(pwd)/api/ (because we decided to pu config in api dir).

All you need to do is be consistent with how you envoke app.py in Dockerfile and in local machine.

There could be a 2 better way to deal with this:

# config.yaml
database:
  host: {% if environment == "prod" %}prod.db.example.com{% else %}dev.db.example.com{% endif %}
  username: {% if environment == "prod" %}prod_user{% else %}dev_user{% endif %}
  password: {% if environment == "prod" %}prod_password{% else %}dev_password{% endif %}
  port: {{ db_port }}
from jinja2 import Environment, FileSystemLoader
import os
from pathlib import Path
import tempfile

def main():
    config_path = Path(__file__).parent / "config.yaml"
    if not config_path.exists():
        raise FileNotFoundError(f"Config file not found: {config_path}")

    environment = os.getenv('ENV', 'dev')  # Default to 'dev' if ENV is not set
    db_port = os.getenv('DB_PORT', '5432')  # Default to '5432' if DB_PORT is not set

    env = Environment(loader=FileSystemLoader(config_path.parent))
    template = env.get_template(config_path.name)
    rendered_config = template.render(environment=environment, db_port=db_port)

    # Create a temporary config file with the rendered template
    with tempfile.NamedTemporaryFile(delete=False, suffix='.yaml') as tmp_config_file:
        tmp_config_file.write(rendered_config.encode())
        tmp_config_path = tmp_config_file.name

    # Create app object using the temporary config file
    foca = Foca(
        config_file=tmp_config_path,
        custom_config_model="service_models.custom_config.CustomConfig",
    )
    foca.config_file
    app = foca.create_app()

    # Optionally delete the temporary file after creating the app
    os.remove(tmp_config_path)

if __name__ == "__main__":
    main()
uniqueg commented 6 months ago

Thanks @JaeAeich.

I think there are other options as well, e.g., anchoring the relative path not to the caller's current working directory, but to some other reference, e.g., the repository root path. I will take care of that when refactoring FOCA.

I will think about the templating suggestion as well (another good point), though I feel that that is another issue. We should probably take the config.yaml files out of the code package and put them in deployment/, together with the docker-compose.yml files. We could then also think about creating a config map in Kubernetes, so that params can be adjusted on running services (another long-standing open issue).