-
Notifications
You must be signed in to change notification settings - Fork 16
environment variables to disable app db and cache. some related cleanup #550
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
services: | ||
fidesops: | ||
container_name: fidesops | ||
build: | ||
context: . | ||
dockerfile: Dockerfile | ||
expose: | ||
- 8080 | ||
healthcheck: | ||
test: [ "CMD", "curl", "-f", "http://0.0.0.0:8080/health" ] | ||
interval: 30s | ||
timeout: 10s | ||
retries: 3 | ||
start_period: 1s | ||
ports: | ||
- "8080:8080" | ||
volumes: | ||
- type: bind | ||
source: ./ | ||
target: /fidesops | ||
read_only: False | ||
- /fidesops/src/fidesops.egg-info | ||
environment: | ||
- FIDESOPS__DEV_MODE=${FIDESOPS__DEV_MODE} | ||
- FIDESOPS__LOG_PII=${FIDESOPS__LOG_PII} | ||
- FIDESOPS__HOT_RELOAD=${FIDESOPS__HOT_RELOAD} | ||
- FIDESOPS__DATABASE__ENABLED=false | ||
- FIDESOPS__REDIS__ENABLED=false | ||
Comment on lines
+27
to
+28
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Commenting here with some
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:
I do this exact pattern in fidesdemo, see: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. certainly more concise for the ENV var piece of it, but the also open to other suggestions to accomplish the above (i.e. setting env var and also not starting up There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. hmm. because |
||
docs: | ||
build: | ||
context: docs/fidesops/ | ||
dockerfile: Dockerfile | ||
volumes: | ||
- ./docs/fidesops:/docs | ||
expose: | ||
- 8000 | ||
ports: | ||
- "8000:8000" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 commentThe 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 |
||
| `PASSWORD` | `FIDESOPS__DATABASE__PASSWORD` | string | apassword | N/A | The password with which to login to the Fidesops application database | | ||
| `PORT` | `FIDESOPS__DATABASE__PORT` | int | 5432 | 5432 | The port at which the Fidesops application database will be accessible | | ||
| `DB` | `FIDESOPS__DATABASE_DB` | string | db | N/A | The name of the database to use in the Fidesops application database | | ||
| `DB` | `FIDESOPS__DATABASE__DB` | string | db | N/A | The name of the database to use in the Fidesops application database | | ||
| `ENABLED` | `FIDESOPS__DATABASE__ENABLED` | bool | True | True | Whether the application database should be enabled. Only set to false for certain narrow uses of the application that do not require a backing application database. | | ||
|---|---|---|---|---|---| | ||
| `HOST` | `FIDESOPS__REDIS__HOST` | string | redis.internal | N/A | The networking address for the Fidesops application Redis cache | | ||
| `PORT` | `FIDESOPS__REDIS__PORT` | int | 6379 | 6379 | The port at which the Fidesops application cache will be accessible | | ||
| `PASSWORD` | `FIDESOPS__REDIS__PASSWORD` | string | anotherpassword | N/A | The password with which to login to the Fidesops application cache | | ||
| `DB_INDEX` | `FIDESOPS__REDIS__DB_INDEX` | int | 0 | 0 | The Fidesops application will use this index in the Redis cache to cache data | | ||
| `DEFAULT_TTL_SECONDS` | `FIDESOPS__REDIS__DEFAULT_TTL_SECONDS` | int | 3600 | 604800 | The number of seconds for which data will live in Redis before automatically expiring | | ||
| `ENABLED` | `FIDESOPS__REDIS__ENABLED` | bool | True | True | Whether the application's redis cache should be enabled. Only set to false for certain narrow uses of the application that do not require a backing redis cache. | | ||
|---|---|---|---|---|---| | ||
| `APP_ENCRYPTION_KEY` | `FIDESOPS__SECURITY__APP_ENCRYPTION_KEY` | string | OLMkv91j8DHiDAULnK5Lxx3kSCov30b3 | N/A | The key used to sign Fidesops API access tokens | | ||
| `CORS_ORIGINS` | `FIDESOPS__SECURITY__CORS_ORIGINS` | List[AnyHttpUrl] | ["https://a-client.com/", "https://another-client.com"/] | N/A | A list of pre-approved addresses of clients allowed to communicate with the Fidesops application server | | ||
|
@@ -64,6 +66,7 @@ USER="postgres" | |
PASSWORD="a-password" | ||
DB="app" | ||
TEST_DB="test" | ||
ENABLED=true | ||
|
||
[redis] | ||
HOST="redis" | ||
|
@@ -72,6 +75,7 @@ PORT=6379 | |
CHARSET="utf8" | ||
DEFAULT_TTL_SECONDS=3600 | ||
DB_INDEX=0 | ||
ENABLED=true | ||
|
||
[security] | ||
APP_ENCRYPTION_KEY="OLMkv91j8DHiDAULnK5Lxx3kSCov30b3" | ||
|
@@ -84,8 +88,8 @@ OAUTH_ROOT_CLIENT_SECRET="fidesopsadminsecret" | |
TASK_RETRY_COUNT=3 | ||
TASK_RETRY_DELAY=20 | ||
TASK_RETRY_BACKOFF=2 | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. updated doc reference because camel-cased booleans in my |
||
``` | ||
|
||
Please note: The configuration is case-sensitive, so the variables must be specified in UPPERCASE. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,17 @@ | ||
from typing import Generator | ||
|
||
from fidesops.common_exceptions import FunctionalityNotConfigured | ||
from fidesops.core.config import config | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a tests for this and the 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 commentThe 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. |
||
"Application database required, but it is currently disabled! Please update your application configuration to enable integration with an application database." | ||
) | ||
try: | ||
SessionLocal = get_db_session() | ||
db = SessionLocal() | ||
|
@@ -16,4 +22,8 @@ def get_db() -> Generator: | |
|
||
def get_cache() -> Generator: | ||
"""Return a connection to our redis cache""" | ||
if not config.redis.ENABLED: | ||
raise FunctionalityNotConfigured( | ||
"Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache." | ||
) | ||
yield get_redis_connection() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from typing import Callable, List | ||
|
||
from fastapi import Request | ||
from fastapi.responses import JSONResponse, Response | ||
from starlette.status import HTTP_500_INTERNAL_SERVER_ERROR | ||
|
||
from fidesops.common_exceptions import FunctionalityNotConfigured | ||
|
||
|
||
class ExceptionHandlers: | ||
@staticmethod | ||
def functionality_not_configured_handler( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @staticmethod
def functionality_not_configured_handler(
... |
||
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 commentThe reason will be displayed to describe this comment to others. Learn more. @sanders41 while you're here - any thoughts on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
) | ||
|
||
@classmethod | ||
def get_handlers(cls) -> List[Callable[[Request, Exception], Response]]: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
return [ExceptionHandlers.functionality_not_configured_handler] |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,9 @@ | |
from starlette.middleware.cors import CORSMiddleware | ||
|
||
from fidesops.api.v1.api import api_router | ||
from fidesops.api.v1.exception_handlers import ExceptionHandlers | ||
from fidesops.api.v1.urn_registry import V1_URL_PREFIX | ||
from fidesops.common_exceptions import FunctionalityNotConfigured | ||
from fidesops.core.config import config | ||
from fidesops.db.database import init_db | ||
from fidesops.tasks.scheduled.scheduler import scheduler | ||
|
@@ -29,21 +31,28 @@ | |
) | ||
|
||
app.include_router(api_router) | ||
for handler in ExceptionHandlers.get_handlers(): | ||
app.add_exception_handler(FunctionalityNotConfigured, handler) | ||
|
||
|
||
def start_webserver() -> None: | ||
"""Run any pending DB migrations and start the webserver.""" | ||
logger.info("****************fidesops****************") | ||
logger.info("Running any pending DB migrations...") | ||
init_db(config.database.SQLALCHEMY_DATABASE_URI) | ||
if config.database.ENABLED: | ||
# don't run db migrations if database is disabled | ||
logger.info("Running any pending DB migrations...") | ||
init_db(config.database.SQLALCHEMY_DATABASE_URI) | ||
|
||
scheduler.start() | ||
|
||
logger.info("Starting scheduled request intake...") | ||
initiate_scheduled_request_intake() | ||
if config.database.ENABLED: | ||
# don't schedule request intake if database is disabled | ||
logger.info("Starting scheduled request intake...") | ||
initiate_scheduled_request_intake() | ||
|
||
logger.info("Starting web server...") | ||
uvicorn.run( | ||
"src.fidesops.main:app", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when as far as i could tell, making this switch here doesn't impact existing behavior, and it also allows the app to run if There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.main:app", | ||
host="0.0.0.0", | ||
port=8080, | ||
log_config=None, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
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()) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
import json | ||
|
||
import pytest | ||
from starlette.testclient import TestClient | ||
|
||
from fidesops.api.v1.scope_registry import CLIENT_CREATE | ||
from fidesops.api.v1.urn_registry import CLIENT, HEALTH, PRIVACY_REQUESTS, V1_URL_PREFIX | ||
from fidesops.core import config | ||
|
||
|
||
@pytest.fixture | ||
def mock_config_db_disabled(): | ||
db_enabled = config.config.database.ENABLED | ||
config.config.database.ENABLED = False | ||
yield | ||
config.config.database.ENABLED = db_enabled | ||
|
||
@pytest.fixture | ||
def mock_config_redis_disabled(): | ||
redis_enabled = config.config.redis.ENABLED | ||
config.config.redis.ENABLED = False | ||
yield | ||
config.config.redis.ENABLED = redis_enabled | ||
|
||
|
||
class TestExceptionHandlers: | ||
@pytest.mark.usefixtures("mock_config_db_disabled") | ||
def test_db_disabled(self, api_client: TestClient, generate_auth_header): | ||
auth_header = generate_auth_header([CLIENT_CREATE]) | ||
# oauth endpoint should not work | ||
expected_response = {"message": "Application database required, but it is currently disabled! Please update your application configuration to enable integration with an application database."} | ||
response = api_client.post(V1_URL_PREFIX + CLIENT, headers=auth_header) | ||
response_body = json.loads(response.text) | ||
assert 500 == response.status_code | ||
assert expected_response == response_body | ||
|
||
# health endpoint should still work | ||
expected_response = {"healthy": True} | ||
response = api_client.get(HEALTH) | ||
response_body = json.loads(response.text) | ||
assert 200 == response.status_code | ||
assert expected_response == response_body | ||
|
||
@pytest.mark.usefixtures("mock_config_redis_disabled") | ||
def test_redis_disabled(self, api_client: TestClient, generate_auth_header): | ||
auth_header = generate_auth_header([CLIENT_CREATE]) | ||
# Privacy requests endpoint should not work | ||
request_body = [ | ||
{ | ||
"requested_at": "2021-08-30T16:09:37.359Z", | ||
"identity": { "email": "[email protected]" }, | ||
"policy_key": "my_separate_policy" | ||
} | ||
] | ||
expected_response = {"message": "Application redis cache required, but it is currently disabled! Please update your application configuration to enable integration with a redis cache."} | ||
response = api_client.post(V1_URL_PREFIX + PRIVACY_REQUESTS, headers=auth_header, json=request_body) | ||
response_body = json.loads(response.text) | ||
assert 500 == response.status_code | ||
assert expected_response == response_body | ||
|
||
# health endpoint should still work | ||
expected_response = {"healthy": True} | ||
response = api_client.get(HEALTH) | ||
response_body = json.loads(response.text) | ||
assert 200 == response.status_code | ||
assert expected_response == response_body |
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