Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-add database user and password as settings, and rename from MYSQL_* to SQL_* #258

Merged
merged 21 commits into from
Apr 28, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3417068
Re-add database user and password as settings, and rename from MYSQL_…
nickeopti Mar 9, 2022
8aa2281
Implement config settings deprecation system
nickeopti Mar 29, 2022
12d5043
Include the accidentally excluded MYSQL_PASSWORD in the deprecation map
nickeopti Mar 29, 2022
c24ff48
Merge main branch into fix-db-connection-settings
nickeopti Mar 29, 2022
c6f6b3b
Merge main branch into fix-db-connection-settings
nickeopti Mar 29, 2022
5b91dd0
Include postgres user/password settings as deprecated
nickeopti Mar 29, 2022
80006c6
Please flake8
nickeopti Mar 29, 2022
49ede5f
Add DeprecationWarning exception
nickeopti Apr 5, 2022
b9f3974
Handle deprecated settings in schema preload, and provide better warn…
nickeopti Apr 5, 2022
24d08a0
Adhere flake8 style guide
nickeopti Apr 5, 2022
285e4b1
Test usage of TC_SQL_* env var credentials
nickeopti Apr 5, 2022
3931a35
Test behaviour of handling of deprecated config settings
nickeopti Apr 5, 2022
2b2555f
Adhere to flake8 style guide
nickeopti Apr 5, 2022
e486b8b
Fix docstring
nickeopti Apr 5, 2022
de85eec
Add comment about when settings shall be removed
nickeopti Apr 27, 2022
05f1917
Improve deprecated setting warning message
nickeopti Apr 27, 2022
8acb448
Adhere to flake8 max line length
nickeopti Apr 27, 2022
3c562ca
Simply warnings for usage of deprecated fields
nickeopti Apr 27, 2022
3976649
Fetch settings once when creating URL instance
nickeopti Apr 27, 2022
4bee849
Update test for setting deprecated settings; for the simpler warnings
nickeopti Apr 27, 2022
a82d805
Update test to correspond to the new warning messages
nickeopti Apr 27, 2022
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
57 changes: 48 additions & 9 deletions terracotta/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
import os
import json
import tempfile
import warnings

from marshmallow import Schema, fields, validate, pre_load, post_load, ValidationError

from terracotta import exceptions


class TerracottaSettings(NamedTuple):
"""Contains all settings for the current Terracotta instance."""
Expand Down Expand Up @@ -67,23 +70,36 @@ class TerracottaSettings(NamedTuple):
#: CORS allowed origins for tiles endpoints
ALLOWED_ORIGINS_TILES: List[str] = [r'http[s]?://(localhost|127\.0\.0\.1):*']

#: MySQL database username (if not given in driver path)
#: SQL database username (if not given in driver path)
SQL_USER: Optional[str] = None

#: SQL database password (if not given in driver path)
SQL_PASSWORD: Optional[str] = None

#: Deprecated, use SQL_USER. MySQL database username (if not given in driver path)
MYSQL_USER: Optional[str] = None

#: MySQL database password (if not given in driver path)
#: Deprecated, use SQL_PASSWORD. MySQL database password (if not given in driver path)
MYSQL_PASSWORD: Optional[str] = None

#: PostgreSQL database username (if not given in driver path)
#: Deprecated, use SQL_USER. PostgreSQL database username (if not given in driver path)
POSTGRESQL_USER: Optional[str] = None

#: PostgreSQL database password (if not given in driver path)
#: Deprecated, use SQL_PASSWORD. PostgreSQL database password (if not given in driver path)
POSTGRESQL_PASSWORD: Optional[str] = None

#: Use a process pool for band retrieval in parallel
USE_MULTIPROCESSING: bool = True


AVAILABLE_SETTINGS: Tuple[str, ...] = tuple(TerracottaSettings._fields)
AVAILABLE_SETTINGS: Tuple[str, ...] = TerracottaSettings._fields

DEPRECATION_MAP: Dict[str, str] = {
nickeopti marked this conversation as resolved.
Show resolved Hide resolved
'MYSQL_USER': 'SQL_USER',
'MYSQL_PASSWORD': 'SQL_PASSWORD',
'POSTGRESQL_USER': 'SQL_USER',
'POSTGRESQL_PASSWORD': 'SQL_PASSWORD',
}


def _is_writable(path: str) -> bool:
Expand Down Expand Up @@ -129,10 +145,13 @@ class SettingSchema(Schema):
ALLOWED_ORIGINS_METADATA = fields.List(fields.String())
ALLOWED_ORIGINS_TILES = fields.List(fields.String())

MYSQL_USER = fields.String()
MYSQL_PASSWORD = fields.String()
POSTGRESQL_USER = fields.String()
POSTGRESQL_PASSWORD = fields.String()
SQL_USER = fields.String(allow_none=True)
SQL_PASSWORD = fields.String(allow_none=True)

