-
Notifications
You must be signed in to change notification settings - Fork 16
environment variables to disable app db and cache. some related cleanup #550
environment variables to disable app db and cache. some related cleanup #550
Conversation
@@ -43,6 +43,9 @@ reset-db: | |||
server: compose-build | |||
@docker-compose up | |||
|
|||
server-no-db: compose-build |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useful for local testing
@@ -30,16 +30,18 @@ The `fidesops.toml` file should specify the following variables: | |||
| TOML Variable | ENV Variable | Type | Example | Default | Description | | |||
|---|---|---|---|---|---| | |||
| `SERVER` | `FIDESOPS__DATABASE__SERVER` | string | postgres.internal | N/A | The networking address for the Fideops Postgres database server | | |||
| `USER` | `FIDESOPS__DATABASE_USER` | string | postgres | N/A | The database user with which to login to the Fidesops application database | | |||
| `PASSWORD` | `FIDESOPS__DATABASE_PASSWORD` | string | apassword | N/A | The password with which to login to the Fidesops application database | | |||
| `USER` | `FIDESOPS__DATABASE__USER` | string | postgres | N/A | The database user with which to login to the Fidesops application database | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these were existing doc typos as far as i could tell -- missing the double underscore
REQUIRE_MANUAL_REQUEST_APPROVAL=True | ||
MASKING_STRICT=True | ||
REQUIRE_MANUAL_REQUEST_APPROVAL=true | ||
MASKING_STRICT=true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated doc reference because camel-cased booleans in my .toml
weren't working -- pretty sure lowercase is needed
) | ||
|
||
@classmethod | ||
def get_handlers(cls) -> List[Callable[[Request, Exception], Response]]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wanted to provide a starting point if more exception handlers would be needed over time...perhaps there's a more elegant/dynamic way of doing this, having to list out the methods seems a bit cumbersome
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 docs changes look great to me, thanks for catching them!
uvicorn.run( | ||
"src.fidesops.main:app", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when init_db()
wasn't run, uvicorn wasn't finding the fidesops
module anymore when this was the run command. i think i was able to pin it down to the fact that within init_db()
, we run src/migrations/env.py
, which happens to add ..
to the sys.path
-- and only because of that the src.fidesops
path is picked up correctly. to me this seems like an unrecognized and unintentional side-effect -- init_db()
should not be needed to get the base app running, i don't think -- if it is intended, then we need to do it in a much less obfuscated way.
as far as i could tell, making this switch here doesn't impact existing behavior, and it also allows the app to run if init_db()
is not called. i don't know of any negative consequences (though i could certainly be missing something!)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Your explanation here makes sense to me and everything seems to work fine with your change.
- FIDESOPS__DATABASE__ENABLED=false | ||
- FIDESOPS__REDIS__ENABLED=false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Commenting here with some docker compose
trivia... you can avoid this separate no-db.yml
by exposing this ENV variable on the main file like so:
- FIDESOPS__DATABASE__ENABLED =${FIDESOPS__DATABASE__ENABLED:-true}
That will default to true, or take the value of the ENV var provided to the docker compose invocation.
Then you just need to set the value of the ENV variable in the Makefile to adjust that behaviour, like:
server-no-db: export FIDESOPS__DATABASE__ENABLED=false
server-no-db: ... <continue existing target here>
I do this exact pattern in fidesdemo, see:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
certainly more concise for the ENV var piece of it, but the no-db.yml
compose also doesn't bootstrap the db
and redis
(and docs
) services, to more closely simulate the "standalone" deployment. as far as i know (and based on our conversation on friday), i don't think there's a way to have branching logic in docker-compose
based on env var? but i can dig on that if you think it's possible, because i definitely do not love having the additional no-db.yml
compose file.
also open to other suggestions to accomplish the above (i.e. setting env var and also not starting up db
and redis
services)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, that's true... it's Hard™️ to make docker compose files dynamic and support different run configurations.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TIL this exists: https://docs.docker.com/compose/profiles/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oh - that looks pretty nice. i can take a shot at that, seems a lot cleaner. thanks for pointing me to that!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm. because fidesops
depends on db
and redis
, i don't think this is going to work for us, without some significant refactor. as far as i can tell, there's no way to express conditional dependencies, and i feel like the no-db use case is enough of an edge case for now that i am reluctant to remove those dependencies...
|
||
|
||
class ExceptionHandlers: | ||
def functionality_not_configured_handler( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@staticmethod
def functionality_not_configured_handler(
...
from fidesops.db.session import get_db_session | ||
from fidesops.util.cache import get_cache as get_redis_connection | ||
|
||
|
||
def get_db() -> Generator: | ||
"""Return our database session""" | ||
if not config.database.ENABLED: | ||
raise FunctionalityNotConfigured( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add a tests for this and the get_cache
. I'm thinking add a new file at tests/api/test_deps.py
unless you think there is a better place to put it.
import pytest
from fidesops.api.deps import get_cache, get_db
from fidesops.common_exceptions import FunctionalityNotConfigured
from fidesops.core import config
@pytest.fixture
def mock_config():
db_enabled = config.config.database.ENABLED
redis_enabled = config.config.redis.ENABLED
config.config.database.ENABLED = False
config.config.redis.ENABLED = False
yield
config.config.database.ENABLED = db_enabled
config.config.redis.ENABLED = redis_enabled
@pytest.mark.usefixtures("mock_config")
def test_get_cache_not_enabled():
with pytest.raises(FunctionalityNotConfigured):
next(get_cache())
@pytest.mark.usefixtures("mock_config")
def test_get_db_not_enabled():
with pytest.raises(FunctionalityNotConfigured):
next(get_db())
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sanders41 thank you for writing the test up! i ran a bit further with this and also added in a test on the endpoint layer to make sure the exception handlers work as expected - let me know what you think.
…tion handlers. make exception handler a static method.
request: Request, exc: FunctionalityNotConfigured | ||
) -> JSONResponse: | ||
return JSONResponse( | ||
status_code=HTTP_500_INTERNAL_SERVER_ERROR, content={"message": str(exc)} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sanders41 while you're here - any thoughts on 500
as the error code here? i considered other options but anything else i could think of seemed like it could be misinterpreted, whereas i feel like a generic 500
with a clear error message probably has the best chance of being well understood. but i'd like a second opinion on this!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
500
make sense to me here. The other option I could see making sense is 503
, but 503
I think would imply things are down temporarily and will come back up which won't be the case so that could be confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. I like the new tests!
…up (#550) * environment variables to disable app db and cache. some related cleanup * update changelog.md * add tests db and redis disabled configs. add tests for endpoint exception handlers. make exception handler a static method. Co-authored-by: Adam Sachs <[email protected]>
Purpose
Provide config options to allow fidesops to start cleanly without db and cache integration. Used for narrow use cases like standalone masking engine.
Changes
FIDESOPS__DATABASE__ENABLED
andFIDESOPS__REDIS__ENABLED
configuration variables to allowfidesops
to run cleanly in a "stateless" mode without any database or redis cache integrationmain.py
's uvicorn invocation to consistently start app even ifinit_db
isn't calledBelow are some snippets of various API responses when
FIDESOPS__DATABASE__ENABLED
andFIDESOPS__REDIS__ENABLED
are bothfalse
:/privacy-request
endpoint requires redis cache, so a500
is returned with appropriate error message:/oauth/token
endpoint requires app db , so a500
is returned with appropriate error message:/masking/mask
endpoint does not require db or redis cache, so it still responds successfully:/health
endpoint does not require db or redis cache, so it still responds successfully:Checklist
CHANGELOG.md
file in appropriate sectionsRun Unsafe PR Checks
label has been applied, and checks have passed, if this PR touches any external servicesTicket
Fixes #545