github-actions[bot] commented on code in PR #68540:
URL: https://github.com/apache/doris/pull/68540#discussion_r4248820622


##########
cloud/src/meta-service/meta_service_resource.cpp:
##########
@@ -1162,28 +1207,72 @@ static int alter_s3_storage_vault_by_id(InstanceInfoPB& 
instance, std::unique_pt
         new_vault.mutable_obj_info()->clear_role_arn();
         new_vault.mutable_obj_info()->clear_external_id();
         new_vault.mutable_obj_info()->clear_cred_provider_type();
+        new_vault.mutable_obj_info()->clear_credential();
 
         new_vault.mutable_obj_info()->set_ak(new_ak_sk_pair.first);
         new_vault.mutable_obj_info()->set_sk(new_ak_sk_pair.second);
         
new_vault.mutable_obj_info()->mutable_encryption_info()->CopyFrom(encryption_info);
     }
 
-    if (obj_info.has_role_arn()) {
-        new_vault.mutable_obj_info()->clear_ak();
-        new_vault.mutable_obj_info()->clear_sk();
-        new_vault.mutable_obj_info()->clear_encryption_info();
+    if (obj_info.has_role_arn() || obj_info.has_cred_provider_type()) {
+        // Only selecting AWS authentication replaces other credentials. 
Clearing
+        // an unused role must preserve static keys and native GCP credentials.
+        if (!obj_info.role_arn().empty() || obj_info.has_cred_provider_type()) 
{
+            new_vault.mutable_obj_info()->clear_ak();
+            new_vault.mutable_obj_info()->clear_sk();
+            new_vault.mutable_obj_info()->clear_encryption_info();
+            new_vault.mutable_obj_info()->clear_credential();
+        }
 
-        new_vault.mutable_obj_info()->set_role_arn(obj_info.role_arn());
-        
new_vault.mutable_obj_info()->set_cred_provider_type(get_cred_provider_type(obj_info));
-        if (obj_info.has_external_id()) {
+        if (obj_info.has_role_arn()) {
+            if (obj_info.role_arn().empty()) {
+                new_vault.mutable_obj_info()->clear_role_arn();
+                // An external ID has no meaning without a role. Clearing the 
role
+                // explicitly clears its dependent value as well.
+                new_vault.mutable_obj_info()->clear_external_id();
+            } else {
+                
new_vault.mutable_obj_info()->set_role_arn(obj_info.role_arn());
+                if (!new_vault.obj_info().has_cred_provider_type() &&
+                    !obj_info.has_cred_provider_type()) {
+                    new_vault.mutable_obj_info()->set_cred_provider_type(
+                            CredProviderTypePB::INSTANCE_PROFILE);
+                }
+            }
+        }
+        if (obj_info.has_cred_provider_type()) {
+            
new_vault.mutable_obj_info()->set_cred_provider_type(obj_info.cred_provider_type());
+        }
+    }
+
+    // external_id is an independent ALTER field. It may be changed without
+    // requiring FE to resend role_arn and cred_provider_type.
+    if (obj_info.has_external_id()) {
+        if (obj_info.external_id().empty()) {
+            new_vault.mutable_obj_info()->clear_external_id();
+        } else {
             
new_vault.mutable_obj_info()->set_external_id(obj_info.external_id());
         }
     }
 
+    if (has_obj_credential(obj_info)) {
+        if (auto error = apply_obj_credential(obj_info, 
new_vault.mutable_obj_info());
+            error.has_value()) {
+            code = MetaServiceCode::INVALID_ARGUMENT;
+            msg = *error;
+            return -1;
+        }
+    }
+
     if (obj_info.has_use_path_style()) {
         
new_vault.mutable_obj_info()->set_use_path_style(obj_info.use_path_style());
     }
 
+    if (auto error = validate_obj_authentication(new_vault.obj_info()); 
error.has_value()) {

Review Comment:
   [P2] Preserve unrelated ALTERs for accepted static-key vaults. A built-in S3 
vault provisioned through `CreateInstanceRequest.vault.obj_info` with AK/SK and 
an explicit `cred_provider_type=DEFAULT` is accepted by 
`create_object_info_with_encrypt`, which encrypts the pair and retains that 
field. The object client uses the static pair before checking `provider_type`, 
so the vault works. This new complete-state check counts even DEFAULT field 
presence as AWS authentication and rejects a `use_path_style`-only ALTER as 
conflicting with those stored keys; it fails before applying an unrelated 
update although CREATE still accepts the same state. Normalize inactive legacy 
provider fields or preserve their static-key semantics, and cover 
provisioning/readback followed by a noncredential ALTER.



##########
common/cpp/obj-client/auth/gcp/gcp_token_provider.cpp:
##########
@@ -0,0 +1,102 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// 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.
+
+#include "cpp/obj-client/auth/gcp/gcp_token_provider.h"
+
+#include <glog/logging.h>
+#include <google/cloud/credentials.h>
+#include <google/cloud/internal/unified_rest_credentials.h>
+
+#include <utility>
+
+namespace doris {
+namespace {
+
+constexpr char GCS_READ_WRITE_SCOPE[] = 
"https://www.googleapis.com/auth/devstorage.read_write";;
+constexpr char CLOUD_PLATFORM_SCOPE[] = 
"https://www.googleapis.com/auth/cloud-platform";;
+
+std::shared_ptr<google::cloud::Credentials> make_credentials(const 
GcpCredentialConfig& credential,
+                                                             const 
std::string& ca_cert_path,
+                                                             GcpTokenScope 
token_scope) {
+    google::cloud::Options options;
+    if (!ca_cert_path.empty()) {
+        options.set<google::cloud::CARootsFilePathOption>(ca_cert_path);
+    }
+
+    // A source credential used for impersonation calls the IAM Credentials API
+    // and therefore needs cloud-platform. The resulting target token uses the
+    // caller-selected scope: object read/write for storage requests, or
+    // cloud-platform when it will call IAM signBlob.
+    const char* requested_scope = token_scope == GcpTokenScope::CloudPlatform
+                                          ? CLOUD_PLATFORM_SCOPE
+                                          : GCS_READ_WRITE_SCOPE;
+    
options.set<google::cloud::ScopesOption>({credential.impersonation_service_account.empty()
+                                                      ? requested_scope
+                                                      : CLOUD_PLATFORM_SCOPE});
+
+    std::shared_ptr<google::cloud::Credentials> credentials;
+    switch (credential.provider_type) {
+    case GcpCredentialProviderType::Default:
+        credentials = google::cloud::MakeGoogleDefaultCredentials(options);
+        break;
+    case GcpCredentialProviderType::ComputeEngine:
+        credentials = google::cloud::MakeComputeEngineCredentials(options);
+        break;
+    }
+    if (!credential.impersonation_service_account.empty()) {
+        auto impersonation_options = options;
+        
impersonation_options.set<google::cloud::ScopesOption>({requested_scope});
+        credentials = google::cloud::MakeImpersonateServiceAccountCredentials(
+                std::move(credentials), 
credential.impersonation_service_account,
+                std::move(impersonation_options));
+    }
+    return credentials;
+}
+
+} // namespace
+
+GcpTokenProvider::GcpTokenProvider(const GcpCredentialConfig& credential,
+                                   const std::string& ca_cert_path, 
GcpTokenScope token_scope)
+        : GcpTokenProvider(google::cloud::rest_internal::MapCredentials(

Review Comment:
   [P2] Return a client error when ADC construction throws. If a BE's 
`GOOGLE_APPLICATION_CREDENTIALS` file contains a JSON object such as 
`{"type":false}`, the pinned [ADC 
loader](https://github.com/googleapis/google-cloud-cpp/blob/v2.45.0/google/cloud/internal/oauth2_google_credentials.cc#L37-L41)
 throws a JSON `type_error` during this eager `MapCredentials` call. The native 
client constructor, `S3ClientFactory::_create_s3_client` and 
`ObjClientHolder::init/reset` do not catch it; a storage-policy push reaches 
them through `update_s3_resource` on `TaskWorkerPool`, whose thread entry has 
no catch, so the BE terminates instead of reporting a failed credential/client 
update. Convert construction exceptions at the Result/Status boundary and cover 
malformed ADC through policy/client construction. The signing-time catch runs 
after these providers have already been constructed.



##########
fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergCatalogFactory.java:
##########
@@ -432,6 +435,36 @@ public static Map<String, String> 
buildCatalogProperties(IcebergCatalogPropertie
                 // s3tables: bespoke instantiation. Preserve the skeleton's 
base+impl routing.
                 break;
         }
+        chosenS3.ifPresent(storage -> 
storage.toBackendProperties().ifPresent(backend -> {
+            Optional<GcsAuth> auth = GcsAuthResolver.resolve(backend.toMap());
+            if (auth.filter(GcsAuth::isAnonymous).isPresent()) {
+                opts.put(AwsClientProperties.CLIENT_CREDENTIALS_PROVIDER,
+                        
"software.amazon.awssdk.auth.credentials.AnonymousCredentialsProvider");
+            }
+            auth.flatMap(GcsAuth::getNativeCredential).ifPresent(credential -> 
{
+                // HadoopCatalog resolves its namespace filesystem 
independently of S3FileIO.
+                // Native GCS credentials configure fs.gs.*, so compatibility 
warehouse schemes
+                // must select that same filesystem before HadoopCatalog 
initializes.
+                String warehouse = 
opts.get(CatalogProperties.WAREHOUSE_LOCATION);
+                if (IcebergCatalogProperties.TYPE_HADOOP.equals(flavor) && 
warehouse != null) {
+                    if (warehouse.regionMatches(true, 0, "s3://", 0, 5)) {
+                        opts.put(CatalogProperties.WAREHOUSE_LOCATION, "gs://" 
+ warehouse.substring(5));
+                    } else if (warehouse.regionMatches(true, 0, "s3a://", 0, 
6)) {
+                        opts.put(CatalogProperties.WAREHOUSE_LOCATION, "gs://" 
+ warehouse.substring(6));
+                    }
+                }
+                putS3FileIODialect(opts, storage);
+                opts.put("provider", "GCP");
+                opts.put(GcpCredential.CREDENTIAL_PROVIDER_TYPE, 
credential.getCredentialProviderType().name());
+                putIfNotBlank(opts, 
GcpCredential.IMPERSONATION_SERVICE_ACCOUNT,
+                        credential.getImpersonationServiceAccount());
+                // Keep explicit FileIO choices. By default, S3 aliases use 
the GCP client factory,
+                // while gs/HDFS paths use HadoopFileIO with the native 
fs.gs.* configuration
+                // (the plugin does not bundle iceberg-gcp's GCSFileIO).
+                opts.putIfAbsent(CatalogProperties.FILE_IO_IMPL, 
"org.apache.iceberg.io.ResolvingFileIO");

Review Comment:
   [P2] Preserve native auth for aliases with explicit HadoopFileIO. For a 
native-GCP HMS/REST catalog with 
`io-impl=org.apache.iceberg.hadoop.HadoopFileIO` and a private GCS table whose 
`metadata_location` uses `s3a://gcp-bucket/...`, this preserves HadoopFileIO 
but native storage config contains only `fs.gs.*`. Only a HadoopCatalog 
warehouse is rewritten above; 
[HadoopFileIO](https://github.com/apache/iceberg/blob/apache-iceberg-1.11.0/core/src/main/java/org/apache/iceberg/hadoop/HadoopFileIO.java#L84-L95)
 passes the table path unchanged to Hadoop, so S3A uses its AWS/default 
credentials and endpoint rather than the selected GCP OAuth/impersonation 
identity. Metadata reads fail on a VM with no AWS keys although the default 
ResolvingFileIO path works. Handle aliases belonging to this GCS binding for 
explicit HadoopFileIO, or reject the unsupported combination, and cover that 
metadata read; the current explicit-IO test only reads a `file:///` path. This 
differs from the existing mixe
 d-AWS-table thread: it loses authentication for a GCS table under the 
preserved custom FileIO.



-- 
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