项目文件夹

文件
wehub-resource-sync bf2343b7e4
Integration Tests - MySQL + Elasticsearch / Detect Changes (push) Has been cancelled
Integration Tests - MySQL + Elasticsearch / integration-tests-mysql-elasticsearch (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + Elasticsearch + Redis / integration-tests-postgres-elasticsearch-redis (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / Detect Changes (push) Has been cancelled
Integration Tests - PostgreSQL + OpenSearch / integration-tests-postgres-opensearch (push) Has been cancelled
Java Checkstyle / java-checkstyle (push) Has been cancelled
Maven Collate Tests / maven-collate-ci (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests-status (push) Has been cancelled
Publish Package to Maven Central Repository / publish-maven-packages (push) Has been cancelled
OpenMetadata Service Unit Tests / Detect Changes (push) Has been cancelled
OpenMetadata Service Unit Tests / openmetadata-service-unit-tests (push) Has been cancelled
OpenMetadata Service Unit Tests / k8s_operator-unit-tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:35:45 +08:00

128 行
5.4 KiB
Python

# Copyright 2025 Collate
# Licensed under the Collate Community License, Version 1.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# https://github.com/open-metadata/OpenMetadata/blob/main/ingestion/LICENSE
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Characterization tests for the SHARED RDS IAM auth path in ``builders.py``.
RDS IAM auth tokens expire after ~15 minutes. ``get_connection_url_common`` mints
the token once and bakes it into the connection URL string, so a single engine
reuses one frozen token for every pooled connection and cannot refresh it.
The MySQL connector now works around this at the connector level (it builds the
engine without a token in the URL and attaches a ``do_connect`` listener that
injects a fresh token per connection) — see
``tests/unit/source/database/test_mysql_iam.py``.
These tests pin the behaviour of the SHARED helper, which is still token-frozen
and is the remaining gap for the other RDS connectors that go through it
(Postgres, Redshift, Greenplum, Timescale). They are expected to FAIL — and
should be flipped to assert refreshed behaviour — once the shared path is fixed
too. ``test_iam_token_is_used_for_authentication`` is the invariant that holds
either way.
"""
from unittest.mock import MagicMock, patch
import pytest
from metadata.generated.schema.entity.services.connections.database.common.iamAuthConfig import (
IamAuthConfigurationSource,
)
from metadata.generated.schema.entity.services.connections.database.mysqlConnection import (
MysqlConnection,
)
from metadata.generated.schema.security.credentials.awsCredentials import (
AWSCredentials,
)
from metadata.ingestion.connections import builders
IAM_TOKEN = "iam-token-v1"
HOST = "myrds.abc.us-east-1.rds.amazonaws.com"
PORT = "3306"
USERNAME = "iam_user"
REGION = "us-east-1"
def _iam_connection() -> MysqlConnection:
return MysqlConnection(
username=USERNAME,
hostPort=f"{HOST}:{PORT}",
authType=IamAuthConfigurationSource(awsConfig=AWSCredentials(awsRegion=REGION)),
)
@pytest.fixture
def mock_rds_token():
"""Patch AWSClient so generate_db_auth_token returns a fixed token and is counted."""
with patch.object(builders, "AWSClient") as mock_aws_client:
rds_client = MagicMock()
rds_client.generate_db_auth_token.return_value = IAM_TOKEN
mock_aws_client.return_value.get_rds_client.return_value = rds_client
yield rds_client
class TestRdsIamTokenRefresh:
def test_iam_token_is_used_for_authentication(self, mock_rds_token):
"""Invariant: the generated IAM token authenticates the connection.
Holds before and after the B1 fix.
"""
url = builders.get_connection_url_common(_iam_connection())
assert IAM_TOKEN in url
assert mock_rds_token.generate_db_auth_token.call_args.kwargs == {
"DBHostname": HOST,
"Port": PORT,
"DBUsername": USERNAME,
"Region": REGION,
}
def test_iam_token_is_generated_once_and_frozen_in_url(self, mock_rds_token):
"""Documents the remaining shared-path gap: token baked into the URL string.
``get_connection_url_common`` embeds the token in the URL, so every engine
built from it reuses that single token for its whole lifetime — it is never
refreshed and goes stale after the ~15 min RDS IAM token TTL. The MySQL
connector bypasses this path; the connectors that still rely on it do not.
EXPECTED TO FAIL once the shared path is fixed to mint a token per
connection rather than embedding it in the URL.
"""
conn = _iam_connection()
first_url = builders.get_connection_url_common(conn)
second_url = builders.get_connection_url_common(conn)
assert IAM_TOKEN in first_url, "token is embedded directly in the URL (the bug)"
assert first_url == second_url, "same frozen token is reused, never refreshed"
def test_no_do_connect_listener_is_registered_for_iam(self, mock_rds_token):
"""Documents the shared-path gap: no per-connection token injection hook.
The shared ``create_generic_db_connection`` does not attach a ``do_connect``
listener for IAM, so an engine built purely through it cannot refresh the
baked-in token once it expires. (The MySQL connector adds this listener
itself; see test_mysql_iam.py.)
EXPECTED TO FAIL once the shared path grows its own IAM listener.
"""
with patch.object(builders, "listen") as mock_listen:
builders.create_generic_db_connection(
connection=_iam_connection(),
get_connection_url_fn=builders.get_connection_url_common,
get_connection_args_fn=builders.get_connection_args_common,
)
do_connect_registrations = [call for call in mock_listen.call_args_list if "do_connect" in call.args]
assert not do_connect_registrations, (
"no do_connect listener is wired for IAM today — this is the gap B1 "
"describes; once fixed, invert this assertion"
)