This is an automated email from the ASF dual-hosted git repository.

sollhui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 7b0d4ffa079 [feat](s3) support S3 storage vault credentials provider 
without role ARN (#64766)
7b0d4ffa079 is described below

commit 7b0d4ffa079616bf00057b13bd4187088b234e27
Author: hui lai <[email protected]>
AuthorDate: Wed Jun 24 14:13:08 2026 +0800

    [feat](s3) support S3 storage vault credentials provider without role ARN 
(#64766)
    
    ### What problem does this PR solve?
    
    S3 storage vault creation only treated role ARN as the
    credential-provider path. When users configured
    `s3.credentials_provider_type` without `s3.role_arn`, FE did not persist
    the provider type into `ObjectStoreInfoPB`, and Cloud meta-service still
    required AK/SK for the vault. The recycler also only read credential
    provider type inside the role ARN branch.
    
    This change allows S3 storage vaults to use an explicit credentials
    provider type without role ARN. FE now writes `cred_provider_type` when
    `s3.credentials_provider_type` or `AWS_CREDENTIALS_PROVIDER_TYPE` is
    set, Cloud meta-service accepts credential-provider-based S3 vaults
    without AK/SK, and the recycler reads the provider type independently
    from role ARN.
---
 cloud/src/meta-service/meta_service_resource.cpp   | 29 +++++++++-----
 cloud/src/recycler/s3_accessor.cpp                 |  9 +++--
 cloud/test/meta_service_test.cpp                   | 46 ++++++++++++++++++++++
 .../datasource/property/storage/S3Properties.java  | 20 ++++++++--
 .../property/storage/S3PropertiesTest.java         | 19 +++++++++
 5 files changed, 107 insertions(+), 16 deletions(-)

diff --git a/cloud/src/meta-service/meta_service_resource.cpp 
b/cloud/src/meta-service/meta_service_resource.cpp
index ed36b54322c..2e19ebbb8a3 100644
--- a/cloud/src/meta-service/meta_service_resource.cpp
+++ b/cloud/src/meta-service/meta_service_resource.cpp
@@ -760,6 +760,14 @@ static int add_hdfs_storage_vault(InstanceInfoPB& 
instance, Transaction* txn,
     return 0;
 }
 
+static bool has_non_empty_role_arn(const ObjectStoreInfoPB& obj) {
+    return obj.has_role_arn() && !obj.role_arn().empty();
+}
+
+static bool use_credential_provider(const ObjectStoreInfoPB& obj) {
+    return obj.has_cred_provider_type() || has_non_empty_role_arn(obj);
+}
+
 static void create_object_info_with_encrypt(const InstanceInfoPB& instance, 
ObjectStoreInfoPB* obj,
                                             bool sse_enabled, MetaServiceCode& 
code,
                                             std::string& msg) {
@@ -1250,7 +1258,9 @@ static int extract_object_storage_info(const 
AlterObjStoreInfoRequest* request,
     auto& [ak, sk, bucket, prefix, endpoint, external_endpoint, region, 
use_path_style, role_arn,
            external_id] = obj_desc;
 
-    if (!obj.has_role_arn()) {
+    bool use_credential_provider_for_add_vault =
+            request->op() == AlterObjStoreInfoRequest::ADD_S3_VAULT && 
obj.has_cred_provider_type();
+    if (!obj.has_role_arn() && !use_credential_provider_for_add_vault) {
         if (!obj.has_ak() || !obj.has_sk()) {
             code = MetaServiceCode::INVALID_ARGUMENT;
             msg = "s3 obj info err " + proto_to_json(*request);
@@ -1306,13 +1316,15 @@ static ObjectStoreInfoPB 
object_info_pb_factory(ObjectStorageDesc& obj_desc,
         last_item.set_user_id(obj.user_id());
     }
 
-    if (!obj.has_role_arn()) {
+    if (!use_credential_provider(obj)) {
         last_item.set_ak(std::move(cipher_ak_sk_pair.first));
         last_item.set_sk(std::move(cipher_ak_sk_pair.second));
         last_item.mutable_encryption_info()->CopyFrom(encryption_info);
     } else {
-        last_item.set_role_arn(role_arn);
-        last_item.set_external_id(external_id);
+        if (has_non_empty_role_arn(obj)) {
+            last_item.set_role_arn(role_arn);
+            last_item.set_external_id(external_id);
+        }
         last_item.set_cred_provider_type(get_cred_provider_type(obj));
     }
     last_item.set_bucket(bucket);
@@ -1476,18 +1488,17 @@ void 
MetaServiceImpl::alter_storage_vault(google::protobuf::RpcController* contr
             return;
         }
         // ATTN: prefix may be empty
-        if (((ak.empty() || sk.empty()) && role_arn.empty()) || bucket.empty() 
||
+        if (((ak.empty() || sk.empty()) && !use_credential_provider(obj)) || 
bucket.empty() ||
             endpoint.empty() || region.empty()) {
             code = MetaServiceCode::INVALID_ARGUMENT;
             msg = "s3 conf info err, please check it";
             return;
         }
 
-        if (!role_arn.empty()) {
-            if (!obj.has_cred_provider_type() || !obj.has_provider() ||
-                obj.provider() != ObjectStoreInfoPB::S3) {
+        if (use_credential_provider(obj)) {
+            if (!obj.has_provider() || obj.provider() != 
ObjectStoreInfoPB::S3) {
                 code = MetaServiceCode::INVALID_ARGUMENT;
-                msg = "s3 conf info err with role_arn, please check it";
+                msg = "s3 conf info err with credentials_provider_type, please 
check it";
                 return;
             }
         }
diff --git a/cloud/src/recycler/s3_accessor.cpp 
b/cloud/src/recycler/s3_accessor.cpp
index d48c51c8469..51cc9023904 100644
--- a/cloud/src/recycler/s3_accessor.cpp
+++ b/cloud/src/recycler/s3_accessor.cpp
@@ -237,13 +237,14 @@ std::optional<S3Conf> S3Conf::from_obj_store_info(const 
ObjectStoreInfoPB& obj_i
             }
         }
 
+        if (obj_info.has_cred_provider_type()) {
+            s3_conf.cred_provider_type = 
cred_provider_type_from_pb(obj_info.cred_provider_type());
+        }
+
         if (obj_info.has_role_arn() && !obj_info.role_arn().empty()) {
             s3_conf.role_arn = obj_info.role_arn();
             s3_conf.external_id = obj_info.external_id();
-            if (obj_info.has_cred_provider_type()) {
-                s3_conf.cred_provider_type =
-                        
cred_provider_type_from_pb(obj_info.cred_provider_type());
-            } else {
+            if (!obj_info.has_cred_provider_type()) {
                 s3_conf.cred_provider_type = CredProviderType::InstanceProfile;
             }
         }
diff --git a/cloud/test/meta_service_test.cpp b/cloud/test/meta_service_test.cpp
index 8b17a385659..98011dd9fe0 100644
--- a/cloud/test/meta_service_test.cpp
+++ b/cloud/test/meta_service_test.cpp
@@ -10498,6 +10498,52 @@ TEST(MetaServiceTest, CreateS3VaultWithIamRole) {
         }
     }
 
+    {
+        AlterObjStoreInfoRequest req;
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ADD_S3_VAULT);
+        StorageVaultPB vault;
+        vault.mutable_obj_info()->set_endpoint("s3.us-east-1.amazonaws.com");
+        vault.mutable_obj_info()->set_region("us-east-1");
+        
vault.mutable_obj_info()->set_bucket("test_credential_provider_bucket");
+        
vault.mutable_obj_info()->set_prefix("test_credential_provider_prefix");
+        vault.mutable_obj_info()->set_provider(
+                ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+        
vault.mutable_obj_info()->set_cred_provider_type(CredProviderTypePB::CONTAINER);
+
+        vault.set_name("s3_vault_with_credential_provider");
+        req.mutable_vault()->CopyFrom(vault);
+
+        brpc::Controller cntl;
+        AlterObjStoreInfoResponse res;
+        meta_service->alter_storage_vault(
+                reinterpret_cast<::google::protobuf::RpcController*>(&cntl), 
&req, &res, nullptr);
+        ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
+
+        {
+            InstanceInfoPB instance;
+            get_test_instance(instance);
+            std::unique_ptr<Transaction> txn;
+            ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), 
TxnErrorCode::TXN_OK);
+            std::string val;
+            ASSERT_EQ(txn->get(storage_vault_key({instance.instance_id(), 
"5"}), &val),
+                      TxnErrorCode::TXN_OK);
+            StorageVaultPB get_obj;
+            get_obj.ParseFromString(val);
+            ASSERT_TRUE(get_obj.obj_info().ak().empty()) << 
get_obj.obj_info().ak();
+            ASSERT_TRUE(get_obj.obj_info().sk().empty()) << 
get_obj.obj_info().sk();
+            ASSERT_FALSE(get_obj.obj_info().has_role_arn());
+            ASSERT_FALSE(get_obj.obj_info().has_external_id());
+            ASSERT_EQ(get_obj.obj_info().cred_provider_type(), 
CredProviderTypePB::CONTAINER)
+                    << get_obj.obj_info().cred_provider_type();
+            ASSERT_EQ(get_obj.obj_info().bucket(), 
"test_credential_provider_bucket")
+                    << get_obj.obj_info().bucket();
+            ASSERT_EQ(get_obj.obj_info().prefix(), 
"test_credential_provider_prefix")
+                    << get_obj.obj_info().prefix();
+            ASSERT_EQ(get_obj.id(), "5") << get_obj.id();
+        }
+    }
+
     LOG(INFO) << "instance:" << instance.ShortDebugString();
     SyncPoint::get_instance()->disable_processing();
     SyncPoint::get_instance()->clear_all_call_backs();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
index d0166e195f9..01c5cc6033c 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/property/storage/S3Properties.java
@@ -657,6 +657,11 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
         properties.putIfAbsent(Env.CONNECTION_TIMEOUT_MS, 
Env.DEFAULT_CONNECTION_TIMEOUT_MS);
     }
 
+    private static boolean hasCredentialsProviderType(Map<String, String> 
properties) {
+        return properties.containsKey(CREDENTIALS_PROVIDER_TYPE)
+                || properties.containsKey(Env.CREDENTIALS_PROVIDER_TYPE);
+    }
+
     public static Cloud.ObjectStoreInfoPB.Builder 
getObjStoreInfoPB(Map<String, String> properties) {
         Cloud.ObjectStoreInfoPB.Builder builder = 
Cloud.ObjectStoreInfoPB.newBuilder();
         if (properties.containsKey(S3Properties.ENDPOINT)) {
@@ -696,12 +701,21 @@ public class S3Properties extends 
AbstractS3CompatibleProperties {
             builder.setUsePathStyle(value.equalsIgnoreCase("true"));
         }
 
+        if (hasCredentialsProviderType(properties)) {
+            builder.setCredProviderType(getCredProviderTypePB(properties));
+        }
+
         if (properties.containsKey(S3Properties.ROLE_ARN)) {
-            builder.setRoleArn(properties.get(S3Properties.ROLE_ARN));
-            if (properties.containsKey(S3Properties.EXTERNAL_ID)) {
+            String roleArn = properties.get(S3Properties.ROLE_ARN);
+            if (!Strings.isNullOrEmpty(roleArn)) {
+                builder.setRoleArn(roleArn);
+            }
+            if (!Strings.isNullOrEmpty(roleArn) && 
properties.containsKey(S3Properties.EXTERNAL_ID)) {
                 
builder.setExternalId(properties.get(S3Properties.EXTERNAL_ID));
             }
-            builder.setCredProviderType(getCredProviderTypePB(properties));
+            if (!Strings.isNullOrEmpty(roleArn) && 
!builder.hasCredProviderType()) {
+                builder.setCredProviderType(getCredProviderTypePB(properties));
+            }
         }
 
         return builder;
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
index 97bb862df33..f9cfde32b06 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/property/storage/S3PropertiesTest.java
@@ -292,6 +292,25 @@ public class S3PropertiesTest {
         
Assertions.assertTrue(S3StorageVault.ALLOW_ALTER_PROPERTIES.contains(S3Properties.CREDENTIALS_PROVIDER_TYPE));
     }
 
+    @Test
+    public void testS3CredentialsProviderTypeWithoutIamRoleForCloud() {
+        origProps.put("s3.endpoint", "s3.us-west-2.amazonaws.com");
+        origProps.put("s3.region", "us-west-2");
+        origProps.put("s3.bucket", "bucket");
+        origProps.put("s3.root.path", "root");
+        origProps.put("s3.credentials_provider_type", "container");
+
+        Assertions.assertEquals(CredProviderTypePB.CONTAINER,
+                
S3Properties.getObjStoreInfoPB(origProps).getCredProviderType());
+        
Assertions.assertFalse(S3Properties.getObjStoreInfoPB(origProps).hasRoleArn());
+
+        origProps.remove("s3.credentials_provider_type");
+        origProps.put("AWS_CREDENTIALS_PROVIDER_TYPE", "env");
+        Assertions.assertEquals(CredProviderTypePB.ENV,
+                
S3Properties.getObjStoreInfoPB(origProps).getCredProviderType());
+        
Assertions.assertFalse(S3Properties.getObjStoreInfoPB(origProps).hasRoleArn());
+    }
+
 
     @Test
     public void testGetAwsCredentialsProviderWithIamRoleAndExternalId() {


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

Reply via email to