airbyte-internal-ops 0.1.5__py3-none-any.whl → 0.1.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. {airbyte_internal_ops-0.1.5.dist-info → airbyte_internal_ops-0.1.7.dist-info}/METADATA +70 -1
  2. {airbyte_internal_ops-0.1.5.dist-info → airbyte_internal_ops-0.1.7.dist-info}/RECORD +25 -26
  3. airbyte_ops_mcp/__init__.py +30 -2
  4. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/connectors/pipeline.py +2 -8
  5. airbyte_ops_mcp/airbyte_repo/list_connectors.py +132 -0
  6. airbyte_ops_mcp/cli/registry.py +90 -1
  7. airbyte_ops_mcp/connection_config_retriever/__init__.py +26 -0
  8. airbyte_ops_mcp/{live_tests/_connection_retriever → connection_config_retriever}/audit_logging.py +5 -6
  9. airbyte_ops_mcp/{live_tests/_connection_retriever → connection_config_retriever}/retrieval.py +8 -22
  10. airbyte_ops_mcp/{live_tests/_connection_retriever → connection_config_retriever}/secrets_resolution.py +8 -42
  11. airbyte_ops_mcp/constants.py +35 -0
  12. airbyte_ops_mcp/live_tests/connection_secret_retriever.py +17 -6
  13. airbyte_ops_mcp/mcp/github_repo_ops.py +10 -0
  14. airbyte_ops_mcp/mcp/prod_db_queries.py +357 -0
  15. airbyte_ops_mcp/mcp/server.py +2 -0
  16. airbyte_ops_mcp/prod_db_access/__init__.py +34 -0
  17. airbyte_ops_mcp/prod_db_access/db_engine.py +126 -0
  18. airbyte_ops_mcp/prod_db_access/py.typed +0 -0
  19. airbyte_ops_mcp/prod_db_access/queries.py +272 -0
  20. airbyte_ops_mcp/prod_db_access/sql.py +353 -0
  21. airbyte_ops_mcp/registry/__init__.py +34 -0
  22. airbyte_ops_mcp/registry/models.py +63 -0
  23. airbyte_ops_mcp/registry/publish.py +368 -0
  24. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/connectors/publish/__init__.py +0 -3
  25. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/connectors/publish/commands.py +0 -242
  26. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/connectors/publish/context.py +0 -175
  27. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/connectors/publish/pipeline.py +0 -1056
  28. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/poetry/publish/__init__.py +0 -3
  29. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/poetry/publish/commands.py +0 -127
  30. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/airbyte_ci/steps/python_registry.py +0 -238
  31. airbyte_ops_mcp/_legacy/airbyte_ci/connector_pipelines/models/contexts/python_registry_publish.py +0 -119
  32. airbyte_ops_mcp/live_tests/_connection_retriever/__init__.py +0 -35
  33. airbyte_ops_mcp/live_tests/_connection_retriever/consts.py +0 -33
  34. airbyte_ops_mcp/live_tests/_connection_retriever/db_access.py +0 -82
  35. {airbyte_internal_ops-0.1.5.dist-info → airbyte_internal_ops-0.1.7.dist-info}/WHEEL +0 -0
  36. {airbyte_internal_ops-0.1.5.dist-info → airbyte_internal_ops-0.1.7.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,126 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+ """Database engine and connection management for Airbyte Cloud Prod DB Replica.
3
+
4
+ This module provides connection pooling and engine management for querying
5
+ the Airbyte Cloud production database replica.
6
+
7
+ For SQL query templates and schema documentation, see sql.py.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import json
13
+ import os
14
+ import traceback
15
+ from typing import Any, Callable
16
+
17
+ import sqlalchemy
18
+ from google.cloud import secretmanager
19
+ from google.cloud.sql.connector import Connector
20
+ from google.cloud.sql.connector.enums import IPTypes
21
+
22
+ from airbyte_ops_mcp.constants import (
23
+ CONNECTION_RETRIEVER_PG_CONNECTION_DETAILS_SECRET_ID,
24
+ )
25
+
26
+ PG_DRIVER = "pg8000"
27
+
28
+ # Lazy-initialized to avoid import-time GCP auth
29
+ _connector: Connector | None = None
30
+
31
+
32
+ def _get_connector() -> Connector:
33
+ """Get the Cloud SQL connector, initializing lazily on first use."""
34
+ global _connector
35
+ if _connector is None:
36
+ _connector = Connector()
37
+ return _connector
38
+
39
+
40
+ def _get_secret_value(
41
+ gsm_client: secretmanager.SecretManagerServiceClient, secret_id: str
42
+ ) -> str:
43
+ """Get the value of the enabled version of a secret.
44
+
45
+ Args:
46
+ gsm_client: GCP Secret Manager client
47
+ secret_id: The id of the secret
48
+
49
+ Returns:
50
+ The value of the enabled version of the secret
51
+ """
52
+ response = gsm_client.list_secret_versions(
53
+ request={"parent": secret_id, "filter": "state:ENABLED"}
54
+ )
55
+ if len(response.versions) == 0:
56
+ raise ValueError(f"No enabled version of secret {secret_id} found")
57
+ enabled_version = response.versions[0]
58
+ response = gsm_client.access_secret_version(name=enabled_version.name)
59
+ return response.payload.data.decode("UTF-8")
60
+
61
+
62
+ def get_database_creator(pg_connection_details: dict) -> Callable:
63
+ """Create a database connection creator function."""
64
+
65
+ def creator() -> Any:
66
+ return _get_connector().connect(
67
+ pg_connection_details["database_address"],
68
+ PG_DRIVER,
69
+ user=pg_connection_details["pg_user"],
70
+ password=pg_connection_details["pg_password"],
71
+ db=pg_connection_details["database_name"],
72
+ ip_type=IPTypes.PRIVATE,
73
+ )
74
+
75
+ return creator
76
+
77
+
78
+ def get_pool(
79
+ gsm_client: secretmanager.SecretManagerServiceClient,
80
+ ) -> sqlalchemy.Engine:
81
+ """Get a SQLAlchemy connection pool for the Airbyte Cloud database.
82
+
83
+ This function supports two connection modes:
84
+ 1. Direct connection via Cloud SQL Python Connector (default, requires VPC access)
85
+ 2. Connection via Cloud SQL Auth Proxy (when CI or USE_CLOUD_SQL_PROXY env var is set)
86
+
87
+ For proxy mode, start the proxy with:
88
+ cloud-sql-proxy prod-ab-cloud-proj:us-west3:prod-pgsql-replica --port=<port>
89
+
90
+ Environment variables:
91
+ CI: If set, uses proxy connection mode
92
+ USE_CLOUD_SQL_PROXY: If set, uses proxy connection mode
93
+ DB_PORT: Port for proxy connection (default: 5432)
94
+
95
+ Args:
96
+ gsm_client: GCP Secret Manager client for retrieving credentials
97
+
98
+ Returns:
99
+ SQLAlchemy Engine connected to the Prod DB Replica
100
+ """
101
+ pg_connection_details = json.loads(
102
+ _get_secret_value(
103
+ gsm_client, CONNECTION_RETRIEVER_PG_CONNECTION_DETAILS_SECRET_ID
104
+ )
105
+ )
106
+
107
+ if os.getenv("CI") or os.getenv("USE_CLOUD_SQL_PROXY"):
108
+ # Connect via Cloud SQL Auth Proxy, running on localhost
109
+ # Port can be configured via DB_PORT env var (default: 5432)
110
+ host = "127.0.0.1"
111
+ port = os.getenv("DB_PORT", "5432")
112
+ try:
113
+ return sqlalchemy.create_engine(
114
+ f"postgresql+{PG_DRIVER}://{pg_connection_details['pg_user']}:{pg_connection_details['pg_password']}@{host}:{port}/{pg_connection_details['database_name']}",
115
+ )
116
+ except Exception as e:
117
+ raise AssertionError(
118
+ f"sqlalchemy.create_engine exception; could not connect to the proxy at {host}:{port}. "
119
+ f"Error: {traceback.format_exception(e)}"
120
+ ) from e
121
+
122
+ # Default: Connect via Cloud SQL Python Connector (requires VPC access)
123
+ return sqlalchemy.create_engine(
124
+ f"postgresql+{PG_DRIVER}://",
125
+ creator=get_database_creator(pg_connection_details),
126
+ )
File without changes
@@ -0,0 +1,272 @@
1
+ # Copyright (c) 2025 Airbyte, Inc., all rights reserved.
2
+ """Query execution functions for Airbyte Cloud Prod DB Replica.
3
+
4
+ This module provides functions that execute SQL queries against the Prod DB Replica
5
+ and return structured results. Each function wraps a SQL template from sql.py.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import logging
11
+ from collections.abc import Mapping
12
+ from datetime import datetime, timedelta, timezone
13
+ from time import perf_counter
14
+ from typing import Any
15
+
16
+ import sqlalchemy
17
+ from google.cloud import secretmanager
18
+
19
+ from airbyte_ops_mcp.prod_db_access.db_engine import get_pool
20
+ from airbyte_ops_mcp.prod_db_access.sql import (
21
+ SELECT_ACTORS_PINNED_TO_VERSION,
22
+ SELECT_CONNECTIONS_BY_CONNECTOR,
23
+ SELECT_CONNECTOR_VERSIONS,
24
+ SELECT_DATAPLANES_LIST,
25
+ SELECT_NEW_CONNECTOR_RELEASES,
26
+ SELECT_ORG_WORKSPACES,
27
+ SELECT_SUCCESSFUL_SYNCS_FOR_VERSION,
28
+ SELECT_SYNC_RESULTS_FOR_VERSION,
29
+ SELECT_WORKSPACE_INFO,
30
+ )
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ def _run_sql_query(
36
+ statement: sqlalchemy.sql.elements.TextClause,
37
+ parameters: Mapping[str, Any] | None = None,
38
+ *,
39
+ query_name: str | None = None,
40
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
41
+ ) -> list[dict[str, Any]]:
42
+ """Execute a SQL text statement and return rows as list[dict], logging elapsed time.
43
+
44
+ Args:
45
+ statement: SQLAlchemy text clause to execute
46
+ parameters: Query parameters to bind
47
+ query_name: Optional name for logging (defaults to first line of SQL)
48
+ gsm_client: GCP Secret Manager client for retrieving credentials.
49
+ If None, a new client will be instantiated.
50
+
51
+ Returns:
52
+ List of row dicts from the query result
53
+ """
54
+ if gsm_client is None:
55
+ gsm_client = secretmanager.SecretManagerServiceClient()
56
+ pool = get_pool(gsm_client)
57
+ start = perf_counter()
58
+ with pool.connect() as conn:
59
+ result = conn.execute(statement, parameters or {})
60
+ rows = [dict(row._mapping) for row in result]
61
+ elapsed = perf_counter() - start
62
+
63
+ name = query_name or "SQL query"
64
+ logger.info("Prod DB query %s returned %d rows in %.3f s", name, len(rows), elapsed)
65
+
66
+ return rows
67
+
68
+
69
+ def query_connections_by_connector(
70
+ connector_definition_id: str,
71
+ organization_id: str | None = None,
72
+ limit: int = 1000,
73
+ *,
74
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
75
+ ) -> list[dict[str, Any]]:
76
+ """Query connections by source connector type, optionally filtered by organization.
77
+
78
+ Args:
79
+ connector_definition_id: Connector definition UUID to filter by
80
+ organization_id: Optional organization UUID to search within
81
+ limit: Maximum number of results (default: 1000)
82
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
83
+
84
+ Returns:
85
+ List of connection records with workspace and dataplane info
86
+ """
87
+ return _run_sql_query(
88
+ SELECT_CONNECTIONS_BY_CONNECTOR,
89
+ parameters={
90
+ "connector_definition_id": connector_definition_id,
91
+ "organization_id": organization_id,
92
+ "limit": limit,
93
+ },
94
+ query_name="SELECT_CONNECTIONS_BY_CONNECTOR",
95
+ gsm_client=gsm_client,
96
+ )
97
+
98
+
99
+ def query_connector_versions(
100
+ connector_definition_id: str,
101
+ *,
102
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
103
+ ) -> list[dict[str, Any]]:
104
+ """Query all versions for a connector definition.
105
+
106
+ Args:
107
+ connector_definition_id: Connector definition UUID
108
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
109
+
110
+ Returns:
111
+ List of version records ordered by last_published DESC
112
+ """
113
+ return _run_sql_query(
114
+ SELECT_CONNECTOR_VERSIONS,
115
+ parameters={"actor_definition_id": connector_definition_id},
116
+ query_name="SELECT_CONNECTOR_VERSIONS",
117
+ gsm_client=gsm_client,
118
+ )
119
+
120
+
121
+ def query_new_connector_releases(
122
+ days: int = 7,
123
+ limit: int = 100,
124
+ *,
125
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
126
+ ) -> list[dict[str, Any]]:
127
+ """Query recently published connector versions.
128
+
129
+ Args:
130
+ days: Number of days to look back (default: 7)
131
+ limit: Maximum number of results (default: 100)
132
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
133
+
134
+ Returns:
135
+ List of recently published connector versions
136
+ """
137
+ cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
138
+ return _run_sql_query(
139
+ SELECT_NEW_CONNECTOR_RELEASES,
140
+ parameters={"cutoff_date": cutoff_date, "limit": limit},
141
+ query_name="SELECT_NEW_CONNECTOR_RELEASES",
142
+ gsm_client=gsm_client,
143
+ )
144
+
145
+
146
+ def query_actors_pinned_to_version(
147
+ connector_version_id: str,
148
+ *,
149
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
150
+ ) -> list[dict[str, Any]]:
151
+ """Query actors (sources/destinations) pinned to a specific connector version.
152
+
153
+ Args:
154
+ connector_version_id: Connector version UUID to search for
155
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
156
+
157
+ Returns:
158
+ List of actors pinned to the specified version
159
+ """
160
+ return _run_sql_query(
161
+ SELECT_ACTORS_PINNED_TO_VERSION,
162
+ parameters={"actor_definition_version_id": connector_version_id},
163
+ query_name="SELECT_ACTORS_PINNED_TO_VERSION",
164
+ gsm_client=gsm_client,
165
+ )
166
+
167
+
168
+ def query_sync_results_for_version(
169
+ connector_version_id: str,
170
+ days: int = 7,
171
+ limit: int = 100,
172
+ successful_only: bool = False,
173
+ *,
174
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
175
+ ) -> list[dict[str, Any]]:
176
+ """Query sync job results for actors pinned to a specific connector version.
177
+
178
+ Args:
179
+ connector_version_id: Connector version UUID to filter by
180
+ days: Number of days to look back (default: 7)
181
+ limit: Maximum number of results (default: 100)
182
+ successful_only: If True, only return successful syncs (default: False)
183
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
184
+
185
+ Returns:
186
+ List of sync job results
187
+ """
188
+ cutoff_date = datetime.now(timezone.utc) - timedelta(days=days)
189
+ query = (
190
+ SELECT_SUCCESSFUL_SYNCS_FOR_VERSION
191
+ if successful_only
192
+ else SELECT_SYNC_RESULTS_FOR_VERSION
193
+ )
194
+ query_name = (
195
+ "SELECT_SUCCESSFUL_SYNCS_FOR_VERSION"
196
+ if successful_only
197
+ else "SELECT_SYNC_RESULTS_FOR_VERSION"
198
+ )
199
+ return _run_sql_query(
200
+ query,
201
+ parameters={
202
+ "actor_definition_version_id": connector_version_id,
203
+ "cutoff_date": cutoff_date,
204
+ "limit": limit,
205
+ },
206
+ query_name=query_name,
207
+ gsm_client=gsm_client,
208
+ )
209
+
210
+
211
+ def query_dataplanes_list(
212
+ *,
213
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
214
+ ) -> list[dict[str, Any]]:
215
+ """Query all dataplane groups with workspace counts.
216
+
217
+ Args:
218
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
219
+
220
+ Returns:
221
+ List of dataplane groups ordered by workspace count DESC
222
+ """
223
+ return _run_sql_query(
224
+ SELECT_DATAPLANES_LIST,
225
+ query_name="SELECT_DATAPLANES_LIST",
226
+ gsm_client=gsm_client,
227
+ )
228
+
229
+
230
+ def query_workspace_info(
231
+ workspace_id: str,
232
+ *,
233
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
234
+ ) -> dict[str, Any] | None:
235
+ """Query workspace info including dataplane group.
236
+
237
+ Args:
238
+ workspace_id: Workspace UUID
239
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
240
+
241
+ Returns:
242
+ Workspace info dict, or None if not found
243
+ """
244
+ rows = _run_sql_query(
245
+ SELECT_WORKSPACE_INFO,
246
+ parameters={"workspace_id": workspace_id},
247
+ query_name="SELECT_WORKSPACE_INFO",
248
+ gsm_client=gsm_client,
249
+ )
250
+ return rows[0] if rows else None
251
+
252
+
253
+ def query_org_workspaces(
254
+ organization_id: str,
255
+ *,
256
+ gsm_client: secretmanager.SecretManagerServiceClient | None = None,
257
+ ) -> list[dict[str, Any]]:
258
+ """Query all workspaces in an organization with dataplane info.
259
+
260
+ Args:
261
+ organization_id: Organization UUID
262
+ gsm_client: GCP Secret Manager client. If None, a new client will be instantiated.
263
+
264
+ Returns:
265
+ List of workspaces in the organization
266
+ """
267
+ return _run_sql_query(
268
+ SELECT_ORG_WORKSPACES,
269
+ parameters={"organization_id": organization_id},
270
+ query_name="SELECT_ORG_WORKSPACES",
271
+ gsm_client=gsm_client,
272
+ )