Repository navigation
Add Azure Blob Storage support to the DataFusion object storage layer - #73374
Merged
pankajastro merged 20 commits intoOct 5, 2026
Merged
pankajastro merged 20 commits into
pankajastro merged 20 commits into
Conversation
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
3 times, most recently
from
September 21, 2026 04:06
38a50f6 to
e3a4925
Compare
pankajastro
marked this pull request as ready for review
September 21, 2026 08:59
pankajastro
requested review from
Lee-W,
ashb,
gopidesupavan and
kaxil
as code owners
September 21, 2026 08:59
kaxil
reviewed
Sep 22, 2026
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
from
September 22, 2026 12:48
268a566 to
417c9c8
Compare
kaxil
reviewed
Sep 22, 2026
Member
|
Round 2 at 016a9bc. The three code comments from the first round are addressed: the account now comes from Four new items, all measured against the real datafusion binding and object_store source:
Inline comments have the details and suggested fixes. Verdict: hold until these are addressed. |
Member
|
@pankajastro Conflicts ! |
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
from
September 23, 2026 10:23
e42b8fc to
85db1b2
Compare
Member
Author
Rebased onto current main, thanks — should be conflict-free now. |
kaxil
reviewed
Sep 23, 2026
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
from
September 23, 2026 12:06
05f7edc to
657a115
Compare
1 task done
Lee-W
reviewed
Sep 24, 2026
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
from
September 24, 2026 08:47
657a115 to
5be81f7
Compare
kaxil
reviewed
Sep 29, 2026
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
from
September 29, 2026 08:05
add8c41 to
268a08e
Compare
kaxil
reviewed
Sep 29, 2026
Lee-W
approved these changes
Oct 2, 2026
Missing comma made "Azure Blob Storage Parquet" read as one item instead of two separate examples in the supported source list.
The Analytics operator docs listed Azure as supported in the intro line but not in the Supported Storage Systems list, still flagged it under "not yet supported", and had no usage example or credential docs, unlike S3 and GCS.
The wasb credential branch took the storage account name unconditionally from the connection's login field. For service-principal auth, login holds the client_id, not the account name, so a connection whose host correctly named the storage account still had the object store target the client_id GUID instead. A connection with no login at all (key/SAS auth) silently fell back to the AZURE_STORAGE_ACCOUNT_NAME environment variable. Resolve the account from host first, matching WasbHook's own behavior, and fall back to login only when host is empty. Also format the storage type name consistently with .value (avoids drifting str-Enum formatting across Python versions) and move the sas_token parsing import to the top of the file.
abfs:// and abfss:// embed the storage account in the URI itself, a different shape from az:// that engine.py does not parse; using them currently fails with an unhelpful "Unsupported storage type" error with no hint that Azure support exists under a different scheme.
Four issues surfaced by review, each verified against the real datafusion binding and object_store source: - An empty connection (no host or login -- the shape of the wasb_default connection airflow db creates) sent the literal string "None" as the account instead of omitting it, breaking the fallback to AZURE_STORAGE_ACCOUNT_NAME that worked before the host-priority fix. - A partial service-principal config (tenant_id set but login or password missing) silently fell through to ambient auth, or sent the client secret as a shared key, instead of raising -- authenticating as a different identity than the one configured. - The account name kept only the first DNS label, so a sovereign-cloud host (*.chinacloudapi.cn, *.usgovcloudapi.net) or the Azurite emulator's loopback URL was silently redirected to the public *.blob.core.windows.net account of the same name; the binding has no endpoint override to correct this after the fact. - DataFusion's MicrosoftAzure always reads AZURE_* worker environment variables before overlaying the connection's credential, and checks an environment-derived access key or workload-identity token ahead of the connection's SAS token or client secret. In the common AKS workload-identity deployment, this meant the connection's credential was silently ignored in favor of the pod's identity. The binding has no way to skip its own environment read, so this can only be caught, not fixed.
WasbHook and GoogleBaseHook still fall back to fields written as extra__wasb__<name> / extra__google_cloud_platform__<name> -- the spelling older Airflow connection UIs used for custom extra fields. This code only looked at the bare key, so a connection created that way had its sas_token or connection_string silently invisible: the unsupported-field guard never saw a prefixed connection_string, and a prefixed sas_token fell through to ambient auth instead of being used.
Same facts, fewer words -- the mechanism and consequence were spelled out twice in slightly different phrasing.
Same facts, plainer words -- "outrank", "ahead of", and "consulted last" weren't adding anything a simpler verb wouldn't.
Names the actual library so it's clear this is DataFusion's underlying binding's behavior, not an Airflow-defined setting, and contrasts it directly with WasbHook's automatic az login to explain why it's worth calling out at all.
The docs spellcheck job uses an American-English dictionary; "honors" is already the spelling used elsewhere in this same doc tree.
Collapse the legacy-field lookup into one dict.get() call, and use the walrus operator so tenant_id/sas_token are only computed where they're actually checked, instead of unconditionally ahead of the branch. Also consolidate the three shared-key test variants (password, shared_access_key extra, account_key extra) into one parametrized test, picking up account_key coverage that was previously untested.
The env-var precedence guard was missing two real object_store aliases (AZURE_STORAGE_MASTER_KEY and the AZURE_CLIENT_ID/SECRET/TENANT_ID triple, including the AKS workload-identity webhook's own env vars) that silently outrank a connection's SAS token, while AZURE_STORAGE_SAS_KEY never can and was raising for nothing. The account-resolution check also rejected a non-public-cloud host even when the worker had already set AZURE_STORAGE_ENDPOINT to fix exactly that, so the documented workaround never actually worked. Finally, the SAS-token branch was checked ahead of the shared-key extra, the opposite of WasbHook.get_conn's order -- a connection with a current shared_access_key but an expired SAS token would work through the hook and fail here. Co-Authored-By: Claude <noreply@anthropic.com>
They were declared next to the unrelated _AZURE_PUBLIC_SUFFIX constant, ~180 lines from _get_credentials, the only place that reads them -- inconsistent with the file's existing convention of keeping a constant next to its sole user. Co-Authored-By: Claude <noreply@anthropic.com>
The flat guard list raised for env vars that could never actually outrank a connection's own credential in object_store's precedence chain -- most notably AZURE_FEDERATED_TOKEN_FILE against a plain shared-key connection, which blocks the single most common Azure Kubernetes deployment shape (the workload-identity webhook injects it into every labelled pod) even though the connection's own key always wins there. Also closes a gap in the sovereign-cloud/endpoint-override check: an Azurite-style host (bare hostname or host:port, with no login) fell through an unrelated Active-Directory-ID fallback and silently resolved to the literal string "None" instead of raising. Co-Authored-By: Claude <noreply@anthropic.com>
… tier Each credential tier (access_key, service_principal, SAS) had a "rejects when outranked" test and a separate "ignores when it can't be outranked" test that only differed in which env vars were set and whether the call was expected to raise. Folding both into a single should_raise-parametrized test per tier keeps the same coverage with far less repetition. Co-Authored-By: Claude <noreply@anthropic.com>
Left over from incrementally adding the emulator-host colon check; the suffix check and the endpoint-override check were always meant to be one combined condition, not a nested if. Co-Authored-By: Claude <noreply@anthropic.com>
…ring object_store's resolve_sas_token() does read this variable into self.sas_key; it's just checked after sas_query_pairs within that same step, so it never wins once the connection sets sas_query_pairs. "Dead" overstated that as unused entirely, which could mislead a future reader into thinking the safety argument doesn't depend on precedence order. Co-Authored-By: Claude <noreply@anthropic.com>
pankajastro
force-pushed
the
fix-datafusion-azure-credentials-afl-214
branch
from
October 5, 2026 06:55
a1c20f8 to
8289d47
Compare
1 task done
pankajastro
added a commit
to pankajastro/airflow
that referenced
this pull request
Oct 5, 2026
PR apache#73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. Co-Authored-By: Claude <noreply@anthropic.com>
pankajastro
added a commit
to pankajastro/airflow
that referenced
this pull request
Oct 6, 2026
PR apache#73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. Co-Authored-By: Claude <noreply@anthropic.com>
pankajastro
added a commit
to pankajastro/airflow
that referenced
this pull request
Oct 7, 2026
PR apache#73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. Co-Authored-By: Claude <noreply@anthropic.com>
pankajastro
added a commit
to pankajastro/airflow
that referenced
this pull request
Oct 8, 2026
PR apache#73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. Co-Authored-By: Claude <noreply@anthropic.com>
pankajastro
added a commit
to pankajastro/airflow
that referenced
this pull request
Oct 9, 2026
PR apache#73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. Co-Authored-By: Claude <noreply@anthropic.com>
pankajastro
added a commit
to pankajastro/airflow
that referenced
this pull request
Oct 14, 2026
PR apache#73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. Co-Authored-By: Claude <noreply@anthropic.com>
kaxil
added a commit
that referenced
this pull request
Oct 14, 2026
#74228) PR #73374 added az:// support to the DataFusion object storage layer but deliberately deferred abfs:// and abfss://, since they embed the storage account in the URI itself (container@account.host/path) rather than relying solely on the wasb connection, raising an open question of which account should win on disagreement. Datasources can now use either scheme. When the URI names an account that disagrees with the one the wasb connection resolves to, registering the datasource raises instead of silently picking one. * Quote az:// and abfs(s):// in docstrings to fix docs spellcheck Sphinx's spellchecker flattens unquoted docstring prose to plain text, so the bare az:// and abfs(s):// in two docstrings were flagged as misspelled words. Wrapping them in double backticks, the convention already used elsewhere in this file, excludes them as RST literals. * Pass host to register_object_store so bucket-style URIs resolve DataFusion's object-store registry keys on (schema, host); omitting host only matches an empty-authority URI like file:///path. Every bucket/container-style scheme (s3://, gs://, az://, and now abfs(s)://) therefore failed at query time with "No suitable object store found", a pre-existing gap in the original az:// support invisible to the test suite because it mocks SessionContext rather than exercising DataFusion's real registry. Found by manually testing abfs:// against a real storage account. * Fix object-store registry collisions, LOCAL regression, and config mutation Addresses review feedback: the abfs(s) object-store registry key collided for two containers on the same storage account, an explicit storage_type=StorageType.LOCAL with a bare path regressed when get_scheme was generalized to take a URI, and a connection resolving no account silently fell back to AZURE_STORAGE_ACCOUNT_NAME instead of the account named in the URI. Fixing the first of those by folding the container alone into the registry's host key turned out to trade one collision for another -- two different accounts sharing a container name would then collide instead. Folding both account and container into the host key avoids both. Also stopped register_datasource from mutating the caller's DataSourceConfig.uri in place: reusing the same config across a second engine fed the already-normalized URI back through get_bucket, which misread the account+container host token as the container name. The canonical URI is now threaded through locally instead. Co-authored-by: Kaxil Naik <kaxilnaik@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
DataFusion credential resolution only handled AWS (and, in a sibling PR, GCS), so a wasb-backed
DataSourceConfigfailed withUnknown connection type wasbat registration, even though DataFusion's storage layer supports Azure Blob Storage.Adds:
StorageType.AZUREandaz://URI recognition inDataSourceConfigAzureObjectStorageProviderusing DataFusion'sMicrosoftAzureobject store bindingwasbbranch in_get_credentialsresolving, in order: Azure AD service principal (tenant_idextra +login/passwordasclient_id/client_secret), SAS token (sas_tokenextra, parsed into query pairs), then shared key (password, orshared_access_key/account_keyextra) — falling back to ambient/environment-based auth when none applyOn the service-principal fields: DataFusion's
MicrosoftAzurebinding (crates/core/src/store.rs) panics ifclient_id/client_secret/tenant_idare only partially set — verified directly:(PyO3 converts this to a
PanicExceptionrather than crashing the process, but it's still an ugly failure mode.) So this PR only forwards all three when the connection genuinely has all three (tenant_idextra plus bothloginandpassword); otherwise it falls through to the next auth mode rather than risking a partial combination.Unsupported fields:
connection_string,managed_identity_client_id,workload_identity_tenant_id, and a URL-formsas_tokenaren't supported by DataFusion's Azure binding. Setting any of these now raises a clear error instead of silently falling back to a different identity.Scope: only
az://URIs are recognized for now —abfs:///abfss://are not. Those schemes embed the storage account in the URI itself (e.g.abfss://container@account.dfs.core.windows.net/path), which is a different shape fromaz://and raises a real question givenhostnow takes priority when resolving the account from the connection: if the URI's account differs from the connection's, which one wins? That's a design decision worth its own PR rather than bundling in here.Was generative AI tooling used to co-author this PR?
Generated-by: Claude Code (Sonnet 5) following the guidelines