From dfa6c7327684710f0d0e1216613afd8ac5ad238b Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Tue, 31 Aug 2021 10:50:27 +0200 Subject: [PATCH] Fix instantiating Vault Secret Backend during configuration When Secrets Backend are instantiated during configuration, not all Airlfow packages are yet imported, because they need Secret Backends. We have a weird cyclical relation between models, configuration and settins which forces us to be extra careful around configuration, settings and backends. In this case top-level import of Connections by the Vault Secret Backend triggered cyclic import problem (importing airflow models require configuration to be fully loaded and initialized) but then it could not be initialized because models needed to be imported first. The fix is to move Connections to be locally imported. --- airflow/providers/hashicorp/secrets/vault.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/airflow/providers/hashicorp/secrets/vault.py b/airflow/providers/hashicorp/secrets/vault.py index 084fdc1b03015..86c5df01a3cba 100644 --- a/airflow/providers/hashicorp/secrets/vault.py +++ b/airflow/providers/hashicorp/secrets/vault.py @@ -16,9 +16,8 @@ # specific language governing permissions and limitations # under the License. """Objects relating to sourcing connections & variables from Hashicorp Vault""" -from typing import Optional +from typing import TYPE_CHECKING, Optional -from airflow.models.connection import Connection from airflow.providers.hashicorp._internal_client.vault_client import _VaultClient from airflow.secrets import BaseSecretsBackend from airflow.utils.log.logging_mixin import LoggingMixin @@ -206,7 +205,12 @@ def get_conn_uri(self, conn_id: str) -> Optional[str]: return response.get("conn_uri") if response else None - def get_connection(self, conn_id: str) -> Optional[Connection]: + # Make sure connection is imported this way for type checking, otherwise when importing + # the backend it will get a circular dependency and fail + if TYPE_CHECKING: + from airflow.models.connection import Connection + + def get_connection(self, conn_id: str) -> 'Optional[Connection]': """ Get connection from Vault as secret. Prioritize conn_uri if exists, if not fall back to normal Connection creation. @@ -215,6 +219,10 @@ def get_connection(self, conn_id: str) -> Optional[Connection]: :rtype: Connection :return: A Connection object constructed from Vault data """ + # The Connection needs to be locally imported because otherwise we get into cyclic import + # problems when instantiating the backend during configuration + from airflow.models.connection import Connection + response = self.get_response(conn_id) if response is None: return None