Skip to content

fix(ingestion): bind the test-connection diagnoses to errors drivers really raise#30155

Open
IceS2 wants to merge 4 commits into
mainfrom
fix/testconn-p0-provenanced-tests
Open

fix(ingestion): bind the test-connection diagnoses to errors drivers really raise#30155
IceS2 wants to merge 4 commits into
mainfrom
fix/testconn-p0-provenanced-tests

Conversation

@IceS2

@IceS2 IceS2 commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Closes #30153

Stacked on #30150 — targets connector-improvements-review so the diff is only this branch's work. GitHub will retarget it to main when #30150 merges.

Every matcher kept or added here is traceable to a real driver observation: the exception class imported, or the driver source / official documentation cited in a comment. Where provenance could not be established, the matcher was deleted rather than guessed at.

The connection reported the wrong outcome

airflow The gate went green against any web server. ometa/client.py returns the raw Response on a JSONDecodeError; _parse_response then catches Exception and returns {}, so check_access never raised. Added a strict test_get_version() (get_raw + raise_for_status, mirroring dbt Cloud's _test_get) and pointed the gate at it; get_version keeps its lenient behaviour for ingestion.
mssql All six error codes were dead. Matchers.errno needs an int at args[0]; pytds (the default) puts the message there, pymssql a (number, message) tuple, pyodbc a SQLSTATE string. Added _mssql_number, reading .number/.msg_no and the pymssql tuple across the chain and .orig. The driver comment named pymssql and pyodbc while the default is pytds — corrected.
databricks get_tables/get_views returned [] when no catalog or schema resolved, so a mandatory step passed. They now raise, as Unity Catalog already did.
powerbi The real envelope nests code under error, so the shared client returned None and DashboardsResponse(**None) raised TypeError. Added PowerBiApiError + _test_get, and split the vague auth diagnosis into the three AADSTS causes — gated on InvalidSourceException, so a status-coded REST error echoing a code still classifies by status.

Rules deleted, with the evidence

  • redshift 28000/28P01 — the module's own docstring already said connect-phase errors carry no SQLSTATE. Confirmed against PostgreSQL 15: wrong password → pgcode=None, missing database → pgcode=None, query-phase denial → pgcode='42501'. That validated the deletion and keeping 42501.
  • databricks contains("forbidden")Error.__str__ returns only the message; the status lives in context["http-code"], and "forbidden" appears nowhere in databricks-sql. Replaced with a regression test pinning that a table named forbidden_items is not misread as "Access denied".
  • snowflake 290404 — a bad account returns 403, and ForbiddenError.__init__ adds ER_HTTP_GENERAL_ERROR + 250001, so the code is 540001. Keyed on the message instead: 540001 is an artifact of that double-add, the message is the driver's deliberate signal.
  • looker's NETWORK_ERRORS fold — driving the real transport shows a ConnectionRefusedError comes back as Response(ok=False, value=b'[Errno 61] Connection refused'). The type is destroyed, so the rules cannot fire; removal changes no diagnosis, because _transport_text already yields the same titles.
  • dbtcloud's NETWORK_ERRORS fold — the rules were dead by precedence, not absence: a real DNS failure does carry gaierror, but the connector's own requests-typed rules always match first. Deleted rather than adding a TCP preflight: dbt Cloud is public SaaS, requests honours HTTPS_PROXY and a raw probe does not, so a preflight would fail a working proxied setup — the case ping's own docstring warns about.

Judgement calls worth a look

  • mssql 262 and 300 dropped, 297 kept. 262 is statement permission (CREATE DATABASE) — test-connection only SELECTs. SQL Server emits 300+297 together on a VIEW SERVER STATE denial, which GetQueries provokes; pytds keeps only the last message's number (297), and 297's own text lacks "permission was denied", so the number is the only signal that catches it.
  • No databricks 403 rule. The mechanism (context["http-code"]) exists, but whether a real 403 populates it could not be established without a live workspace, so no matcher.

Tests

The fixtures are the point. mssql builds its errors through pytds's own _create_exception_by_message and the real pyodbc class, and proves the codes with German messages that no English contains rule can match. Several connectors now drive TestConnectionRunner.run() to a TestConnectionResult against a real mssql+pytds engine, so SQLAlchemy does the .orig wrapping itself.

Mutation-checked: neutering _mssql_number fails 9 tests — against the previous state, where neutering Matchers.errno entirely left all 20 mssql tests green.

Not validated live against real Airflow, Power BI, Databricks or Snowflake; provenance is driver source, official docs, or a local PostgreSQL container. Only the connectors named here were audited.

Greptile Summary

This PR aligns ingestion test-connection diagnoses with errors produced by the supported drivers and APIs. The main changes are:

  • Adds strict, bounded test requests for Airflow and Power BI.
  • Preserves HTTP status failures for connector-specific diagnosis.
  • Corrects MSSQL error-number extraction across supported drivers.
  • Fails Databricks checks when no catalog or schema can be resolved.
  • Updates error matchers and tests for Looker, Redshift, Snowflake, and dbt Cloud.

Confidence Score: 5/5

This looks safe to merge.

  • Raw request failures no longer become None before Airflow or Power BI handles them.
  • Retry exhaustion preserves a status-bearing error for the connector diagnosis rules.
  • No blocking issues remain in the updated code.

Important Files Changed

Filename Overview
ingestion/src/metadata/ingestion/ometa/client.py Adds retry overrides to raw GET requests and re-raises unexpected raw-request failures.
ingestion/src/metadata/ingestion/source/pipeline/airflow/api/client.py Adds a strict, bounded version check that preserves actionable HTTP failures.
ingestion/src/metadata/ingestion/source/dashboard/powerbi/client.py Adds strict test-only API calls and status-bearing Power BI errors.

Reviews (3): Last reviewed commit: "Merge branch 'main' into fix/testconn-p0..." | Re-trigger Greptile

Context used (3)

  • Context used - CLAUDE.md (source)
  • Context used - openmetadata-ui-core-components/CLAUDE.md (source)
  • Context used - AGENTS.md (source)

@github-actions github-actions Bot added Ingestion safe to test Add this label to run secure Github workflows on PRs labels Jul 16, 2026
Base automatically changed from connector-improvements-review to main July 17, 2026 13:56
…ly raise (#30153)

Rebased onto main after #30150 squash-merged; the dedup this branch was stacked
on now lives in main, so this carries only the provenance fixes.

Every matcher kept or added is traceable to a real driver observation - the
exception class imported, or the driver source / official docs cited. Where
provenance could not be established the matcher was deleted rather than guessed.

- airflow: the gate passed against any web server (JSONDecodeError swallowed to
  {}) and, for MWAA, against a hardcoded stub; both now use a strict accessor
  that really fails. A raw get_raw error (a mistyped host) no longer strands the
  caller on a swallowed None, and a 429 surfaces the driver's real HTTPError
  instead of a blank LimitsException.
- mssql: the six error numbers were dead under every supported driver (pytds
  keeps only the last message's number); dropped 4060/300/911/262, kept
  18456/229/297, verified live against Azure SQL Edge.
- powerbi: the status rules were dead on the real envelope (None -> TypeError);
  fixed, retry budget bounded, AADSTS causes split.
- databricks: GetTables/GetViews no longer pass with nothing resolved; the dead,
  over-broad "forbidden" rule removed.
- redshift/snowflake/looker/dbtcloud: rules that cannot fire deleted, with the
  fixtures that "proved" them.

Fixtures build errors through the driver's own factory and drive
TestConnectionRunner end to end, so they cannot drift from the driver.
@IceS2
IceS2 force-pushed the fix/testconn-p0-provenanced-tests branch from 2364af3 to 4bf5838 Compare July 17, 2026 14:32
…g fact

The provenance comments explained why each dead rule is absent - valuable, but
written as multi-paragraph essays. Each is now the fact plus a source reference:
the pytds message-folding quirk, why NETWORK_ERRORS is not folded (looker,
dbtcloud), the snowflake login-403, the strict-accessor rationale. No rule,
matcher or test changed.
@IceS2
IceS2 marked this pull request as ready for review July 17, 2026 16:55
@IceS2
IceS2 requested a review from a team as a code owner July 17, 2026 16:55
Copilot AI review requested due to automatic review settings July 17, 2026 16:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

return cast(
"requests.Response",
self._request("GET", path, data, headers=headers, retry_wait=retry_wait, raw=True),
self._request("GET", path, data, headers=headers, retry_wait=retry_wait, retries=retries, raw=True),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Retry Exhaustion Returns None

When all bounded attempts receive a retryable status, _request exits the retry loop with None, which this method casts to Response. Repeated Airflow 504s then fail at response.raise_for_status(), and repeated Power BI 429s fail at response.ok, producing an unclassified AttributeError instead of the intended HTTP diagnosis.

Context Used: CLAUDE.md (source)

…haviour

The source-level tests were updated with the fixes; the parallel topology tests
still asserted the old behaviour and failed CI.

- databricks get_tables/get_views: no longer return [] on an unresolved schema
  (the false-pass a mandatory step used to hide) - now raise, as the source test
  already checks.
- airflow: the CheckAccess gate reads via test_get_version, not the lenient
  get_version, so its decoration tests mock the strict accessor.
Copilot AI review requested due to automatic review settings July 17, 2026 17:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI review requested due to automatic review settings July 17, 2026 19:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Refactors test-connection diagnostic matchers to align with actual driver error behavior, improving reliability by replacing speculative rules with provenance-backed logic. Verified against real SQLAlchemy engine wrappers and driver-specific error patterns.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (34 flaky)

✅ 4537 passed · ❌ 0 failed · 🟡 34 flaky · ⏭️ 95 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 436 0 4 16
✅ Shard 2 11 0 0 0
🟡 Shard 3 817 0 15 8
🟡 Shard 4 822 0 1 18
🟡 Shard 5 837 0 4 5
🟡 Shard 6 786 0 2 46
🟡 Shard 7 828 0 8 2
🟡 34 flaky test(s) (passed on retry)
  • Flow/TestConnectionModal.spec.ts › modal opens with gate card and capability checks sections (shard 1, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: metric (shard 1, 1 retry)
  • Pages/SearchSettings.spec.ts › Latest preview config wins when a superseded request resolves late (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › the browse tree only shows the asset-type categories a user can access (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Database (shard 3, 2 retries)
  • Features/BulkImportWithDotInName.spec.ts › Full import cycle with dot in service name (shard 3, 1 retry)
  • Features/ContextCenterArchive.spec.ts › archive page lazy-loads more rows on scroll within its own scroll container (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › Article listing search filters, clears, and shows empty state (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › Article list cards, recently viewed widget, and pagination work (shard 3, 1 retry)
  • Features/ContextCenterArticles.spec.ts › description: switching articles does not bleed unsaved content into next article (shard 3, 1 retry)
  • Features/ContextCenterMemories.spec.ts › clearing search restores the unfiltered list (shard 3, 1 retry)
  • Features/ContextCenterPermission.spec.ts › user with editAll permission can see restore action but not delete action on an archived document, and can restore it (shard 3, 1 retry)
  • Features/ContextCenterPermission.spec.ts › user with deleteAll permission can see delete action but not restore action on an archived document, and can delete it (shard 3, 1 retry)
  • Features/ContextCenterPermission.spec.ts › a document archived by a different user can be permanently deleted, via the UI, by a user with Delete permission (shard 3, 1 retry)
  • Features/CuratedAssets.spec.ts › Test Glossary Terms with display name filter (shard 3, 1 retry)
  • Features/DataQuality/BundleSuiteBulkOperations.spec.ts › Add test case to existing Bundle Suite (shard 3, 1 retry)
  • Features/DataQuality/TestLibrary.spec.ts › should maintain page on edit and reset to first page on delete (shard 3, 1 retry)
  • Features/Glossary/GlossaryAdvancedOperations.spec.ts › should remove tags from term (shard 3, 1 retry)
  • Features/Glossary/GlossaryMiscOperations.spec.ts › should delete term and remove tag from assets (shard 3, 1 retry)
  • Features/SearchExport.spec.ts › Export queues a background job and downloads from the jobs tray (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Sql Query (shard 5, 1 retry)
  • Pages/CustomProperties.spec.ts › Set & Update all CP types on dataProduct (shard 5, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Set (shard 5, 1 retry)
  • Pages/DataContractsSemanticRules.spec.ts › Validate Description Rule Is_Not_Set (shard 5, 1 retry)
  • Pages/ExploreBrowse.spec.ts › service type drill-down disables unrelated roots and query-panel Clear resets it (shard 6, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 6, 1 retry)
  • Features/AutoPilot.spec.ts › Create Service and check the AutoPilot status (shard 7, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Glossary Bulk Import Export (shard 7, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Import partial success - some terms pass, some fail (shard 7, 1 retry)
  • Pages/InputOutputPorts.spec.ts › Output ports section collapse/expand (shard 7, 1 retry)
  • ... and 4 more

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ingestion safe to test Add this label to run secure Github workflows on PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test-connection: dead error-pack rules and four connectors reporting the wrong outcome

2 participants