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

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new 66681cfe5cf branch-3.0: [feat](cloud) Support alter operation for 
obj_info and s3 vault obj_info #51162 (#51685)
66681cfe5cf is described below

commit 66681cfe5cf8244cd62a0b49f71314934cd0af3e
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jun 19 16:36:43 2025 +0800

    branch-3.0: [feat](cloud) Support alter operation for obj_info and s3 vault 
obj_info #51162 (#51685)
    
    Cherry-picked from #51162
    
    Co-authored-by: Lei Zhang <[email protected]>
---
 cloud/src/meta-service/meta_service_http.cpp       |   8 +-
 cloud/src/meta-service/meta_service_resource.cpp   | 131 +++++++--
 cloud/test/meta_service_test.cpp                   | 301 +++++++++++++++++++++
 .../org/apache/doris/catalog/S3StorageVault.java   |   6 +-
 .../org/apache/doris/catalog/StorageVault.java     |   6 +-
 .../apache/doris/cloud/rpc/MetaServiceClient.java  |   4 +
 .../apache/doris/cloud/rpc/MetaServiceProxy.java   |   4 +
 gensrc/proto/cloud.proto                           |   1 +
 .../test_alter_s3_vault_with_role.groovy           | 126 +++++++++
 .../node_mgr/test_ms_alter_obj_info.groovy         | 206 ++++++++++++++
 .../node_mgr/test_ms_alter_s3_vault.groovy         | 237 ++++++++++++++++
 11 files changed, 999 insertions(+), 31 deletions(-)

