diff --git a/deps/init/oceanbase.el7.aarch64.deps b/deps/init/oceanbase.el7.aarch64.deps index d74704f5b..64fab39aa 100644 --- a/deps/init/oceanbase.el7.aarch64.deps +++ b/deps/init/oceanbase.el7.aarch64.deps @@ -29,6 +29,7 @@ devdeps-boost-1.74.0-22022110914.el7.aarch64.rpm devdeps-s2geometry-0.9.0-12023092021.el7.aarch64.rpm devdeps-icu-69.1-72022112416.el7.aarch64.rpm devdeps-cos-c-sdk-5.0.21-202024080916.el7.aarch64.rpm +devdeps-protobuf-c-1.4.1-100000072023102410.el7.aarch64.rpm devdeps-s3-cpp-sdk-1.11.156-102023122011.el7.aarch64.rpm devdeps-protobuf-c-1.4.1-100000072023102410.el7.aarch64.rpm devdeps-roaringbitmap-croaring-3.0.0-42024042816.el7.aarch64.rpm diff --git a/deps/init/oceanbase.el7.x86_64.deps b/deps/init/oceanbase.el7.x86_64.deps index 9213c9425..04d2cfd8f 100644 --- a/deps/init/oceanbase.el7.x86_64.deps +++ b/deps/init/oceanbase.el7.x86_64.deps @@ -32,6 +32,7 @@ devdeps-s2geometry-0.9.0-12023092021.el7.x86_64.rpm devdeps-icu-69.1-72022112416.el7.x86_64.rpm devdeps-cloud-qpl-1.1.0-272023061419.el7.x86_64.rpm devdeps-cos-c-sdk-5.0.21-202024080916.el7.x86_64.rpm +devdeps-protobuf-c-1.4.1-100000062023102016.el7.x86_64.rpm devdeps-s3-cpp-sdk-1.11.156-102023122011.el7.x86_64.rpm devdeps-protobuf-c-1.4.1-100000062023102016.el7.x86_64.rpm devdeps-roaringbitmap-croaring-3.0.0-42024042816.el7.x86_64.rpm diff --git a/deps/init/oceanbase.el8.aarch64.deps b/deps/init/oceanbase.el8.aarch64.deps index 622a1b479..f3a1c01f4 100644 --- a/deps/init/oceanbase.el8.aarch64.deps +++ b/deps/init/oceanbase.el8.aarch64.deps @@ -29,6 +29,7 @@ devdeps-boost-1.74.0-22022110914.el8.aarch64.rpm devdeps-s2geometry-0.9.0-12023092021.el8.aarch64.rpm devdeps-icu-69.1-72022112416.el8.aarch64.rpm devdeps-cos-c-sdk-5.0.21-202024080916.el8.aarch64.rpm +devdeps-protobuf-c-1.4.1-100000072023102410.el8.aarch64.rpm devdeps-s3-cpp-sdk-1.11.156-102023122011.el8.aarch64.rpm devdeps-protobuf-c-1.4.1-100000072023102410.el8.aarch64.rpm devdeps-roaringbitmap-croaring-3.0.0-42024042816.el8.aarch64.rpm diff --git a/deps/init/oceanbase.el8.x86_64.deps b/deps/init/oceanbase.el8.x86_64.deps index 6587046d8..0c1fb05b4 100644 --- a/deps/init/oceanbase.el8.x86_64.deps +++ b/deps/init/oceanbase.el8.x86_64.deps @@ -30,6 +30,7 @@ devdeps-boost-1.74.0-22022110914.el8.x86_64.rpm devdeps-s2geometry-0.9.0-12023092021.el8.x86_64.rpm devdeps-icu-69.1-72022112416.el8.x86_64.rpm devdeps-cos-c-sdk-5.0.21-202024080916.el8.x86_64.rpm +devdeps-protobuf-c-1.4.1-100000062023102016.el8.x86_64.rpm devdeps-s3-cpp-sdk-1.11.156-102023122011.el8.x86_64.rpm devdeps-protobuf-c-1.4.1-100000062023102016.el8.x86_64.rpm devdeps-roaringbitmap-croaring-3.0.0-42024042816.el8.x86_64.rpm diff --git a/deps/oblib/src/lib/ob_errno.h b/deps/oblib/src/lib/ob_errno.h index 073fa4f49..46972e59f 100644 --- a/deps/oblib/src/lib/ob_errno.h +++ b/deps/oblib/src/lib/ob_errno.h @@ -119,6 +119,7 @@ constexpr int OB_HASH_PLACEMENT_RETRY = -4205; constexpr int OB_HASH_FULL = -4206; constexpr int OB_WAIT_NEXT_TIMEOUT = -4208; constexpr int OB_MAJOR_FREEZE_NOT_FINISHED = -4213; +constexpr int OB_CURL_ERROR = -4216; constexpr int OB_INVALID_DATE_VALUE = -4219; constexpr int OB_INACTIVE_SQL_CLIENT = -4220; constexpr int OB_INACTIVE_RPC_PROXY = -4221; diff --git a/deps/oblib/src/lib/restore/CMakeLists.txt b/deps/oblib/src/lib/restore/CMakeLists.txt index 20aed1a04..524290b19 100644 --- a/deps/oblib/src/lib/restore/CMakeLists.txt +++ b/deps/oblib/src/lib/restore/CMakeLists.txt @@ -17,6 +17,7 @@ oblib_add_library(restore OBJECT cos/ob_cos_wrapper_handle.h cos/ob_singleton.h ob_object_device.cpp + hmac_signature.cpp ob_object_device.h ob_object_storage_base.cpp) diff --git a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.cpp b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.cpp index f61818ab5..e7ab15fef 100644 --- a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.cpp +++ b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.cpp @@ -618,6 +618,7 @@ int ObCosWrapper::create_cos_handle( OB_COS_customMem &custom_mem, const struct ObCosAccount &account, const bool check_md5, + const char *cos_sts_token, ObCosWrapper::Handle **h) { int ret = OB_SUCCESS; @@ -666,6 +667,9 @@ int ObCosWrapper::create_cos_handle( cos_str_set(&ctx->options->config->access_key_id, account.access_id_); cos_str_set(&ctx->options->config->access_key_secret, account.access_key_); cos_str_set(&ctx->options->config->appid, account.appid_); + if (nullptr != cos_sts_token) { + cos_str_set(&ctx->options->config->sts_token, cos_sts_token); + } ctx->options->config->is_cname = 0; // connection timeout, default 60s ctx->options->ctl->options->connect_timeout = 60; diff --git a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.h b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.h index 581d1ec5f..819343f9b 100644 --- a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.h +++ b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper.h @@ -230,6 +230,7 @@ public: OB_COS_customMem &custom_mem, const struct ObCosAccount &account, const bool check_md5, + const char *cos_sts_token, Handle **h); // You can not use handle any more after destroy_cos_handle is called. diff --git a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.cpp b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.cpp index cd937d7a7..1311efb3e 100644 --- a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.cpp +++ b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.cpp @@ -12,6 +12,7 @@ #include "lib/utility/ob_print_utils.h" #include "ob_cos_wrapper_handle.h" +#include "lib/restore/ob_i_storage.h" namespace oceanbase { @@ -46,6 +47,10 @@ void ObCosMemAllocator::reuse() allocator_.reuse(); } +void ObCosMemAllocator::reset() +{ + allocator_.clear(); +} // Memory function used for cos context static void *ob_cos_malloc(void *opaque, size_t size) @@ -68,7 +73,7 @@ static void ob_cos_free(void *opaque, void *address) /*--------------------------------ObCosWrapperHandle-----------------------------------*/ ObCosWrapperHandle::ObCosWrapperHandle() - : is_inited_(false), handle_(nullptr), cos_account_(), allocator_(), + : is_inited_(false), handle_(nullptr), cos_account_(), sts_token_(), allocator_(), delete_mode_(ObStorageDeleteMode::STORAGE_DELETE_MODE) {} @@ -83,11 +88,13 @@ int ObCosWrapperHandle::init(const ObObjectStorageInfo *storage_info) } else if (OB_ISNULL(storage_info)) { ret = OB_INVALID_ARGUMENT; OB_LOG(WARN, "storage info is null", K(ret)); - } else if (OB_FAIL(storage_info->get_storage_info_str(storage_info_str, sizeof(storage_info_str)))) { + } else if (OB_FAIL(storage_info->get_authorization_str( + storage_info_str, sizeof(storage_info_str), sts_token_))) { OB_LOG(WARN, "fail to get cos storage info str", K(ret), K(storage_info)); } else if (OB_FAIL(cos_account_.parse_from(storage_info_str, strlen(storage_info_str)))) { - OB_LOG(WARN, "fail to parse cos account from storage info str", K(ret)); - } else if (strlen(cos_account_.delete_mode_) > 0 && OB_FAIL(set_delete_mode(cos_account_.delete_mode_))) { + OB_LOG(WARN, "fail to parse cos account from authorization str", K(ret)); + } else if (strlen(cos_account_.delete_mode_) > 0 + && OB_FAIL(set_delete_mode(cos_account_.delete_mode_))) { OB_LOG(WARN, "fail to set cos delete mode", K(cos_account_.delete_mode_), K(ret)); } else { is_inited_ = true; @@ -100,25 +107,28 @@ void ObCosWrapperHandle::reset() destroy_cos_handle(); is_inited_ = false; delete_mode_ = ObStorageDeleteMode::STORAGE_DELETE_MODE; + // clear memory used by cos account sts token + cos_account_.clear(); + sts_token_.reset(); + allocator_.reset(); } -int create_cos_handle( +int ObCosWrapperHandle::create_cos_handle( ObCosMemAllocator &allocator, - const qcloud_cos::ObCosAccount &cos_account, const bool check_md5, qcloud_cos::ObCosWrapper::Handle *&handle) { int ret = OB_SUCCESS; handle = nullptr; qcloud_cos::OB_COS_customMem cos_mem = {ob_cos_malloc, ob_cos_free, &allocator}; - if (OB_FAIL(qcloud_cos::ObCosWrapper::create_cos_handle(cos_mem, cos_account, - check_md5, &handle))) { + const char *sts_data = sts_token_.get_data(); + if (OB_FAIL(qcloud_cos::ObCosWrapper::create_cos_handle( + cos_mem, cos_account_, check_md5, sts_data, &handle))) { OB_LOG(WARN, "failed to create tmp cos handle", K(ret)); } else if (OB_ISNULL(handle)) { ret = OB_OBJECT_STORAGE_IO_ERROR; OB_LOG(WARN, "create tmp handle succeed, but returned handle is null", K(ret)); } - return ret; } @@ -131,7 +141,7 @@ int ObCosWrapperHandle::create_cos_handle(const bool check_md5) } else if (IS_NOT_INIT) { ret = OB_NOT_INIT; OB_LOG(WARN, "handle is not inited", K(ret)); - } else if (OB_FAIL(common::create_cos_handle(allocator_, cos_account_, check_md5, handle_))) { + } else if (OB_FAIL(create_cos_handle(allocator_, check_md5, handle_))) { OB_LOG(WARN, "failed to create cos handle", K(ret)); } return ret; @@ -149,7 +159,7 @@ int ObCosWrapperHandle::create_tmp_cos_handle( ret = OB_NOT_INIT; OB_LOG(WARN, "handle is not inited", K(ret)); } else if (OB_FAIL(qcloud_cos::ObCosWrapper::create_cos_handle(cos_mem, cos_account_, - check_md5, &handle))) { + check_md5, sts_token_.data_, &handle))) { OB_LOG(WARN, "failed to create tmp cos handle", K(ret)); } else if (OB_ISNULL(handle)) { ret = OB_OBJECT_STORAGE_IO_ERROR; diff --git a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.h b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.h index ee364a194..bb962c358 100644 --- a/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.h +++ b/deps/oblib/src/lib/restore/cos/ob_cos_wrapper_handle.h @@ -35,18 +35,11 @@ public: void *alloc(size_t size); void free(void *addr); void reuse(); + void reset(); private: ObArenaAllocator allocator_; }; -// Create a temporary cos_handle object, -// utilizing the 'allocator' to allocate the necessary memory. -int create_cos_handle( - ObCosMemAllocator &allocator, - const qcloud_cos::ObCosAccount &cos_account, - const bool check_md5, - qcloud_cos::ObCosWrapper::Handle *&handle); - class ObCosWrapperHandle { public: @@ -55,6 +48,12 @@ public: int init(const common::ObObjectStorageInfo *storage_info); void reset(); + // Create a temporary cos_handle object, + // utilizing the 'allocator' to allocate the necessary memory. + int create_cos_handle( + ObCosMemAllocator &allocator, + const bool check_md5, + qcloud_cos::ObCosWrapper::Handle *&handle); int create_cos_handle(const bool check_md5); void destroy_cos_handle(); qcloud_cos::ObCosWrapper::Handle *get_ptr() { return handle_; } @@ -86,6 +85,7 @@ private: bool is_inited_; qcloud_cos::ObCosWrapper::Handle *handle_; qcloud_cos::ObCosAccount cos_account_; + ObSTSToken sts_token_; ObCosMemAllocator allocator_; ObString bucket_name_; ObString object_name_; diff --git a/deps/oblib/src/lib/restore/hmac_signature.cpp b/deps/oblib/src/lib/restore/hmac_signature.cpp new file mode 100644 index 000000000..d982a5d93 --- /dev/null +++ b/deps/oblib/src/lib/restore/hmac_signature.cpp @@ -0,0 +1,311 @@ +/** + * Copyright (c) 2021 OceanBase + * OceanBase CE is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan PubL v2. + * You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PubL v2 for more details. + */ + +#include "hmac_signature.h" +#include "lib/encode/ob_base64_encode.h" +#include "lib/allocator/page_arena.h" +#include "lib/container/ob_array_iterator.h" + +namespace oceanbase +{ +namespace common +{ + +// Function to generate a signature nonce +int generate_signature_nonce(char *nonce, const int64_t buf_size) +{ + int ret = OB_SUCCESS; + // Get current timestamp in milliseconds + const int64_t curr_time_us = ObTimeUtility::current_time(); + if (OB_ISNULL(nonce) || OB_UNLIKELY(buf_size <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid args to generate signature nonce", K(ret), KP(nonce), K(buf_size)); + } else { + int64_t random_num = ObRandom::rand(0, OB_MAX_STS_SIGNATURE_RAND_NUM); + if (OB_FAIL(databuff_printf(nonce, buf_size, "%ld%ld", curr_time_us, random_num))) { + OB_LOG(WARN, "failed to gen signature nonce", K(ret), K(nonce), K(buf_size), K(curr_time_us), K(random_num)); + } + } + return ret; +} + +// request_id is used as the identifier for connecting to ocp. +// observer needs to ensure that request_id is unique and there are no other requirements. +int generate_request_id(char *request_id, const int64_t buf_size) +{ + int ret = OB_SUCCESS; + char signature_nonce[OB_MAX_STS_SIGNATURE_NONCE_LENTH] = {0}; + // Get current time + if (OB_FAIL(generate_signature_nonce(signature_nonce, sizeof(signature_nonce)))) { + OB_LOG(WARN, "generage signature nonce failed", K(ret), K(signature_nonce), K(sizeof(signature_nonce))); + } else if (OB_FAIL(databuff_printf(request_id, buf_size, "observer-%s", signature_nonce))) { + OB_LOG(WARN, "failed to generate request id", K(ret), K(request_id), K(buf_size), K(signature_nonce)); + } + return ret; +} + +int base64_encoded(const char *input, const int64_t input_len, char *encoded_result, const int64_t encoded_result_buf_len) +{ + int ret = OB_SUCCESS; + int64_t encoded_buf_len = ObBase64Encoder::needed_encoded_length(input_len); + int64_t encoded_pos = 0; + + if (OB_ISNULL(input) || OB_UNLIKELY(input_len <= 0) || OB_ISNULL(encoded_result) || OB_UNLIKELY(encoded_result_buf_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid args to encode base64", K(ret), KP(input), K(input_len), KP(encoded_result), K(encoded_result_buf_len)); + } else if (OB_UNLIKELY(encoded_buf_len >= encoded_result_buf_len)) { + ret = OB_SIZE_OVERFLOW; + OB_LOG(WARN, "size overflow", K(ret), K(encoded_buf_len), K(encoded_result_buf_len)); + } else if (OB_FAIL(ObBase64Encoder::encode((const uint8_t *)input, input_len, encoded_result, encoded_result_buf_len, encoded_pos))) { + OB_LOG(WARN, "encode base64 fails", K(ret), KP(input), K(input_len), KP(encoded_result), K(encoded_result_buf_len), + K(encoded_buf_len), K(encoded_pos)); + } else if (OB_UNLIKELY(encoded_pos >= encoded_result_buf_len)) { + ret = OB_SIZE_OVERFLOW; + OB_LOG(WARN, "size overflow", K(ret), K(encoded_pos), K(encoded_result_buf_len)); + } else { + encoded_result[encoded_pos] = '\0'; + } + return ret; +} + +int percent_encode(const char *content, char *buf, const int64_t buf_len) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(content) || OB_ISNULL(buf) || OB_UNLIKELY(buf_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid argument", K(ret), KP(content), KP(buf), K(buf_len)); + } else { + char *p = buf; + const int64_t content_len = strlen(content); + int64_t num = 0; + const char *hex = "0123456789ABCDEF"; + int64_t triple_num = 0; + if (OB_UNLIKELY(content_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid content", K(ret), K(content_len)); + } + for (int64_t i = 0; OB_SUCC(ret) && i < content_len; ++i) { + char c = content[i]; + if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + num++; + } + } + triple_num = content_len - num; + if (OB_FAIL(ret)) { + } else if (OB_UNLIKELY(content_len + 2 * triple_num >= buf_len)) { + ret = OB_SIZE_OVERFLOW; + OB_LOG(WARN, "content is too long", K(ret), KP(content), K(content_len), K(triple_num), K(buf_len)); + } else { + for (uint64_t i = 0; OB_SUCC(ret) && i < content_len; ++i) { + char c = content[i]; + if (std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + *p++ = c; + } else if (c == ' ') { + *p++ = '%'; + *p++ = '2'; + *p++ = '0'; + } else { + *p++ = '%'; + *p++ = hex[(c >> 4) & 0xF]; + *p++ = hex[c & 0xF]; + } + } + *p = '\0'; + } + } + return ret; +} + +// The signature generated by hmac_sha1 may contain \0, +// result_len is used to record the actual length of the signature after hmac_sha1 +int hmac_sha1(const char *key, const char *data, char *encoded_result, const int64_t encode_buf_len, + uint32_t &result_len) +{ + int ret = OB_SUCCESS; + unsigned char *result = nullptr; + ObArenaAllocator allocator("hmac_sha1"); + // The buffer should have at least EVP_MAX_MD_SIZE bytes of space to ensure that it can store the + // output of any hashing algorithm. + if (OB_ISNULL(key) || OB_ISNULL(data) || OB_ISNULL(encoded_result) + || OB_UNLIKELY(encode_buf_len < EVP_MAX_MD_SIZE)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid argument", K(ret), KP(key), KP(data), KP(encoded_result), + K(encode_buf_len), K(result_len)); + } + // The max length of hmac_sha1 is 20 + else if (OB_ISNULL(result = static_cast(allocator.alloc(sizeof(char) * 20)))) { + ret = OB_ALLOCATE_MEMORY_FAILED; + OB_LOG(WARN, "failed to malloc", K(ret)); + } else { + HMAC_CTX *hmac = HMAC_CTX_new(); + if (OB_ISNULL(hmac)) { + ret = OB_ALLOCATE_MEMORY_FAILED; + OB_LOG(WARN, "failed to allocate memory", K(ret)); + } else if (OB_UNLIKELY(1 != HMAC_Init_ex(hmac, key, strlen(key), EVP_sha1(), nullptr))) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "failed to init hmac", K(ret), KP(key), K(strlen(key))); + } else { + if (OB_UNLIKELY(1 != HMAC_Update(hmac, reinterpret_cast(data), strlen(data)))) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "failed to update hmac", K(ret), KP(data), K(strlen(data))); + } + } + if (OB_SUCC(ret)) { + if (OB_UNLIKELY(1 != HMAC_Final(hmac, result, &result_len))) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "failed to final hmac", K(ret), KP(result), K(result_len)); + } else if (OB_UNLIKELY(result_len >= encode_buf_len)) { + ret = OB_SIZE_OVERFLOW; + OB_LOG(WARN, "encoded result is too long", K(ret), K(result_len), K(encode_buf_len)); + } else { + MEMCPY(encoded_result, result, result_len); + encoded_result[result_len] = '\0'; + } + } + if (OB_NOT_NULL(hmac)) { + HMAC_CTX_free(hmac); + } + } + return ret; +} + +struct Compare +{ + // Sort lexicographically by key + bool operator()(const std::pair &a, const std::pair &b) const + { + // Check if either key is NULL + bool flag = false; + if (OB_ISNULL(a.first) && OB_ISNULL(b.first)) { + flag = false; // Both are NULL, considered equal + } else if (OB_ISNULL(a.first)) { + flag = true; // NULL is considered less than any non-NULL string + } else if (OB_ISNULL(b.first)) { + flag = false; // Any non-NULL string is considered greater than NULL + } else { + int key_cmp = strcmp(a.first, b.first); + if (key_cmp < 0) { + flag = true; + } else if (key_cmp > 0) { + flag = false; + } else { + // If keys are equal, compare values + // Check if either value is NULL + if (OB_ISNULL(a.second) && OB_ISNULL(b.second)) { + flag = false; // Both are NULL, considered equal + } else if (OB_ISNULL(a.second)) { + flag = true; // NULL is considered less than any non-NULL string + } else if (OB_ISNULL(b.second)) { + flag = false; // Any non-NULL string is considered greater than NULL + } else { + flag = strcmp(a.second, b.second) < 0; + } + } + } + return flag; + } +}; + +int sign_request( + ObArray> params, + const char *method, char *signature, + const int64_t signature_buf_len) +{ + int ret = OB_SUCCESS; + if (OB_UNLIKELY(params.count() <= 0) || OB_ISNULL(method) || OB_ISNULL(signature) + || OB_UNLIKELY(signature_buf_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid argument", K(ret), K(params.count()), KP(method), KP(signature), + K(signature_buf_len)); + } else { + // Sort the parameters by key + lib::ob_sort(params.begin(), params.end(), Compare()); + // Make the string to sign + const char *sk = nullptr; + // Currently, the longest key used for signature will not exceed 64 + const int64_t OB_MAX_HMAC_ENCODED_KEY = 64; + constexpr int64_t OB_MAX_HMAC_ENCODED_VALUE = MAX(OB_MAX_STS_SIGNATURE_NONCE_LENTH, + MAX(OB_MAX_STS_REQUEST_ID_LENTH, MAX(OB_MAX_ROLE_ARN_LENGTH, MAX(OB_MAX_STS_AK_LENGTH, OB_MAX_STS_SK_LENGTH)))); + char encoded_key[OB_MAX_HMAC_ENCODED_KEY] = {0}; + char encoded_value[OB_MAX_HMAC_ENCODED_VALUE] = {0}; + char delimiter[4] = {0}; + char params_to_sign[OB_MAX_STS_CONCAT_LENGTH] = {0}; + char concat_params[OB_MAX_STS_CONCAT_LENGTH] = {0}; + char sign_result[OB_MAX_STS_CONCAT_LENGTH] = {0}; + char encoded_result[OB_MAX_STS_SIGNATURE_LENGTH] = {0}; + uint32_t sign_result_len = 0; + ObArenaAllocator allocator("hmac_signature"); + ObStringBuffer encoded_params(&allocator); + + const char *first = nullptr; + const char *second = nullptr; + // e.g.Action=GetResourceSTSCredential&CloudProvider=aliyun&RequestId=obrequest&RequestSource=OBSERVER + // &ResourceAccount=oceanbase&ResourceType=OSS&SignatureNonce=xxxxx + for (uint64_t i = 0; OB_SUCC(ret) && i < params.count(); ++i) { + first = params.at(i).first; + second = params.at(i).second; + if (OB_ISNULL(first) || OB_ISNULL(second)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "params is not allowed to be nullptr", K(ret), K(first), KP(second), K(i)); + } else if (MEMCMP(first, "ObSignature", strlen(first)) == 0 + || MEMCMP(first, "ObSignatureKey", strlen(first)) == 0 + || MEMCMP(first, "ObSignatureSecret", strlen(first)) == 0 + || MEMCMP(first, "UID", strlen(first)) == 0 + || MEMCMP(first, "OpSource", strlen(first)) == 0) { + if (MEMCMP(first, "ObSignatureSecret", strlen(first)) == 0) { + sk = second; + } + continue; + } else if (OB_FAIL(percent_encode(first, encoded_key, sizeof(encoded_key)))) { + OB_LOG(WARN, "failed to percent encode", K(ret), K(i), K(first)); + } else if (OB_FAIL(percent_encode(second, encoded_value, sizeof(encoded_value)))) { + OB_LOG(WARN, "failed to percent encode", K(ret), K(i), KP(second)); + } else if (i != 0 && OB_FAIL(encoded_params.append("&"))) { + OB_LOG(WARN, "failed to append", K(ret), K(i)); + } else if (OB_FAIL(encoded_params.append(encoded_key))) { + OB_LOG(WARN, "failed to append", K(ret), KP(encoded_key)); + } else if (OB_FAIL(encoded_params.append("="))) { + OB_LOG(WARN, "failed to append", K(ret)); + } else if (OB_FAIL(encoded_params.append(encoded_value))) { + OB_LOG(WARN, "failed to append", K(ret), KP(encoded_value)); + } + } + // e.g. params_to_sign="POST&%2F&Action%3DGetResourceSTSCredential%26CloudProvider%3Daliyun%26 + // RequestId%3Dobrequest%26RequestSource%3DOBSERVER%26ResourceAccount%3Doceanbase%26ResourceType%3DOSS%26SignatureNonce%3Dxxxxxx" + if (FAILEDx(percent_encode("/", delimiter, sizeof(delimiter)))) { + OB_LOG(WARN, "failed to percent encode", K(ret), K(delimiter), K(sizeof(delimiter))); + } else if (OB_FAIL(percent_encode(encoded_params.ptr(), concat_params, sizeof(concat_params)))) { + OB_LOG(WARN, "failed to percent encode", K(ret), KP(encoded_params.ptr()), K(encoded_params.length()), KP(concat_params)); + } else if (OB_FAIL(databuff_printf( + params_to_sign, sizeof(params_to_sign), "%s&%s&%s", method, delimiter, concat_params))) { + OB_LOG(WARN, "failed to percent encode", K(ret), K(method), K(delimiter), KP(concat_params), KP(params_to_sign)); + } + // signature with HMAC-SHA1 + else if (OB_FAIL(hmac_sha1(sk, params_to_sign, sign_result, sizeof(sign_result), sign_result_len))) { + OB_LOG(WARN, "failed to hmac sha1", K(ret), KP(params_to_sign), K(sign_result), K(sign_result_len)); + } else if (OB_FAIL(base64_encoded(sign_result, sign_result_len, encoded_result, sizeof(encoded_result)))) { + OB_LOG(WARN, "failed to base64 encode", K(ret), K(sign_result)); + } + const int64_t len = strlen(encoded_result); + if (OB_FAIL(ret)) { + } else if (len >= signature_buf_len) { + ret = OB_SIZE_OVERFLOW; + OB_LOG(WARN, "signature is too long", K(ret), K(signature_buf_len), K(len)); + } else { + MEMCPY(signature, encoded_result, len); + signature[len] = '\0'; + } + } + return ret; +} +} // namespace common +} // namespace oceanbase \ No newline at end of file diff --git a/deps/oblib/src/lib/restore/hmac_signature.h b/deps/oblib/src/lib/restore/hmac_signature.h new file mode 100644 index 000000000..1d251878c --- /dev/null +++ b/deps/oblib/src/lib/restore/hmac_signature.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2021 OceanBase + * OceanBase CE is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan PubL v2. + * You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, + * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, + * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * See the Mulan PubL v2 for more details. + */ +#ifndef OB_HMAC_SIGNATURE_H_ +#define OB_HMAC_SIGNATURE_H_ +#include +#include +#include +#include +#include +#include +#include +#include "lib/container/ob_array.h" +#include "lib/allocator/ob_malloc.h" +#include "lib/string/ob_string_buffer.h" +#include "share/rc/ob_tenant_base.h" +#include "lib/restore/ob_storage_info.h" +namespace oceanbase +{ +namespace common +{ +int generate_signature_nonce(char *nonce, const int64_t buf_size); + +int generate_request_id(char *request_id, const int64_t buf_size); + +int base64_encoded(const char *input, const int64_t input_len, char *encoded_result, + const int64_t encoded_result_buf_len); + +int percent_encode(const char *content, char *buf, const int64_t buf_len); + +int hmac_sha1(const char *key, const char *data, char *encoded_result, const int64_t encode_buf_len, + uint32_t &result_len); + +int sign_request(ObArray> params, const char *method, + char *signature, const int64_t signature_buf_len); + +} // namespace common +} // namespace oceanbase +#endif // OB_HMAC_SIGNATURE_H_ \ No newline at end of file diff --git a/deps/oblib/src/lib/restore/ob_i_storage.cpp b/deps/oblib/src/lib/restore/ob_i_storage.cpp index f22366bdd..71cfcb37a 100644 --- a/deps/oblib/src/lib/restore/ob_i_storage.cpp +++ b/deps/oblib/src/lib/restore/ob_i_storage.cpp @@ -339,6 +339,25 @@ int record_failed_files_idx(const hash::ObHashMap &files_to_d return ret; } +int ob_set_field(const char *value, char *field, const uint32_t field_length) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(value) || OB_ISNULL(field)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid arguments", K(ret), KP(value), KP(field)); + } else { + const int64_t value_len = strlen(value); + if (value_len >= field_length) { + ret = OB_SIZE_OVERFLOW; + OB_LOG(WARN, "value is too long", K(ret), KP(value), K(value_len), K(field_length)); + } else { + MEMCPY(field, value, value_len); + field[value_len] = '\0'; + } + } + return ret; +} + int ob_apr_abort_fn(int retcode) { int ret = OB_ALLOCATE_MEMORY_FAILED; @@ -846,6 +865,27 @@ ObObjectStorageGuard::ObObjectStorageGuard( } } + +// when accessing the object storage, if the error code returned is OB_BACKUP_PERMISSION_DENIED, +// it may be due to expired temporary ak/sk +// attempt to refresh the temporary ak/sk, and if the refresh fails, +// only log the error message to avoid overriding the original error code. +static void try_refresh_device_credential( + const int ob_errcode, const ObObjectStorageInfo *storage_info) +{ + int ret = OB_SUCCESS; + if (ob_errcode != OB_OBJECT_STORAGE_PERMISSION_DENIED) { + // do nothing + } else if (OB_ISNULL(storage_info) || OB_UNLIKELY(!storage_info->is_valid())) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid argument", K(ret), K(ob_errcode), KPC(storage_info)); + } else if (storage_info->is_assume_role_mode() + && OB_FAIL(ObDeviceCredentialMgr::get_instance().curl_credential( + *storage_info, true /*update_access_time*/))) { + OB_LOG(WARN, "failed to refresh credential", K(ret), K(ob_errcode), KPC(storage_info)); + } +} + void ObObjectStorageGuard::print_access_storage_log_() const { const int64_t cost_time_us = ObTimeUtility::current_time() - start_time_us_; @@ -880,6 +920,7 @@ bool ObObjectStorageGuard::is_slow_io_(const int64_t cost_time_us) const ObObjectStorageGuard::~ObObjectStorageGuard() { print_access_storage_log_(); + try_refresh_device_credential(ob_errcode_, storage_info_); lib::ObMallocHookAttrGuard::~ObMallocHookAttrGuard(); } diff --git a/deps/oblib/src/lib/restore/ob_i_storage.h b/deps/oblib/src/lib/restore/ob_i_storage.h index 8a3dc166c..5b3ee97bb 100644 --- a/deps/oblib/src/lib/restore/ob_i_storage.h +++ b/deps/oblib/src/lib/restore/ob_i_storage.h @@ -72,6 +72,7 @@ int check_files_map_validity(const hash::ObHashMap &files_to_ // record all files's idx remained in files_to_delete int record_failed_files_idx(const hash::ObHashMap &files_to_delete, ObIArray &failed_files_idx); +int ob_set_field(const char *value, char *field, const uint32_t field_length); int ob_apr_abort_fn(int retcode); struct ObStorageObjectMetaBase diff --git a/deps/oblib/src/lib/restore/ob_storage.cpp b/deps/oblib/src/lib/restore/ob_storage.cpp index 8af2fa903..a21d05b66 100644 --- a/deps/oblib/src/lib/restore/ob_storage.cpp +++ b/deps/oblib/src/lib/restore/ob_storage.cpp @@ -669,43 +669,53 @@ int ObStorageUtil::read_seal_meta_if_needed( seal_meta_uri, OB_MAX_URI_LENGTH))) { OB_LOG(WARN, "fail to construct s3 seal_meta name", K(ret), K(uri)); } else { - ObStorageReader reader; - int64_t seal_meta_len = 0; - if (OB_FAIL(reader.open(seal_meta_uri, storage_info_))) { - if (OB_OBJECT_NOT_EXIST == ret) { - obj_meta.is_exist_ = false; - ret = OB_SUCCESS; - } + ObStorageReader *reader = nullptr; + if (OB_ISNULL(reader = static_cast(allocator.alloc(sizeof(ObStorageReader))))) { + ret = OB_ALLOCATE_MEMORY_FAILED; + OB_LOG(WARN, "fail to alloc buf for reader", K(ret)); + } else if (FALSE_IT(new (reader) ObStorageReader())) { } else { - // If exist seal meta, directly read it content. - seal_meta_len = reader.get_length(); - if (seal_meta_len > 0) { - int64_t read_size = 0; - char *buf = NULL; - pos = 0; - if (OB_ISNULL(buf = static_cast(allocator.alloc(seal_meta_len + 1)))) { - ret = OB_ALLOCATE_MEMORY_FAILED; - OB_LOG(WARN, "fail to alloc buf for reading seal meta file", K(ret), K(seal_meta_uri), K(seal_meta_len)); - } else if (OB_FAIL(reader.pread(buf, seal_meta_len, 0/*offset*/, read_size))) { - OB_LOG(WARN, "failed to read seal meta file content", K(ret), K(seal_meta_uri), K(seal_meta_len)); - } else if (OB_UNLIKELY(seal_meta_len != read_size)) { - ret = OB_ERR_UNEXPECTED; - OB_LOG(WARN, "fail to read seal meta file entire content", - K(ret), K(seal_meta_uri), K(seal_meta_len), K(read_size)); - } else if (OB_FAIL(obj_meta.deserialize(buf, read_size, pos))) { - OB_LOG(WARN, "fail to deserialize storage object meta", K(ret), K(seal_meta_uri), K(read_size), KP(buf)); - } else { - obj_meta.is_exist_ = true; + int64_t seal_meta_len = 0; + if (OB_FAIL(reader->open(seal_meta_uri, storage_info_))) { + if (OB_OBJECT_NOT_EXIST == ret) { + obj_meta.is_exist_ = false; + ret = OB_SUCCESS; } } else { - ret = OB_ERR_UNEXPECTED; - OB_LOG(WARN, "the seal meta file is empty", K(ret), K(seal_meta_uri)); - } + // If exist seal meta, directly read it content. + seal_meta_len = reader->get_length(); + if (seal_meta_len > 0) { + int64_t read_size = 0; + char *buf = NULL; + pos = 0; + if (OB_ISNULL(buf = static_cast(allocator.alloc(seal_meta_len + 1)))) { + ret = OB_ALLOCATE_MEMORY_FAILED; + OB_LOG(WARN, "fail to alloc buf for reading seal meta file", K(ret), K(seal_meta_uri), K(seal_meta_len)); + } else if (OB_FAIL(reader->pread(buf, seal_meta_len, 0/*offset*/, read_size))) { + OB_LOG(WARN, "failed to read seal meta file content", K(ret), K(seal_meta_uri), K(seal_meta_len)); + } else if (OB_UNLIKELY(seal_meta_len != read_size)) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "fail to read seal meta file entire content", + K(ret), K(seal_meta_uri), K(seal_meta_len), K(read_size)); + } else if (OB_FAIL(obj_meta.deserialize(buf, read_size, pos))) { + OB_LOG(WARN, "fail to deserialize storage object meta", K(ret), K(seal_meta_uri), K(read_size), KP(buf)); + } else { + obj_meta.is_exist_ = true; + } + } else { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "the seal meta file is empty", K(ret), K(seal_meta_uri)); + } - if (OB_TMP_FAIL(reader.close())) { - OB_LOG(WARN, "fail to close reader", K(ret), K(tmp_ret), K(seal_meta_uri)); + if (OB_TMP_FAIL(reader->close())) { + OB_LOG(WARN, "fail to close reader", K(ret), K(tmp_ret), K(seal_meta_uri)); + } } } + if (OB_NOT_NULL(reader)) { + reader->~ObStorageReader(); + reader = nullptr; + } } return ret; } @@ -2361,6 +2371,8 @@ int ObStorageWriter::open(const common::ObString &uri, common::ObObjectStorageIn STORAGE_LOG(WARN, "writer_ is null", K(ret), K(uri)); } else if (OB_FAIL(writer_->open(uri, storage_info))) { STORAGE_LOG(WARN, "failed to open writer", K(ret), K(uri)); + } else { + storage_info_ = storage_info; } } @@ -2534,31 +2546,35 @@ int ObStorageAppender::repeatable_pwrite_(const char *buf, const int64_t size, c int64_t read_buf_size = 0; int64_t actual_write_offset = 0; char *read_buffer = nullptr; - ObStorageReader reader; + ObStorageReader *reader = nullptr; ObArenaAllocator allocator; if (OB_ISNULL(appender_)) { ret = OB_NOT_INIT; STORAGE_LOG(WARN, "not opened", K(ret)); - } else if (OB_FAIL(reader.open(uri_, storage_info_))) { + } else if (OB_ISNULL(reader = static_cast(allocator.alloc(sizeof(ObStorageReader))))) { + ret = OB_ALLOCATE_MEMORY_FAILED; + OB_LOG(WARN, "fail to alloc buf for reader", K(ret)); + } else if(FALSE_IT(new (reader) ObStorageReader())) { + } else if (OB_FAIL(reader->open(uri_, storage_info_))) { STORAGE_LOG(WARN, "failed to open reader", K(ret)); - } else if (reader.get_length() <= offset) { + } else if (reader->get_length() <= offset) { // This situation also has concurrency issues. // The length read by the reader may be old, so offset not match needs to be returned for retry. ret = OB_OBJECT_STORAGE_PWRITE_OFFSET_NOT_MATCH; - STORAGE_LOG(WARN, "offset is invalid", K(offset), "length", reader.get_length(), K(ret)); - } else if (OB_FALSE_IT(actual_write_offset = reader.get_length() - offset)) { + STORAGE_LOG(WARN, "offset is invalid", K(offset), "length", reader->get_length(), K(ret)); + } else if (OB_FALSE_IT(actual_write_offset = reader->get_length() - offset)) { } else if (OB_FALSE_IT(read_buf_size = std::min(actual_write_offset, size))) { } else if (OB_ISNULL(read_buffer = static_cast(allocator.alloc(read_buf_size)))) { ret = OB_ALLOCATE_MEMORY_FAILED; OB_LOG(WARN, "failed to allocate memory", K(ret), K(size)); - } else if (OB_FAIL(reader.pread(read_buffer, read_buf_size, offset, read_size))) { + } else if (OB_FAIL(reader->pread(read_buffer, read_buf_size, offset, read_size))) { STORAGE_LOG(WARN, "failed to pread", K(ret)); } else if (0 != MEMCMP(buf, read_buffer, read_buf_size)) { ret = OB_OBJECT_STORAGE_PWRITE_CONTENT_NOT_MATCH; STORAGE_LOG(WARN, "data inconsistent", K(ret)); - } else if (offset + size > reader.get_length()) { - if (OB_FAIL(appender_->pwrite(buf + actual_write_offset, size - actual_write_offset, reader.get_length()))) { + } else if (offset + size > reader->get_length()) { + if (OB_FAIL(appender_->pwrite(buf + actual_write_offset, size - actual_write_offset, reader->get_length()))) { if (OB_OBJECT_STORAGE_PWRITE_OFFSET_NOT_MATCH == ret) { ret = OB_IO_ERROR; STORAGE_LOG(WARN, "There may be concurrency problems that require the caller to retry", K(ret)); @@ -2566,10 +2582,13 @@ int ObStorageAppender::repeatable_pwrite_(const char *buf, const int64_t size, c } } - if (OB_SUCCESS != (tmp_ret = reader.close())) { + if (OB_SUCCESS != (tmp_ret = reader->close())) { STORAGE_LOG(WARN, "failed to close reader", K(tmp_ret)); } - + if (OB_NOT_NULL(reader)) { + reader->~ObStorageReader(); + reader = nullptr; + } return ret; } diff --git a/deps/oblib/src/lib/restore/ob_storage_cos_base.cpp b/deps/oblib/src/lib/restore/ob_storage_cos_base.cpp index b53685d9f..2d09c9cb5 100644 --- a/deps/oblib/src/lib/restore/ob_storage_cos_base.cpp +++ b/deps/oblib/src/lib/restore/ob_storage_cos_base.cpp @@ -935,9 +935,8 @@ int ObStorageCosReader::pread( // To maintain thread safety, a new temporary cos_handle should be created for each individual // pread operation rather than reusing the same handle. This approach ensures that memory // allocation is safely performed without conflicts across concurrent operations. - } else if (OB_FAIL(create_cos_handle( - allocator, handle_.get_cos_account(), - checksum_type_ == ObStorageChecksumType::OB_MD5_ALGO, tmp_cos_handle))) { + } else if (OB_FAIL(handle_.create_cos_handle( + allocator, checksum_type_ == ObStorageChecksumType::OB_MD5_ALGO, tmp_cos_handle))) { OB_LOG(WARN, "fail to create tmp cos handle", K(ret), K_(checksum_type)); } else { // When is_range_read is true, it indicates that only a part of the data is read. diff --git a/deps/oblib/src/lib/restore/ob_storage_info.cpp b/deps/oblib/src/lib/restore/ob_storage_info.cpp index f25e1ca13..bbd8907ff 100644 --- a/deps/oblib/src/lib/restore/ob_storage_info.cpp +++ b/deps/oblib/src/lib/restore/ob_storage_info.cpp @@ -18,7 +18,9 @@ #include "share/backup/ob_backup_struct.h" #include "share/object_storage/ob_object_storage_struct.h" #include "lib/utility/utility.h" - +#include "lib/json/ob_json.h" +#include "lib/restore/hmac_signature.h" +#include "lib/string/ob_string.h" namespace oceanbase { @@ -36,11 +38,123 @@ const char *get_storage_checksum_type_str(const ObStorageChecksumType &type) return str; } +//***********************ObSTSToken*************************** +ObSTSToken::ObSTSToken() : + data_(nullptr), + allocator_(OB_DEVICE_CREDENTIAL_ALLOCATOR), + data_len_(0), + is_below_predefined_length_(false), + is_inited_(false) +{ + data_arr_[0] = '\0'; +} + +ObSTSToken::~ObSTSToken() +{ + reset(); +} + +void ObSTSToken::reset() +{ + data_ = nullptr; + data_arr_[0] = '\0'; + data_len_ = 0; + is_below_predefined_length_ = false; + allocator_.clear(); + is_inited_ = false; +} + +int ObSTSToken::set(const ObString &token) +{ + int ret = OB_SUCCESS; + if (data_len_ > 0) { + reset(); + } + if (!token.empty()) { + if (OB_UNLIKELY(token.length() > OB_PREDEFINED_STS_TOKEN_LENGTH - 1) + && OB_FAIL(ob_dup_cstring(allocator_, token, data_))) { + OB_LOG(WARN, "failed to deep copy ObSTSToken", K(ret), K(token)); + } else if (token.length() <= OB_PREDEFINED_STS_TOKEN_LENGTH - 1) { + MEMCPY(data_arr_, token.ptr(), token.length()); + data_arr_[token.length()] = '\0'; + is_below_predefined_length_ = true; + } + if (OB_SUCC(ret)) { + data_len_ = token.length(); + is_inited_ = true; + } + } + return ret; +} + +bool ObSTSToken::is_valid() const +{ + return is_inited_; +} + +int ObSTSToken::assign(const ObSTSToken &token) +{ + int ret = OB_SUCCESS; + if (token.is_below_predefined_length() && OB_FAIL(set(ObString(token.data_arr_)))) { + OB_LOG(WARN, "assign ObSTSToken failed", K(ret), K(token)); + } else if (!token.is_below_predefined_length() && OB_FAIL(set(ObString(token.data_)))) { + OB_LOG(INFO, "assign ObSTSToken failed", K(ret), K(token)); + } + return ret; +} + +const char *ObSTSToken::get_data() const +{ + const char *data = nullptr; + if (data_len_ > 0) { + if (is_below_predefined_length_) { + data = data_arr_; + } else if (!is_below_predefined_length_) { + data = data_; + } + } + return data; +} + +//***********************ObObjectStorageCredential*************************** +ObObjectStorageCredential::ObObjectStorageCredential() + : sts_token_(), expiration_s_(0), access_time_us_(0), born_time_us_(0) +{ + access_id_[0] = '\0'; + access_key_[0] = '\0'; +} + +void ObObjectStorageCredential::reset() +{ + access_id_[0] = '\0'; + access_key_[0] = '\0'; + sts_token_.reset(); + expiration_s_ = 0; + access_time_us_ = 0; + born_time_us_ = 0; +} + +int ObObjectStorageCredential::assign(const ObObjectStorageCredential &credential) +{ + int ret = OB_SUCCESS; + MEMCPY(access_id_, credential.access_id_, sizeof(access_id_)); + MEMCPY(access_key_, credential.access_key_, sizeof(access_key_)); + expiration_s_ = credential.expiration_s_; + access_time_us_ = credential.access_time_us_; + born_time_us_ = credential.born_time_us_; + if (OB_FAIL(sts_token_.assign(credential.sts_token_))) { + LOG_WARN("failed to assign sts token", K(ret), K(credential), KPC(this)); + } + char *a = nullptr; + return ret; +} + //***********************ObObjectStorageInfo*************************** ObObjectStorageInfo::ObObjectStorageInfo() : delete_mode_(ObStorageDeleteMode::STORAGE_DELETE_MODE), device_type_(ObStorageType::OB_STORAGE_MAX_TYPE), - checksum_type_(ObStorageChecksumType::OB_NO_CHECKSUM_ALGO) + checksum_type_(ObStorageChecksumType::OB_NO_CHECKSUM_ALGO), + is_assume_role_mode_(false) { endpoint_[0] = '\0'; access_id_[0] = '\0'; @@ -48,6 +162,8 @@ ObObjectStorageInfo::ObObjectStorageInfo() extension_[0] = '\0'; max_iops_ = 0; max_bandwidth_ = 0; + role_arn_[0] = '\0'; + external_id_[0] = '\0'; } ObObjectStorageInfo::~ObObjectStorageInfo() @@ -64,8 +180,9 @@ void ObObjectStorageInfo::reset() access_id_[0] = '\0'; access_key_[0] = '\0'; extension_[0] = '\0'; - max_iops_ = 0; - max_bandwidth_ = 0; + role_arn_[0] = '\0'; + external_id_[0] = '\0'; + is_assume_role_mode_ = false; } bool ObObjectStorageInfo::is_valid() const @@ -84,6 +201,9 @@ int64_t ObObjectStorageInfo::hash() const hash_value = murmurhash(extension_, static_cast(strlen(extension_)), hash_value); hash_value = murmurhash(&max_iops_, static_cast(sizeof(max_iops_)), hash_value); hash_value = murmurhash(&max_bandwidth_, static_cast(sizeof(max_bandwidth_)), hash_value); + hash_value = murmurhash(role_arn_, static_cast(strlen(role_arn_)), hash_value); + hash_value = murmurhash(external_id_, static_cast(strlen(external_id_)), hash_value); + hash_value = murmurhash(&is_assume_role_mode_, static_cast(sizeof(is_assume_role_mode_)), hash_value); return hash_value; } @@ -95,6 +215,9 @@ bool ObObjectStorageInfo::operator ==(const ObObjectStorageInfo &storage_info) c && (0 == STRCMP(access_id_, storage_info.access_id_)) && (0 == STRCMP(access_key_, storage_info.access_key_)) && (0 == STRCMP(extension_, storage_info.extension_)) + && (0 == STRCMP(role_arn_, storage_info.role_arn_)) + && (0 == STRCMP(external_id_, storage_info.external_id_)) + && is_assume_role_mode_ == storage_info.is_assume_role_mode_ && max_iops_ == storage_info.max_iops_ && max_bandwidth_ == storage_info.max_bandwidth_; } @@ -129,6 +252,25 @@ int ObObjectStorageInfo::reset_access_id_and_access_key( return ret; } +bool ObObjectStorageInfo::is_assume_role_mode() const +{ + return is_assume_role_mode_; +} + +ObClusterVersionBaseMgr *ObObjectStorageInfo::cluster_version_mgr_ = nullptr; +int ObObjectStorageInfo::register_cluster_version_mgr(ObClusterVersionBaseMgr *cluster_version_mgr) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(cluster_version_mgr)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("cluster_version_mgr is null", K(ret)); + } else { + cluster_version_mgr_ = cluster_version_mgr; + LOG_INFO("register_cluster_version_mgr", K(ret), KP_(cluster_version_mgr)); + } + return ret; +} + const char *ObObjectStorageInfo::get_type_str() const { return get_storage_type_str(device_type_); @@ -169,21 +311,13 @@ int ObObjectStorageInfo::set(const common::ObStorageType device_type, const char LOG_WARN("storage info is empty", K(ret), K_(device_type)); } } else if (OB_FAIL(parse_storage_info_(storage_info, has_needed_extension))) { - LOG_WARN("parse storage info failed", K(ret)); - } else if (OB_STORAGE_FILE != device_type - && (0 == strlen(endpoint_) || 0 == strlen(access_id_) || 0 == strlen(access_key_))) { - ret = OB_INVALID_BACKUP_DEST; - LOG_WARN("backup device is not nfs, endpoint/access_id/access_key do not allow to be empty", - K(ret), K_(device_type), K_(endpoint), K_(access_id)); + LOG_WARN("parse storage info failed", K(ret), KP(storage_info), K_(device_type)); } else if (OB_STORAGE_COS == device_type && !has_needed_extension) { ret = OB_INVALID_BACKUP_DEST; LOG_WARN("invalid cos info, appid do not allow to be empty", K(ret), K_(extension)); - } else if (OB_STORAGE_FILE == device_type - && (0 != strlen(endpoint_) || 0 != strlen(access_id_) || 0 != strlen(access_key_))) { + } else if (OB_FAIL(validate_arguments())) { ret = OB_INVALID_BACKUP_DEST; - LOG_WARN("backup device is nfs, endpoint/access_id/access_key must be empty", - K(ret), K_(device_type), K_(endpoint), K_(access_id)); - } else { + LOG_WARN("invalid arguments after parse storage info", K(ret), KPC(this)); } if (OB_FAIL(ret)) { @@ -192,6 +326,43 @@ int ObObjectStorageInfo::set(const common::ObStorageType device_type, const char return ret; } +int ObObjectStorageInfo::validate_arguments() const +{ + int ret = OB_SUCCESS; + if (OB_UNLIKELY(!ObObjectStorageInfo::is_valid())) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid argument", K(ret), K(device_type_)); + } else if (OB_STORAGE_FILE != device_type_) { + if (OB_UNLIKELY(0 == strlen(endpoint_))) { + ret = OB_INVALID_BACKUP_DEST; + LOG_WARN("backup device is not nfs, endpoint do not allow to be empty", K(ret), + K_(device_type), K_(endpoint)); + } else { + if (is_assume_role_mode_) { + if (OB_UNLIKELY(0 != strlen(access_id_) || 0 != strlen(access_key_))) { + ret = OB_INVALID_BACKUP_DEST; + LOG_WARN("ak/sk should not given in assume_role mode", + K(ret), K_(access_id), KP_(access_key), K(strlen(access_id_)), K(strlen(access_key_))); + } + } else { + if (OB_UNLIKELY(0 == strlen(access_id_) || 0 == strlen(access_key_))) { + ret = OB_INVALID_BACKUP_DEST; + LOG_WARN("ak/sk must be given in in ak/sk mode", K(ret), K_(access_id), KP_(access_key), + K(strlen(access_id_)), K(strlen(access_key_))); + } + } + } + } else { + if (OB_UNLIKELY(0 != strlen(endpoint_) || 0 != strlen(access_id_) || 0 != strlen(access_key_) + || 0 != strlen(role_arn_) || 0 != strlen(external_id_))) { + ret = OB_INVALID_BACKUP_DEST; + LOG_WARN("backup device is nfs, endpoint/access_id/access_key must be empty", K(ret), + K_(device_type), K_(endpoint), K_(access_id), KP_(access_key), KP_(role_arn), KP_(external_id)); + } + } + return ret; +} + int ObObjectStorageInfo::set(const char *uri, const char *storage_info) { int ret = OB_SUCCESS; @@ -285,7 +456,7 @@ int ObObjectStorageInfo::parse_storage_info_(const char *storage_info, bool &has if (OB_STORAGE_FILE == device_type_) { ret = OB_INVALID_BACKUP_DEST; OB_LOG(WARN, "OB_STORAGE_FILE don't support delete mode yet", - K(ret), K_(device_type), K(token)); + K(ret), K_(device_type), K(token)); } else if (OB_FAIL(check_delete_mode_(token + strlen(DELETE_MODE)))) { OB_LOG(WARN, "failed to check delete mode", K(ret), K(token)); } else if (OB_FAIL(set_storage_info_field_(token, extension_, sizeof(extension_)))) { @@ -304,7 +475,43 @@ int ObObjectStorageInfo::parse_storage_info_(const char *storage_info, bool &has } else if (OB_FAIL(set_storage_info_field_(token, extension_, sizeof(extension_)))) { LOG_WARN("fail to set checksum type into extension", K(ret), K(token)); } - } else { + } else if (0 == strncmp(ROLE_ARN, token, strlen(ROLE_ARN))) { + if (ObStorageType::OB_STORAGE_FILE == device_type_) { + ret = OB_INVALID_BACKUP_DEST; + LOG_WARN("OB_STORAGE_FILE don't support assume role yet", + K(ret), K_(device_type), KP(token)); + } else if (OB_FAIL(set_storage_info_field_(token, role_arn_, sizeof(role_arn_)))) { + LOG_WARN("failed to set role arn", K(ret), KP(token), KP_(role_arn), K(sizeof(role_arn_))); + } + } else if (0 == strncmp(EXTERNAL_ID, token, strlen(EXTERNAL_ID))) { + if (ObStorageType::OB_STORAGE_FILE == device_type_) { + ret = OB_INVALID_BACKUP_DEST; + LOG_WARN("OB_STORAGE_FILE don't support external id yet", K(ret), + K_(device_type), KP(token)); + } else if (OB_FAIL(set_storage_info_field_(token, external_id_, sizeof(external_id_)))) { + LOG_WARN("failed to set external id", K(ret), KP(token), KP_(external_id), + K(sizeof(external_id_))); + } + } + } + + // If access by assume role, try to get temporary ak/sk into cache to speed up access + if (OB_SUCC(ret)) { + if (strlen(role_arn_) > strlen(ROLE_ARN)) { + if (OB_ISNULL(cluster_version_mgr_)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("cluster version mgr is null", K(ret), KP(cluster_version_mgr_)); + } else if (OB_FAIL(cluster_version_mgr_->is_supported_assume_version())) { + LOG_WARN("assume role is not supported in when the mininum version is below 4.2.5", + K(ret), KPC(this)); + } else { + is_assume_role_mode_ = true; + ObObjectStorageCredential credential; + if (OB_FAIL(ObDeviceCredentialMgr::get_instance().get_credential(*this, credential))) { + LOG_WARN("failed to get credential by role arn first", K(ret), KPC(this), + KP(storage_info), K(credential)); + } + } } } } @@ -432,29 +639,44 @@ int ObObjectStorageInfo::assign(const ObObjectStorageInfo &storage_info) MEMCPY(extension_, storage_info.extension_, sizeof(extension_)); max_iops_ = storage_info.max_iops_; max_bandwidth_ = storage_info.max_bandwidth_; + MEMCPY(role_arn_, storage_info.role_arn_, sizeof(role_arn_)); + MEMCPY(external_id_, storage_info.external_id_, sizeof(external_id_)); + is_assume_role_mode_ = storage_info.is_assume_role_mode_; return ret; } -int ObObjectStorageInfo::get_storage_info_str(char *storage_info, const int64_t info_len) const +int ObObjectStorageInfo::get_info_str_(char *storage_info, const int64_t info_len) const { int ret = OB_SUCCESS; const int64_t key_len = MAX(OB_MAX_BACKUP_SERIALIZEKEY_LENGTH, OB_MAX_BACKUP_ACCESSKEY_LENGTH); - char key[key_len] = { 0 }; - if (!is_valid()) { + char key[key_len] = {0}; + if (OB_UNLIKELY(!is_valid())) { ret = OB_INVALID_ARGUMENT; - LOG_WARN("storage info not init", K(ret)); - } else if (OB_ISNULL(storage_info) || (info_len <= 0)) { + LOG_WARN("storage info is invalid", K(ret), K_(device_type)); + } else if (OB_ISNULL(storage_info) || OB_UNLIKELY(info_len <= 0)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid args", K(ret), KP(storage_info), K(info_len)); - } else if (OB_STORAGE_FILE != device_type_) { + } else if (OB_STORAGE_FILE != device_type_ && !is_assume_role_mode_) { + // Access object storage by ak/sk if (OB_FAIL(get_access_key_(key, sizeof(key)))) { LOG_WARN("failed to get access key", K(ret)); } else if (OB_FAIL(databuff_printf(storage_info, info_len, "%s&%s&%s", endpoint_, access_id_, key))) { LOG_WARN("failed to set storage info", K(ret), K(info_len)); } } + return ret; +} - if (OB_SUCC(ret) && 0 != strlen(extension_) && info_len > strlen(storage_info)) { +int ObObjectStorageInfo::append_extension_str_(char *storage_info, const int64_t info_len) const +{ + int ret = OB_SUCCESS; + if (OB_UNLIKELY(!is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("storage info is invalid", K(ret), K_(device_type)); + } else if (OB_ISNULL(storage_info) || OB_UNLIKELY(info_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid args", K(ret), KP(storage_info), K(info_len)); + } else if (0 != strlen(extension_) && info_len > strlen(storage_info)) { // if OB_STORAGE_FILE's extension is not empty, delimiter should be included int64_t str_len = strlen(storage_info); if (str_len > 0 && OB_FAIL(databuff_printf(storage_info, info_len, str_len, "&"))) { @@ -463,7 +685,73 @@ int ObObjectStorageInfo::get_storage_info_str(char *storage_info, const int64_t LOG_WARN("failed to add extension", K(ret), K(info_len), K(str_len), K_(extension)); } } + return ret; +} +int ObObjectStorageInfo::get_storage_info_str(char *storage_info, const int64_t info_len) const +{ + int ret = OB_SUCCESS; + if (OB_UNLIKELY(!is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("storage info is invalid", K(ret), K_(device_type)); + } else if (OB_ISNULL(storage_info) || OB_UNLIKELY(info_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid args", K(ret), KP(storage_info), K(info_len)); + } else if (!is_assume_role_mode_ && OB_FAIL(get_info_str_(storage_info, info_len))) { + OB_LOG(WARN, "failed to get storage info str", K(ret), K(info_len), KPC(this)); + } else if (is_assume_role_mode_) { + // Access object storage by assume_role + int64_t pos = 0; + if (OB_FAIL(databuff_printf(storage_info, info_len, pos, "%s&%s&%s%s", endpoint_, role_arn_, + CHECKSUM_TYPE, get_checksum_type_str()))) { + LOG_WARN("failed to set storage info of other types", K(ret), K(info_len), KPC(this)); + } + // `external_id` is optional + else if (strlen(external_id_) > strlen(EXTERNAL_ID)) { + if (OB_FAIL(databuff_printf(storage_info, info_len, pos, "&%s", external_id_))) { + LOG_WARN("failed to set storage info of other types", K(ret), K(info_len), KPC(this)); + } + } + } + if (OB_SUCC(ret) && OB_FAIL(append_extension_str_(storage_info, info_len))) { + LOG_WARN("failed to append extension str", K(ret), K(info_len), KPC(this)); + } + return ret; +} + +// This function is used to obtain storage_info_str containing ak/sk, +// regardless of whether the access_mode is assume_role mode or access_id mode +int ObObjectStorageInfo::get_authorization_str( + char *authorization_str, const int64_t authorization_str_len, ObSTSToken &sts_token) const +{ + int ret = OB_SUCCESS; + if (OB_UNLIKELY(!is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("storage info is invalid", K(ret), K_(device_type)); + } else if (OB_ISNULL(authorization_str) || OB_UNLIKELY(authorization_str_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid args", K(ret), KP(authorization_str), K(authorization_str_len)); + } else if (!is_assume_role_mode_ + && OB_FAIL(get_info_str_(authorization_str, authorization_str_len))) { + OB_LOG(WARN, "failed to get authorization str", K(ret), K(authorization_str), KPC(this), + K(authorization_str_len)); + } else if (is_assume_role_mode_) { + // access by assume role + ObObjectStorageCredential credential; + if (OB_FAIL(ObDeviceCredentialMgr::get_instance().get_credential(*this, credential))) { + OB_LOG(WARN, "failed to get credential", K(ret), KPC(this), K(credential)); + } else if (OB_FAIL(databuff_printf(authorization_str, authorization_str_len, + "%s&%s%s&%s%s&%s%s", endpoint_, ACCESS_ID, credential.access_id_, ACCESS_KEY, + credential.access_key_, CHECKSUM_TYPE, get_checksum_type_str()))) { + OB_LOG(WARN, "failed to set storage info of other types", K(ret), KP(authorization_str), + K(authorization_str_len), KPC(this)); + } else if (OB_FAIL(sts_token.assign(credential.sts_token_))) { + OB_LOG(WARN, "failed to set sts_token_", K(ret), K(sts_token), K(credential.sts_token_)); + } + } + if (OB_SUCC(ret) && OB_FAIL(append_extension_str_(authorization_str, authorization_str_len))) { + LOG_WARN("failed to append extension str", K(ret), K(authorization_str_len), KPC(this)); + } return ret; } @@ -478,10 +766,24 @@ int ObObjectStorageInfo::get_device_map_key_str(char *key_str, const int64_t len } else if (OB_ISNULL(key_str) || (len <= 0)) { ret = OB_INVALID_ARGUMENT; LOG_WARN("invalid args", K(ret), KP(key_str), K(len)); - } else if (OB_FAIL(databuff_printf(key_str, len, "%u&%u&%s&%s&%s&%s", - static_cast(device_type_), static_cast(checksum_type_), - endpoint_, access_id_, access_key_, extension_))) { - LOG_WARN("failed to set key str", K(ret), K_(device_type), K_(checksum_type), K_(extension)); + } else if (is_assume_role_mode_) { + // access by assume_role + int64_t pos = 0; + if (OB_FAIL(databuff_printf(key_str, len, pos, "%u&%u&%s&%s&%s", + static_cast(device_type_), static_cast(checksum_type_), endpoint_, + role_arn_, extension_))) { + LOG_WARN("failed to set key str with assume role", K(ret), K(len), K(pos), KPC(this)); + } else if (strlen(external_id_) > strlen(EXTERNAL_ID)) { + if (OB_FAIL(databuff_printf(key_str, len, pos, "&%s", external_id_))) { + LOG_WARN("failed to set key str with assume role", K(ret), K(len), K(pos), KPC(this)); + } + } + } + // access by access_id + else if (OB_FAIL(databuff_printf(key_str, len, "%u&%u&%s&%s&%s&%s", + static_cast(device_type_), static_cast(checksum_type_), + endpoint_, access_id_, access_key_, extension_))) { + LOG_WARN("failed to set key str with access_id", K(ret), K(len), KPC(this)); } return ret; } @@ -494,7 +796,18 @@ int64_t ObObjectStorageInfo::get_device_map_key_len() const // therefore, ObStorageType and ObStorageChecksumType each occupies up to 10 characters. // 10(one ObStorageType) + 10(one ObStorageChecksumType) + 5(five '&') + 1(one '\0') = 26. // reserve some free space, increase 26 to 30. - return (STRLEN(endpoint_) + STRLEN(access_id_) + STRLEN(access_key_) + STRLEN(extension_) + 30); + int64_t len = 0; + if (is_assume_role_mode_) { + // When accessing by assume_role, the length of key_str is also need reversed free space. + len = STRLEN(endpoint_) + STRLEN(role_arn_) + STRLEN(extension_) + 30; + // external_id is optional + if (STRLEN(external_id_) > STRLEN(EXTERNAL_ID)) { + len += STRLEN(external_id_); + } + } else { + len = STRLEN(endpoint_) + STRLEN(access_id_) + STRLEN(access_key_) + STRLEN(extension_) + 30; + } + return len; } int ObObjectStorageInfo::get_access_key_(char *key_buf, const int64_t key_buf_len) const @@ -510,5 +823,899 @@ int ObObjectStorageInfo::get_access_key_(char *key_buf, const int64_t key_buf_le return ret; } +//***********************ObStsCredential*************************** +ObStsCredential::ObStsCredential() + : tenant_id_(OB_SERVER_TENANT_ID), is_inited_(false) +{ + sts_ak_[0] = '\0'; + sts_sk_[0] = '\0'; + sts_url_[0] = '\0'; } + +ObStsCredential::~ObStsCredential() +{ + reset(); } + +int ObStsCredential::init(const uint64_t tenant_id) +{ + int ret = OB_SUCCESS; + if (IS_INIT) { + ret = OB_INIT_TWICE; + LOG_WARN("sts credential init twice", K(ret)); + } else if (OB_UNLIKELY(!is_valid_tenant_id(tenant_id))) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid tenant_id", K(ret), K(tenant_id)); + } else if (OB_FAIL(get_sts_credential())) { + LOG_WARN("failed to get sts credential", K(ret), K(tenant_id_)); + } else { + tenant_id_ = tenant_id; + is_inited_ = true; + } + return ret; +} + +int ObStsCredential::get_sts_credential() +{ + int ret = OB_SUCCESS; + char sts_credential[OB_MAX_STS_CREDENTIAL_LENGTH] = {0}; + int64_t sts_credential_len = 0; + if (OB_ISNULL(sts_credential_mgr_)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("sts_credential_mgr is null", K(ret), KPC(this)); + } else if (OB_FAIL(sts_credential_mgr_->get_sts_credential(sts_credential, sizeof(sts_credential)))) { + LOG_WARN("fail to get sts credential", K(ret), KPC(this)); + } else if (FALSE_IT(sts_credential_len = strlen(sts_credential))) { + } else if (OB_UNLIKELY(sts_credential_len <= 0 + || sts_credential_len >= OB_MAX_STS_CREDENTIAL_LENGTH)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("sts_credential is invalid", K(ret), K(sts_credential), K(sts_credential_len)); + } else if (OB_FAIL(check_sts_credential_format(sts_credential, *this))) { + LOG_WARN("failed to check sts credential format", K(ret), KP(sts_credential), KPC(this)); + } + return ret; +} + +void ObStsCredential::reset() +{ + is_inited_ = false; + tenant_id_ = OB_SERVER_TENANT_ID; + sts_ak_[0] = '\0'; + sts_sk_[0] = '\0'; + sts_url_[0] = '\0'; +} + +//***********************ObDeviceCredentialKey*************************** + +ObDeviceCredentialKey::ObDeviceCredentialKey() + : tenant_id_(OB_SERVER_TENANT_ID), is_inited_(false) +{ + role_arn_[0] = '\0'; + external_id_[0] = '\0'; +} + +ObDeviceCredentialKey::~ObDeviceCredentialKey() +{ + reset(); +} + +int ObDeviceCredentialKey::init(const ObObjectStorageInfo &storage_info) +{ + int ret = OB_SUCCESS; + if (IS_INIT) { + ret = OB_INIT_TWICE; + LOG_WARN("init twice", K(ret)); + } else if (OB_UNLIKELY(!storage_info.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid args", K(ret), K(storage_info)); + } else if (OB_FAIL(init_(storage_info.role_arn_, storage_info.external_id_))) { + LOG_WARN("failed to init device credential key", K(ret), K(storage_info)); + } + return ret; +} + +int ObDeviceCredentialKey::init_(const char *role_arn, const char *external_id) +{ + int ret = OB_SUCCESS; + if (IS_INIT) { + ret = OB_INIT_TWICE; + LOG_WARN("init twice", K(ret)); + } else if (OB_ISNULL(role_arn) || OB_ISNULL(external_id)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid args", K(ret), KP(role_arn), KP(external_id)); + } else { + // format: + // role_arn: role_arn=xxx + // external_id: external_id=xxx + const int64_t role_arn_len = strlen(role_arn); + const int64_t external_id_len = strlen(external_id); + if (OB_UNLIKELY(0 != STRNCMP(role_arn, ROLE_ARN, STRLEN(ROLE_ARN)) + || role_arn_len >= OB_MAX_ROLE_ARN_LENGTH)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid role arn", K(ret), K(role_arn_len), KP(role_arn), K(ROLE_ARN)); + } + // External id is optional, so the length of external id can be 0 + else if (OB_UNLIKELY(external_id_len < 0 || external_id_len >= OB_MAX_EXTERNAL_ID_LENGTH + || (external_id_len > 0 && 0 != STRNCMP(external_id, EXTERNAL_ID, STRLEN(EXTERNAL_ID))))) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid external id", K(ret), KP(external_id), K(external_id_len), K(EXTERNAL_ID)); + } else { + tenant_id_ = ObObjectStorageTenantGuard::get_tenant_id(); + if (OB_UNLIKELY(!is_valid_tenant_id(tenant_id_))) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid tenant_id", K(ret), K_(tenant_id)); + } else { + MEMCPY(role_arn_, role_arn, role_arn_len); + role_arn_[role_arn_len] = '\0'; + MEMCPY(external_id_, external_id, external_id_len); + external_id_[external_id_len] = '\0'; + is_inited_ = true; + } + if (OB_FAIL(ret)) { + reset(); + } + } + } + return ret; +} + +void ObDeviceCredentialKey::reset() +{ + role_arn_[0] = '\0'; + external_id_[0] = '\0'; + tenant_id_ = OB_SERVER_TENANT_ID; + is_inited_ = false; +} + +uint64_t ObDeviceCredentialKey::hash() const +{ + uint64_t hash_value = 0; + hash_value = murmurhash(role_arn_, static_cast(strlen(role_arn_)), hash_value); + hash_value = murmurhash(external_id_, static_cast(strlen(external_id_)), hash_value); + hash_value = murmurhash(&tenant_id_, sizeof(tenant_id_), hash_value); + hash_value = murmurhash(&is_inited_, static_cast(sizeof(is_inited_)), hash_value); + return hash_value; +} + +int ObDeviceCredentialKey::assign(const ObDeviceCredentialKey &other) +{ + int ret = OB_SUCCESS; + if (this == &other) { + } else if (OB_UNLIKELY(!other.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid argument", K(ret), K(other)); + } else { + MEMCPY(role_arn_, other.role_arn_, sizeof(role_arn_)); + MEMCPY(external_id_, other.external_id_, sizeof(external_id_)); + tenant_id_ = other.tenant_id_; + is_inited_ = other.is_inited_; + } + return ret; +} + +bool ObDeviceCredentialKey::operator==(const ObDeviceCredentialKey &other) const +{ + return (is_inited_ == other.is_inited_) + && (0 == STRCMP(role_arn_, other.role_arn_)) + && (0 == STRCMP(external_id_, other.external_id_)) + && (tenant_id_ == other.tenant_id_); +} + +bool ObDeviceCredentialKey::operator!=(const ObDeviceCredentialKey &other) const +{ + return !(*this == other); +} + +bool ObDeviceCredentialKey::is_valid() const +{ + return is_inited_; +} + +int ObDeviceCredentialKey::construct_signed_url(char *url_buf, const int64_t url_buf_len) const +{ + int ret = OB_SUCCESS; + const char *role_arn = role_arn_ + STRLEN(ROLE_ARN); + const char *external_id = external_id_ + STRLEN(EXTERNAL_ID); + char json_data[OB_MAX_ASSUME_ROLE_JSON_DATA_LENGTH] = {0}; + char signature_nonce[OB_MAX_STS_SIGNATURE_NONCE_LENTH] = {0}; + // request_id is used by OCP to identify the request when connected to the service of OCP + char request_id[OB_MAX_STS_REQUEST_ID_LENTH] = {0}; + char signature[OB_MAX_STS_SIGNATURE_LENGTH] = {0}; + ObStsCredential sts_credential; + using ParamType = std::pair; + ObArray params; + + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + OB_LOG(WARN, "ObDeviceCredentialKey is not valid", K(ret)); + } else if (OB_ISNULL(url_buf) || OB_UNLIKELY(url_buf_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid args", K(ret), KP(url_buf), K(url_buf_len)); + } else if (OB_FAIL(generate_signature_nonce(signature_nonce, sizeof(signature_nonce)))) { + LOG_WARN("failed to generate signature nonce", + K(ret), K(signature_nonce), K(sizeof(signature_nonce))); + } else if (OB_FAIL(generate_request_id(request_id, sizeof(request_id)))) { + LOG_WARN("failed to generate request id", K(ret), K(request_id), K(sizeof(request_id))); + } else if (OB_FAIL(sts_credential.init(tenant_id_))) { + LOG_WARN("failed to init sts credential", K(ret), K(tenant_id_)); + } else { + if (OB_FAIL(params.push_back(ParamType("Action", STS_ACTION)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + } else if (OB_FAIL(params.push_back(ParamType("RequestSource", STS_RESOURCE_SOURCE)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + } else if (OB_FAIL(params.push_back(ParamType("RequestId", request_id)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + } else if (OB_FAIL(params.push_back(ParamType("RoleArn", role_arn)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + } else if (OB_FAIL(params.push_back(ParamType("SignatureNonce", signature_nonce)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + } else if (OB_FAIL(params.push_back(ParamType("ObSignatureKey", sts_credential.sts_ak_)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + } else if (OB_FAIL(params.push_back(ParamType("ObSignatureSecret", sts_credential.sts_sk_)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count()), K(params)); + // be careful to handle external_id_ and external_id + } else if (external_id_[0] != '\0' + && OB_FAIL(params.push_back(ParamType("ExternalId", external_id)))) { + OB_LOG(WARN, "fail to push back", K(ret), K(params.count())); + } + + int64_t pos = 0; + if (FAILEDx(databuff_printf(url_buf, url_buf_len, pos, + "%s?Action=%s&RequestSource=%s" + "&RequestId=%s&RoleArn=%s&SignatureNonce=%s&ObSignatureKey=%s", + sts_credential.sts_url_, STS_ACTION, STS_RESOURCE_SOURCE, + request_id, role_arn, signature_nonce, sts_credential.sts_ak_))) { + LOG_WARN("failed to generate json payload", K(ret), K(url_buf_len), KPC(this)); + } else if (external_id_[0] != '\0' + && OB_FAIL(databuff_printf(url_buf, url_buf_len, pos, "&ExternalId=%s", external_id))) { + LOG_WARN("failed to concat external id", K(ret), K(url_buf_len), K(pos), KPC(this)); + } else if (OB_FAIL(sign_request(params, "POST", signature, sizeof(signature)))) { + LOG_WARN("failed to sign request", K(ret), K(signature), K(params), KPC(this)); + } else if (OB_FAIL(databuff_printf(url_buf, url_buf_len, pos, "&ObSignature=%s", signature))) { + LOG_WARN("failed to concat sts signature", K(ret), K(pos), K(url_buf_len), KPC(this)); + } + } + return ret; +} + +ObTenantStsCredentialBaseMgr *ObStsCredential::sts_credential_mgr_ = nullptr; +int ObStsCredential::register_sts_credential_mgr( + ObTenantStsCredentialBaseMgr *sts_credential_mgr) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(sts_credential_mgr)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "sts_credential_mgr is null", K(ret), KP(sts_credential_mgr)); + } else { + sts_credential_mgr_ = sts_credential_mgr; + } + return ret; +} + +int check_sts_credential_format(const char *sts_credential_str, ObStsCredential &sts_credential) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(sts_credential_str) + || OB_UNLIKELY(strlen(sts_credential_str) <= 0 + || strlen(sts_credential_str) >= OB_MAX_STS_CREDENTIAL_LENGTH)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid args", K(ret), KP(sts_credential_str)); + } else { + // sts_credential_str: sts_ak=xxx&sts_sk=xxx&sts_url=xxx + // sts_ak, sts_sk, sts_url are separated by '&' + // sts_ak, sts_sk, sts_url are connected by '=' + char tmp_sts_credential[OB_MAX_STS_CREDENTIAL_LENGTH] = {0}; + char *token = nullptr; + char *saved_ptr = nullptr; + int64_t sts_credential_len = strlen(sts_credential_str); + char *sts_ak = sts_credential.sts_ak_; + char *sts_sk = sts_credential.sts_sk_; + char *sts_url = sts_credential.sts_url_; + + MEMCPY(tmp_sts_credential, sts_credential_str, sts_credential_len); + tmp_sts_credential[sts_credential_len] = '\0'; + token = tmp_sts_credential; + for (char *str = token; OB_SUCC(ret); str = nullptr) { + token = ::strtok_r(str, "&", &saved_ptr); + if (nullptr == token) { + break; + } else if (0 == strncmp(STS_AK, token, strlen(STS_AK))) { + if (OB_FAIL(ob_set_field(token + strlen(STS_AK), sts_ak, sizeof(sts_credential.sts_ak_)))) { + LOG_WARN("failed to set sts ak", K(ret), KP(token), K(sts_ak), + K(sizeof(sts_credential.sts_ak_))); + } + } else if (0 == strncmp(STS_SK, token, strlen(STS_SK))) { + if (OB_FAIL(ob_set_field(token + strlen(STS_SK), sts_sk, sizeof(sts_credential.sts_sk_)))) { + LOG_WARN("failed to set sts sk", K(ret), KP(token), KP(sts_sk), + K(sizeof(sts_credential.sts_sk_))); + } + } else if (0 == strncmp(STS_URL, token, strlen(STS_URL))) { + if (OB_FAIL(ob_set_field(token + strlen(STS_URL), sts_url, sizeof(sts_credential.sts_url_)))) { + LOG_WARN("failed to set sts url", K(ret), K(token), K(sts_url), + K(sizeof(sts_credential.sts_url_))); + } + } + } + if (OB_SUCC(ret)) { + if (OB_UNLIKELY(sts_ak[0] == '\0' || sts_sk[0] == '\0' || sts_url[0] == '\0')) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid sts credential", K(ret), K(sts_ak), KP(sts_sk), K(sts_url)); + } + } + } + return ret; +} + + +//***********************ObDeviceCredentialMgr*************************** +const char *ObDeviceCredentialMgr::response_items_[RESPONSE_ITEM_CNT] = { + "AccessKeyId", "AccessKeySecret", "SecurityToken", "DurationSeconds"}; + +ObDeviceCredentialMgr &ObDeviceCredentialMgr::get_instance() +{ + static ObDeviceCredentialMgr device_credential_mgr; + return device_credential_mgr; +} + +ObDeviceCredentialMgr::ObDeviceCredentialMgr() + : is_inited_(false), + credential_map_(), + credential_lock_(common::ObLatchIds::OB_DEVICE_CREDENTIAL_MGR_LOCK), + credential_duration_us_(0) +{} + +ObDeviceCredentialMgr::~ObDeviceCredentialMgr() +{ + destroy(); +} + +int ObDeviceCredentialMgr::init() +{ + int ret = OB_SUCCESS; + const int64_t DEFAULT_MAP_BUCKET_CNT = 100; + if (IS_INIT) { + ret = OB_INIT_TWICE; + LOG_WARN("init twice", K(ret)); + } else if (OB_FAIL(credential_map_.create(DEFAULT_MAP_BUCKET_CNT, "CredentialMap", "CredentialMap"))) { + LOG_WARN("fail to init credential map", K(ret), K(DEFAULT_MAP_BUCKET_CNT)); + } else { + credential_duration_us_ = MAX_CREDENTIAL_IDLE_DURATION_US; + is_inited_ = true; + } + return ret; +} + +void ObDeviceCredentialMgr::destroy() +{ + is_inited_ = false; + credential_duration_us_ = 0; + credential_map_.destroy(); +} + +int ObDeviceCredentialMgr::connect_to_sts( + const ObDeviceCredentialKey &credential_key, + ResponseAndAllocator &res_and_allocator) +{ + int ret = OB_SUCCESS; + OBJECT_STORAGE_GUARD(nullptr/*storage_info*/, "ObDeviceCredentialMgr::connect_to_sts", IO_HANDLED_SIZE_ZERO); + CURL *curl = nullptr; + char json_data[OB_MAX_ASSUME_ROLE_JSON_DATA_LENGTH] = {0}; + + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr not init", K(ret)); + } else if (OB_UNLIKELY(!credential_key.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid argument", K(ret), K(credential_key)); + } else if (OB_ISNULL(curl = curl_easy_init())) { + ret = OB_CURL_ERROR; + LOG_WARN("fail to init curl", K(ret)); + } else if (OB_FAIL(credential_key.construct_signed_url(json_data, sizeof(json_data)))) { + LOG_WARN("fail to construct signed url", K(ret), K(credential_key)); + } else { + CURLcode cc; + int64_t http_code = 0; + const int64_t no_signal = 1; + const int64_t no_delay = 1; + struct curl_slist *headers = nullptr; + struct curl_slist *new_header = nullptr; + + const char *header_list[] = { + "Accept: application/json", + "Content-Type: application/json", + "charset: utf-8" + }; + + const uint32_t num_headers = sizeof(header_list) / sizeof(header_list[0]); + for (uint32_t i = 0; OB_SUCC(ret) && i < num_headers; ++i) { + if (OB_ISNULL(new_header = curl_slist_append(headers, header_list[i]))) { + ret = OB_ALLOCATE_MEMORY_FAILED; + LOG_WARN("failed to append header", K(ret), K(i), K(header_list[i])); + } else { + headers = new_header; + } + } + + if (OB_FAIL(ret)) { + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers))) { + LOG_WARN("set http header failed", K(cc)); + } + // libcurl must carry data field, otherwise it will get stuck. An empty data field is added here + // for normal access. + else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""))) { + LOG_WARN("set post fields failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0))) { + LOG_WARN("set post fields size failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_POST, 1))) { + LOG_WARN("set post failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_READFUNCTION, nullptr))) { + LOG_WARN("set read function failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &on_write_data_))) { + LOG_WARN("set write function failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&res_and_allocator))) { + LOG_WARN("set wrrite data failed", K(ret)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_NOSIGNAL, no_signal))) { + LOG_WARN("set no signal failed", K(cc), K(no_signal)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, no_delay))) { + LOG_WARN("set no delay failed", K(cc), K(no_delay)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, OB_MAX_STS_CURL_CONNECTTIMEOUT_MS))) { // 10s + LOG_WARN("set connection timeout ms failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_TIMEOUT, OB_MAX_STS_CURL_TIMEOUT_SECONDS))) { // 10s + LOG_WARN("set timeout failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_URL, json_data))) { + LOG_WARN("set url failed", K(cc)); + } +#ifndef NDEBUG + else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, &debug_callback))) { + LOG_WARN("set debug function failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L))) { + LOG_WARN("set curl verbose failed", K(cc)); + } +#endif + else if (CURLE_OK != (cc = curl_easy_perform(curl))) { + LOG_WARN("perform curl failed", K(cc)); + } else if (CURLE_OK != (cc = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code))) { + LOG_WARN("curl get response code failed", K(cc)); + } else if ((http_code / 100) != 2) { // http status code 2xx means success + ret = OB_CURL_ERROR; + LOG_WARN("unexpected http status code", K(ret), K(http_code)); + } + if (OB_SUCC(ret) && (CURLE_OK != cc)) { + ret = OB_CURL_ERROR; + LOG_WARN("fail to curl credential", K(ret), K(cc), KP(json_data)); + } + if (OB_NOT_NULL(headers)) { + curl_slist_free_all(headers); + } + } + if (OB_NOT_NULL(curl)) { + curl_easy_cleanup(curl); + } + return ret; +} + +int ObDeviceCredentialMgr::curl_credential( + const ObObjectStorageInfo &storage_info, + const bool update_access_time) +{ + int ret = OB_SUCCESS; + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr not init", K(ret)); + } else if (OB_UNLIKELY(!storage_info.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("storage_info is invalid", K(ret), K(storage_info)); + } else { + ObDeviceCredentialKey credential_key; + if (OB_FAIL(credential_key.init(storage_info))) { + LOG_WARN("failed to init credential key", K(ret), K(storage_info)); + } else if (OB_FAIL(curl_credential(credential_key, update_access_time))) { + LOG_WARN("failed to curl credential", K(ret), K(credential_key)); + } + } + return ret; +} + +int ObDeviceCredentialMgr::curl_credential( + const ObDeviceCredentialKey &credential_key, + const bool update_access_time) +{ + int ret = OB_SUCCESS; + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr not init", K(ret)); + } else if (OB_UNLIKELY(!credential_key.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("credential_key is invalid", K(ret), K(credential_key)); + } else { + char *response = nullptr; + ObArenaAllocator allocator(OB_DEVICE_CREDENTIAL_ALLOCATOR); + ResponseAndAllocator res_and_allocator(response, allocator); + if (OB_FAIL(connect_to_sts(credential_key, res_and_allocator))) { + LOG_WARN("fail to connect to sts", K(ret), K(credential_key)); + } else { + ObObjectStorageCredential device_credential; + CredentialAccessTimeCallBack read_callback(false /*update_access_time*/); + if (OB_FAIL(parse_device_credential_(res_and_allocator.response_, device_credential))) { + LOG_WARN("fail to parse device credential", K(ret), K(res_and_allocator.response_), K(credential_key)); + } else { + SpinWLockGuard w_guard(credential_lock_); + if (update_access_time) { + device_credential.access_time_us_ = ObTimeUtility::current_time(); + } else { + if (OB_FAIL(credential_map_.read_atomic(credential_key, read_callback))) { + if (ret == OB_HASH_NOT_EXIST) { + ret = OB_SUCCESS; + device_credential.access_time_us_ = ObTimeUtility::current_time(); + } else { + LOG_WARN("fail to get credential access time", K(ret), K(device_credential), K(credential_key)); + } + } else { + device_credential.access_time_us_ = read_callback.original_access_time_us_; + } + } + + if (FAILEDx(credential_map_.set_refactored( + credential_key, device_credential, true /*overwrite*/))) { + LOG_WARN("fail to set refactored", K(ret), K(credential_key), K(device_credential)); + } + } + } + } + return ret; +} + +int ObDeviceCredentialMgr::get_credential( + const ObObjectStorageInfo &storage_info, + ObObjectStorageCredential &device_credential) +{ + int ret = OB_SUCCESS; + ObDeviceCredentialKey credential_key; + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr not init", K(ret)); + } else if (OB_UNLIKELY(!storage_info.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("storage_info is invalid", K(ret), K(storage_info)); + } else if (OB_FAIL(credential_key.init(storage_info))) { + LOG_WARN("failed to init credential key", K(ret), K(storage_info)); + } else if (OB_FAIL(get_credential(credential_key, device_credential))) { + LOG_WARN("failed to get credential", K(ret), K(storage_info), K(credential_key)); + } + return ret; +} + +int ObDeviceCredentialMgr::get_credential( + const ObDeviceCredentialKey &credential_key, ObObjectStorageCredential &device_credential) +{ + int ret = OB_SUCCESS; + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr not init", K(ret)); + } else if (OB_UNLIKELY(!credential_key.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("credential_key is invalid", K(ret), K(credential_key)); + } else if (OB_FAIL(get_credential_from_map_(credential_key, device_credential))) { + if (OB_HASH_NOT_EXIST == ret) { + ret = OB_SUCCESS; // ignore ret + if (OB_FAIL(curl_credential(credential_key, true /*update_access_time*/))) { + LOG_WARN("fail to curl credential", K(ret), K(credential_key)); + } else if (OB_FAIL(get_credential_from_map_(credential_key, device_credential))) { + LOG_WARN("fail to get_refactored", K(ret), K(credential_key)); + } + } else { + LOG_WARN("fail to get_refactored", K(ret), K(credential_key)); + } + } + + if (OB_SUCC(ret)) { + int tmp_ret = OB_SUCCESS; + const int64_t remained_time_us = device_credential.born_time_us_ + + device_credential.expiration_s_ * 1000LL * 1000LL + - ObTimeUtility::current_time(); + if (remained_time_us < CREDENTIAL_TASK_SCHEDULE_INTERVAL_US / 2) { + ObObjectStorageCredential tmp_credential; + if (OB_TMP_FAIL(curl_credential(credential_key, true/*update_access_time*/))) { + LOG_WARN("fail to refresh the nearly expiring credential", K(ret), + K(tmp_ret), K(credential_key), K(device_credential), K(remained_time_us)); + } else { + LOG_INFO("succeed refreshing the cached credential", + K(credential_key), K(remained_time_us)); + + if (OB_TMP_FAIL(get_credential_from_map_(credential_key, tmp_credential))) { + LOG_WARN("fail to get refreshed credential", + K(ret), K(tmp_ret), K(credential_key), K(remained_time_us)); + } else if (OB_FAIL(device_credential.assign(tmp_credential))) { + LOG_WARN("succeed refreshing the cached credential but fail to assign", + K(ret), K(credential_key), K(remained_time_us)); + } + } + } + + // If the old credential has expired, it is necessary to use the refreshed key + if (OB_SUCC(ret) && remained_time_us <= 0 && OB_TMP_FAIL(tmp_ret)) { + ret = tmp_ret; + OB_LOG(WARN, "the old credential has expired, and fail to refresh", + K(ret), K(remained_time_us)); + } + } + return ret; +} + +int ObDeviceCredentialMgr::get_credential_from_map_( + const ObDeviceCredentialKey &credential_key, + ObObjectStorageCredential &device_credential) +{ + int ret = OB_SUCCESS; + SpinRLockGuard r_guard(credential_lock_); + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr is not inited", K(ret)); + } else if (OB_UNLIKELY(!credential_key.is_valid())) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("credential_key is invalid", K(ret), K(credential_key)); + } else { + CredentialAccessTimeCallBack callback(true /*update_access_time*/); + if (OB_FAIL(credential_map_.get_refactored(credential_key, device_credential))) { + LOG_WARN("fail to get_refactored", K(ret), K(credential_key)); + } else if (OB_FAIL(credential_map_.atomic_refactored(credential_key, callback))) { + LOG_WARN("fail to update access time", K(ret), K(device_credential), K(credential_key)); + } + } + return ret; +} + +int64_t ObDeviceCredentialMgr::on_write_data_( + const void *ptr, + const int64_t size, + const int64_t nmemb, + void *user_data) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(ptr) || OB_UNLIKELY((size < 0 || nmemb < 0)) || OB_ISNULL(user_data)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid argument", K(ret), KP(ptr), K(size), K(nmemb), KP(user_data)); + } else { + // "阿里云STS服务返回的安全令牌(STS + // Token)的长度不固定,强烈建议您不要假设安全令牌的最大长度。" there exists sts_token in the + // response of curl. therefore, use allocator to alloc mem dynamically + ResponseAndAllocator *res_and_allocator = static_cast(user_data); + char *&response = res_and_allocator->response_; + ObArenaAllocator &allocator = res_and_allocator->allocator_; + ObArenaAllocator tmp_allocator(OB_DEVICE_CREDENTIAL_ALLOCATOR); + if (nullptr != response) { // already has received response data + // 1. save previous response data to tmp_response + char *tmp_response = nullptr; + const int64_t prev_response_size = STRLEN(response); + if (OB_FAIL(ob_dup_cstring(tmp_allocator, ObString(response), tmp_response))) { + LOG_WARN("fail to dup cstring", K(ret), K(response), K(prev_response_size)); + } + // 2. copy previous response data and current response data to response + if (OB_SUCC(ret)) { + const int64_t curr_response_size = prev_response_size + size * nmemb; + if (OB_ISNULL(response = static_cast(allocator.alloc(curr_response_size + 1)))) { + ret = OB_ALLOCATE_MEMORY_FAILED; + LOG_WARN("fail to alloc memory", K(ret), "sz", curr_response_size + 1); + } else { + MEMCPY(response, tmp_response, prev_response_size); + const char *ptr_data = static_cast(ptr); + MEMCPY(response + prev_response_size, ptr_data, size * nmemb); + response[curr_response_size] = '\0'; + } + } + } else { // first time to receive response data + // copy response data to response + if (OB_FAIL(ob_dup_cstring(allocator, ObString(static_cast(ptr)), response))) { + LOG_WARN("fail to dup cstring", K(ret), K(ptr), K(size), K(nmemb)); + } + } + } + return (OB_SUCCESS == ret) ? (size * nmemb) : 0; +} + +int64_t ObDeviceCredentialMgr::debug_callback( + CURL *handle, + curl_infotype type, + char *data, + size_t size, + void *userp) +{ + UNUSED(handle); + int ret = OB_SUCCESS; + if (OB_ISNULL(data) + || OB_UNLIKELY(size == 0 || type < 0 || type >= curl_infotype::CURLINFO_END)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("invalid argument", K(ret), KP(data), K(size), K(type)); + } else { + switch (type) { + case CURLINFO_HEADER_OUT: + case CURLINFO_DATA_OUT: + LOG_WARN("Send data", K(size), K(data)); + break; + case CURLINFO_HEADER_IN: + case CURLINFO_DATA_IN: + LOG_WARN("Recv data", K(size), K(data)); + break; + case CURLINFO_TEXT: { + int http_code = 0; + curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_code); + if (200 != http_code) { + LOG_WARN("unexpected http status code", K(http_code)); + } + break; + } + default: + break; + } + } + return ret; +} + +int ObDeviceCredentialMgr::parse_device_credential_( + const char *res_ptr, + ObObjectStorageCredential &credential) +{ + int ret = OB_SUCCESS; + ObArenaAllocator allocator(OB_DEVICE_CREDENTIAL_ALLOCATOR); + json::Value *root = nullptr; + json::Parser parser; + if (OB_ISNULL(res_ptr)) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("res_ptr is null", K(ret)); + } else if (OB_FAIL(parser.init(&allocator))) { + LOG_WARN("parser init failed", K(ret)); + } else if (OB_FAIL(parser.parse(res_ptr, STRLEN(res_ptr), root))) { + LOG_WARN("parse json failed", K(ret)); + } else if (OB_ISNULL(root)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("no root value", K(ret)); + } else if (json::JT_OBJECT != root->get_type()) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("error json format", K(ret), K(root->get_type())); + } else { + ObString access_key_id; + ObString access_key_secret; + ObString security_token; + int64_t expiration = 0; + int64_t code = 0; + ObString err_code; + ObString success_val; + const json::Value *response_val = nullptr; + ObString request_id; + DLIST_FOREACH_X(it, root->get_object(), OB_SUCC(ret)) { + if (it->name_.case_compare("Code") == 0) { + code = (it->value_->get_number()); + } else if (it->name_.case_compare("ErrorCode") == 0) { + err_code = (it->value_->get_string()); + } else if (it->name_.case_compare("Success") == 0) { + success_val = it->value_->get_string(); + } else if (it->name_.case_compare("Data") == 0) { + response_val = it->value_; + } else if (it->name_.case_compare("RequestId") == 0) { + request_id = (it->value_->get_string()); + } + } + + if (OB_FAIL(ret)) { + } else if (OB_UNLIKELY(code != 200 || !success_val.case_compare("true"))) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("unexpected response", K(ret), K(code), K(err_code), K(success_val), K(request_id)); + } else if (OB_UNLIKELY(json::JT_OBJECT != response_val->get_type())) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid response value", K(ret), K(response_val->get_type())); + } else { + DLIST_FOREACH_X(it, response_val->get_object(), OB_SUCC(ret)) + { + for (int64_t i = 0; (i < ARRAYSIZEOF(response_items_)) && OB_SUCC(ret); ++i) { + if (0 == it->name_.case_compare(response_items_[i])) { + if (i != 3 && OB_UNLIKELY(json::JT_STRING != it->value_->get_type())) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid response item", K(ret), K(it->name_), K(it->value_->get_type())); + } else if (i == 3 && OB_UNLIKELY(json::JT_NUMBER != it->value_->get_type())) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid response item", K(ret), K(it->name_), K(it->value_->get_type())); + } else { + switch (i) { +#define EXTRACT_JSON_STRING_PARAM(param_id, param) \ + case param_id: \ + param = it->value_->get_string(); \ + break; +#define EXTRACT_JSON_NUMBER_PARAM(param_id, param) \ + case param_id: \ + param = it->value_->get_number(); \ + break; + EXTRACT_JSON_STRING_PARAM(ResponseItem::AccessKeyId, access_key_id); + EXTRACT_JSON_STRING_PARAM(ResponseItem::AccessKeySecret, access_key_secret); + EXTRACT_JSON_STRING_PARAM(ResponseItem::SecurityToken, security_token); + EXTRACT_JSON_NUMBER_PARAM(ResponseItem::DurationSeconds, expiration); +#undef EXTRACT_JSON_STRING_PARAM + default: + break; + } + } + } + } + } + } + + if (OB_SUCC(ret)) { + if (OB_UNLIKELY(access_key_id.empty() || access_key_secret.empty() || security_token.empty() + || expiration <= 0)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("exists empty response item", K(ret), "access_key_id", access_key_id.empty(), + "access_key_secret", access_key_secret.empty(), "security_token", + security_token.empty(), "expiration", expiration); + } else if (OB_FAIL(databuff_printf(credential.access_id_, sizeof(credential.access_id_), + "%.*s", access_key_id.length(), access_key_id.ptr()))) { + LOG_WARN("failed to set credential access id", K(ret), K(access_key_id)); + } else if (OB_FAIL(databuff_printf(credential.access_key_, sizeof(credential.access_key_), + "%.*s", access_key_secret.length(), access_key_secret.ptr()))) { + LOG_WARN("failed to set credential access key", K(ret), K(access_key_secret.length())); + } else if (OB_FAIL(credential.sts_token_.set(security_token))) { + LOG_WARN("failed to set credential sts token", K(ret)); + } else { + if (OB_UNLIKELY(expiration < CREDENTIAL_TASK_SCHEDULE_INTERVAL_US / 1000LL / 1000LL / 2)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid expiration", K(ret), K(expiration)); + } else { + credential.expiration_s_ = expiration; + credential.born_time_us_ = ObTimeUtility::current_time(); + } + } + } + } + return ret; +} + +int ObDeviceCredentialMgr::refresh() +{ + int ret = OB_SUCCESS; + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("ObDeviceCredentialMgr not init", K(ret)); + } else { + ObArray credential_key_to_refresh; + ObArray credential_key_to_delete; + CredentialMap::iterator iter = credential_map_.begin(); + { + SpinWLockGuard r_guard(credential_lock_); + while (OB_SUCC(ret) && iter != credential_map_.end()) { + ObDeviceCredentialKey &tmp_key = iter->first; + if (ObTimeUtility::current_time() - iter->second.access_time_us_ >= credential_duration_us_) { + if (OB_FAIL(credential_key_to_delete.push_back(tmp_key))) { + LOG_WARN("failed to push back into credential key array", K(ret), + K(credential_key_to_delete), K(tmp_key), K_(credential_duration_us)); + } + } else { + if (OB_FAIL(credential_key_to_refresh.push_back(tmp_key))) { + LOG_WARN("failed to push back into credential key array", K(ret), + K(credential_key_to_refresh), K(tmp_key), K_(credential_duration_us)); + } + } + ++iter; + } + } + //A write lock is requested in curl_credential, so the above read lock needs to be released as soon as possible + for (int64_t i = 0; OB_SUCC(ret) && i < credential_key_to_refresh.count(); i++) { + if (OB_FAIL(curl_credential(credential_key_to_refresh[i], false /*update_access_time*/))) { + LOG_WARN("failed to refresh credential", K(ret), K(i), K(credential_key_to_refresh)); + } + } + // Minimize the scope of write locks + SpinWLockGuard w_guard(credential_lock_); + for (int64_t i = 0; OB_SUCC(ret) && i < credential_key_to_delete.count(); i++) { + if (OB_FAIL(credential_map_.erase_refactored(credential_key_to_delete[i]))) { + if (OB_HASH_NOT_EXIST == ret) { + ret = OB_SUCCESS; + } else { + LOG_WARN("failed to delete credential", K(ret), K(i), K(credential_key_to_delete[i])); + } + } else { + LOG_INFO("succeed delete expired credential", K(i), K(credential_key_to_delete[i])); + } + } + } + return ret; +} +} // namespace common +} // namespace oceanbase diff --git a/deps/oblib/src/lib/restore/ob_storage_info.h b/deps/oblib/src/lib/restore/ob_storage_info.h index eeda8d4f9..efb947683 100644 --- a/deps/oblib/src/lib/restore/ob_storage_info.h +++ b/deps/oblib/src/lib/restore/ob_storage_info.h @@ -18,6 +18,10 @@ #include "common/storage/ob_device_common.h" #include "lib/utility/ob_print_utils.h" #include "lib/utility/ob_unify_serialize.h" +#include +#include "lib/hash/ob_hashmap.h" +#include "lib/allocator/page_arena.h" +#include "lib/string/ob_string.h" namespace oceanbase { @@ -29,10 +33,36 @@ const int64_t OB_MAX_BACKUP_EXTENSION_LENGTH = 512; const int64_t OB_MAX_BACKUP_ENDPOINT_LENGTH = 256; const int64_t OB_MAX_BACKUP_ACCESSID_LENGTH = 256; const int64_t OB_MAX_BACKUP_ACCESSKEY_LENGTH = 256; -const int64_t OB_MAX_BACKUP_STORAGE_INFO_LENGTH = 1536; +const int64_t OB_MAX_BACKUP_STORAGE_INFO_LENGTH = 1600; +// OB_MAX_DEVICE_KEY_LENGTH = OB_MAX_BACKUP_STORAGE_INFO_LENGTH + strlen("&storage_type=x") +const int64_t OB_MAX_DEVICE_KEY_LENGTH = OB_MAX_BACKUP_STORAGE_INFO_LENGTH + 15; const int64_t OB_MAX_BACKUP_ENCRYPTKEY_LENGTH = OB_MAX_BACKUP_ACCESSKEY_LENGTH + 32; const int64_t OB_MAX_BACKUP_SERIALIZEKEY_LENGTH = OB_MAX_BACKUP_ENCRYPTKEY_LENGTH * 2; +// We have agreed with OCP that the maximum role_arn length shall not exceed 256 +static constexpr int64_t OB_MAX_ROLE_ARN_LENGTH = 256; +// The limit on the maximum length of external_id in obs/cos/oss/s3 is 128 +static constexpr int64_t OB_MAX_EXTERNAL_ID_LENGTH = 128; +static constexpr int64_t OB_MAX_ASSUME_ROLE_JSON_DATA_LENGTH = 1024; +// STS_AK and STS_SK are used to connect to STS service of OCP. +// We have agreed with ocp that the maximum length of sts_sk/sts_ak is 32. +// And the maximum length of sts_url is 512. +static constexpr int64_t OB_MAX_STS_AK_LENGTH = 64; +static constexpr int64_t OB_MAX_STS_SK_LENGTH = 64; +static constexpr int64_t OB_MAX_STS_URL_LENGTH = 512; +static constexpr int64_t OB_PREDEFINED_STS_TOKEN_LENGTH = 1024; +static constexpr int64_t OB_MAX_STS_CREDENTIAL_LENGTH = OB_MAX_STS_AK_LENGTH + OB_MAX_STS_SK_LENGTH + OB_MAX_STS_URL_LENGTH; +static constexpr int64_t OB_MAX_STS_SIGNATURE_LENGTH = 64; +static constexpr int64_t OB_MAX_STS_CONCAT_LENGTH = 512; +static constexpr int64_t OB_MAX_STS_SIGNATURE_NONCE_LENTH = 64; +static constexpr int64_t OB_MAX_STS_SIGNATURE_RAND_NUM = 10000; +static constexpr int64_t OB_MAX_STS_REQUEST_ID_LENTH = 64; +static constexpr int64_t OB_MAX_STS_CURL_CONNECTTIMEOUT_MS = 10000; // 10s +static constexpr int64_t OB_MAX_STS_CURL_TIMEOUT_SECONDS = 10; // 10s + +// To ensure that the temporary credentials in the credential map are always valid, +// the credentials are refreshed every 20 minutes. +static constexpr int64_t CREDENTIAL_TASK_SCHEDULE_INTERVAL_US = 1200LL * 1000LL * 1000LL; // 20min const char *const ACCESS_ID = "access_id="; const char *const ACCESS_KEY = "access_key="; const char *const HOST = "host="; @@ -51,6 +81,21 @@ const char *const CHECKSUM_TYPE_NO_CHECKSUM = "no_checksum"; const char *const CHECKSUM_TYPE_MD5 = "md5"; const char *const CHECKSUM_TYPE_CRC32 = "crc32"; +const char *const ROLE_ARN = "role_arn="; +const char *const EXTERNAL_ID = "external_id="; +const char *const STS_AK = "sts_ak="; +const char *const STS_SK = "sts_sk="; +const char *const STS_URL = "sts_url="; +const char *const OSS_ROLE_ARN_PREFIX = "acs"; +const char *const OBS_ROLE_ARN_PREFIX = "iam"; +const char *const S3_ROLE_ARN_PREFIX = "arn"; +const char *const COS_ROLE_ARN_PREFIX = "qcs"; +const char *const STS_ACTION = "GetResourceSTSCredential"; +const char *const STS_RESOURCE_SOURCE = "OBSERVER"; + +const char *const OB_DEVICE_CREDENTIAL_ALLOCATOR = "ObjDeviceCredentialAlloc"; +static constexpr int64_t MAX_CREDENTIAL_IDLE_DURATION_US = 24 * 3600 * 1000 * 1000L; // 24h + enum ObStorageAddressingModel { OB_VIRTUAL_HOSTED_STYLE = 0, @@ -68,13 +113,80 @@ bool is_oss_supported_checksum(const ObStorageChecksumType checksum_type); bool is_cos_supported_checksum(const ObStorageChecksumType checksum_type); bool is_s3_supported_checksum(const ObStorageChecksumType checksum_type); const char *get_storage_checksum_type_str(const ObStorageChecksumType &type); - // [Extensions] // load_data_* : sql/engine/cmd/ob_load_data_storage_info.h +struct ObSTSToken +{ + ObSTSToken(); + virtual ~ObSTSToken(); + TO_STRING_KV(KP_(data), KP_(data_arr), K_(is_below_predefined_length)); + void reset(); + int set(const ObString &token); + int assign(const ObSTSToken &token); + const char *get_data() const; + // "阿里云STS服务返回的安全令牌(STS Token)的长度不固定,强烈建议您不要假设安全令牌的最大长度。" + // therefore, use allocator to alloc mem for sts_token dynamically + int64_t length() const {return data_len_;}; + bool is_valid() const; + bool is_below_predefined_length() const {return is_below_predefined_length_;}; + char *data_; + ObArenaAllocator allocator_; + // Since cloud vendors all claim that the length of sts_token is variable, we previously used + // allocator for memory allocation. But later we found that allocating memory every time would + // increase the cpu consumption, so we optimize it by using a fixed-length char array. When the + // length of sts_token is less than 1024, allocation is no longer performed, but data_arr is used + // directly for storage. Please refer to the documentation for details: + // + char data_arr_[OB_PREDEFINED_STS_TOKEN_LENGTH]; + int64_t data_len_; + bool is_below_predefined_length_; + bool is_inited_; +}; + +struct ObObjectStorageCredential +{ + ObObjectStorageCredential(); + virtual ~ObObjectStorageCredential() + { + reset(); + } + TO_STRING_KV(K_(expiration_s), K_(access_time_us), K_(born_time_us), + K_(access_id), KP_(access_key), K_(sts_token)); + void reset(); + int assign(const ObObjectStorageCredential &credential); + + // Temporary ak and sk to access bucket + char access_id_[OB_MAX_BACKUP_ACCESSID_LENGTH]; + char access_key_[OB_MAX_BACKUP_ACCESSKEY_LENGTH]; + ObSTSToken sts_token_; + // Expiration time of current ak/sk returned from STS Service + int64_t expiration_s_; + // Latest access time of the credential + int64_t access_time_us_; + int64_t born_time_us_; +}; + +class ObClusterVersionBaseMgr +{ +public: + ObClusterVersionBaseMgr() {} + virtual ~ObClusterVersionBaseMgr() {} + virtual int is_supported_assume_version() const + { + return OB_SUCCESS; + }; + static ObClusterVersionBaseMgr &get_instance() + { + static ObClusterVersionBaseMgr mgr; + return mgr; + } +}; + class ObObjectStorageInfo { OB_UNIS_VERSION(1); + public: ObObjectStorageInfo(); virtual ~ObObjectStorageInfo(); @@ -87,6 +199,14 @@ public: ObStorageChecksumType get_checksum_type() const; const char *get_checksum_type_str() const; virtual int get_storage_info_str(char *storage_info, const int64_t info_len) const; + + // the following two functions are designed for Assume Role. + int validate_arguments() const; + bool is_assume_role_mode() const; + virtual int get_authorization_str(char *authorization_str, + const int64_t authorization_str_len, + ObSTSToken &sts_token) const; + // the following two functions are designed for ObDeviceManager, which manages all devices by a device_map_ int get_device_map_key_str(char *key_str, const int64_t len) const; int64_t get_device_map_key_len() const; @@ -101,7 +221,8 @@ public: int reset_access_id_and_access_key( const char *access_id, const char *access_key); TO_STRING_KV(K_(endpoint), K_(access_id), K_(extension), "type", get_type_str(), - K_(checksum_type), K_(max_iops), K_(max_bandwidth)); + K_(checksum_type), K_(max_iops), K_(max_bandwidth), KP_(role_arn), KP_(external_id)); + static int register_cluster_version_mgr(ObClusterVersionBaseMgr *cluster_version_mgr); protected: virtual int get_access_key_(char *key_buf, const int64_t key_buf_len) const; @@ -110,13 +231,16 @@ protected: int check_addressing_model_(const char *addressing_model) const; int set_checksum_type_(const char *checksum_type_str); int set_storage_info_field_(const char *info, char *field, const int64_t length); + int get_info_str_(char *storage_info, const int64_t info_len) const; + int append_extension_str_(char *storage_info, const int64_t info_len) const; + public: int delete_mode_; // TODO: Rename device_type_ to storage_protocol_type_ for better clarity - // Prefix in the storage_info string, such as 's3://', indicates the protocol used to access the target - // Currently, both OBS and GCS are accessed via the s3 protocol, - // hence s3_region is updated to be an optional parameter + // Prefix in the storage_info string, such as 's3://', indicates the protocol used to access the + // target. Currently, both OBS and GCS are accessed via the s3 protocol, hence s3_region is updated + // to be an optional parameter common::ObStorageType device_type_; // Optional parameter. If not provided, the default value OB_MD5_ALGO will be used. // For OSS/COS, OB_NO_CHECKSUM_ALGO indicates that no checksum algorithm will be used. @@ -129,8 +253,160 @@ public: char extension_[OB_MAX_BACKUP_EXTENSION_LENGTH]; int64_t max_iops_; int64_t max_bandwidth_; + + // Support access object storage by assume role + char role_arn_[OB_MAX_ROLE_ARN_LENGTH]; + char external_id_[OB_MAX_EXTERNAL_ID_LENGTH]; + bool is_assume_role_mode_; + static ObClusterVersionBaseMgr *cluster_version_mgr_; }; +class ObTenantStsCredentialBaseMgr +{ +public: + ObTenantStsCredentialBaseMgr() {} + ~ObTenantStsCredentialBaseMgr() {} + virtual int get_sts_credential(char *sts_credential, const int64_t sts_credential_buf_len) = 0; +}; + +class ObStsCredential +{ +public: + ObStsCredential(); + virtual ~ObStsCredential(); + int init(const uint64_t tenant_id); + void reset(); + int get_sts_credential(); + static int register_sts_credential_mgr(ObTenantStsCredentialBaseMgr *sts_credential_mgr); + TO_STRING_KV(K_(tenant_id), K_(sts_ak), KP_(sts_sk), K_(sts_url), KP_(sts_credential_mgr)); +public: + uint64_t tenant_id_; + char sts_ak_[OB_MAX_STS_AK_LENGTH]; + char sts_sk_[OB_MAX_STS_SK_LENGTH]; + char sts_url_[OB_MAX_STS_URL_LENGTH]; + bool is_inited_; +private: + static ObTenantStsCredentialBaseMgr *sts_credential_mgr_; +}; + +class ObDeviceCredentialKey +{ +public: + ObDeviceCredentialKey(); + virtual ~ObDeviceCredentialKey(); + int init(const ObObjectStorageInfo &storage_info); + void reset(); + uint64_t hash() const; + int hash(uint64_t &hash_val) const + { + hash_val = hash(); + return OB_SUCCESS; + } + int assign(const ObDeviceCredentialKey &other); + bool operator==(const ObDeviceCredentialKey &other) const; + bool operator!=(const ObDeviceCredentialKey &other) const; + bool is_valid() const; + + int construct_signed_url(char *url_buf, const int64_t url_buf_len) const; + TO_STRING_KV(K_(is_inited), KP_(role_arn), KP_(external_id), K_(tenant_id)); + +public: + char role_arn_[OB_MAX_ROLE_ARN_LENGTH]; + char external_id_[OB_MAX_EXTERNAL_ID_LENGTH]; + // tenant_id is used to distinguish different tenants + uint64_t tenant_id_; + bool is_inited_; + +private: + int init_(const char *role_arn, const char *external_id); +}; + +int check_sts_credential_format(const char *sts_credential, ObStsCredential &credential_key); + +class ObDeviceCredentialMgr +{ +public: + enum ResponseItem + { + AccessKeyId = 0, + AccessKeySecret = 1, + SecurityToken = 2, + DurationSeconds = 3 + }; + // It is used to receive the response from STS + class ResponseAndAllocator + { + public: + ResponseAndAllocator(char *&response, common::ObArenaAllocator &allocator) + : response_(response), allocator_(allocator) + {} + ~ResponseAndAllocator() + {} + char *&response_; + ObArenaAllocator &allocator_; + }; + + static ObDeviceCredentialMgr &get_instance(); + virtual ~ObDeviceCredentialMgr(); + int init(); + // curl STS service to perform as assume role is used, and then update @credential_map_ + void destroy(); + int connect_to_sts( + const ObDeviceCredentialKey &credential_key, ResponseAndAllocator &res_and_allocator); + int curl_credential( + const ObObjectStorageInfo &storage_info, const bool update_access_time = true); + int curl_credential( + const ObDeviceCredentialKey &credential_key, const bool update_access_time = true); + int get_credential( + const ObObjectStorageInfo &storage_info, ObObjectStorageCredential &device_credential); + int get_credential( + const ObDeviceCredentialKey &credential_key, ObObjectStorageCredential &device_credential); + bool operator=(const ObDeviceCredentialMgr &) = delete; + // refresh all managed credentials + int refresh(); + void set_credential_duration_us(const int64_t duration_us) + { + credential_duration_us_ = duration_us; + } + +private: + ObDeviceCredentialMgr(); + static int64_t on_write_data_( + const void *ptr, const int64_t size, const int64_t nmemb, void *user_data); + static int64_t debug_callback( + CURL *handle, curl_infotype type, char *data, size_t size, void *userp); + int get_credential_from_map_( + const ObDeviceCredentialKey &credential_key, ObObjectStorageCredential &device_credential); + int parse_device_credential_(const char *res_ptr, ObObjectStorageCredential &credential); + +private: + static const int64_t RESPONSE_ITEM_CNT = 4; + static const char *response_items_[RESPONSE_ITEM_CNT]; + typedef hash::ObHashMap CredentialMap; + bool is_inited_; + CredentialMap credential_map_; + common::SpinRWLock credential_lock_; + // The time when the credential expires from the cache + int64_t credential_duration_us_; +}; + +class CredentialAccessTimeCallBack +{ +public: + explicit CredentialAccessTimeCallBack(const bool update_access_time_us) + : update_access_time_us_(update_access_time_us), original_access_time_us_(0) + {} + void operator()(hash::HashMapPair &v) + { + original_access_time_us_ = v.second.access_time_us_; + if (update_access_time_us_) { + v.second.access_time_us_ = ObTimeUtility::current_time(); + } + }; + + bool update_access_time_us_; + int64_t original_access_time_us_; +}; } } diff --git a/deps/oblib/src/lib/restore/ob_storage_oss_base.cpp b/deps/oblib/src/lib/restore/ob_storage_oss_base.cpp index e63e0bb8e..a7832f400 100644 --- a/deps/oblib/src/lib/restore/ob_storage_oss_base.cpp +++ b/deps/oblib/src/lib/restore/ob_storage_oss_base.cpp @@ -555,8 +555,8 @@ void ObOssAccount::reset_account() memset(oss_domain_, 0, MAX_OSS_ENDPOINT_LENGTH); memset(oss_id_, 0, MAX_OSS_ID_LENGTH); memset(oss_key_, 0, MAX_OSS_KEY_LENGTH); - oss_sts_token_ = nullptr; delete_mode_ = ObStorageDeleteMode::STORAGE_DELETE_MODE; + sts_token_.reset(); is_inited_ = false; } @@ -610,10 +610,10 @@ int ObStorageOssBase::init_with_storage_info(common::ObObjectStorageInfo *storag } else if (OB_ISNULL(storage_info) || OB_UNLIKELY(!storage_info->is_valid())) { ret = OB_INVALID_ARGUMENT; OB_LOG(WARN, "oss account is invalid, fail to init oss base!", K(ret), KPC(storage_info)); - } else if (OB_FAIL(storage_info->get_storage_info_str(info_str, sizeof(info_str)))) { - OB_LOG(WARN, "fail to get storage info str", K(ret), KPC(storage_info)); + } else if (OB_FAIL(storage_info->get_authorization_str(info_str, sizeof(info_str), oss_account_.sts_token_))) { + OB_LOG(WARN, "fail to get authorization str", K(ret), KPC(storage_info)); } else if (OB_FAIL(oss_account_.parse_oss_arg(info_str))) { - OB_LOG(WARN, "fail to build oss account", K(ret)); + OB_LOG(WARN, "fail to build oss account", K(ret), KP(info_str), KPC(storage_info)); } else if (OB_FAIL(init_oss_options(aos_pool_, oss_option_))) { OB_LOG(WARN, "fail to init oss options", K(aos_pool_), K(oss_option_), K(ret)); } else if (OB_ISNULL(aos_pool_) || OB_ISNULL(oss_option_)) { @@ -682,15 +682,15 @@ int ObOssAccount::parse_oss_arg(const common::ObString &storage_info) if (NULL == token) { break; } else if (0 == strncmp(HOST, token, strlen(HOST))) { - if (OB_FAIL(set_oss_field(token + strlen(HOST), oss_domain_, sizeof(oss_domain_)))) { + if (OB_FAIL(ob_set_field(token + strlen(HOST), oss_domain_, sizeof(oss_domain_)))) { OB_LOG(WARN, "failed to set oss_domain", K(ret), KCSTRING(token)); } } else if (0 == strncmp(ACCESS_ID, token, strlen(ACCESS_ID))) { - if (OB_FAIL(set_oss_field(token + strlen(ACCESS_ID), oss_id_, sizeof(oss_id_)))) { + if (OB_FAIL(ob_set_field(token + strlen(ACCESS_ID), oss_id_, sizeof(oss_id_)))) { OB_LOG(WARN, "failed to set oss_id_", K(ret), KCSTRING(token)); } } else if (0 == strncmp(ACCESS_KEY, token, strlen(ACCESS_KEY))) { - if (OB_FAIL(set_oss_field(token + strlen(ACCESS_KEY), oss_key_, sizeof(oss_key_)))) { + if (OB_FAIL(ob_set_field(token + strlen(ACCESS_KEY), oss_key_, sizeof(oss_key_)))) { OB_LOG(WARN, "failed to set oss_key_", K(ret)); } } else if (0 == strncmp(STS_TOKEN_KEY, token, strlen(STS_TOKEN_KEY))) { @@ -700,7 +700,7 @@ int ObOssAccount::parse_oss_arg(const common::ObString &storage_info) if (OB_ISNULL(oss_sts_token_ = reinterpret_cast(allocator_.alloc(sts_token_len + 1)))) { ret = OB_ALLOCATE_MEMORY_FAILED; OB_LOG(WARN, "fail to alloc memory", K(ret), K(sts_token_len)); - } else if (OB_FAIL(set_oss_field(token + strlen(STS_TOKEN_KEY), oss_sts_token_, sts_token_len + 1))) { + } else if (OB_FAIL(ob_set_field(token + strlen(STS_TOKEN_KEY), oss_sts_token_, sts_token_len + 1))) { OB_LOG(WARN, "failed to set oss_sts_token_", K(ret), KCSTRING(token)); } } else if (0 == strncmp(DELETE_MODE, token, strlen(DELETE_MODE))) { @@ -724,25 +724,6 @@ int ObOssAccount::parse_oss_arg(const common::ObString &storage_info) return ret; } -int ObOssAccount::set_oss_field(const char *info, char *field, const int64_t length) -{ - int ret = OB_SUCCESS; - - if (NULL == info || NULL == field) { - ret = OB_INVALID_ARGUMENT; - OB_LOG(WARN, "invalid args", K(ret), KP(info), KP(field)); - } else { - const int64_t info_len = strlen(info); - if (info_len >= length) { - ret = OB_INVALID_ARGUMENT; - OB_LOG(WARN, "info is too long ", K(ret), K(info_len), K(length)); - } else { - MEMCPY(field, info, info_len); - field[info_len] = '\0'; - } - } - return ret; -} /* only used by pread and init. Initialize one aos_pol and oss_option. Other methods shouldn't call this method. * pread need alloc memory on aos_pol and release after read finish */ int ObStorageOssBase::init_oss_options(aos_pool_t *&aos_pool, oss_request_options_t *&oss_option) @@ -770,12 +751,14 @@ int ObStorageOssBase::init_oss_options(aos_pool_t *&aos_pool, oss_request_option ret = OB_OBJECT_STORAGE_IO_ERROR; OB_LOG(WARN, "fail to create aos http request options", K(ret)); } else { + const char *sts_data = oss_account_.sts_token_.get_data(); aos_str_set(&oss_option->config->endpoint, oss_endpoint_); aos_str_set(&oss_option->config->access_key_id, oss_account_.oss_id_); aos_str_set(&oss_option->config->access_key_secret, oss_account_.oss_key_); - if (OB_NOT_NULL(oss_account_.oss_sts_token_)) { - aos_str_set(&oss_option->config->sts_token, oss_account_.oss_sts_token_); + if (OB_NOT_NULL(sts_data)) { + aos_str_set(&oss_option->config->sts_token, sts_data); } + oss_option->config->is_cname = 0; // Set connection timeout, the default value is 10s diff --git a/deps/oblib/src/lib/restore/ob_storage_oss_base.h b/deps/oblib/src/lib/restore/ob_storage_oss_base.h index 7462cc862..6e5a36e16 100644 --- a/deps/oblib/src/lib/restore/ob_storage_oss_base.h +++ b/deps/oblib/src/lib/restore/ob_storage_oss_base.h @@ -129,9 +129,10 @@ public: ObOssAccount(); virtual ~ObOssAccount(); int parse_oss_arg(const common::ObString &storage_info); - static int set_oss_field(const char *info, char *field, const int64_t length); void reset_account(); int set_delete_mode(const char *parameter); + TO_STRING_KV(K_(oss_domain), K_(delete_mode), K_(oss_id), KP_(oss_key), K_(is_inited), K_(sts_token)); + char oss_domain_[MAX_OSS_ENDPOINT_LENGTH]; char oss_id_[MAX_OSS_ID_LENGTH]; char oss_key_[MAX_OSS_KEY_LENGTH]; @@ -141,8 +142,7 @@ public: common::ObArenaAllocator allocator_; ObStorageDeleteMode delete_mode_; bool is_inited_; - - TO_STRING_KV(K_(is_inited), K_(delete_mode), K_(oss_domain), K_(oss_id)); + ObSTSToken sts_token_; }; class ObStorageOssBase diff --git a/deps/oblib/src/lib/restore/ob_storage_s3_base.cpp b/deps/oblib/src/lib/restore/ob_storage_s3_base.cpp index 990d9ea6e..a2265ddd2 100644 --- a/deps/oblib/src/lib/restore/ob_storage_s3_base.cpp +++ b/deps/oblib/src/lib/restore/ob_storage_s3_base.cpp @@ -230,7 +230,15 @@ int ObS3Client::init(const ObS3Account &account) S3ClientConfiguration config(init_values); // Re-enables IMDS access for subsequent operations if needed config.disableIMDS = false; - Aws::Auth::AWSCredentials credentials(account.access_id_, account.secret_key_); + Aws::Auth::AWSCredentials credentials; + const char *sts_data = account.sts_token_.get_data(); + if (OB_NOT_NULL(sts_data)) { + credentials = Aws::Auth::AWSCredentials(account.access_id_, account.secret_key_, sts_data); + } else { + credentials = Aws::Auth::AWSCredentials(account.access_id_, account.secret_key_); + } + + SpinWLockGuard guard(lock_); if (OB_UNLIKELY(is_inited_)) { ret = OB_INIT_TWICE; @@ -1025,6 +1033,7 @@ void ObS3Account::reset() MEMSET(endpoint_, 0, sizeof(endpoint_)); MEMSET(access_id_, 0, sizeof(access_id_)); MEMSET(secret_key_, 0, sizeof(secret_key_)); + sts_token_.reset(); addressing_model_ = ObStorageAddressingModel::OB_VIRTUAL_HOSTED_STYLE; } @@ -1059,23 +1068,23 @@ int ObS3Account::parse_from(const char *storage_info_str, const int64_t size) if (OB_ISNULL(token)) { break; } else if (0 == strncmp(REGION, token, strlen(REGION))) { - if (OB_FAIL(set_field(token + strlen(REGION), region_, sizeof(region_)))) { + if (OB_FAIL(ob_set_field(token + strlen(REGION), region_, sizeof(region_)))) { OB_LOG(WARN, "failed to set s3 region", K(ret), KCSTRING(token)); } } else if (0 == strncmp(HOST, token, strlen(HOST))) { - if (OB_FAIL(set_field(token + strlen(HOST), endpoint_, sizeof(endpoint_)))) { + if (OB_FAIL(ob_set_field(token + strlen(HOST), endpoint_, sizeof(endpoint_)))) { OB_LOG(WARN, "failed to set s3 endpoint", K(ret), KCSTRING(token)); } else { bitmap |= 1; } } else if (0 == strncmp(ACCESS_ID, token, strlen(ACCESS_ID))) { - if (OB_FAIL(set_field(token + strlen(ACCESS_ID), access_id_, sizeof(access_id_)))) { + if (OB_FAIL(ob_set_field(token + strlen(ACCESS_ID), access_id_, sizeof(access_id_)))) { OB_LOG(WARN, "failed to set s3 access id", K(ret), KCSTRING(token)); } else { bitmap |= (1 << 1); } } else if (0 == strncmp(ACCESS_KEY, token, strlen(ACCESS_KEY))) { - if (OB_FAIL(set_field(token + strlen(ACCESS_KEY), secret_key_, sizeof(secret_key_)))) { + if (OB_FAIL(ob_set_field(token + strlen(ACCESS_KEY), secret_key_, sizeof(secret_key_)))) { OB_LOG(WARN, "failed to set s3 secret key", K(ret), KP(token)); } else { bitmap |= (1 << 2); @@ -1120,25 +1129,6 @@ int ObS3Account::parse_from(const char *storage_info_str, const int64_t size) return ret; } -int ObS3Account::set_field(const char *value, char *field, const uint32_t field_length) -{ - int ret = OB_SUCCESS; - if (OB_ISNULL(value) || OB_ISNULL(field)) { - ret = OB_INVALID_ARGUMENT; - OB_LOG(WARN, "invliad arguments", K(ret), KP(value), KP(field)); - } else { - const int64_t value_len = strlen(value); - if (value_len >= field_length) { - ret = OB_SIZE_OVERFLOW; - OB_LOG(WARN, "value is too long", K(ret), KP(value), K(value_len), K(field_length)); - } else { - MEMCPY(field, value, value_len); - field[value_len] = '\0'; - } - } - return ret; -} - /*--------------------------------ObStorageS3Base--------------------------------*/ ObStorageS3Base::ObStorageS3Base() : allocator_(OB_STORAGE_S3_ALLOCATOR), @@ -1199,8 +1189,8 @@ int ObStorageS3Base::inner_open(const ObString &uri, ObObjectStorageInfo *storag OB_LOG(WARN, "failed to init s3 base, invalid arguments", K(ret), K(uri), KPC(storage_info)); } else if (OB_FAIL(build_bucket_and_object_name(allocator_, uri, bucket_, object_))) { OB_LOG(WARN, "failed to parse uri", K(ret), K(uri)); - } else if (OB_FAIL(storage_info->get_storage_info_str(info_str, sizeof(info_str)))) { - OB_LOG(WARN, "failed to get storage info str", K(ret), KPC(storage_info)); + } else if (OB_FAIL(storage_info->get_authorization_str(info_str, sizeof(info_str), s3_account_.sts_token_))) { + OB_LOG(WARN, "failed to get authorization str", K(ret), KPC(storage_info)); } else if (OB_FAIL(s3_account_.parse_from(info_str, strlen(info_str)))) { OB_LOG(WARN, "failed to build s3 account", K(ret)); } else if (OB_FAIL(ObS3Env::get_instance().get_or_create_s3_client(s3_account_, s3_client_))) { diff --git a/deps/oblib/src/lib/restore/ob_storage_s3_base.h b/deps/oblib/src/lib/restore/ob_storage_s3_base.h index fa694aad8..4fc58e5d3 100644 --- a/deps/oblib/src/lib/restore/ob_storage_s3_base.h +++ b/deps/oblib/src/lib/restore/ob_storage_s3_base.h @@ -136,10 +136,9 @@ struct ObS3Account void reset(); bool is_valid() const { return is_valid_; } int64_t hash() const; - TO_STRING_KV(K_(is_valid), K_(delete_mode), K_(region), K_(endpoint), K_(access_id), K_(addressing_model)); + TO_STRING_KV(K_(is_valid), K_(delete_mode), K_(region), K_(endpoint), K_(access_id), KP_(secret_key), K_(sts_token), K_(addressing_model)); int parse_from(const char *storage_info_str, const int64_t size); - int set_field(const char *value, char *field, const uint32_t field_length); bool is_valid_; int64_t delete_mode_; @@ -147,6 +146,7 @@ struct ObS3Account char endpoint_[MAX_S3_ENDPOINT_LENGTH]; char access_id_[MAX_S3_ACCESS_ID_LENGTH]; // ak char secret_key_[MAX_S3_SECRET_KEY_LENGTH]; // sk + ObSTSToken sts_token_; ObStorageAddressingModel addressing_model_; }; @@ -392,7 +392,6 @@ protected: private: bool is_inited_; ObS3Account s3_account_; - friend class ObStorageS3Util; DISALLOW_COPY_AND_ASSIGN(ObStorageS3Base); }; diff --git a/deps/oblib/unittest/lib/CMakeLists.txt b/deps/oblib/unittest/lib/CMakeLists.txt index 2547f00e9..90b2be584 100644 --- a/deps/oblib/unittest/lib/CMakeLists.txt +++ b/deps/oblib/unittest/lib/CMakeLists.txt @@ -92,11 +92,11 @@ oblib_addtest(rc/test_context.cpp) oblib_addtest(resource/test_resource_mgr.cpp) #oblib_addtest(restore/test_storage_file.cpp) #oblib_addtest(restore/test_storage_oss.cpp) -oblib_addtest(restore/test_storage_cos.cpp) -oblib_addtest(restore/test_storage_s3.cpp) +#oblib_addtest(restore/test_storage_cos.cpp) +#oblib_addtest(restore/test_storage_s3.cpp) # oblib_addtest(restore/test_object_storage.cpp) # oblib_addtest(restore/test_common_storage.cpp) -oblib_addtest(restore/test_storage_info.cpp) +#oblib_addtest(restore/test_storage_info.cpp) #oblib_addtest(restore/test_storage.cpp) oblib_addtest(stat/test_di_cache.cpp) oblib_addtest(stat/test_diagnose_info.cpp) diff --git a/src/observer/ob_server.cpp b/src/observer/ob_server.cpp index 23fa41aba..5d31481ba 100644 --- a/src/observer/ob_server.cpp +++ b/src/observer/ob_server.cpp @@ -125,6 +125,7 @@ #include "observer/table/ttl/ob_table_ttl_task.h" #include "storage/high_availability/ob_storage_ha_diagnose_service.h" #include "logservice/palf/log_cache.h" +#include "share/ob_device_credential_task.h" #ifdef OB_BUILD_ARBITRATION #include "logservice/arbserver/palf_env_lite_mgr.h" #include "logservice/arbserver/ob_arb_srv_network_frame.h" @@ -403,6 +404,8 @@ int ObServer::init(const ObServerOptions &opts, const ObPLogWriterCfg &log_cfg) #endif } else if (OB_FAIL(schema_status_proxy_.init())) { LOG_ERROR("fail to init schema status proxy", KR(ret)); + } else if (OB_FAIL(device_credential_task_.init(CREDENTIAL_TASK_SCHEDULE_INTERVAL_US))) { + LOG_ERROR("fail to init device_credential_task", KR(ret), K(CREDENTIAL_TASK_SCHEDULE_INTERVAL_US)); } else if (OB_FAIL(init_schema())) { LOG_ERROR("init schema failed", KR(ret)); } else if (OB_FAIL(init_network())) { diff --git a/src/observer/ob_server.h b/src/observer/ob_server.h index 13a59d109..19a14ca06 100644 --- a/src/observer/ob_server.h +++ b/src/observer/ob_server.h @@ -83,6 +83,7 @@ #ifdef OB_BUILD_SHARED_STORAGE #include "close_modules/shared_storage/storage/shared_storage/ob_tenant_gc_task.h" #endif +#include "share/ob_device_credential_task.h" namespace oceanbase { @@ -477,6 +478,7 @@ private: ObRefreshIOCalibrationTimeTask refresh_io_calibration_task_; // retry to success & no repeat blocksstable::ObStorageEnv storage_env_; share::ObSchemaStatusProxy schema_status_proxy_; + ObDeviceCredentialTask device_credential_task_; // for locality ObLocalityManager locality_manager_; diff --git a/src/observer/table/ob_table_connection_mgr.cpp b/src/observer/table/ob_table_connection_mgr.cpp index 77d38aaf2..a902fc52a 100644 --- a/src/observer/table/ob_table_connection_mgr.cpp +++ b/src/observer/table/ob_table_connection_mgr.cpp @@ -13,6 +13,7 @@ #define USING_LOG_PREFIX SERVER #include "ob_table_connection_mgr.h" #include "rpc/ob_rpc_request_operator.h" +#include "lib/allocator/ob_sql_mem_leak_checker.h" using namespace oceanbase::table; using namespace oceanbase::common; @@ -55,6 +56,7 @@ ObTableConnectionMgr::ObTableConnectionMgr() ObTableConnectionMgr &ObTableConnectionMgr::get_instance() { ObTableConnectionMgr *instance = NULL; + DISABLE_SQL_MEMLEAK_GUARD; while (OB_UNLIKELY(once_ < 2)) { if (ATOMIC_BCAS(&once_, 0, 1)) { instance = OB_NEW(ObTableConnectionMgr, ObModIds::TABLE_PROC); diff --git a/src/rootserver/restore/ob_restore_scheduler.cpp b/src/rootserver/restore/ob_restore_scheduler.cpp index 5a515f43d..4873dc755 100644 --- a/src/rootserver/restore/ob_restore_scheduler.cpp +++ b/src/rootserver/restore/ob_restore_scheduler.cpp @@ -254,6 +254,9 @@ int ObRestoreScheduler::restore_tenant(const ObPhysicalRestoreJob &job_info) LOG_WARN("update restore option", K(ret), K(new_tenant_id), K(job_id), K(tenant_id_)); } else if (OB_FAIL(may_update_restore_concurrency_(new_tenant_id, job_info))) { LOG_WARN("failed to update restore concurrency", K(ret), K(new_tenant_id), K(job_info)); + } else if (!job_info.get_sts_credential().empty() + && OB_FAIL(set_tenant_sts_crendential_config_(*sql_proxy_, new_tenant_id, job_info))) { + LOG_WARN("fail to set tenant sts credential config", K(ret), K(new_tenant_id)); } else { restore_service_->wakeup(); } @@ -452,6 +455,26 @@ int ObRestoreScheduler::restore_pre(const ObPhysicalRestoreJob &job_info) return ret; } +int ObRestoreScheduler::set_tenant_sts_crendential_config_( + common::ObISQLClient &proxy, const uint64_t tenant_id, const share::ObPhysicalRestoreJob &job_info) +{ + int ret = OB_SUCCESS; + int64_t affected_row = 0; + ObSqlString sql; + const ObString &sts_credential = job_info.get_sts_credential(); + bool is_exist = false; + if (!is_user_tenant(tenant_id) || sts_credential.empty()) { + ret = OB_INVALID_ARGUMENT; + LOG_WARN("not user tenant or sts credential is empty", K(job_info)); + } else if (OB_FAIL(sql.assign_fmt("alter system set sts_credential='%.*s'", sts_credential.length(), sts_credential.ptr()))) { + LOG_WARN("failed to assign fmt", K(ret)); + } else if (OB_FAIL(sql_proxy_->write(tenant_id, sql.ptr(), affected_row))) { + LOG_WARN("failed to set sts credential", K(ret), K(tenant_id)); + } else { + LOG_INFO("update restore tenant sts credential", K(tenant_id)); + } + return ret; +} int ObRestoreScheduler::wait_sys_job_ready_(const ObPhysicalRestoreJob &job, bool &is_ready) { int ret = OB_SUCCESS; diff --git a/src/rootserver/restore/ob_restore_scheduler.h b/src/rootserver/restore/ob_restore_scheduler.h index b6c44f527..6efa47ed8 100644 --- a/src/rootserver/restore/ob_restore_scheduler.h +++ b/src/rootserver/restore/ob_restore_scheduler.h @@ -122,6 +122,8 @@ private: int wait_restore_safe_mview_merge_info_(); int try_collect_ls_mv_merge_scn_(const share::SCN &tenant_mv_merge_scn); int update_restore_progress_by_bytes_(const ObPhysicalRestoreJob &job, const int64_t total_bytes, const int64_t finish_bytes); + int set_tenant_sts_crendential_config_(common::ObISQLClient &proxy, + const uint64_t tenant_id, const share::ObPhysicalRestoreJob &job_info); private: bool inited_; share::schema::ObMultiVersionSchemaService *schema_service_; diff --git a/src/rootserver/restore/ob_restore_util.cpp b/src/rootserver/restore/ob_restore_util.cpp index 7b91b11be..b30b07877 100644 --- a/src/rootserver/restore/ob_restore_util.cpp +++ b/src/rootserver/restore/ob_restore_util.cpp @@ -98,6 +98,10 @@ int ObRestoreUtil::fill_physical_restore_job( } } } + + if (FAILEDx(fill_sts_credential_(arg, job))) { + LOG_WARN("fail to fill sts credential", K(ret)); + } } LOG_INFO("finish fill_physical_restore_job", K(job_id), K(arg), K(job)); @@ -599,6 +603,20 @@ int ObRestoreUtil::fill_encrypt_info_( return ret; } +int ObRestoreUtil::fill_sts_credential_( + const obrpc::ObPhysicalRestoreTenantArg &arg, + share::ObPhysicalRestoreJob &job) +{ + int ret = OB_SUCCESS; + bool is_valid = false; + if (arg.sts_credential_.empty()) { + //TODO(mingqiao): add format check function from shifangdan + } else if (OB_FAIL(job.set_sts_credential(arg.sts_credential_))) { + LOG_WARN("fail to set sts crendential"); + } + return ret; +} + int ObRestoreUtil::get_restore_source( const bool restore_using_compl_log, const ObIArray& tenant_path_array, diff --git a/src/rootserver/restore/ob_restore_util.h b/src/rootserver/restore/ob_restore_util.h index ad691e898..a8843a5ef 100644 --- a/src/rootserver/restore/ob_restore_util.h +++ b/src/rootserver/restore/ob_restore_util.h @@ -238,6 +238,9 @@ private: static int fill_encrypt_info_( const obrpc::ObPhysicalRestoreTenantArg &arg, share::ObPhysicalRestoreJob &job); + static int fill_sts_credential_( + const obrpc::ObPhysicalRestoreTenantArg &arg, + share::ObPhysicalRestoreJob &job); DISALLOW_COPY_AND_ASSIGN(ObRestoreUtil); }; diff --git a/src/share/CMakeLists.txt b/src/share/CMakeLists.txt index bcfe654ef..f21625e6d 100644 --- a/src/share/CMakeLists.txt +++ b/src/share/CMakeLists.txt @@ -235,6 +235,7 @@ ob_set_subtarget(ob_share common ob_domain_index_builder_util.cpp ob_service_name_proxy.cpp ob_compatibility_control.cpp + ob_device_credential_task.cpp ) ob_set_subtarget(ob_share common_mixed diff --git a/src/share/backup/ob_backup_struct.cpp b/src/share/backup/ob_backup_struct.cpp index 9247cf868..e6bb8ed88 100644 --- a/src/share/backup/ob_backup_struct.cpp +++ b/src/share/backup/ob_backup_struct.cpp @@ -1151,10 +1151,23 @@ int ObBackupStorageInfo::get_authorization_info(char *authorization, const int64 LOG_WARN("invalid args", K(ret), KP(authorization), K(length)); } else if (OB_STORAGE_FILE == device_type_) { // do nothing - } else if (OB_FAIL(get_access_key_(access_key_buf, sizeof(access_key_buf)))) { - LOG_WARN("failed to get access key", K(ret)); - } else if (OB_FAIL(databuff_printf(authorization, length, "%s&%s", access_id_, access_key_buf))) { - LOG_WARN("failed to set authorization", K(ret), K(length), K_(access_id), K(strlen(access_key_buf))); + } else if (!is_assume_role_mode_) { + // access by ak/sk mode + if (OB_FAIL(get_access_key_(access_key_buf, sizeof(access_key_buf)))) { + LOG_WARN("failed to get access key", K(ret)); + } else if (OB_FAIL(databuff_printf(authorization, length, "%s&%s", access_id_, access_key_buf))) { + LOG_WARN("failed to set authorization", K(ret), K(length), K_(access_id), K(strlen(access_key_buf))); + } + } else { + // access by assume role mode + int64_t pos = 0; + if (OB_FAIL(databuff_printf(authorization, length, pos, "%s", role_arn_))) { + LOG_WARN("failed to set authorization", K(ret), K(length), KP_(role_arn)); + } else if (external_id_[0] != '\0') { + if (OB_FAIL(databuff_printf(authorization, length, pos, "&%s", external_id_))) { + LOG_WARN("failed to set authorization", K(ret), K(length), KP_(external_id)); + } + } } return ret; @@ -1400,6 +1413,8 @@ int ObBackupDest::parse_backup_dest_str_(const char *backup_dest) LOG_WARN("failed to get storage type", K(ret)); } else { // oss://backup_dir/?host=xxx.com&access_id=111&access_key=222 + // oss://backup_dir/?host=xxx.com&role_arn=xxx&external_id=xxx + // oss://backup_dir/?host=xxx.com&role_arn=xxx (external_id is optional) // file:///root_backup_dir" while (backup_dest[pos] != '\0') { if ('?' == backup_dest[pos]) { diff --git a/src/share/config/ob_config_helper.cpp b/src/share/config/ob_config_helper.cpp index ab72e6784..1e1fe0ded 100644 --- a/src/share/config/ob_config_helper.cpp +++ b/src/share/config/ob_config_helper.cpp @@ -890,6 +890,23 @@ bool ObConfigPlanCacheGCChecker::check(const ObConfigItem &t) const return is_valid; } +bool ObConfigSTScredentialChecker::check(const ObConfigItem &t) const +{ + int ret = OB_SUCCESS; + bool flag = true; + const char *tmp_credential = t.str(); + ObStsCredential key; + if (OB_ISNULL(tmp_credential) || OB_UNLIKELY(strlen(tmp_credential) <= 0 + || strlen(tmp_credential) > OB_MAX_STS_CREDENTIAL_LENGTH)) { + flag = false; + OB_LOG(WARN, "invalid sts credential", KP(tmp_credential)); + } else if (OB_FAIL(check_sts_credential_format(tmp_credential, key))) { + flag = false; + OB_LOG(WARN, "fail to check sts credential format", K(ret), K(key), KP(tmp_credential)); + } + return flag; +} + bool ObConfigUseLargePagesChecker::check(const ObConfigItem &t) const { bool is_valid = false; diff --git a/src/share/config/ob_config_helper.h b/src/share/config/ob_config_helper.h index 2bb27a3fc..9e00f3012 100644 --- a/src/share/config/ob_config_helper.h +++ b/src/share/config/ob_config_helper.h @@ -301,6 +301,18 @@ private: DISALLOW_COPY_AND_ASSIGN(ObConfigSyslogFileUncompressedCountChecker); }; +// Used to check the format of STS credential +class ObConfigSTScredentialChecker + : public ObConfigChecker +{ +public: + ObConfigSTScredentialChecker() {} + virtual ~ObConfigSTScredentialChecker() {} + bool check(const ObConfigItem &t) const; +private: + DISALLOW_COPY_AND_ASSIGN(ObConfigSTScredentialChecker); +}; + class ObConfigUseLargePagesChecker : public ObConfigChecker { diff --git a/src/share/ob_device_credential_task.cpp b/src/share/ob_device_credential_task.cpp new file mode 100644 index 000000000..eee0af950 --- /dev/null +++ b/src/share/ob_device_credential_task.cpp @@ -0,0 +1,87 @@ +/** + * Copyright (c) 2021 OceanBase + * OceanBase is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan + * PubL v2. You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the + * Mulan PubL v2 for more details. + */ + +#define USING_LOG_PREFIX SHARE + +#include "share/ob_device_credential_task.h" +#include "lib/time/ob_time_utility.h" +#include "share/ob_thread_mgr.h" +#include "share/config/ob_server_config.h" + +namespace oceanbase +{ +namespace share +{ +using namespace oceanbase::common; + +ObDeviceCredentialTask::ObDeviceCredentialTask() : is_inited_(false), schedule_interval_us_(0) +{} + +ObDeviceCredentialTask::~ObDeviceCredentialTask() +{} + +int ObDeviceCredentialTask::init(const int64_t interval_us) +{ + int ret = OB_SUCCESS; + if (IS_INIT) { + ret = OB_INIT_TWICE; + LOG_ERROR("ObDeviceCredentialTask has already been inited", K(ret)); + } else if (interval_us <= 0) { + ret = OB_INVALID_ARGUMENT; + LOG_ERROR("invalid argument", K(ret), K(interval_us)); + } else { + schedule_interval_us_ = interval_us; + if (OB_FAIL(TG_SCHEDULE(lib::TGDefIDs::ServerGTimer, *this, schedule_interval_us_, true /*schedule repeatly*/))) { + LOG_ERROR("fail to schedule task ObDeviceCredentialTask", K(ret), K(interval_us), KPC(this)); + } else { + is_inited_ = true; + } + if (OB_FAIL(ret)) { + reset(); + } + } + return ret; +} + +void ObDeviceCredentialTask::reset() +{ + is_inited_ = false; + schedule_interval_us_ = 0; +} + +void ObDeviceCredentialTask::runTimerTask() +{ + int ret = OB_SUCCESS; + const int64_t start_us = common::ObTimeUtility::fast_current_time(); + LOG_INFO("device credential task start", K(start_us)); + if (IS_NOT_INIT) { + ret = OB_NOT_INIT; + LOG_WARN("device credential task not init", K(ret)); + } else if (OB_FAIL(do_work_())) { + LOG_WARN("fail to do work", K(ret)); + } + const int64_t cost_us = common::ObTimeUtility::fast_current_time() - start_us; + LOG_INFO("device credential task finish", K(cost_us)); +} + +int ObDeviceCredentialTask::do_work_() +{ + int ret = OB_SUCCESS; + ObCurTraceId::init(GCONF.self_addr_); + if (OB_FAIL(ObDeviceCredentialMgr::get_instance().refresh())) { + OB_LOG(WARN, "failed to refresh device credentials", K(ret)); + } + return ret; +} + +} // namespace share +} // namespace oceanbase \ No newline at end of file diff --git a/src/share/ob_device_credential_task.h b/src/share/ob_device_credential_task.h new file mode 100644 index 000000000..750149d70 --- /dev/null +++ b/src/share/ob_device_credential_task.h @@ -0,0 +1,47 @@ +/** + * Copyright (c) 2021 OceanBase + * OceanBase is licensed under Mulan PubL v2. + * You can use this software according to the terms and conditions of the Mulan + * PubL v2. You may obtain a copy of Mulan PubL v2 at: + * http://license.coscl.org.cn/MulanPubL-2.0 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY + * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the + * Mulan PubL v2 for more details. + */ + +#ifndef OCEANBASE_SHARE_DEVICE_OB_DEVICE_CREDENTIAL_TASK_H_ +#define OCEANBASE_SHARE_DEVICE_OB_DEVICE_CREDENTIAL_TASK_H_ + +#include "lib/restore/ob_storage_info.h" +#include "lib/task/ob_timer.h" + +namespace oceanbase +{ +namespace share +{ +// To ensure that the temporary credentials in the credential map are always valid, +// the credentials are refreshed every 20 minutes. + +class ObDeviceCredentialTask : public common::ObTimerTask +{ +public: + ObDeviceCredentialTask(); + virtual ~ObDeviceCredentialTask(); + int init(const int64_t interval_us); + void reset(); + virtual void runTimerTask() override; + TO_STRING_KV(K_(is_inited), K_(schedule_interval_us)); + +private: + int do_work_(); + +private: + bool is_inited_; + int64_t schedule_interval_us_; +}; + +} // namespace share +} // namespace oceanbase + +#endif // OCEANBASE_SHARE_DEVICE_OB_DEVICE_CREDENTIAL_TASK_H_ \ No newline at end of file diff --git a/src/share/ob_device_manager.cpp b/src/share/ob_device_manager.cpp index 10d1464b4..b68113ed3 100644 --- a/src/share/ob_device_manager.cpp +++ b/src/share/ob_device_manager.cpp @@ -26,6 +26,84 @@ namespace oceanbase { namespace common { +int ObTenantStsCredentialMgr::get_sts_credential( + char *sts_credential, const int64_t sts_credential_buf_len) +{ + int ret = OB_SUCCESS; + int64_t tenant_id = ObObjectStorageTenantGuard::get_tenant_id(); + if (OB_ISNULL(sts_credential) || OB_UNLIKELY(sts_credential_buf_len <= 0)) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "invalid args", K(ret), + K(tenant_id), KP(sts_credential), K(sts_credential_buf_len)); + } else if (OB_UNLIKELY(!is_valid_tenant_id(tenant_id) || is_virtual_tenant_id(tenant_id))) { + // If the tenant is invalid or illegal, the sts_credential of the system tenant will be used as + // a backup. Please refer to the following document for specific reasons. + // + tenant_id = OB_SYS_TENANT_ID; + OB_LOG(WARN, "invalid tenant ctx, use sys tenant", K(ret), K(tenant_id)); + } + if (OB_SUCC(ret)) { + if (is_meta_tenant(tenant_id)) { + tenant_id = gen_user_tenant_id(tenant_id); + } + const char *tmp_credential = nullptr; + + omt::ObTenantConfigGuard tenant_config(TENANT_CONF(tenant_id)); + int tmp_ret = OB_SUCCESS; + // If the tenant does not have sts_credential, return OB_EAGAIN to wait for the next try. + if (OB_TMP_FAIL(check_sts_credential(tenant_config))) { + ret = OB_EAGAIN; + OB_LOG(WARN, "fail to check sts credential, should try again", K(ret), K(tmp_ret), K(tenant_id)); + } else { + tmp_credential = tenant_config->sts_credential; + } + + if (OB_SUCC(ret)) { + if (OB_FAIL(databuff_printf(sts_credential, sts_credential_buf_len, + "%s", tmp_credential))) { + OB_LOG(WARN, "fail to deep copy sts_credential", K(ret), K(tenant_id), KP(tmp_credential)); + } else if (OB_UNLIKELY(sts_credential[0] == '\0')) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "sts_credential is null", K(ret), K(tenant_id), KP(tmp_credential)); + } + OB_LOG(INFO, "get sts credential successfully", K(tenant_id)); + } + if (OB_FAIL(ret) && REACH_TIME_INTERVAL(LOG_INTERVAL_US)) { + OB_LOG(WARN, "try to get sts credential", K(ret), K(tenant_id), KP(tmp_credential)); + } + } + return ret; +} + +int ObTenantStsCredentialMgr::check_sts_credential(omt::ObTenantConfigGuard &tenant_config) const +{ + int ret = OB_SUCCESS; + const char *sts_credential = nullptr; + if (OB_UNLIKELY(!tenant_config.is_valid())) { + ret = OB_INVALID_ARGUMENT; + OB_LOG(WARN, "tenant config is invalid", K(ret)); + } else if (OB_ISNULL(sts_credential = tenant_config->sts_credential)) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "tenant config is invalid", K(ret), KP(sts_credential)); + } else if (OB_UNLIKELY(sts_credential[0] == '\0')) { + ret = OB_ERR_UNEXPECTED; + OB_LOG(WARN, "sts_credential is null", K(ret), KP(sts_credential)); + } + return ret; +} + +int ObClusterVersionMgr::is_supported_assume_version() const +{ + int ret = OB_SUCCESS; + uint64 min_cluster_version = GET_MIN_CLUSTER_VERSION(); + if (min_cluster_version < MOCK_CLUSTER_VERSION_4_2_5_0 + || (min_cluster_version >= CLUSTER_VERSION_4_3_0_0 + && min_cluster_version < CLUSTER_VERSION_4_3_5_0)) { + ret = OB_NOT_SUPPORTED; + OB_LOG(WARN, "cluster version is too low for assume role", K(ret), K(GET_MIN_CLUSTER_VERSION())); + } + return ret; +} const int ObDeviceManager::MAX_DEVICE_INSTANCE; ObDeviceManager::ObDeviceManager() : allocator_(), device_count_(0), is_init_(false) @@ -59,6 +137,11 @@ int ObDeviceManager::init_devices_env() OB_LOG(WARN, "fail to init cos storage", K(ret)); } else if (OB_FAIL(init_s3_env())) { OB_LOG(WARN, "fail to init s3 storage", K(ret)); + } else if (OB_FAIL(ObStsCredential::register_sts_credential_mgr( + &ObTenantStsCredentialMgr::get_instance()))) { + OB_LOG(WARN, "fail to register sts crendential", K(ret)); + } else if (OB_FAIL(ObDeviceCredentialMgr::get_instance().init())) { + OB_LOG(WARN, "fail to init device credential mgr", K(ret)); } else { // When compliantRfc3986Encoding is set to true: // - Adhere to RFC 3986 by supporting the encoding of reserved characters @@ -117,6 +200,7 @@ void ObDeviceManager::destroy() fin_cos_env(); fin_s3_env(); lock_.destroy(); + ObDeviceCredentialMgr::get_instance().destroy(); is_init_ = false; device_count_ = 0; OB_LOG_RET(WARN, ret_dev, "release the init resource", K(ret_dev), K(ret_handle)); diff --git a/src/share/ob_device_manager.h b/src/share/ob_device_manager.h index 49b5d6f72..3b7f7e8e6 100644 --- a/src/share/ob_device_manager.h +++ b/src/share/ob_device_manager.h @@ -17,6 +17,8 @@ #include "lib/allocator/ob_fifo_allocator.h" #include "lib/hash/ob_hashmap.h" #include "lib/lock/ob_qsync_lock.h" +#include "lib/restore/ob_storage_info.h" +#include "observer/omt/ob_tenant_config_mgr.h" namespace oceanbase { @@ -24,6 +26,33 @@ namespace common { class ObObjectStorageInfo; +class ObTenantStsCredentialMgr : public ObTenantStsCredentialBaseMgr +{ +public: + ObTenantStsCredentialMgr() {} + virtual ~ObTenantStsCredentialMgr() {} + virtual int get_sts_credential(char *sts_credential, const int64_t sts_credential_buf_len) override; + virtual int check_sts_credential(omt::ObTenantConfigGuard &tenant_config) const; + static ObTenantStsCredentialBaseMgr &get_instance() + { + static ObTenantStsCredentialMgr mgr; + return mgr; + } + const static int64_t LOG_INTERVAL_US = 5 * 1000 * 1000; // 5s +}; + +class ObClusterVersionMgr: public ObClusterVersionBaseMgr +{ +public: + ObClusterVersionMgr() {} + virtual ~ObClusterVersionMgr() {} + virtual int is_supported_assume_version() const override; + static ObClusterVersionMgr &get_instance() + { + static ObClusterVersionMgr mgr; + return mgr; + } +}; class ObDeviceManager { @@ -33,6 +62,10 @@ public: void destroy(); static ObDeviceManager &get_instance(); + int get_device_key(const common::ObString &storage_info, + const common::ObString &storage_type_prefix, + char *device_key, + const int64_t device_key_len) const; /*for object device, will return a new object to caller*/ /*ofs/local will share in upper logical*/ // 1. ObObjectStorageInfo is a member of ObObjectDevice, which is used for accessing object storage. diff --git a/src/share/ob_errno.def b/src/share/ob_errno.def index a55e64c60..6e5fb66da 100755 --- a/src/share/ob_errno.def +++ b/src/share/ob_errno.def @@ -286,7 +286,7 @@ DEFINE_ERROR(OB_ABORT_MAJOR_FREEZE_FAILED, -4212, -1, "HY000", "abort major free DEFINE_ERROR_EXT_DEP(OB_MAJOR_FREEZE_NOT_FINISHED, -4213, -1, "HY000", "last major freeze not finish", "%s"); DEFINE_ERROR(OB_PARTITION_NOT_LEADER, -4214, -1, "HY000", "partition is not leader partition"); DEFINE_ERROR(OB_WAIT_MAJOR_FREEZE_RESPONSE_TIMEOUT, -4215, -1, "HY000", "wait major freeze response timeout"); -DEFINE_ERROR(OB_CURL_ERROR, -4216, -1, "HY000", "curl error"); +DEFINE_ERROR_DEP(OB_CURL_ERROR, -4216, -1, "HY000", "curl error"); DEFINE_ERROR_EXT(OB_MAJOR_FREEZE_NOT_ALLOW, -4217, -1, "HY000", "Major freeze not allowed now", "%s"); DEFINE_ERROR(OB_PREPARE_FREEZE_FAILED, -4218, -1, "HY000", "prepare freeze failed"); DEFINE_ORACLE_ERROR_EXT_DEP(OB_INVALID_DATE_VALUE, -4219, ER_TRUNCATED_WRONG_VALUE, "22007", "Incorrect value", "Incorrect datetime value: '%.*s' for column '%s'", 1861, "literal does not match format string", "literal does not match format string: '%.*s' for column '%s'"); diff --git a/src/share/ob_errno.h b/src/share/ob_errno.h index 433931f16..1f455fe8b 100644 --- a/src/share/ob_errno.h +++ b/src/share/ob_errno.h @@ -106,7 +106,6 @@ constexpr int OB_COMMIT_MAJOR_FREEZE_FAILED = -4211; constexpr int OB_ABORT_MAJOR_FREEZE_FAILED = -4212; constexpr int OB_PARTITION_NOT_LEADER = -4214; constexpr int OB_WAIT_MAJOR_FREEZE_RESPONSE_TIMEOUT = -4215; -constexpr int OB_CURL_ERROR = -4216; constexpr int OB_MAJOR_FREEZE_NOT_ALLOW = -4217; constexpr int OB_PREPARE_FREEZE_FAILED = -4218; constexpr int OB_PARTITION_NOT_EXIST = -4225; diff --git a/src/share/parameter/ob_parameter_seed.ipp b/src/share/parameter/ob_parameter_seed.ipp index 18b05d999..905bb95e3 100644 --- a/src/share/parameter/ob_parameter_seed.ipp +++ b/src/share/parameter/ob_parameter_seed.ipp @@ -1467,6 +1467,11 @@ DEF_BOOL(plsql_debug, OB_TENANT_PARAMETER, "False", DEF_BOOL(plsql_v2_compatibility, OB_TENANT_PARAMETER, "False", "allows to control store routine compile action at DDL stage", ObParameterAttr(Section::OBSERVER, Source::DEFAULT, EditLevel::DYNAMIC_EFFECTIVE)); +DEF_STR_WITH_CHECKER(sts_credential, OB_TENANT_PARAMETER, "", + common::ObConfigSTScredentialChecker, + "STS credential for object storage, " + "values: sts_url=xxx&sts_ak=xxx&sts_sk=xxx", + ObParameterAttr(Section::OBSERVER, Source::DEFAULT, EditLevel::DYNAMIC_EFFECTIVE)); // for bloom filter DEF_BOOL(_bloom_filter_enabled, OB_TENANT_PARAMETER, "True", "enable join bloom filter", diff --git a/src/share/restore/ob_physical_restore_info.cpp b/src/share/restore/ob_physical_restore_info.cpp index 45c31d482..7ad8a99bb 100644 --- a/src/share/restore/ob_physical_restore_info.cpp +++ b/src/share/restore/ob_physical_restore_info.cpp @@ -331,6 +331,8 @@ int ObPhysicalRestoreJob::assign(const ObPhysicalRestoreJob &other) LOG_WARN("failed to assign path list", KR(ret), K(other)); } else if (OB_FAIL(white_list_.assign(other.white_list_))) { LOG_WARN("failed to assign white list", KR(ret), K(other)); + } else if (OB_FAIL(deep_copy_ob_string(allocator_, other.sts_credential_, sts_credential_))) { + LOG_WARN("failed to copy string", KR(ret), K(other)); } } @@ -374,6 +376,7 @@ void ObPhysicalRestoreJob::reset() recover_table_ = false; using_complement_log_ = false; + sts_credential_.reset(); passwd_array_.reset(); multi_restore_path_list_.reset(); diff --git a/src/share/restore/ob_physical_restore_info.h b/src/share/restore/ob_physical_restore_info.h index 37fe12fff..fab95316a 100644 --- a/src/share/restore/ob_physical_restore_info.h +++ b/src/share/restore/ob_physical_restore_info.h @@ -191,6 +191,8 @@ public: Property_declare_int(bool, using_complement_log) Property_declare_int(int64_t, backup_compatible) + //for sts + Property_declare_ObString(sts_credential) private: //job_id and tenant_id in __all_restore_job primary_key ObRestoreJobPersistKey restore_key_; diff --git a/src/share/restore/ob_physical_restore_table_operator.cpp b/src/share/restore/ob_physical_restore_table_operator.cpp index 72cee159c..62b721119 100644 --- a/src/share/restore/ob_physical_restore_table_operator.cpp +++ b/src/share/restore/ob_physical_restore_table_operator.cpp @@ -268,6 +268,7 @@ int ObPhysicalRestoreTableOperator::fill_dml_splicer( ADD_COLUMN_MACRO_IN_TABLE_OPERATOR(job_info, recover_table); ADD_COLUMN_MACRO_IN_TABLE_OPERATOR(job_info, using_complement_log); ADD_COLUMN_MACRO_IN_TABLE_OPERATOR(job_info, backup_compatible); + ADD_COLUMN_MACRO_IN_TABLE_OPERATOR(job_info, sts_credential); // source_cluster_version if (OB_SUCC(ret)) { @@ -525,6 +526,7 @@ int ObPhysicalRestoreTableOperator::retrieve_restore_option( RETRIEVE_STR_VALUE(kms_encrypt_key, job); RETRIEVE_INT_VALUE(concurrency, job); RETRIEVE_INT_VALUE(backup_compatible, job); + RETRIEVE_STR_VALUE(sts_credential, job); if (OB_SUCC(ret)) { if (name == "backup_dest") { diff --git a/src/sql/engine/cmd/ob_load_data_direct_impl.cpp b/src/sql/engine/cmd/ob_load_data_direct_impl.cpp index 8bc423ea8..affac7b7b 100644 --- a/src/sql/engine/cmd/ob_load_data_direct_impl.cpp +++ b/src/sql/engine/cmd/ob_load_data_direct_impl.cpp @@ -505,7 +505,6 @@ int ObLoadDataDirectImpl::DataReader::init(const DataAccessParam &data_access_pa file_read_param.file_location_ = data_access_param.file_location_; file_read_param.filename_ = data_desc.filename_; file_read_param.compression_format_ = data_access_param.compression_format_; - file_read_param.access_info_ = data_access_param.access_info_; file_read_param.packet_handle_ = nullptr; if (OB_NOT_NULL(execute_ctx.exec_ctx_.get_session_info()) && OB_NOT_NULL(execute_ctx.exec_ctx_.get_session_info()->get_pl_query_sender())) { @@ -514,7 +513,9 @@ int ObLoadDataDirectImpl::DataReader::init(const DataAccessParam &data_access_pa file_read_param.session_ = execute_ctx.exec_ctx_.get_session_info(); file_read_param.timeout_ts_ = THIS_WORKER.get_timeout_ts(); - if (OB_FAIL(ObFileReader::open(file_read_param, allocator_, file_reader_))) { + if (OB_FAIL(file_read_param.access_info_.assign(data_access_param.access_info_))) { + LOG_WARN("fail to assign access info", KR(ret), K_(data_access_param.access_info)); + } else if (OB_FAIL(ObFileReader::open(file_read_param, allocator_, file_reader_))) { LOG_WARN("failed to open file", KR(ret), K(data_desc)); } else if (file_reader_->seekable()) { @@ -833,14 +834,16 @@ int ObLoadDataDirectImpl::SimpleDataSplitUtils::split(const DataAccessParam &dat ObFileReadParam file_read_param; file_read_param.file_location_ = data_access_param.file_location_; file_read_param.filename_ = data_desc.filename_; - file_read_param.access_info_ = data_access_param.access_info_; file_read_param.compression_format_ = data_access_param.compression_format_; file_read_param.packet_handle_ = NULL; file_read_param.session_ = NULL; file_read_param.timeout_ts_ = THIS_WORKER.get_timeout_ts(); ObFileReader *file_reader = NULL; - if (OB_FAIL(ObFileReader::open(file_read_param, allocator, file_reader))) { + + if (OB_FAIL(file_read_param.access_info_.assign(data_access_param.access_info_))) { + LOG_WARN("fail to assign access info", KR(ret), K_(data_access_param.access_info)); + } else if (OB_FAIL(ObFileReader::open(file_read_param, allocator, file_reader))) { LOG_WARN("failed to open file.", KR(ret), K(data_desc)); } else if (!file_reader->seekable()) { if (OB_FAIL(data_desc_iter.add_data_desc(data_desc))) { @@ -2397,7 +2400,9 @@ int ObLoadDataDirectImpl::init_execute_param() data_access_param.file_column_num_ = field_or_var_list.count(); data_access_param.file_format_ = load_stmt_->get_data_struct_in_file(); data_access_param.file_cs_type_ = load_args.file_cs_type_; - data_access_param.access_info_ = load_args.access_info_; + if (OB_FAIL(data_access_param.access_info_.assign(load_args.access_info_))) { + LOG_WARN("fail to set access info", KR(ret)); + } data_access_param.compression_format_ = load_args.compression_format_; } // column_ids_ diff --git a/src/sql/engine/cmd/ob_load_data_impl.cpp b/src/sql/engine/cmd/ob_load_data_impl.cpp index e675b2aba..d1f3deaa6 100644 --- a/src/sql/engine/cmd/ob_load_data_impl.cpp +++ b/src/sql/engine/cmd/ob_load_data_impl.cpp @@ -2804,13 +2804,15 @@ int ObLoadDataSPImpl::ToolBox::init(ObExecContext &ctx, ObLoadDataStmt &load_stm file_read_param.file_location_ = load_file_storage; // file_read_param.filename_ = load_args.file_name_; file_read_param.compression_format_ = load_args.compression_format_; - file_read_param.access_info_ = load_args.access_info_; file_read_param.packet_handle_ = nullptr; if (OB_NOT_NULL(ctx.get_my_session()) && OB_NOT_NULL(ctx.get_my_session()->get_pl_query_sender())) { file_read_param.packet_handle_ = &ctx.get_my_session()->get_pl_query_sender()->get_packet_sender(); } file_read_param.session_ = ctx.get_my_session(); file_read_param.timeout_ts_ = THIS_WORKER.get_timeout_ts(); + if (OB_FAIL(file_read_param.access_info_.assign(load_args.access_info_))) { + LOG_WARN("fail to assign access info", K(ret), K(load_args.access_info_)); + } } OZ (init_file_size(ctx)); diff --git a/src/sql/parser/non_reserved_keywords_mysql_mode.c b/src/sql/parser/non_reserved_keywords_mysql_mode.c index 0219e96b6..36889b6ed 100644 --- a/src/sql/parser/non_reserved_keywords_mysql_mode.c +++ b/src/sql/parser/non_reserved_keywords_mysql_mode.c @@ -1094,7 +1094,8 @@ static const NonReservedKeyword Mysql_none_reserved_keywords[] = {"rb_and_agg", RB_AND_AGG}, {"rb_iterate", RB_ITERATE}, {"optimizer_costs", OPTIMIZER_COSTS}, - {"micro_index_clustered", MICRO_INDEX_CLUSTERED} + {"micro_index_clustered", MICRO_INDEX_CLUSTERED}, + {"tenant_sts_credential", TENANT_STS_CREDENTIAL} }; /** https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-prepared-statements.html diff --git a/src/sql/parser/sql_parser_mysql_mode.y b/src/sql/parser/sql_parser_mysql_mode.y index f520cc4be..026b9c1ca 100644 --- a/src/sql/parser/sql_parser_mysql_mode.y +++ b/src/sql/parser/sql_parser_mysql_mode.y @@ -365,7 +365,7 @@ END_P SET_VAR DELIMITER TEMPLATE TEMPORARY TEMPTABLE TENANT TEXT THAN TIME TIMESTAMP TIMESTAMPADD TIMESTAMPDIFF TP_NO TP_NAME TRACE TRADITIONAL TRANSACTION TRIGGERS TRIM TRUNCATE TYPE TYPES TASK TABLET_SIZE TABLEGROUP_ID TENANT_ID THROTTLE TIME_ZONE_INFO TOP_K_FRE_HIST TIMES TRIM_SPACE TTL - TRANSFER + TRANSFER TENANT_STS_CREDENTIAL UNCOMMITTED UNCONDITIONAL UNDEFINED UNDO_BUFFER_SIZE UNDOFILE UNICODE UNINSTALL UNIT UNIT_GROUP UNIT_NUM UNLOCKED UNTIL UNUSUAL UPGRADE USE_BLOOM_FILTER UNKNOWN USE_FRM USER USER_RESOURCES UNBOUNDED UP UNLIMITED USER_SPECIFIED @@ -531,8 +531,9 @@ END_P SET_VAR DELIMITER %type permanent_tablespace permanent_tablespace_options permanent_tablespace_option alter_tablespace_actions alter_tablespace_action alter_tablespace_options opt_force_purge %type opt_tablespace_option opt_tablespace_options opt_tablespace_engine opt_alter_tablespace_option opt_alter_tablespace_options %type opt_sql_throttle_for_priority opt_sql_throttle_using_cond sql_throttle_one_or_more_metrics sql_throttle_metric -%type opt_copy_id opt_backup_dest opt_backup_backup_dest opt_tenant_info opt_with_active_piece get_format_unit opt_backup_tenant_list opt_backup_to opt_description policy_name opt_recovery_window opt_redundancy opt_backup_copies opt_restore_until opt_backup_key_info opt_encrypt_key +%type opt_copy_id opt_backup_dest opt_backup_backup_dest opt_tenant_info opt_with_active_piece get_format_unit opt_backup_tenant_list opt_backup_to opt_description policy_name opt_recovery_window opt_redundancy opt_backup_copies opt_restore_until opt_encrypt_key %type opt_recover_tenant recover_table_list recover_table_relation_name restore_remap_list remap_relation_name table_relation_name opt_recover_remap_item_list restore_remap_item_list restore_remap_item remap_item remap_table_val opt_tenant +%type opt_restore_with_config_list restore_with_config_list restore_with_config restore_with_item %type new_or_old new_or_old_column_ref diagnostics_info_ref %type on_empty on_error json_on_response opt_returning_type opt_on_empty_or_error json_value_expr opt_ascii opt_truncate_clause %type json_extract_unquote_expr json_extract_expr json_query_expr opt_multivalue opt_asis opt_array opt_pretty opt_wrapper opt_scalars opt_query_on_error_or_empty_or_mismatch on_empty_query on_error_query on_mismatch_query opt_response_query @@ -18996,7 +18997,7 @@ alter_with_opt_hint SYSTEM CLEAR RESTORE SOURCE malloc_terminal_node($$, result->malloc_pool_, T_CLEAR_RESTORE_SOURCE); } | -alter_with_opt_hint SYSTEM RECOVER TABLE { result->is_for_remap_ = 1; } recover_table_list opt_recover_tenant opt_backup_dest opt_restore_until WITH STRING_VALUE opt_encrypt_key opt_backup_key_info opt_recover_remap_item_list opt_description +alter_with_opt_hint SYSTEM RECOVER TABLE { result->is_for_remap_ = 1; } recover_table_list opt_recover_tenant opt_backup_dest opt_restore_until WITH STRING_VALUE opt_encrypt_key opt_restore_with_config_list opt_recover_remap_item_list opt_description { (void)($1); ParseNode *tables = NULL; @@ -19010,7 +19011,7 @@ alter_with_opt_hint SYSTEM RESTORE FROM STRING_VALUE opt_restore_until PREVIEW malloc_non_terminal_node($$, result->malloc_pool_, T_PHYSICAL_RESTORE_TENANT, 2, $5, $6); } | -alter_with_opt_hint SYSTEM RESTORE relation_name opt_backup_dest opt_restore_until WITH STRING_VALUE opt_encrypt_key opt_backup_key_info opt_description +alter_with_opt_hint SYSTEM RESTORE relation_name opt_backup_dest opt_restore_until WITH STRING_VALUE opt_encrypt_key opt_restore_with_config_list opt_description { (void)($1); malloc_non_terminal_node($$, result->malloc_pool_, T_PHYSICAL_RESTORE_TENANT, 7, $4, $5, $6, $8, $9, $10, $11); @@ -22252,12 +22253,45 @@ opt_restore_until: } ; -opt_backup_key_info: -/*EMPTY*/ { $$ = NULL; } -| WITH KEY FROM STRING_VALUE opt_encrypt_key +opt_restore_with_config_list: +/*empty*/ { - result->contain_sensitive_data_ = true; - malloc_non_terminal_node($$, result->malloc_pool_, T_BACKUP_KEY, 2, $4, $5); + $$ = NULL; +} +| restore_with_config_list +{ + ParseNode *with_items = NULL; + merge_nodes(with_items, result, T_RESTORE_WITH_CONFIG_LIST, $1); + $$ = with_items; +} +; + +restore_with_config_list: +restore_with_config +{ + $$ = $1; +} +| restore_with_config_list restore_with_config +{ + malloc_non_terminal_node($$, result->malloc_pool_, T_LINK_NODE, 2, $1, $2); +} +; + +restore_with_config: +WITH restore_with_item +{ + $$ = $2; +} +; + +restore_with_item: +KEY FROM STRING_VALUE opt_encrypt_key +{ + malloc_non_terminal_node($$, result->malloc_pool_, T_BACKUP_KEY, 2, $3, $4); +} +| TENANT_STS_CREDENTIAL STRING_VALUE +{ + malloc_non_terminal_node($$, result->malloc_pool_, T_STS_CREDENTIAL, 1, $2); } ; @@ -24478,6 +24512,7 @@ ACCESS_INFO | OVERWRITE | OPTIMIZER_COSTS | MICRO_INDEX_CLUSTERED +| TENANT_STS_CREDENTIAL ; unreserved_keyword_special: diff --git a/src/sql/resolver/cmd/ob_alter_system_resolver.cpp b/src/sql/resolver/cmd/ob_alter_system_resolver.cpp index 5801c7f45..dea8b6f88 100644 --- a/src/sql/resolver/cmd/ob_alter_system_resolver.cpp +++ b/src/sql/resolver/cmd/ob_alter_system_resolver.cpp @@ -3913,22 +3913,9 @@ int ObPhysicalRestoreTenantResolver::resolve(const ParseNode &parse_tree) && OB_FAIL(Util::resolve_string(parse_tree.children_[4], stmt->get_rpc_arg().encrypt_key_))) { LOG_WARN("failed to resolve encrypt key", K(ret)); - } else if (OB_NOT_NULL(parse_tree.children_[5])) { - ParseNode *kms_node = parse_tree.children_[5]; - if (2 != kms_node->num_child_) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("num of children not match", K(ret), "child_num", kms_node->num_child_); - } else if (OB_ISNULL(kms_node->children_[0])) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("kms uri should not be NULL", K(ret)); - } else if (OB_FAIL(Util::resolve_string(kms_node->children_[0], - stmt->get_rpc_arg().kms_uri_))) { - LOG_WARN("failed to resolve kms uri", K(ret)); - } else if (OB_NOT_NULL(kms_node->children_[1]) - && OB_FAIL(Util::resolve_string(kms_node->children_[1], - stmt->get_rpc_arg().kms_encrypt_key_))) { - LOG_WARN("failed to resolve kms encrypt key", K(ret)); - } + } else if (OB_NOT_NULL(parse_tree.children_[5]) + && OB_FAIL(resolve_restore_with_config_item(parse_tree.children_[5], stmt->get_rpc_arg()))) { + LOG_WARN("fail to resolve config item", K(ret)); } ParseNode *description_node = parse_tree.children_[6]; @@ -3972,6 +3959,50 @@ int ObPhysicalRestoreTenantResolver::resolve(const ParseNode &parse_tree) return ret; } +int ObPhysicalRestoreTenantResolver::resolve_restore_with_config_item(const ParseNode *node, obrpc::ObPhysicalRestoreTenantArg &arg) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(node)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("node must not be null", K(ret)); + } else if (T_RESTORE_WITH_CONFIG_LIST != node->type_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid nde type", K(ret), "node_type", node->type_); + } else if (node->num_child_ > 2) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("children num not match", K(ret), "num_child", node->num_child_); + } else { + for (int64_t i = 0; i < node->num_child_ && OB_SUCC(ret); i++) { + const ParseNode *child_node = node->children_[i]; + if (OB_ISNULL(child_node)) { + } else if (T_BACKUP_KEY == child_node->type_) { + if (2 != child_node->num_child_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("num of children not match", K(ret), "child_num", child_node->num_child_); + } else if (OB_ISNULL(child_node->children_[0])) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("kms uri should not be NULL", K(ret)); + } else if (OB_FAIL(Util::resolve_string(child_node->children_[0], + arg.kms_uri_))) { + LOG_WARN("failed to resolve kms uri", K(ret)); + } else if (OB_NOT_NULL(child_node->children_[1]) + && OB_FAIL(Util::resolve_string(child_node->children_[1], + arg.kms_encrypt_key_))) { + LOG_WARN("failed to resolve kms encrypt key", K(ret)); + } + } else if (T_STS_CREDENTIAL == child_node->type_) { + if (1 != child_node->num_child_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("num of children not match", K(ret), "child_num", child_node->num_child_); + } else if (OB_FAIL(Util::resolve_string(child_node->children_[0], arg.sts_credential_))) { + LOG_WARN("fail to resolve string", K(ret)); + } + } + } + } + return ret; +} + #ifdef OB_BUILD_TDE_SECURITY int ObPhysicalRestoreTenantResolver::resolve_kms_encrypt_info(common::ObString store_option) { @@ -6052,22 +6083,9 @@ int ObRecoverTableResolver::resolve(const ParseNode &parse_tree) } else if (OB_NOT_NULL(parse_tree.children_[5]) && OB_FAIL(Util::resolve_string(parse_tree.children_[5], stmt->get_rpc_arg().restore_tenant_arg_.encrypt_key_))) { LOG_WARN("failed to resolve encrypt key", K(ret)); - } else if (OB_NOT_NULL(parse_tree.children_[6])) { - ParseNode *kms_node = parse_tree.children_[6]; - if (2 != kms_node->num_child_) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("num of children not match", K(ret), "child_num", kms_node->num_child_); - } else if (OB_ISNULL(kms_node->children_[0])) { - ret = OB_ERR_UNEXPECTED; - LOG_WARN("kms uri should not be NULL", K(ret)); - } else if (OB_FAIL(Util::resolve_string(kms_node->children_[0], - stmt->get_rpc_arg().restore_tenant_arg_.kms_uri_))) { - LOG_WARN("failed to resolve kms uri", K(ret)); - } else if (OB_NOT_NULL(kms_node->children_[1]) - && OB_FAIL(Util::resolve_string(kms_node->children_[1], - stmt->get_rpc_arg().restore_tenant_arg_.kms_encrypt_key_))) { - LOG_WARN("failed to resolve kms encrypt key", K(ret)); - } + } else if (OB_NOT_NULL(parse_tree.children_[6]) + && OB_FAIL(resolve_restore_with_config_item_(parse_tree.children_[6], stmt->get_rpc_arg()))){ + LOG_WARN("fail to resolve restore_with config item", K(ret)); } if (OB_FAIL(ret)) { @@ -6623,6 +6641,50 @@ int ObRecoverTableResolver::resolve_tenant_( return ret; } +int ObRecoverTableResolver::resolve_restore_with_config_item_(const ParseNode *node, obrpc::ObRecoverTableArg &arg) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(node)) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("node must not be null", K(ret)); + } else if (T_RESTORE_WITH_CONFIG_LIST != node->type_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("invalid nde type", K(ret), "node_type", node->type_); + } else if (node->num_child_ > 2) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("children num not match", K(ret), "num_child", node->num_child_); + } else { + for (int64_t i = 0; i < node->num_child_ && OB_SUCC(ret); i++) { + const ParseNode *child_node = node->children_[i]; + if (OB_ISNULL(child_node)) { + } else if (T_BACKUP_KEY == child_node->type_) { + if (2 != child_node->num_child_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("num of children not match", K(ret), "child_num", child_node->num_child_); + } else if (OB_ISNULL(child_node->children_[0])) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("kms uri should not be NULL", K(ret)); + } else if (OB_FAIL(Util::resolve_string(child_node->children_[0], + arg.restore_tenant_arg_.kms_uri_))) { + LOG_WARN("failed to resolve kms uri", K(ret)); + } else if (OB_NOT_NULL(child_node->children_[1]) + && OB_FAIL(Util::resolve_string(child_node->children_[1], + arg.restore_tenant_arg_.kms_encrypt_key_))) { + LOG_WARN("failed to resolve kms encrypt key", K(ret)); + } + } else if (T_STS_CREDENTIAL == child_node->type_) { + if (1 != child_node->num_child_) { + ret = OB_ERR_UNEXPECTED; + LOG_WARN("num of children not match", K(ret), "child_num", child_node->num_child_); + } else if (OB_FAIL(Util::resolve_string(child_node->children_[0], arg.restore_tenant_arg_.sts_credential_))) { + LOG_WARN("fail to resolve string", K(ret)); + } + } + } + } + return ret; +} + int ObRecoverTenantResolver::resolve(const ParseNode &parse_tree) { int ret = OB_SUCCESS; diff --git a/src/sql/resolver/cmd/ob_alter_system_resolver.h b/src/sql/resolver/cmd/ob_alter_system_resolver.h index a9e6da562..0081716b8 100644 --- a/src/sql/resolver/cmd/ob_alter_system_resolver.h +++ b/src/sql/resolver/cmd/ob_alter_system_resolver.h @@ -208,6 +208,7 @@ class ObPhysicalRestoreTenantResolver : public ObSystemCmdResolver #endif int resolve_decryption_passwd(obrpc::ObPhysicalRestoreTenantArg &arg); int resolve_restore_source_array(obrpc::ObPhysicalRestoreTenantArg &arg); + int resolve_restore_with_config_item(const ParseNode *node, obrpc::ObPhysicalRestoreTenantArg &arg); }; class ObRecoverTenantResolver : public ObSystemCmdResolver @@ -341,6 +342,7 @@ private: #endif int resolve_backup_set_pwd_(common::ObString &pwd); int resolve_restore_source_(common::ObString &restore_source); + int resolve_restore_with_config_item_(const ParseNode *node, obrpc::ObRecoverTableArg &arg); }; DEF_SIMPLE_CMD_RESOLVER(ObTableTTLResolver); diff --git a/src/sql/resolver/cmd/ob_load_data_stmt.h b/src/sql/resolver/cmd/ob_load_data_stmt.h index d81cd3ff1..6dd5b8ab3 100644 --- a/src/sql/resolver/cmd/ob_load_data_stmt.h +++ b/src/sql/resolver/cmd/ob_load_data_stmt.h @@ -88,14 +88,14 @@ struct ObLoadArgument K_(file_iter), K_(compression_format)); - void assign(const ObLoadArgument &other) { + int assign(const ObLoadArgument &other) { + int ret = OB_SUCCESS; load_file_storage_ = other.load_file_storage_; is_default_charset_ = other.is_default_charset_; ignore_rows_ = other.ignore_rows_; dupl_action_ = other.dupl_action_; file_cs_type_ = other.file_cs_type_; file_name_ = other.file_name_; - access_info_ = other.access_info_; database_name_ = other.database_name_; table_name_ = other.table_name_; combined_name_ = other.combined_name_; @@ -106,6 +106,10 @@ struct ObLoadArgument part_level_ = other.part_level_; file_iter_.copy(other.file_iter_); compression_format_ = other.compression_format_; + if (OB_FAIL(access_info_.assign(other.access_info_))) { + OB_LOG(WARN, "fail to assign access info", K(ret), K_(other.access_info)); + } + return ret; } ObLoadFileLocation load_file_storage_; diff --git a/tools/deploy/mysql_test/test_suite/inner_table/r/mysql/all_virtual_sys_parameter_stat.result b/tools/deploy/mysql_test/test_suite/inner_table/r/mysql/all_virtual_sys_parameter_stat.result index ad88723b4..fe0d61046 100644 --- a/tools/deploy/mysql_test/test_suite/inner_table/r/mysql/all_virtual_sys_parameter_stat.result +++ b/tools/deploy/mysql_test/test_suite/inner_table/r/mysql/all_virtual_sys_parameter_stat.result @@ -258,6 +258,7 @@ standby_fetch_log_bandwidth_limit storage_meta_cache_priority storage_rowsets_size strict_check_os_params +sts_credential sync_io_thread_count syslog_compress_func syslog_disk_size diff --git a/tools/ob_admin/io_bench/ob_admin_io_adapter_bench.cpp b/tools/ob_admin/io_bench/ob_admin_io_adapter_bench.cpp index 8332187b2..6c7c7b743 100644 --- a/tools/ob_admin/io_bench/ob_admin_io_adapter_bench.cpp +++ b/tools/ob_admin/io_bench/ob_admin_io_adapter_bench.cpp @@ -74,7 +74,7 @@ int ObAdminIOAdapterBenchmarkExecutor::parse_cmd_(int argc, char *argv[]) int ret = OB_SUCCESS; int opt = 0; int index = -1; - const char *opt_str = "h:d:s:t:r:l:o:n:f:p:b:c:j:e:"; + const char *opt_str = "h:d:s:t:r:l:o:n:f:p:b:c:j:e:i:"; struct option longopts[] = {{"help", 0, NULL, 'h'}, {"file-path-prefix", 1, NULL, 'd'}, {"storage-info", 1, NULL, 's'}, @@ -89,6 +89,7 @@ int ObAdminIOAdapterBenchmarkExecutor::parse_cmd_(int argc, char *argv[]) {"clean-before-execution", 0, NULL, 'b'}, {"clean-after-execution", 0, NULL, 'c'}, {"s3_url_encode_type", 0, NULL, 'e'}, + {"sts_credential", 0, NULL, 'i'}, {NULL, 0, NULL, 0}}; while (OB_SUCC(ret) && -1 != (opt = getopt_long(argc, argv, opt_str, longopts, &index))) { switch (opt) { @@ -188,6 +189,12 @@ int ObAdminIOAdapterBenchmarkExecutor::parse_cmd_(int argc, char *argv[]) } break; } + case 'i': { + if (OB_FAIL(set_sts_credential_key(optarg))) { + STORAGE_LOG(WARN, "failed to set sts credential", KR(ret)); + } + break; + } default: { print_usage_(); exit(1); @@ -333,6 +340,9 @@ int ObAdminIOAdapterBenchmarkExecutor::print_usage_() printf("\tob_admin bench_io_adapter -d's3://home/admin/backup_info' " "-s'host=xxx.com&access_id=111&access_key=222®ion=333'\t" "-e'compliantRfc3986Encoding'"); + printf("\tob_admin bench_io_adapter -d's3://home/admin/backup_info' " + "-s'host=xxx.com&role_arn=111®ion=333'\t" + "-i'sts_url=xxx&sts_ak=xxx&sts_sk=xxx'"); return ret; } diff --git a/tools/ob_admin/ob_admin_executor.cpp b/tools/ob_admin/ob_admin_executor.cpp index d1fca19fe..d2b4ed721 100644 --- a/tools/ob_admin/ob_admin_executor.cpp +++ b/tools/ob_admin/ob_admin_executor.cpp @@ -44,6 +44,7 @@ ObAdminExecutor::ObAdminExecutor() // 设置MTL上下文 mock_server_tenant_.set(&blocksstable::ObDecodeResourcePool::get_instance()); share::ObTenantEnv::set_tenant(&mock_server_tenant_); + omt::ObTenantConfigMgr::get_instance().add_tenant_config(OB_SYS_TENANT_ID); storage_env_.data_dir_ = data_dir_; storage_env_.sstable_dir_ = sstable_dir_; @@ -215,6 +216,28 @@ int ObAdminExecutor::set_s3_url_encode_type(const char *type_str) const } return ret; } +int ObAdminExecutor::set_sts_credential_key(const char *sts_credential) +{ + int ret = OB_SUCCESS; + if (OB_ISNULL(sts_credential)) { + ret = OB_INVALID_ARGUMENT; + STORAGE_LOG(WARN, "sts credential is null", KR(ret), KP(sts_credential)); + } else { + if (OB_FAIL(ObDeviceManager::get_instance().init_devices_env())) { + STORAGE_LOG(WARN, "fail to init device env", KR(ret)); + } else { + omt::ObTenantConfigGuard tenant_config(TENANT_CONF(OB_SYS_TENANT_ID)); + if (OB_UNLIKELY(!tenant_config.is_valid())) { + ret = OB_ERR_UNEXPECTED; + STORAGE_LOG( + WARN, "tenant config is invalid", KR(ret), K(OB_SYS_TENANT_ID)); + } else { + tenant_config->sts_credential = sts_credential; + } + } + } + return ret; +} } } diff --git a/tools/ob_admin/ob_admin_executor.h b/tools/ob_admin/ob_admin_executor.h index f358835c4..f4d63e166 100644 --- a/tools/ob_admin/ob_admin_executor.h +++ b/tools/ob_admin/ob_admin_executor.h @@ -45,6 +45,7 @@ protected: int prepare_decoder(); int load_config(); int set_s3_url_encode_type(const char *type_str) const; + int set_sts_credential_key(const char *sts_credential); protected: share::ObTenantBase mock_server_tenant_; diff --git a/unittest/share/test_storage_device_manager.cpp b/unittest/share/test_storage_device_manager.cpp index f2cb9b7be..d544c5fe9 100644 --- a/unittest/share/test_storage_device_manager.cpp +++ b/unittest/share/test_storage_device_manager.cpp @@ -193,7 +193,7 @@ TEST_F(TestDeviceManager, test_device_manager) device_num = manager.get_device_cnt(); ASSERT_EQ(1, device_num); //since we do not release automatic - //MAX_DEVICE_INSTANCE different deivce + //MAX_DEVICE_INSTANCE different deivce for (int i = 0; i < max_dev_num; i++ ) { ObObjectStorageInfo tmp_storage_info; tmp_storage_info.device_type_ = ObStorageType::OB_STORAGE_OSS;