MYSQL_USER = fields.String(allow_none=True)
MYSQL_PASSWORD = fields.String(allow_none=True)
POSTGRESQL_USER = fields.String(allow_none=True)
POSTGRESQL_PASSWORD = fields.String(allow_none=True)

USE_MULTIPROCESSING = fields.Boolean()

Expand All @@ -150,6 +169,26 @@ def decode_lists(self, data: Dict[str, Any], **kwargs: Any) -> Dict[str, Any]:
) from exc
return data

@pre_load
def handle_deprecated_fields(self, data: Dict[str, Any], **kwargs: Any) -> Dict[str, Any]:
for deprecated_field, new_field in DEPRECATION_MAP.items():
if data.get(deprecated_field):
if not data.get(new_field):
warnings.warn(
f'Setting TC_{deprecated_field} is being deprecated. '
nickeopti marked this conversation as resolved.
Show resolved Hide resolved
f'Please use TC_{new_field} instead.',
exceptions.DeprecationWarning
)
data[new_field] = data[deprecated_field]
else:
warnings.warn(
f'Both the deprecated TC_{deprecated_field} setting '
f'and its replacement TC_{new_field} is set: '
'This may result in unexpected behaviour.',
exceptions.DeprecationWarning
)
nickeopti marked this conversation as resolved.
Show resolved Hide resolved
return data

@post_load
def make_settings(self, data: Dict[str, Any], **kwargs: Any) -> TerracottaSettings:
# encode tuples
Expand Down
4 changes: 2 additions & 2 deletions terracotta/drivers/relational_meta_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ def _parse_path(cls, connection_string: str) -> URL:

url = URL.create(
drivername=f'{cls.SQL_DIALECT}+{cls.SQL_DRIVER}',
username=con_params.username,
password=con_params.password,
username=con_params.username or terracotta.get_settings().SQL_USER,
password=con_params.password or terracotta.get_settings().SQL_PASSWORD,
nickeopti marked this conversation as resolved.
Show resolved Hide resolved
host=con_params.hostname,
port=con_params.port,
database=con_params.path[1:], # remove leading '/' from urlparse
Expand Down
4 changes: 4 additions & 0 deletions terracotta/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ class InvalidDatabaseError(Exception):

class PerformanceWarning(UserWarning):
pass


class DeprecationWarning(UserWarning):
pass
20 changes: 20 additions & 0 deletions tests/drivers/test_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,23 @@ def test_invalid_key_types(driver_path, provider):
with pytest.raises(exceptions.InvalidKeyError) as exc:
db.get_datasets({'not-a-key': 'val'})
assert 'unrecognized keys' in str(exc)


@pytest.mark.parametrize('provider', TESTABLE_DRIVERS)
def test_use_credentials_from_settings(driver_path, provider, monkeypatch):
with monkeypatch.context() as m:
m.setenv('TC_SQL_USER', 'foo')
m.setenv('TC_SQL_PASSWORD', 'bar')

from terracotta import drivers, update_settings
update_settings()
dionhaefner marked this conversation as resolved.
Show resolved Hide resolved

if 'sqlite' not in provider:
meta_store_class = drivers.load_driver(provider)
assert meta_store_class._parse_path('').username == 'foo'
assert meta_store_class._parse_path('').password == 'bar'

driver_path_without_credentials = driver_path[driver_path.find('@') + 1:]
db = drivers.get_driver(driver_path_without_credentials, provider)
assert db.meta_store.url.username == 'foo'
assert db.meta_store.url.password == 'bar'
23 changes: 23 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,26 @@ def test_update_config():
update_settings(DEFAULT_TILE_SIZE=[50, 50])
new_settings = get_settings()
assert new_settings.DRIVER_PATH == 'test' and new_settings.DEFAULT_TILE_SIZE == (50, 50)


def test_deprecation_behaviour(monkeypatch):
from terracotta import config, exceptions, get_settings, update_settings
for deprecated_field, new_field in config.DEPRECATION_MAP.items():
with monkeypatch.context() as m:
m.setenv(f'TC_{deprecated_field}', 'foo')

with pytest.warns(exceptions.DeprecationWarning) as warning:
update_settings()
dionhaefner marked this conversation as resolved.
Show resolved Hide resolved
assert f'TC_{deprecated_field} is being deprecated' in str(warning[0])

assert getattr(get_settings(), deprecated_field) == 'foo'
assert getattr(get_settings(), new_field) == 'foo'

m.setenv(f'TC_{new_field}', 'bar')

with pytest.warns(exceptions.DeprecationWarning) as warning:
update_settings()
assert f'and its replacement TC_{new_field} is set' in str(warning[0])

assert getattr(get_settings(), deprecated_field) == 'foo'
assert getattr(get_settings(), new_field) == 'bar'