andygrove commented on code in PR #7221:
URL: https://github.com/apache/datafusion-comet/pull/7221#discussion_r4371472718


##########
spark/src/main/scala/org/apache/comet/objectstore/AbfsAuthResolver.scala:
##########
@@ -467,6 +469,38 @@ private[comet] object AbfsAuthResolver extends Logging {
         Markers.ERROR_MESSAGE -> message)
   }
 
+  /**
+   * Why the native store cannot read `uri` under `outcome`, or None when it 
can: a mechanism
+   * Hadoop named that runs Java code per request, a built-in OAuth provider 
with no
+   * `object_store` counterpart, or a resolution that failed. All are known on 
the driver, so the
+   * scan falls back to Spark's own reader, which authenticates through 
hadoop-azure, instead of
+   * planning natively and failing every task.
+   */
+  def nativeFallbackReason(uri: URI, outcome: Outcome): Option[String] = {
+    // The container and host only: a password in the user info stays out of 
the reason.
+    val authority = Option(uri.getRawAuthority).getOrElse("")
+    val at = authority.lastIndexOf('@')
+    val location = s"${uri.getScheme}://" +
+      (if (at < 0) authority
+       else authority.substring(0, at).takeWhile(_ != ':') + 
authority.substring(at))
+    def unsupported(mechanism: String): String =
+      s"Native scan does not support ABFS authentication $mechanism for 
$location"
+    outcome match {
+      case Declined(authType, providerClass, assertionProviderClass) =>
+        val classes = providerClass.map(c => s" with provider 
$c").getOrElse("") +
+          assertionProviderClass.map(c => s" and client assertion provider 
$c").getOrElse("")
+        Some(unsupported(s"auth type $authType$classes"))
+      case Resolved(
+            AuthTypes.OAUTH,
+            Some(c @ (Providers.REFRESH_TOKEN | Providers.USER_PASSWORD)),
+            _) =>
+        Some(unsupported(s"auth type ${AuthTypes.OAUTH} with provider $c"))
+      case Failed(_, message) =>

Review Comment:
   Good catch. The `Failed` arm of `nativeFallbackReason` now replaces the raw 
authority in Hadoop's message with the password-free one, so the `sharedKey` 
and `authorityParts` messages no longer carry it into the reason. I did it 
there and left the three messages alone because `AbfsAuthParitySuite` compares 
them with Hadoop's. The new test in `NativeConfigSuite` is the case you 
described: a SharedKey key for `accta`, a short account name and a password in 
the URL. It checks the reason has `abfss://data@accta` and not the password. 
Pushed in c2a7adb75f.



##########
spark/src/main/scala/org/apache/comet/objectstore/AbfsAuthResolver.scala:
##########
@@ -467,6 +469,38 @@ private[comet] object AbfsAuthResolver extends Logging {
         Markers.ERROR_MESSAGE -> message)
   }
 
+  /**
+   * Why the native store cannot read `uri` under `outcome`, or None when it 
can: a mechanism
+   * Hadoop named that runs Java code per request, a built-in OAuth provider 
with no
+   * `object_store` counterpart, or a resolution that failed. All are known on 
the driver, so the
+   * scan falls back to Spark's own reader, which authenticates through 
hadoop-azure, instead of
+   * planning natively and failing every task.
+   */
+  def nativeFallbackReason(uri: URI, outcome: Outcome): Option[String] = {
+    // The container and host only: a password in the user info stays out of 
the reason.
+    val authority = Option(uri.getRawAuthority).getOrElse("")
+    val at = authority.lastIndexOf('@')
+    val location = s"${uri.getScheme}://" +
+      (if (at < 0) authority
+       else authority.substring(0, at).takeWhile(_ != ':') + 
authority.substring(at))
+    def unsupported(mechanism: String): String =
+      s"Native scan does not support ABFS authentication $mechanism for 
$location"
+    outcome match {
+      case Declined(authType, providerClass, assertionProviderClass) =>
+        val classes = providerClass.map(c => s" with provider 
$c").getOrElse("") +
+          assertionProviderClass.map(c => s" and client assertion provider 
$c").getOrElse("")
+        Some(unsupported(s"auth type $authType$classes"))
+      case Resolved(
+            AuthTypes.OAUTH,
+            Some(c @ (Providers.REFRESH_TOKEN | Providers.USER_PASSWORD)),
+            _) =>
+        Some(unsupported(s"auth type ${AuthTypes.OAUTH} with provider $c"))
+      case Failed(_, message) =>
+        Some(s"Hadoop ABFS authentication failed on the driver for $location: 
$message")
+      case _: Resolved | Unconfigured => None

Review Comment:
   Added. `NativeConfigSuite` now has a test with one `Resolved` case per 
supported mechanism (ClientCreds, MSI, Workload Identity, fixed SAS token), 
each asserting `Right` with `comet.azure.resolution=resolved`. Workload 
Identity and the fixed SAS token only run where hadoop-azure has them (3.4.1 
and later). Widening the RefreshToken/UserPassword arm would now fail it.



##########
spark/src/main/scala/org/apache/comet/objectstore/AbfsAuthResolver.scala:
##########
@@ -467,6 +469,38 @@ private[comet] object AbfsAuthResolver extends Logging {
         Markers.ERROR_MESSAGE -> message)
   }
 
+  /**
+   * Why the native store cannot read `uri` under `outcome`, or None when it 
can: a mechanism
+   * Hadoop named that runs Java code per request, a built-in OAuth provider 
with no
+   * `object_store` counterpart, or a resolution that failed. All are known on 
the driver, so the
+   * scan falls back to Spark's own reader, which authenticates through 
hadoop-azure, instead of
+   * planning natively and failing every task.
+   */
+  def nativeFallbackReason(uri: URI, outcome: Outcome): Option[String] = {
+    // The container and host only: a password in the user info stays out of 
the reason.
+    val authority = Option(uri.getRawAuthority).getOrElse("")
+    val at = authority.lastIndexOf('@')
+    val location = s"${uri.getScheme}://" +
+      (if (at < 0) authority
+       else authority.substring(0, at).takeWhile(_ != ':') + 
authority.substring(at))
+    def unsupported(mechanism: String): String =
+      s"Native scan does not support ABFS authentication $mechanism for 
$location"
+    outcome match {
+      case Declined(authType, providerClass, assertionProviderClass) =>
+        val classes = providerClass.map(c => s" with provider 
$c").getOrElse("") +
+          assertionProviderClass.map(c => s" and client assertion provider 
$c").getOrElse("")

Review Comment:
   Added two direct `nativeFallbackReason` tests that run on every profile: 
`Declined("OAuth", Some(WORKLOAD_IDENTITY), Some("com.example.Assertion"))`, 
which checks the provider and the client assertion provider suffix, and 
`Declined("UserboundSASWithOAuth", None)`.



##########
docs/source/user-guide/latest/datasources.md:
##########
@@ -479,15 +479,15 @@ hadoop-azure must be on the driver classpath. It already 
is whenever Spark can l
 | `WorkloadIdentityTokenProvider` with a 
`fs.azure.account.oauth2.client.assertion.provider.type` (hadoop-azure 3.5.0 
and later) | Not supported |                                                    
                                                                                
                                                                                
                                                          |
 | `UserboundSASWithOAuth` (hadoop-azure 3.5.0 and later)                       
                                                  | Not supported |             
                                                                                
                                                                                
                                                                                
                 |
 
-Custom provider classes hand each token request to Java code the native scan 
cannot call, and the refresh token and user password flows have no 
`object_store` counterpart. When Hadoop selects one of these, the scan fails 
with an error naming the auth type and, where one is configured, the class. 
Configure a supported mechanism for that account, or keep the path on Spark's 
own scan. Values arrive as Hadoop resolved them, trimmed and defaulted where 
the ABFS driver trims and defaults them, so the store sees what the driver 
would have used.
+Custom provider classes hand each token request to Java code the native scan 
cannot call, and the refresh token and user password flows have no 
`object_store` counterpart. When Hadoop selects one of these, the scan falls 
back to Spark's own scan, which authenticates through hadoop-azure, and the 
fallback reason names the auth type and, where one is configured, the class. 
Configure a supported mechanism for that account to read it natively. Values 
arrive as Hadoop resolved them, trimmed and defaulted where the ABFS driver 
trims and defaults them, so the store sees what the driver would have used.

Review Comment:
   You're right. `resolve` returns `Declined(Custom, None)`, so there is no 
class to name. I changed the docs instead of the code: the reason names the 
provider class for SAS, OAuth and Workload Identity, and `Custom` names only 
the auth type.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to