-
Notifications
You must be signed in to change notification settings - Fork 297
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
refactor(core): Standardize configuration and readiness steps in container lifecycle #527
base: main
Are you sure you want to change the base?
Conversation
Referencing #459 for context |
would this have the potential to break existing code that depends on conventions from v3 and v4? |
…ault wait timeout for `wait_for_logs` (#525) Removes usage of `sqlalchemy`, as part of the work described in #526. - Adds default timeout to the `wait_for_logs` waiting strategy, the same timeout used by default in the `wait_container_is_ready` strategy. - Changes wait strategy for `mysql` container to wait for logs indicating that the DB engine is ready to accept connections (MySQL performs a restart as part of its startup procedure, so the logs will always appear twice. - Add More tests for different `mysql` and `mariadb` versions to ensure consistency in wait strategy. - Remove x86 emulation for ARM devices for MariaDB, as it MariaDB images support ARM architectures already. - Change wait strategy for `oracle-free`, as the images produce a consistent `DATABASE IS READY TO USE!` log message on startup. Next steps will be to remove `sqlalchemy` as a bundled dependency entirely, but I have not included it in this PR as I consider it a bigger change than just changing wait strategies as an internal implementation detail. I plan to do this as part of a bigger rework where i remove the `DbContainer` class and standardize configuration hooks and wait strategies across containers (not just DB containers, all containers in need of a configuration and readiness step). See #527 for WIP. --------- Co-authored-by: David Ankin <[email protected]>
Returns a connection URL following the RFC-1738 format. | ||
Compatible with database clients such as SQLAlchemy and other popular database client libraries. | ||
|
||
Example: postgres+psycopg2://myuser:mypassword@localhost:5432/mytestdb |
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.
It would be nice to document that setting driver to None or "" will remove the driver from the URL (psycopg v3 does need a url without dialect and any regular http URL also needs that :-))
Or at least add an example without driver.
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.
I would actually split this into a generic "create_connection_url" (scheme
instead of dialect
and driver
, path
instead of dbname
) and one call (create_db_api_connection_url
?) which takes db api inputs (like the current one) and calls create_connection_url
with the corresponding arguments.
@@ -53,6 +54,43 @@ def inside_container() -> bool: | |||
return os.path.exists("/.dockerenv") | |||
|
|||
|
|||
def create_connection_string( |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
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.
create_connection_url -> e.g. you couldn't create a kafka one with this, couldn't you?
@@ -53,6 +54,43 @@ def inside_container() -> bool: | |||
return os.path.exists("/.dockerenv") | |||
|
|||
|
|||
def create_connection_string( | |||
dialect: str, |
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.
If this would be DB API only, I would say "dialect" is fine, but if this should also be used to generate http URLs, I would call this base_scheme.
... client = BlobServiceClient.from_connection_string( | ||
... connection_string, | ||
... azurite_container.get_connection_string(), |
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.
For ArangoDbContainer
is get_conenction_url`, here it's '*_string'. Should we unify that?
@wait_container_is_ready(OSError) | ||
def _connect(self) -> None: | ||
def _wait_until_ready(self) -> None: | ||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: | ||
s.connect((self.get_container_host_ip(), int(self.get_exposed_port(next(iter(self.ports)))))) |
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.
Might it make sense to also pull this into utils as a wait_for_port
? At least I had the need in my code to basically write something similar:
@wait_container_is_ready(NoSuchPortExposed)
def _wait_for_port() -> None:
zks.get_service_port("schemaregistry", 8085)
_wait_for_port()
|
||
return response.json() | ||
|
||
def get_influxdb_version(self) -> str: |
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.
Should this function be kept? It's seems it has value outside the normal startup procedure?
], | ||
) | ||
def test_influxdbcontainer_get_influxdb_version( | ||
image: str, influxdb_container_class: Type[InfluxDbContainer], expected_version: str | ||
): | ||
with influxdb_container_class(image) as influxdb_container: | ||
assert influxdb_container.get_influxdb_version().startswith(expected_version) | ||
version = get(f"{influxdb_container.get_url()}/health").json()["version"] | ||
assert version == expected_version |
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.
This test implies that influxdb_container.get_influxdb_version()
is API?
@@ -43,13 +54,29 @@ def __init__( | |||
self.dbname = dbname | |||
self.dialect = dialect | |||
|
|||
@wait_container_is_ready(*ADDITIONAL_TRANSIENT_ERRORS) | |||
def _wait_until_ready(self): | |||
import sqlalchemy |
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.
This will import sqla and fail the tests if it is not available. Which is something we just removed form PG and kafka. Is there no way to check for a port with nc or so?
microsoft/mssql-docker#133 has some interesting suggestion for a healthcheck
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.
See fefb9d0 where it is already removed?
|
||
def _wait_until_ready(self) -> None: | ||
pass | ||
|
||
def start(self): |
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.
def start(self): | |
def start(self) -> Self: |
Not sure what version we target (so either whats in python or via an backport), but I saw that this PR removes a lot of overwritten start()
methods which just do this to get the proper type passed out, so not doing this here will mean that typing will regress.
if i were to try to merge this, what should i keep in mind @santi ? Anything not already reflected here? I did a first pass and seems like i will have to revisit this several times in order to make sense of this fully... also, like i asked above, is this breaking at all? |
@alexanderankin Hey, sorry for the delay on this one. This is just a WIP draft that is not ready for a merge quite yet, and has probably drifted a bit from the |
Aims for this PR:
_configure()
and_wait_until_ready()
hooks in baseDockerContainer
image to be used by all container implementations in need of a configuration and readiness step.DbContainer
class, as the two points above will render it redundant.