diff --git a/cloud/src/meta-service/meta_service_http.cpp 
b/cloud/src/meta-service/meta_service_http.cpp
index 53d9dbbef83..d12ff59ffe0 100644
--- a/cloud/src/meta-service/meta_service_http.cpp
+++ b/cloud/src/meta-service/meta_service_http.cpp
@@ -229,7 +229,8 @@ static HttpResponse 
process_get_obj_store_info(MetaServiceImpl* service, brpc::C
 static HttpResponse process_alter_obj_store_info(MetaServiceImpl* service, 
brpc::Controller* ctrl) {
     static std::unordered_map<std::string_view, 
AlterObjStoreInfoRequest::Operation> operations {
             {"add_obj_info", AlterObjStoreInfoRequest::ADD_OBJ_INFO},
-            {"legacy_update_ak_sk", 
AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK}};
+            {"legacy_update_ak_sk", 
AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK},
+            {"alter_obj_info", AlterObjStoreInfoRequest::ALTER_OBJ_INFO}};
 
     auto& path = ctrl->http_request().unresolved_path();
     auto it = operations.find(remove_version_prefix(path));
@@ -251,6 +252,7 @@ static HttpResponse 
process_alter_storage_vault(MetaServiceImpl* service, brpc::
     static std::unordered_map<std::string_view, 
AlterObjStoreInfoRequest::Operation> operations {
             {"drop_s3_vault", AlterObjStoreInfoRequest::DROP_S3_VAULT},
             {"add_s3_vault", AlterObjStoreInfoRequest::ADD_S3_VAULT},
+            {"alter_s3_vault", AlterObjStoreInfoRequest::ALTER_S3_VAULT},
             {"drop_hdfs_vault", AlterObjStoreInfoRequest::DROP_HDFS_INFO},
             {"add_hdfs_vault", AlterObjStoreInfoRequest::ADD_HDFS_INFO}};
 
@@ -740,6 +742,10 @@ void 
MetaServiceImpl::http(::google::protobuf::RpcController* controller,
             {"alter_s3_vault", process_alter_storage_vault},
             {"drop_s3_vault", process_alter_storage_vault},
             {"drop_hdfs_vault", process_alter_storage_vault},
+            {"alter_obj_info", process_alter_obj_store_info},
+            {"v1/alter_obj_info", process_alter_obj_store_info},
+            {"v1/alter_s3_vault", process_alter_storage_vault},
+
             // for tools
             {"decode_key", process_decode_key},
             {"encode_key", process_encode_key},
diff --git a/cloud/src/meta-service/meta_service_resource.cpp 
b/cloud/src/meta-service/meta_service_resource.cpp
index eb88694ff57..bbd94b577b1 100644
--- a/cloud/src/meta-service/meta_service_resource.cpp
+++ b/cloud/src/meta-service/meta_service_resource.cpp
@@ -677,14 +677,6 @@ static int alter_s3_storage_vault(InstanceInfoPB& 
instance, std::unique_ptr<Tran
         return -1;
     }
 
-    if (obj_info.has_ak() ^ obj_info.has_sk()) {
-        code = MetaServiceCode::INVALID_ARGUMENT;
-        std::stringstream ss;
-        ss << "Accesskey and secretkey must be alter together";
-        msg = ss.str();
-        return -1;
-    }
-
     const auto& name = vault.name();
     // Here we try to get mutable iter since we might need to alter the vault 
name
     auto name_itr = 
std::find_if(instance.mutable_storage_vault_names()->begin(),
@@ -723,6 +715,8 @@ static int alter_s3_storage_vault(InstanceInfoPB& instance, 
std::unique_ptr<Tran
         return -1;
     }
 
+    auto origin_vault_info = new_vault.DebugString();
+
     if (vault.has_alter_name()) {
         if (!is_valid_storage_vault_name(vault.alter_name())) {
             code = MetaServiceCode::INVALID_ARGUMENT;
@@ -742,13 +736,26 @@ static int alter_s3_storage_vault(InstanceInfoPB& 
instance, std::unique_ptr<Tran
         new_vault.set_name(vault.alter_name());
         *name_itr = vault.alter_name();
     }
-    auto origin_vault_info = new_vault.DebugString();
 
-    // For ak or sk is not altered.
-    EncryptionInfoPB encryption_info = new_vault.obj_info().encryption_info();
-    AkSkPair new_ak_sk_pair {new_vault.obj_info().ak(), 
new_vault.obj_info().sk()};
+    if (obj_info.has_role_arn() && (obj_info.has_ak() || obj_info.has_sk())) {
+        code = MetaServiceCode::INVALID_ARGUMENT;
+        msg = "invaild argument, both set ak/sk and role_arn is not allowed";
+        LOG(WARNING) << msg;
+        return -1;
+    }
+
+    if (obj_info.has_ak() ^ obj_info.has_sk()) {
+        code = MetaServiceCode::INVALID_ARGUMENT;
+        std::stringstream ss;
+        ss << "Accesskey and secretkey must be alter together";
+        msg = ss.str();
+        return -1;
+    }
 
     if (obj_info.has_ak()) {
+        EncryptionInfoPB encryption_info = 
new_vault.obj_info().encryption_info();
+        AkSkPair new_ak_sk_pair {new_vault.obj_info().ak(), 
new_vault.obj_info().sk()};
+
         // ak and sk must be altered together, there is check before.
         auto ret = encrypt_ak_sk_helper(obj_info.ak(), obj_info.sk(), 
&encryption_info,
                                         &new_ak_sk_pair, code, msg);
@@ -758,15 +765,36 @@ static int alter_s3_storage_vault(InstanceInfoPB& 
instance, std::unique_ptr<Tran
             LOG(WARNING) << msg;
             return -1;
         }
+        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()->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();
+
+        new_vault.mutable_obj_info()->set_role_arn(obj_info.role_arn());
+        
new_vault.mutable_obj_info()->set_cred_provider_type(CredProviderTypePB::INSTANCE_PROFILE);
+        if (obj_info.has_external_id()) {
+            
new_vault.mutable_obj_info()->set_external_id(obj_info.external_id());
+        }
     }
 
-    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_use_path_style()) {
         
new_vault.mutable_obj_info()->set_use_path_style(obj_info.use_path_style());
     }
 
+    auto now_time = std::chrono::system_clock::now();
+    uint64_t time =
+            
std::chrono::duration_cast<std::chrono::seconds>(now_time.time_since_epoch()).count();
+    new_vault.mutable_obj_info()->set_mtime(time);
+
     auto new_vault_info = new_vault.DebugString();
     val = new_vault.SerializeAsString();
     if (val.empty()) {
@@ -825,6 +853,7 @@ static int extract_object_storage_info(const 
AlterObjStoreInfoRequest* request,
         if (!obj.has_ak() || !obj.has_sk()) {
             code = MetaServiceCode::INVALID_ARGUMENT;
             msg = "s3 obj info err " + proto_to_json(*request);
+            LOG(INFO) << msg;
             return -1;
         }
 
@@ -839,13 +868,12 @@ static int extract_object_storage_info(const 
AlterObjStoreInfoRequest* request,
         ak = cipher_ak_sk_pair.first;
         sk = cipher_ak_sk_pair.second;
     } else {
-        if (!obj.has_cred_provider_type() ||
-            obj.cred_provider_type() != CredProviderTypePB::INSTANCE_PROFILE ||
-            !obj.has_provider() || obj.provider() != ObjectStoreInfoPB::S3) {
+        if (obj.has_ak() || obj.has_sk()) {
             code = MetaServiceCode::INVALID_ARGUMENT;
-            msg = "s3 conf info err with role_arn, please check it";
+            msg = "invaild argument, both set ak/sk and role_arn is not 
allowed";
             return -1;
         }
+
         role_arn = obj.has_role_arn() ? obj.role_arn() : "";
         external_id = obj.has_external_id() ? obj.external_id() : "";
     }
@@ -1044,6 +1072,16 @@ void 
MetaServiceImpl::alter_storage_vault(google::protobuf::RpcController* contr
             return;
         }
 
+        if (!role_arn.empty()) {
+            if (!obj.has_cred_provider_type() ||
+                obj.cred_provider_type() != 
CredProviderTypePB::INSTANCE_PROFILE ||
+                !obj.has_provider() || obj.provider() != 
ObjectStoreInfoPB::S3) {
+                code = MetaServiceCode::INVALID_ARGUMENT;
+                msg = "s3 conf info err with role_arn, please check it";
+                return;
+            }
+        }
+
         auto& objs = instance.obj_info();
         for (auto& it : objs) {
             if (bucket == it.bucket() && prefix == it.prefix() && endpoint == 
it.endpoint() &&
@@ -1210,6 +1248,7 @@ void 
MetaServiceImpl::alter_obj_store_info(google::protobuf::RpcController* cont
     switch (request->op()) {
     case AlterObjStoreInfoRequest::ADD_OBJ_INFO:
     case AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK:
+    case AlterObjStoreInfoRequest::ALTER_OBJ_INFO:
     case AlterObjStoreInfoRequest::UPDATE_AK_SK: {
         auto tmp_desc = ObjectStorageDesc {ak,       sk,
                                            bucket,   prefix,
@@ -1287,7 +1326,8 @@ void 
MetaServiceImpl::alter_obj_store_info(google::protobuf::RpcController* cont
     }
 
     switch (request->op()) {
-    case AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK: {
+    case AlterObjStoreInfoRequest::LEGACY_UPDATE_AK_SK:
+    case AlterObjStoreInfoRequest::ALTER_OBJ_INFO: {
         // get id
         std::string id = request->obj().has_id() ? request->obj().id() : "0";
         int idx = std::stoi(id);
@@ -1301,20 +1341,55 @@ void 
MetaServiceImpl::alter_obj_store_info(google::protobuf::RpcController* cont
                 
const_cast<std::decay_t<decltype(instance.obj_info())>&>(instance.obj_info());
         for (auto& it : obj_info) {
             if (std::stoi(it.id()) == idx) {
-                if (it.ak() == ak && it.sk() == sk) {
-                    // not change, just return ok
-                    code = MetaServiceCode::OK;
-                    msg = "";
-                    return;
+                if (role_arn.empty()) {
+                    if (it.ak() == ak && it.sk() == sk) {
+                        // not change, just return ok
+                        code = MetaServiceCode::OK;
+                        msg = "ak/sk not changed";
+                        return;
+                    }
+                    it.clear_role_arn();
+                    it.clear_external_id();
+                    it.clear_cred_provider_type();
+
+                    it.set_ak(ak);
+                    it.set_sk(sk);
+                    it.mutable_encryption_info()->CopyFrom(encryption_info);
+                } else {
+                    if (!ak.empty() || !sk.empty()) {
+                        code = MetaServiceCode::INVALID_ARGUMENT;
+                        msg = "invaild argument, both set ak/sk and role_arn 
is not allowed";
+                        LOG(INFO) << msg;
+                        return;
+                    }
+
+                    if (it.provider() != ObjectStoreInfoPB::S3) {
+                        code = MetaServiceCode::INVALID_ARGUMENT;
+                        msg = "role_arn is only supported for s3 provider";
+                        LOG(INFO) << msg << " provider=" << it.provider();
+                        return;
+                    }
+
+                    if (it.role_arn() == role_arn && it.external_id() == 
external_id) {
+                        // not change, just return ok
+                        code = MetaServiceCode::OK;
+                        msg = "ak/sk not changed";
+                        return;
+                    }
+                    it.clear_ak();
+                    it.clear_sk();
+                    it.clear_encryption_info();
+
+                    it.set_role_arn(role_arn);
+                    it.set_external_id(external_id);
+                    
it.set_cred_provider_type(CredProviderTypePB::INSTANCE_PROFILE);
                 }
+
                 auto now_time = std::chrono::system_clock::now();
                 uint64_t time = 
std::chrono::duration_cast<std::chrono::seconds>(
                                         now_time.time_since_epoch())
                                         .count();
                 it.set_mtime(time);
-                it.set_ak(ak);
-                it.set_sk(sk);
-                it.mutable_encryption_info()->CopyFrom(encryption_info);
             }
         }
     } break;
diff --git a/cloud/test/meta_service_test.cpp b/cloud/test/meta_service_test.cpp
index b5e609be3a8..fbbfbff19fe 100644
--- a/cloud/test/meta_service_test.cpp
+++ b/cloud/test/meta_service_test.cpp
@@ -9445,4 +9445,305 @@ TEST(MetaServiceTest, StaleCommitRowset) {
     ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT) << 
res.status().code();
 }
 
+TEST(MetaServiceTest, AlterObjInfoTest) {
+    auto meta_service = get_meta_service();
+
+    auto sp = SyncPoint::get_instance();
+    sp->enable_processing();
+
+    sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
+        auto* ret = try_any_cast<int*>(args[0]);
+        *ret = 0;
+        auto* key = try_any_cast<std::string*>(args[1]);
+        *key = "selectdbselectdbselectdbselectdb";
+        auto* key_id = try_any_cast<int64_t*>(args[2]);
+        *key_id = 1;
+    });
+
+    std::unique_ptr<Transaction> txn;
+    ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
+    std::string key;
+    std::string val;
+    InstanceKeyInfo key_info {"test_instance"};
+    instance_key(key_info, &key);
+
+    ObjectStoreInfoPB obj_info;
+    obj_info.set_id("1");
+    obj_info.set_ak("access_key_132131");
+    obj_info.set_sk("secret_key_434124");
+    
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+    InstanceInfoPB instance;
+    instance.add_obj_info()->CopyFrom(obj_info);
+    val = instance.SerializeAsString();
+    txn->put(key, val);
+    ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
+
+    auto get_test_instance = [&](InstanceInfoPB& i) {
+        std::string key;
+        std::string val;
+        std::unique_ptr<Transaction> txn;
+        ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), 
TxnErrorCode::TXN_OK);
+        InstanceKeyInfo key_info {"test_instance"};
+        instance_key(key_info, &key);
+        ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
+        i.ParseFromString(val);
+    };
+
+    std::string cipher_sk = 
"JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
+    std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
+
+    // update failed
+    {
+        AlterObjStoreInfoRequest req;
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
+        req.mutable_obj()->set_id("2");
+        req.mutable_obj()->set_ak("new_ak");
+        req.mutable_obj()->set_sk(plain_sk);
+
+        brpc::Controller cntl;
+        AlterObjStoreInfoResponse res;
+        meta_service->alter_obj_store_info(
+                reinterpret_cast<::google::protobuf::RpcController*>(&cntl), 
&req, &res, nullptr);
+        ASSERT_EQ(res.status().code(), MetaServiceCode::INVALID_ARGUMENT);
+        InstanceInfoPB instance;
+        get_test_instance(instance);
+        ASSERT_EQ(instance.obj_info(0).id(), "1");
+        ASSERT_EQ(instance.obj_info(0).ak(), "access_key_132131");
+        ASSERT_EQ(instance.obj_info(0).sk(), "secret_key_434124");
+    }
+
+    // update ak/sk successful
+    {
+        AlterObjStoreInfoRequest req;
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
+        req.mutable_obj()->set_id("1");
+        req.mutable_obj()->set_ak("new_access_key_132131");
+        req.mutable_obj()->set_sk(plain_sk);
+
+        brpc::Controller cntl;
+        AlterObjStoreInfoResponse res;
+        meta_service->alter_obj_store_info(
+                reinterpret_cast<::google::protobuf::RpcController*>(&cntl), 
&req, &res, nullptr);
+        ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
+        InstanceInfoPB instance;
+        get_test_instance(instance);
+        LOG(INFO) << "instance:" << instance.ShortDebugString();
+        ASSERT_EQ(instance.obj_info(0).id(), "1");
+        ASSERT_EQ(instance.obj_info(0).ak(), "new_access_key_132131");
+        ASSERT_EQ(instance.obj_info(0).sk(), cipher_sk);
+    }
+
+    // update from ak/sk to role_arn
+    {
+        AlterObjStoreInfoRequest req;
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
+        req.mutable_obj()->set_id("1");
+        
req.mutable_obj()->set_role_arn("arn:aws:iam::1453123012:role/test-role");
+        req.mutable_obj()->set_external_id("external_id_13123");
+        
req.mutable_obj()->set_cred_provider_type(CredProviderTypePB::INSTANCE_PROFILE);
+        
req.mutable_obj()->set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+
+        brpc::Controller cntl;
+        AlterObjStoreInfoResponse res;
+        meta_service->alter_obj_store_info(
+                reinterpret_cast<::google::protobuf::RpcController*>(&cntl), 
&req, &res, nullptr);
+        ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
+        InstanceInfoPB instance;
+        get_test_instance(instance);
+        LOG(INFO) << "instance:" << instance.ShortDebugString();
+        ASSERT_EQ(instance.obj_info(0).id(), "1");
+        ASSERT_EQ(instance.obj_info(0).role_arn(), 
"arn:aws:iam::1453123012:role/test-role");
+        ASSERT_EQ(instance.obj_info(0).external_id(), "external_id_13123");
+        ASSERT_EQ(instance.obj_info(0).provider(),
+                  ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+        ASSERT_EQ(instance.obj_info(0).cred_provider_type(), 
CredProviderTypePB::INSTANCE_PROFILE);
+        ASSERT_TRUE(instance.obj_info(0).ak().empty());
+        ASSERT_TRUE(instance.obj_info(0).sk().empty());
+        ASSERT_FALSE(instance.obj_info(0).has_encryption_info());
+    }
+
+    // update from role_arn to ak/sk
+    {
+        AlterObjStoreInfoRequest req;
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ALTER_OBJ_INFO);
+        req.mutable_obj()->set_id("1");
+        req.mutable_obj()->set_ak("new_access_key_132131");
+        req.mutable_obj()->set_sk(plain_sk);
+
+        brpc::Controller cntl;
+        AlterObjStoreInfoResponse res;
+        meta_service->alter_obj_store_info(
+                reinterpret_cast<::google::protobuf::RpcController*>(&cntl), 
&req, &res, nullptr);
+        ASSERT_EQ(res.status().code(), MetaServiceCode::OK);
+        InstanceInfoPB instance;
+        get_test_instance(instance);
+        LOG(INFO) << "instance:" << instance.ShortDebugString();
+        ASSERT_EQ(instance.obj_info(0).id(), "1");
+        ASSERT_EQ(instance.obj_info(0).ak(), "new_access_key_132131");
+        ASSERT_EQ(instance.obj_info(0).sk(), cipher_sk);
+        ASSERT_EQ(instance.obj_info(0).provider(),
+                  ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+        ASSERT_FALSE(instance.obj_info(0).has_cred_provider_type());
+        ASSERT_FALSE(instance.obj_info(0).has_role_arn());
+        ASSERT_FALSE(instance.obj_info(0).has_external_id());
+    }
+
+    SyncPoint::get_instance()->disable_processing();
+    SyncPoint::get_instance()->clear_all_call_backs();
+}
+
+TEST(MetaServiceTest, AlterS3StorageVaultWithRoleArnTest) {
+    auto meta_service = get_meta_service();
+
+    auto sp = SyncPoint::get_instance();
+    sp->enable_processing();
+    sp->set_call_back("encrypt_ak_sk:get_encryption_key", [](auto&& args) {
+        auto* ret = try_any_cast<int*>(args[0]);
+        *ret = 0;
+        auto* key = try_any_cast<std::string*>(args[1]);
+        *key = "selectdbselectdbselectdbselectdb";
+        auto* key_id = try_any_cast<int64_t*>(args[2]);
+        *key_id = 1;
+    });
+    std::pair<std::string, std::string> pair;
+    sp->set_call_back("extract_object_storage_info:get_aksk_pair", [&](auto&& 
args) {
+        auto* ret = try_any_cast<std::pair<std::string, 
std::string>*>(args[0]);
+        pair = *ret;
+    });
+
+    std::unique_ptr<Transaction> txn;
+    ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), TxnErrorCode::TXN_OK);
+    std::string key;
+    std::string val;
+    InstanceKeyInfo key_info {"test_instance"};
+    instance_key(key_info, &key);
+
+    ObjectStoreInfoPB obj_info;
+    obj_info.set_id("1");
+    obj_info.set_ak("123456ab");
+    obj_info.set_sk("@ak$");
+    
obj_info.set_provider(ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+    StorageVaultPB vault;
+    constexpr char vault_name[] = "test_alter_s3_vault_111";
+    vault.mutable_obj_info()->MergeFrom(obj_info);
+    vault.set_name(vault_name);
+    vault.set_id("2");
+    InstanceInfoPB instance;
+    instance.add_storage_vault_names(vault.name());
+    instance.add_resource_ids(vault.id());
+    instance.set_instance_id("GetObjStoreInfoTestInstance");
+    val = instance.SerializeAsString();
+    txn->put(key, val);
+    txn->put(storage_vault_key({instance.instance_id(), "2"}), 
vault.SerializeAsString());
+    ASSERT_EQ(txn->commit(), TxnErrorCode::TXN_OK);
+    txn = nullptr;
+
+    auto get_test_instance = [&](InstanceInfoPB& i) {
+        std::string key;
+        std::string val;
+        std::unique_ptr<Transaction> txn;
+        ASSERT_EQ(meta_service->txn_kv()->create_txn(&txn), 
TxnErrorCode::TXN_OK);
+        InstanceKeyInfo key_info {"test_instance"};
+        instance_key(key_info, &key);
+        ASSERT_EQ(txn->get(key, &val), TxnErrorCode::TXN_OK);
+        i.ParseFromString(val);
+    };
+
+    // update from ak/sk to role_arn
+    {
+        AlterObjStoreInfoRequest req;
+        constexpr char new_vault_name[] = "new_test_alter_s3_vault_111";
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
+        StorageVaultPB vault;
+        vault.set_alter_name(new_vault_name);
+        ObjectStoreInfoPB obj;
+        obj.set_role_arn("arn:aws:iam::12311321:role/test-alter-role");
+        obj.set_external_id("external_id_123123");
+        vault.mutable_obj_info()->MergeFrom(obj);
+        vault.set_name(vault_name);
+        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) << 
res.status().msg();
+
+        InstanceInfoPB instance;
+        get_test_instance(instance);
+        LOG(INFO) << "instance:" << instance.ShortDebugString();
+
+        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(), "2"}), 
&val),
+                  TxnErrorCode::TXN_OK);
+        StorageVaultPB get_obj;
+        get_obj.ParseFromString(val);
+        ASSERT_EQ(get_obj.id(), "2");
+        ASSERT_EQ(get_obj.obj_info().role_arn(), 
"arn:aws:iam::12311321:role/test-alter-role");
+        ASSERT_EQ(get_obj.obj_info().external_id(), "external_id_123123");
+        ASSERT_EQ(get_obj.obj_info().provider(),
+                  ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+        ASSERT_EQ(get_obj.obj_info().cred_provider_type(), 
CredProviderTypePB::INSTANCE_PROFILE);
+        ASSERT_TRUE(get_obj.obj_info().ak().empty());
+        ASSERT_TRUE(get_obj.obj_info().sk().empty());
+        ASSERT_FALSE(get_obj.obj_info().has_encryption_info());
+        ASSERT_EQ(get_obj.name(), new_vault_name) << 
get_obj.obj_info().ShortDebugString();
+    }
+
+    std::string cipher_sk = 
"JUkuTDctR+ckJtnPkLScWaQZRcOtWBhsLLpnCRxQLxr734qB8cs6gNLH6grE1FxO";
+    std::string plain_sk = "Hx60p12123af234541nsVsffdfsdfghsdfhsdf34t";
+
+    // update from role_arn to ak_sk
+    {
+        AlterObjStoreInfoRequest req;
+        constexpr char new_vault_name[] = "new_test_alter_s3_vault_111";
+        req.set_cloud_unique_id("test_cloud_unique_id");
+        req.set_op(AlterObjStoreInfoRequest::ALTER_S3_VAULT);
+        StorageVaultPB vault;
+        ObjectStoreInfoPB obj;
+        obj.set_ak("123456ab");
+        obj.set_sk(plain_sk);
+        vault.mutable_obj_info()->MergeFrom(obj);
+        vault.set_name(new_vault_name);
+        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) << 
res.status().msg();
+
+        InstanceInfoPB instance;
+        get_test_instance(instance);
+        LOG(INFO) << "instance:" << instance.ShortDebugString();
+
+        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(), "2"}), 
&val),
+                  TxnErrorCode::TXN_OK);
+        StorageVaultPB get_obj;
+        get_obj.ParseFromString(val);
+        ASSERT_EQ(get_obj.id(), "2");
+        ASSERT_EQ(get_obj.obj_info().provider(),
+                  ObjectStoreInfoPB::Provider::ObjectStoreInfoPB_Provider_S3);
+        ASSERT_EQ(get_obj.obj_info().ak(), "123456ab");
+        ASSERT_EQ(get_obj.obj_info().sk(), cipher_sk);
+        ASSERT_TRUE(get_obj.obj_info().role_arn().empty());
+        ASSERT_TRUE(get_obj.obj_info().external_id().empty());
+        ASSERT_TRUE(get_obj.obj_info().has_encryption_info());
+        ASSERT_FALSE(get_obj.obj_info().has_cred_provider_type());
+        ASSERT_EQ(get_obj.name(), new_vault_name) << 
get_obj.obj_info().ShortDebugString();
+    }
+
+    SyncPoint::get_instance()->disable_processing();
+    SyncPoint::get_instance()->clear_all_call_backs();
+}
 } // namespace doris::cloud
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/S3StorageVault.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/S3StorageVault.java
index f8196c7ea80..58097d3c1bc 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/S3StorageVault.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/S3StorageVault.java
@@ -70,6 +70,8 @@ public class S3StorageVault extends StorageVault {
         public static final String REGION = S3Properties.REGION;
         public static final String ENDPOINT = S3Properties.ENDPOINT;
         public static final String BUCKET = S3Properties.BUCKET;
+        public static final String ROLE_ARN = S3Properties.ROLE_ARN;
+        public static final String EXTERNAL_ID = S3Properties.EXTERNAL_ID;
     }
 
     public static final HashSet<String> ALLOW_ALTER_PROPERTIES = new 
HashSet<>(Arrays.asList(
@@ -77,7 +79,9 @@ public class S3StorageVault extends StorageVault {
             StorageVault.PropertyKey.TYPE,
             PropertyKey.ACCESS_KEY,
             PropertyKey.SECRET_KEY,
-            PropertyKey.USE_PATH_STYLE
+            PropertyKey.USE_PATH_STYLE,
+            PropertyKey.ROLE_ARN,
+            PropertyKey.EXTERNAL_ID
     ));
 
     @SerializedName(value = "properties")
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVault.java 
b/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVault.java
index c9fe0c8bcc6..906b4585313 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVault.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/StorageVault.java
@@ -222,7 +222,11 @@ public abstract class StorageVault {
             Cloud.ObjectStoreInfoPB.Builder builder = 
Cloud.ObjectStoreInfoPB.newBuilder();
             builder.mergeFrom(vault.getObjInfo());
             builder.clearId();
-            builder.setSk("xxxxxxx");
+
+            if (vault.getObjInfo().hasAk()) {
+                builder.setSk("xxxxxxx");
+            }
+
             if (!vault.getObjInfo().hasUsePathStyle()) {
                 // There is no `use_path_style` field in old version, think 
`use_path_style` false
                 builder.setUsePathStyle(false);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceClient.java 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceClient.java
index d027777bf19..048d8ab93df 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceClient.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceClient.java
@@ -364,6 +364,10 @@ public class MetaServiceClient {
                 .alterCluster(request);
     }
 
+    /**
+     * This method is deprecated, there is no code to call it.
+     */
+    @Deprecated
     public Cloud.AlterObjStoreInfoResponse 
alterObjStoreInfo(Cloud.AlterObjStoreInfoRequest request) {
         if (!request.hasCloudUniqueId()) {
             Cloud.AlterObjStoreInfoRequest.Builder builder = 
Cloud.AlterObjStoreInfoRequest.newBuilder();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceProxy.java 
b/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceProxy.java
index 1e5aa7ed111..95753821c35 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceProxy.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/cloud/rpc/MetaServiceProxy.java
@@ -385,6 +385,10 @@ public class MetaServiceProxy {
         return w.executeRequest((client) -> 
client.removeDeleteBitmapUpdateLock(request));
     }
 
+    /**
+     * This method is deprecated, there is no code to call it.
+     */
+    @Deprecated
     public Cloud.AlterObjStoreInfoResponse 
alterObjStoreInfo(Cloud.AlterObjStoreInfoRequest request)
             throws RpcException {
         return w.executeRequest((client) -> client.alterObjStoreInfo(request));
diff --git a/gensrc/proto/cloud.proto b/gensrc/proto/cloud.proto
index 7b50747856c..4e5c27f6a43 100644
--- a/gensrc/proto/cloud.proto
+++ b/gensrc/proto/cloud.proto
@@ -878,6 +878,7 @@ message AlterObjStoreInfoRequest {
         UPDATE_AK_SK    = 1;
         ADD_OBJ_INFO    = 2;
         LEGACY_UPDATE_AK_SK = 3;
+        ALTER_OBJ_INFO = 4;
 
         ADD_HDFS_INFO = 100;
         DROP_HDFS_INFO = 101;
diff --git 
a/regression-test/suites/aws_iam_role_p0/test_alter_s3_vault_with_role.groovy 
b/regression-test/suites/aws_iam_role_p0/test_alter_s3_vault_with_role.groovy
new file mode 100644
index 00000000000..990afd876f5
--- /dev/null
+++ 
b/regression-test/suites/aws_iam_role_p0/test_alter_s3_vault_with_role.groovy
@@ -0,0 +1,126 @@
+// 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.
+
+import com.google.common.base.Strings;
+
+suite("test_alter_s3_vault_with_role") {
+    if (!isCloudMode()) {
+        logger.info("skip ${name} case, because not cloud mode")
+        return
+    }
+
+    if (!enableStoragevault()) {
+        logger.info("skip ${name} case, because storage vault not enabled")
+        return
+    }
+
+    def randomStr = UUID.randomUUID().toString().replace("-", "")
+    def s3VaultName = "s3_" + randomStr
+
+    def endpoint = context.config.awsEndpoint
+    def region = context.config.awsRegion
+    def bucket = context.config.awsBucket
+    def roleArn = context.config.awsRoleArn
+    def externalId = context.config.awsExternalId
+    def prefix = context.config.awsPrefix
+    def awsAccessKey = context.config.awsAccessKey
+    def awsSecretKey = context.config.awsSecretKey
+
+    sql """
+        CREATE STORAGE VAULT IF NOT EXISTS ${s3VaultName}
+        PROPERTIES (
+            "type"="S3",
+            "s3.endpoint"="${endpoint}",
+            "s3.region" = "${region}",
+            "s3.role_arn" = "${roleArn}",
+            "s3.external_id" = "${externalId}",
+            "s3.root.path" = "${prefix}/aws_iam_role_p0/${s3VaultName}",
+            "s3.bucket" = "${bucket}",
+            "s3.external_endpoint" = "",
+            "provider" = "S3",
+            "use_path_style" = "false"
+        );
+    """
+
+    sql """
+        CREATE TABLE ${s3VaultName} (
+            C_CUSTKEY     INTEGER NOT NULL,
+            C_NAME        INTEGER NOT NULL
+        )
+        DUPLICATE KEY(C_CUSTKEY, C_NAME)
+        DISTRIBUTED BY HASH(C_CUSTKEY) BUCKETS 1
+        PROPERTIES (
+            "replication_num" = "1",
+            "storage_vault_name" = ${s3VaultName}
+        )
+    """
+    sql """ insert into ${s3VaultName} values(1, 1); """
+    sql """ sync;"""
+    def result = sql """ select * from ${s3VaultName}; """
+    assertEquals(result.size(), 1);
+
+    sql """
+        ALTER STORAGE VAULT ${s3VaultName}
+        PROPERTIES (
+            "type"="S3",
+            "s3.access_key" = "${awsAccessKey}",
+            "s3.secret_key" = "${awsSecretKey}"
+        );
+    """
+
+    def vaultInfos = sql """SHOW STORAGE VAULTS;"""
+
+    for (int i = 0; i < vaultInfos.size(); i++) {
+        logger.info("vault info: ${vaultInfos[i]}")
+        if (vaultInfos[i][0].equals(s3VaultName)) {
+            def newProperties = vaultInfos[i][2]
+            logger.info("newProperties: ${newProperties}")
+            assertTrue(newProperties.contains(awsAccessKey))
+            assertFalse(newProperties.contains("role_arn"))
+        }
+    }
+
+    sql """ insert into ${s3VaultName} values(2, 2); """
+    sql """ sync;"""
+    result = sql """ select * from ${s3VaultName}; """
+    assertEquals(result.size(), 2);
+
+    sql """
+        ALTER STORAGE VAULT ${s3VaultName}
+        PROPERTIES (
+            "type"="S3",
+            "s3.role_arn" = "${roleArn}",
+            "s3.external_id" = "${externalId}"
+        );
+    """
+
+    vaultInfos = sql """SHOW STORAGE VAULTS;"""
+    for (int i = 0; i < vaultInfos.size(); i++) {
+        logger.info("vault info: ${vaultInfos[i]}")
+        if (vaultInfos[i][0].equals(s3VaultName)) {
+            def newProperties = vaultInfos[i][2]
+            logger.info("newProperties: ${newProperties}")
+            assertFalse(newProperties.contains(awsAccessKey))
+            assertTrue(newProperties.contains(roleArn))
+        }
+    }
+
+    sql """ insert into ${s3VaultName} values(3, 3); """
+    sql """ sync;"""
+    result = sql """ select * from ${s3VaultName}; """
+    assertEquals(result.size(), 3);
+}
\ No newline at end of file
diff --git 
a/regression-test/suites/cloud_p0/node_mgr/test_ms_alter_obj_info.groovy 
b/regression-test/suites/cloud_p0/node_mgr/test_ms_alter_obj_info.groovy
new file mode 100644
index 00000000000..b864db1838e
--- /dev/null
+++ b/regression-test/suites/cloud_p0/node_mgr/test_ms_alter_obj_info.groovy
@@ -0,0 +1,206 @@
+// 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.
+import groovy.json.JsonOutput
+import org.apache.doris.regression.suite.ClusterOptions
+
+suite('test_ms_alter_obj_info', 'p0, docker') {
+    if (!isCloudMode()) {
+        return;
+    }
+
+    def options = new ClusterOptions()
+    options.feConfigs += [
+        'cloud_cluster_check_interval_second=1',
+        'sys_log_verbose_modules=org',
+        'heartbeat_interval_second=1'
+    ]
+    options.setFeNum(1)
+    options.setBeNum(1)
+    options.cloudMode = true
+
+    def create_instance_api = { msHttpPort, request_body, check_func ->
+        httpTest {
+            endpoint msHttpPort
+            uri "/MetaService/http/create_instance?token=$token"
+            body request_body
+            check check_func
+        }
+    }
+
+
+    def get_instance_api = { msHttpPort, instance_id, check_func ->
+        httpTest {
+            op "get"
+            endpoint msHttpPort
+            uri 
"/MetaService/http/get_instance?token=${token}&instance_id=${instance_id}"
+            check check_func
+        }
+    }
+
+    def alter_obj_info_api = { msHttpPort, request_body, check_func ->
+        httpTest {
+            endpoint msHttpPort
+            uri "/MetaService/http/alter_obj_info?token=$token"
+            body request_body
+            check check_func
+        }
+    }
+
+    docker(options) {
+        def ms = cluster.getAllMetaservices().get(0)
+        def msHttpPort = ms.host + ":" + ms.httpPort
+        logger.info("ms1 addr={}, port={}, ms endpoint={}", ms.host, 
ms.httpPort, msHttpPort)
+
+        // Inventory function test
+        def token = "greedisgood9999"
+        def instance_id = "instance_id_test_in_docker"
+        def name = "user_1"
+        def user_id = "10000"
+
+        def cloudUniqueId = "1:${instance_id}:xxxxx"
+        // create instance
+        /*
+        curl -X GET 
'127.0.0.1:5000/MetaService/http/create_instance?token=greedisgood9999' -d '{
+            "instance_id": "instance_id_deadbeef",
+            "name": "user_1",
+            "user_id": "10000",
+            "obj_info": {
+                "ak": "test-ak1",
+                "sk": "test-sk1",
+                "bucket": "test-bucket",
+                "prefix": "test-prefix",
+                "endpoint": "test-endpoint",
+                "region": "test-region",
+                "provider" : S3",
+                "external_endpoint" : "endpoint"
+            }
+        }'
+        */
+        def jsonOutput = new JsonOutput()
+        def s3 = [
+                    ak: "test-ak1",
+                    sk : "test-sk1",
+                    bucket : "test-bucket",
+                    prefix: "test-prefix",
+                    endpoint: "test-endpoint",
+                    region: "test-region",
+                    provider : "S3",
+                    external_endpoint: "test-external-endpoint"
+                ]
+        def map = [instance_id: "${instance_id}", name: "${name}", user_id: 
"${user_id}", obj_info: s3]
+        def instance_body = jsonOutput.toJson(map)
+
+        create_instance_api.call(msHttpPort, instance_body) {
+                respCode, body ->
+                    log.info("http cli result: ${body} ${respCode}".toString())
+                    def json = parseJson(body)
+                    assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        def json
+        get_instance_api.call(msHttpPort, instance_id) {
+            respCode, body ->
+                log.info("get instance resp: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        // alter s3 info to instance
+        /*
+            curl 
'127.0.0.1:5000/MetaService/http/add_obj_info?token=greedisgood9999' -d '{
+                "cloud_unique_id": "cloud_unique_id_compute_node0",
+                "obj": {
+                    "id": `1`,
+                    "ak": "test-ak2",
+                    "sk": "test-sk2"
+                }
+            }'
+        */
+
+        def alter_obj_info_api_body = [cloud_unique_id:"${cloudUniqueId}",
+                                    obj:[id:"1", ak:"new-ak2", sk:"new-sk2"]]
+        jsonOutput = new JsonOutput()
+        def alterObjInfoBody = jsonOutput.toJson(alter_obj_info_api_body)
+        logger.info("alter obj info body: ${alterObjInfoBody}")
+
+        alter_obj_info_api.call(msHttpPort, alterObjInfoBody) {
+            respCode, body ->
+                log.info("http cli result: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        get_instance_api.call(msHttpPort, instance_id) {
+            respCode, body ->
+                log.info("get instance resp: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        assertTrue(json.result.obj_info[0]["ak"].equalsIgnoreCase("new-ak2"))
+        assertTrue(json.result.obj_info[0]["sk"].equalsIgnoreCase("new-sk2"))
+
+
+        alter_obj_info_api_body = [cloud_unique_id:"${cloudUniqueId}",
+                                    obj:[id:"1", role_arn:"new-role-arn", 
external_id:"new-external-id"]]
+        jsonOutput = new JsonOutput()
+        alterObjInfoBody = jsonOutput.toJson(alter_obj_info_api_body)
+        logger.info("alter obj info body: ${alterObjInfoBody}")
+
+        alter_obj_info_api.call(msHttpPort, alterObjInfoBody) {
+            respCode, body ->
+                log.info("http cli result: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        get_instance_api.call(msHttpPort, instance_id) {
+            respCode, body ->
+                log.info("get instance resp: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        
assertTrue(json.result.obj_info[0]["role_arn"].equalsIgnoreCase("new-role-arn"))
+        
assertTrue(json.result.obj_info[0]["external_id"].equalsIgnoreCase("new-external-id"))
+        
assertTrue(json.result.obj_info[0]["cred_provider_type"].equalsIgnoreCase("INSTANCE_PROFILE"))
+
+        alter_obj_info_api_body = [cloud_unique_id:"${cloudUniqueId}",
+                                    obj:[id:"1", ak:"new-ak3", sk:"new-sk3"]]
+        jsonOutput = new JsonOutput()
+        alterObjInfoBody = jsonOutput.toJson(alter_obj_info_api_body)
+        logger.info("alter obj info body: ${alterObjInfoBody}")
+
+        alter_obj_info_api.call(msHttpPort, alterObjInfoBody) {
+            respCode, body ->
+                log.info("http cli result: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        get_instance_api.call(msHttpPort, instance_id) {
+            respCode, body ->
+                log.info("get instance resp: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        assertTrue(json.result.obj_info[0]["ak"].equalsIgnoreCase("new-ak3"))
+        assertTrue(json.result.obj_info[0]["sk"].equalsIgnoreCase("new-sk3"))
+    }
+
+}
\ No newline at end of file
diff --git 
a/regression-test/suites/cloud_p0/node_mgr/test_ms_alter_s3_vault.groovy 
b/regression-test/suites/cloud_p0/node_mgr/test_ms_alter_s3_vault.groovy
new file mode 100644
index 00000000000..c837ecf90c6
--- /dev/null
+++ b/regression-test/suites/cloud_p0/node_mgr/test_ms_alter_s3_vault.groovy
@@ -0,0 +1,237 @@
+// 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.
+import groovy.json.JsonOutput
+import org.apache.doris.regression.suite.ClusterOptions
+
+suite('test_ms_alter_s3_vault', 'p0, docker') {
+    if (!isCloudMode()) {
+        return;
+    }
+
+    def options = new ClusterOptions()
+    options.feConfigs += [
+        'cloud_cluster_check_interval_second=1',
+        'sys_log_verbose_modules=org',
+        'heartbeat_interval_second=1'
+    ]
+    options.setFeNum(1)
+    options.setBeNum(1)
+    options.cloudMode = true
+
+    def create_instance_api = { msHttpPort, request_body, check_func ->
+        httpTest {
+            endpoint msHttpPort
+            uri "/MetaService/http/create_instance?token=$token"
+            body request_body
+            check check_func
+        }
+    }
+
+
+    def get_instance_api = { msHttpPort, instance_id, check_func ->
+        httpTest {
+            op "get"
+            endpoint msHttpPort
+            uri 
"/MetaService/http/get_instance?token=${token}&instance_id=${instance_id}"
+            check check_func
+        }
+    }
+
+    def show_storage_vaults_api = { msHttpPort, request_body, check_func ->
+        httpTest {
+            endpoint msHttpPort
+            uri "/MetaService/http/show_storage_vaults?token=${token}"
+            body request_body
+            check check_func
+        }
+    }
+
+    def alter_s3_vault_api = { msHttpPort, request_body, check_func ->
+        httpTest {
+            endpoint msHttpPort
+            uri "/MetaService/http/alter_s3_vault?token=$token"
+            body request_body
+            check check_func
+        }
+    }
+
+    docker(options) {
+        def ms = cluster.getAllMetaservices().get(0)
+        def msHttpPort = ms.host + ":" + ms.httpPort
+        logger.info("ms1 addr={}, port={}, ms endpoint={}", ms.host, 
ms.httpPort, msHttpPort)
+
+        // Inventory function test
+        def token = "greedisgood9999"
+        def instance_id = "instance_id_test_in_docker"
+        def name = "user_1"
+        def user_id = "10000"
+
+        def cloudUniqueId = "1:${instance_id}:xxxxx"
+        // create instance
+        /*
+        curl -X GET 
'127.0.0.1:5000/MetaService/http/create_instance?token=greedisgood9999' -d '{
+            "instance_id": "instance_id_deadbeef",
+            "name": "user_1",
+            "user_id": "10000",
+            "vault": {
+                "obj_info": {
+                    "ak": "test-ak1",
+                    "sk": "test-sk1",
+                    "bucket": "test-bucket",
+                    "prefix": "test-prefix",
+                    "endpoint": "test-endpoint",
+                    "region": "test-region",
+                    "provider" : S3",
+                    "external_endpoint" : "endpoint"
+                }
+            }
+        }'
+        */
+        def jsonOutput = new JsonOutput()
+        def s3 = [
+                    ak: "test-ak1",
+                    sk : "test-sk1",
+                    bucket : "test-bucket",
+                    prefix: "test-prefix",
+                    endpoint: "test-endpoint",
+                    region: "test-region",
+                    provider : "S3",
+                    external_endpoint: "test-external-endpoint"
+                ]
+        def map = [instance_id: "${instance_id}", name: "${name}", user_id: 
"${user_id}", vault: [obj_info: s3]]
+        def instance_body = jsonOutput.toJson(map)
+        logger.info("instance_body: ${instance_body}")
+
+        create_instance_api.call(msHttpPort, instance_body) {
+                respCode, body ->
+                    log.info("http cli result: ${body} ${respCode}".toString())
+                    def json = parseJson(body)
+                    assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        def json
+        get_instance_api.call(msHttpPort, instance_id) {
+            respCode, body ->
+                log.info("get instance resp: ${body} ${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        // show stoarge vaults
+        /*
+
+            curl 
http://127.0.0.1:5000/MetaService/http/show_storage_vaults?token=greedisgood9999
 -d '{
+                "cloud_unique_id":"cloud_unique_id_compute_node0"
+            }'
+        */
+
+        def show_storage_vaults_api_body = [cloud_unique_id:"${cloudUniqueId}"]
+        show_storage_vaults_api.call(msHttpPort, 
jsonOutput.toJson(show_storage_vaults_api_body)) {
+            respCode, body ->
+                log.info("show_storage_vaults_api resp: ${body} 
${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        def mtime = json.result.storage_vault[0]["obj_info"]["mtime"]
+        def ctime = json.result.storage_vault[0]["obj_info"]["ctime"]
+
+        // alter s3 vaults
+        /*
+            curl 
http://127.0.0.1:5000/MetaService/http/v1/alter_s3_vault?token=greedisgood9999 
-d '{
+                "cloud_unique_id":"cloud_unique_id_compute_node0",
+                "vault": {
+                    "name": "built_in_storage_vault",
+                    "obj_info": {
+                        "ak": "test-ak2",
+                        "sk": "test-sk2"
+                    }
+                }
+            }'
+
+            curl 
http://127.0.0.1:5000/MetaService/http/v1/alter_s3_vault?token=greedisgood9999 
-d '{
+                "cloud_unique_id":"cloud_unique_id_compute_node0",
+                "vault": {
+                    "name": "built_in_storage_vault",
+                    "obj_info": {
+                        "role_arn": "test-role-arn",
+                        "external_id": "test-external-id"
+                    }
+                }
+            }'
+        */
+        sleep(2000)
+        def alter_s3_vault_body = [cloud_unique_id:"${cloudUniqueId}", 
+                                    vault:[
+                                        name:"built_in_storage_vault",
+                                        obj_info:[ak:"test-ak2", sk:"test-sk2"]
+                                    ]]
+
+        alter_s3_vault_api.call(msHttpPort, 
jsonOutput.toJson(alter_s3_vault_body)) {
+            respCode, body ->
+                log.info("alter_s3_vault_api resp: ${body} 
${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        show_storage_vaults_api.call(msHttpPort, 
jsonOutput.toJson(show_storage_vaults_api_body)) {
+            respCode, body ->
+                log.info("show_storage_vaults_api resp: ${body} 
${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        
assertTrue(json.result.storage_vault[0]["obj_info"]["ak"].equalsIgnoreCase("test-ak2"))
+        
assertTrue(json.result.storage_vault[0]["obj_info"]["sk"].equalsIgnoreCase("test-sk2"))
+
+        def mtime2 = json.result.storage_vault[0]["obj_info"]["mtime"]
+        def ctime2 = json.result.storage_vault[0]["obj_info"]["ctime"]
+        assertTrue(mtime2 > mtime)
+        assertTrue(ctime2 == ctime)
+
+
+        sleep(2000)
+        alter_s3_vault_body = [cloud_unique_id:"${cloudUniqueId}", 
+                                    vault:[
+                                        name:"built_in_storage_vault",
+                                        obj_info:[role_arn:"test-role-arn", 
external_id:"test-external-id"]
+                                    ]]
+
+        alter_s3_vault_api.call(msHttpPort, 
jsonOutput.toJson(alter_s3_vault_body)) {
+            respCode, body ->
+                log.info("alter_s3_vault_api resp: ${body} 
${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        show_storage_vaults_api.call(msHttpPort, 
jsonOutput.toJson(show_storage_vaults_api_body)) {
+            respCode, body ->
+                log.info("show_storage_vaults_api resp: ${body} 
${respCode}".toString())
+                json = parseJson(body)
+                assertTrue(json.code.equalsIgnoreCase("OK"))
+        }
+
+        
assertTrue(json.result.storage_vault[0]["obj_info"]["role_arn"].equalsIgnoreCase("test-role-arn"))
+        
assertTrue(json.result.storage_vault[0]["obj_info"]["external_id"].equalsIgnoreCase("test-external-id"))
+        
assertTrue(json.result.storage_vault[0]["obj_info"]["cred_provider_type"].equalsIgnoreCase("INSTANCE_PROFILE"))
+
+        def mtime3 = json.result.storage_vault[0]["obj_info"]["mtime"]
+        def ctime3 = json.result.storage_vault[0]["obj_info"]["ctime"]
+        assertTrue(mtime3 > mtime)
+        assertTrue(ctime3 == ctime)
+    }
+}
\ No newline at end of file


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

Reply via email to