From 7fb1561df3f4f2c44744f97d4f77490d85c03a0e Mon Sep 17 00:00:00 2001 From: Lei Zhang Date: Tue, 20 May 2025 16:02:37 +0800 Subject: [PATCH] [feat](cloud) Support alter operation for obj_info and s3 vault obj_info ``` obj_info mode: curl '127.0.0.1:5000/MetaService/http/v1/?alter_obj_info?token=greedisgood9999' -d '{ "cloud_unique_id": "${cloud_unique_id}", "obj": { "id": "1", "ak": "new_ak", "sk": "new_sk" } }' curl '127.0.0.1:5000/MetaService/http/v1/?alter_obj_info?token=greedisgood9999' -d '{ "cloud_unique_id": "${cloud_unique_id}", "obj": { "id": "1", "role_arn": "new_role_arn", "external_id": "new_external_id" } }' s3 vault mode: curl '127.0.0.1:5000/MetaService/http/v1/?alter_s3_vault?token=greedisgood9999' -d '{ "cloud_unique_id": "${cloud_unique_id}", "vault": { "name": "built_in_storage_vault", "obj_info": { "ak": "test-ak2", "sk": "test-sk2" } } }' curl '127.0.0.1:5000/MetaService/http/v1/?alter_s3_vault?token=greedisgood9999' -d '{ "cloud_unique_id": "${cloud_unique_id}", "vault": { "name": "built_in_storage_vault", "obj_info": { "role_arn": "test-role-arn", "external_id": "test-external-id" } } }' ``` --- cloud/src/meta-service/meta_service_http.cpp | 8 +- .../meta-service/meta_service_resource.cpp | 131 ++++++-- cloud/test/meta_service_test.cpp | 301 ++++++++++++++++++ .../apache/doris/catalog/S3StorageVault.java | 6 +- .../apache/doris/catalog/StorageVault.java | 6 +- .../doris/cloud/rpc/MetaServiceClient.java | 4 + .../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(-) create mode 100644 regression-test/suites/aws_iam_role_p0/test_alter_s3_vault_with_role.groovy create mode 100644 regression-test/suites/cloud_p0/node_mgr/test_ms_alter_obj_info.groovy create mode 100644 regression-test/suites/cloud_p0/node_mgr/test_ms_alter_s3_vault.groovy diff --git a/cloud/src/meta-service/meta_service_http.cpp b/cloud/src/meta-service/meta_service_http.cpp index 40c5d796d916da..0999623047d1ef 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 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 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 eb88694ff5776f..bbd94b577b1bab 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_ptrbegin(), @@ -723,6 +715,8 @@ static int alter_s3_storage_vault(InstanceInfoPB& instance, std::unique_ptrclear_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(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&>(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( 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 31dd25eab75072..ac370d0f847b6b 100644 --- a/cloud/test/meta_service_test.cpp +++ b/cloud/test/meta_service_test.cpp @@ -9660,4 +9660,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(args[0]); + *ret = 0; + auto* key = try_any_cast(args[1]); + *key = "selectdbselectdbselectdbselectdb"; + auto* key_id = try_any_cast(args[2]); + *key_id = 1; + }); + + std::unique_ptr 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 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(args[0]); + *ret = 0; + auto* key = try_any_cast(args[1]); + *key = "selectdbselectdbselectdbselectdb"; + auto* key_id = try_any_cast(args[2]); + *key_id = 1; + }); + std::pair pair; + sp->set_call_back("extract_object_storage_info:get_aksk_pair", [&](auto&& args) { + auto* ret = try_any_cast*>(args[0]); + pair = *ret; + }); + + std::unique_ptr 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 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 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 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 d1f6e27358d697..3a7b9e55bff2db 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 @@ -71,6 +71,8 @@ public static class PropertyKey { 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 ALLOW_ALTER_PROPERTIES = new HashSet<>(Arrays.asList( @@ -78,7 +80,9 @@ public static class PropertyKey { 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 988f1d429e8330..f0d48bce0610b8 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 @@ -264,7 +264,11 @@ public static List convertToShowStorageVaultProperties(Cloud.StorageVaul 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 d027777bf194e0..048d8ab93dfdb8 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 Cloud.AlterClusterResponse alterCluster(Cloud.AlterClusterRequest request .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 1e5aa7ed111a48..95753821c35b05 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 Cloud.RemoveDeleteBitmapUpdateLockResponse removeDeleteBitmapUpdateLock( 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 3aefcea127d33d..928e80dbbf70b8 100644 --- a/gensrc/proto/cloud.proto +++ b/gensrc/proto/cloud.proto @@ -877,6 +877,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 00000000000000..990afd876f5438 --- /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 00000000000000..b864db1838e171 --- /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 00000000000000..c837ecf90c69b0 --- /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