diff options
-rw-r--r-- | oslo_db/sqlalchemy/engines.py | 20 | ||||
-rw-r--r-- | oslo_db/tests/sqlalchemy/test_sqlalchemy.py | 46 |
2 files changed, 66 insertions, 0 deletions
diff --git a/oslo_db/sqlalchemy/engines.py b/oslo_db/sqlalchemy/engines.py index 601be76..a9ac662 100644 --- a/oslo_db/sqlalchemy/engines.py +++ b/oslo_db/sqlalchemy/engines.py @@ -100,6 +100,24 @@ def _setup_logging(connection_debug=0): logger.setLevel(logging.WARNING) +def _vet_url(url): + if "+" not in url.drivername and not url.drivername.startswith("sqlite"): + if url.drivername.startswith("mysql"): + LOG.warning( + "URL %r does not contain a '+drivername' portion, " + "and will make use of a default driver. " + "A full dbname+drivername:// protocol is recommended. " + "For MySQL, it is strongly recommended that mysql+pymysql:// " + "be specified for maximum service compatibility", url + ) + else: + LOG.warning( + "URL %r does not contain a '+drivername' portion, " + "and will make use of a default driver. " + "A full dbname+drivername:// protocol is recommended.", url + ) + + def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None, idle_timeout=3600, connection_debug=0, max_pool_size=None, max_overflow=None, @@ -112,6 +130,8 @@ def create_engine(sql_connection, sqlite_fk=False, mysql_sql_mode=None, url = sqlalchemy.engine.url.make_url(sql_connection) + _vet_url(url) + engine_args = { "pool_recycle": idle_timeout, 'convert_unicode': True, diff --git a/oslo_db/tests/sqlalchemy/test_sqlalchemy.py b/oslo_db/tests/sqlalchemy/test_sqlalchemy.py index a37cdc6..bcbad45 100644 --- a/oslo_db/tests/sqlalchemy/test_sqlalchemy.py +++ b/oslo_db/tests/sqlalchemy/test_sqlalchemy.py @@ -664,6 +664,52 @@ class CreateEngineTest(oslo_test.BaseTestCase): engines._thread_yield ) + def test_warn_on_missing_driver(self): + + warnings = mock.Mock() + + def warn_interpolate(msg, args): + # test the interpolation itself to ensure the password + # is concealed + warnings.warning(msg % args) + + with mock.patch( + "oslo_db.sqlalchemy.engines.LOG.warning", + warn_interpolate): + + engines._vet_url( + url.make_url("mysql://scott:tiger@some_host/some_db")) + engines._vet_url(url.make_url( + "mysql+mysqldb://scott:tiger@some_host/some_db")) + engines._vet_url(url.make_url( + "mysql+pymysql://scott:tiger@some_host/some_db")) + engines._vet_url(url.make_url( + "postgresql+psycopg2://scott:tiger@some_host/some_db")) + engines._vet_url(url.make_url( + "postgresql://scott:tiger@some_host/some_db")) + + self.assertEqual( + [ + mock.call.warning( + "URL mysql://scott:***@some_host/some_db does not contain " + "a '+drivername' portion, " + "and will make use of a default driver. " + "A full dbname+drivername:// protocol is recommended. " + "For MySQL, it is strongly recommended that " + "mysql+pymysql:// " + "be specified for maximum service compatibility", + + ), + mock.call.warning( + "URL postgresql://scott:***@some_host/some_db does not " + "contain a '+drivername' portion, " + "and will make use of a default driver. " + "A full dbname+drivername:// protocol is recommended." + ) + ], + warnings.mock_calls + ) + class ProcessGuardTest(test_base.DbTestCase): def test_process_guard(self): |