Coverage Report

Created: 2026-07-17 01:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/util/s3_util.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "util/s3_util.h"
19
20
#include <aws/core/auth/AWSAuthSigner.h>
21
#include <aws/core/auth/AWSCredentials.h>
22
#include <aws/core/auth/AWSCredentialsProviderChain.h>
23
#include <aws/core/auth/STSCredentialsProvider.h>
24
#include <aws/core/client/DefaultRetryStrategy.h>
25
#include <aws/core/platform/Environment.h>
26
#include <aws/core/utils/logging/LogLevel.h>
27
#include <aws/core/utils/logging/LogSystemInterface.h>
28
#include <aws/core/utils/memory/stl/AWSStringStream.h>
29
#include <aws/identity-management/auth/STSAssumeRoleCredentialsProvider.h>
30
#include <aws/s3/S3Client.h>
31
#include <aws/sts/STSClient.h>
32
#include <bvar/reducer.h>
33
#include <cpp/token_bucket_rate_limiter.h>
34
35
#include <atomic>
36
#include <charconv>
37
#include <limits>
38
#include <mutex>
39
40
#include "util/cpu_info.h"
41
#include "util/string_util.h"
42
43
#ifdef USE_AZURE
44
#include <azure/core/diagnostics/logger.hpp>
45
#include <azure/core/http/curl_transport.hpp>
46
#include <azure/storage/blobs/blob_container_client.hpp>
47
#endif
48
#include <cstdlib>
49
#include <filesystem>
50
#include <fstream>
51
#include <functional>
52
#include <memory>
53
#include <ostream>
54
#include <utility>
55
56
#include "cloud/config.h"
57
#include "common/config.h"
58
#include "common/logging.h"
59
#include "common/status.h"
60
#include "cpp/aws_logger.h"
61
#include "cpp/custom_aws_credentials_provider_chain.h"
62
#include "cpp/obj_retry_strategy.h"
63
#include "cpp/sync_point.h"
64
#include "cpp/util.h"
65
#ifdef USE_AZURE
66
#include "io/fs/azure_obj_storage_client.h"
67
#endif
68
#include "exec/scan/scanner_scheduler.h"
69
#include "io/fs/obj_storage_client.h"
70
#include "io/fs/s3_obj_storage_client.h"
71
#include "runtime/exec_env.h"
72
#include "util/s3_uri.h"
73
74
namespace doris {
75
namespace s3_bvar {
76
bvar::LatencyRecorder s3_get_latency("s3_get");
77
bvar::LatencyRecorder s3_put_latency("s3_put");
78
bvar::LatencyRecorder s3_delete_object_latency("s3_delete_object");
79
bvar::LatencyRecorder s3_delete_objects_latency("s3_delete_objects");
80
bvar::LatencyRecorder s3_head_latency("s3_head");
81
bvar::LatencyRecorder s3_multi_part_upload_latency("s3_multi_part_upload");
82
bvar::LatencyRecorder s3_list_latency("s3_list");
83
bvar::LatencyRecorder s3_list_object_versions_latency("s3_list_object_versions");
84
bvar::LatencyRecorder s3_get_bucket_version_latency("s3_get_bucket_version");
85
bvar::LatencyRecorder s3_copy_object_latency("s3_copy_object");
86
}; // namespace s3_bvar
87
88
namespace {
89
90
34
doris::Status is_s3_conf_valid(const S3ClientConf& conf) {
91
34
    if (conf.endpoint.empty()) {
92
0
        return Status::InvalidArgument<false>("Invalid s3 conf, empty endpoint");
93
0
    }
94
34
    if (conf.region.empty()) {
95
0
        return Status::InvalidArgument<false>("Invalid s3 conf, empty region");
96
0
    }
97
98
34
    if (conf.role_arn.empty()) {
99
        // Allow anonymous access when both ak and sk are empty
100
28
        bool hasAk = !conf.ak.empty();
101
28
        bool hasSk = !conf.sk.empty();
102
103
        // Either both credentials are provided or both are empty (anonymous access)
104
28
        if (hasAk && conf.sk.empty()) {
105
1
            return Status::InvalidArgument<false>("Invalid s3 conf, empty sk");
106
1
        }
107
27
        if (hasSk && conf.ak.empty()) {
108
1
            return Status::InvalidArgument<false>("Invalid s3 conf, empty ak");
109
1
        }
110
27
    }
111
32
    return Status::OK();
112
34
}
113
114
// Return true is convert `str` to int successfully
115
0
bool to_int(std::string_view str, int& res) {
116
0
    auto [_, ec] = std::from_chars(str.data(), str.data() + str.size(), res);
117
0
    return ec == std::errc {};
118
0
}
119
120
#ifdef USE_AZURE
121
0
std::string env_or_empty(const char* env_name) {
122
0
    if (const char* value = std::getenv(env_name); value != nullptr) {
123
0
        return value;
124
0
    }
125
0
    return "";
126
0
}
127
128
0
std::string build_azure_tls_debug_context(const std::string& selected_ca_file) {
129
0
    bool selected_ca_exists = false;
130
0
    bool selected_ca_readable = false;
131
0
    if (!selected_ca_file.empty()) {
132
0
        std::error_code ec;
133
0
        selected_ca_exists = std::filesystem::exists(selected_ca_file, ec) && !ec;
134
0
        std::ifstream input(selected_ca_file);
135
0
        selected_ca_readable = input.good();
136
0
    }
137
138
0
    return fmt::format(
139
0
            "tls_debug(ca_cert_file_paths='{}', selected_ca_file='{}', selected_ca_exists={}, "
140
0
            "selected_ca_readable={}, SSL_CERT_FILE='{}', CURL_CA_BUNDLE='{}', SSL_CERT_DIR='{}')",
141
0
            config::ca_cert_file_paths, selected_ca_file, selected_ca_exists, selected_ca_readable,
142
0
            env_or_empty("SSL_CERT_FILE"), env_or_empty("CURL_CA_BUNDLE"),
143
0
            env_or_empty("SSL_CERT_DIR"));
144
0
}
145
#endif
146
147
constexpr char USE_PATH_STYLE[] = "use_path_style";
148
149
constexpr char AZURE_PROVIDER_STRING[] = "AZURE";
150
constexpr char S3_PROVIDER[] = "provider";
151
constexpr char S3_AK[] = "AWS_ACCESS_KEY";
152
constexpr char S3_SK[] = "AWS_SECRET_KEY";
153
constexpr char S3_ENDPOINT[] = "AWS_ENDPOINT";
154
constexpr char S3_REGION[] = "AWS_REGION";
155
constexpr char S3_TOKEN[] = "AWS_TOKEN";
156
constexpr char S3_MAX_CONN_SIZE[] = "AWS_MAX_CONNECTIONS";
157
constexpr char S3_REQUEST_TIMEOUT_MS[] = "AWS_REQUEST_TIMEOUT_MS";
158
constexpr char S3_CONN_TIMEOUT_MS[] = "AWS_CONNECTION_TIMEOUT_MS";
159
constexpr char S3_NEED_OVERRIDE_ENDPOINT[] = "AWS_NEED_OVERRIDE_ENDPOINT";
160
161
constexpr char S3_ROLE_ARN[] = "AWS_ROLE_ARN";
162
constexpr char S3_EXTERNAL_ID[] = "AWS_EXTERNAL_ID";
163
constexpr char S3_CREDENTIALS_PROVIDER_TYPE[] = "AWS_CREDENTIALS_PROVIDER_TYPE";
164
165
#ifdef BE_TEST
166
std::atomic<int64_t> s3_rate_limiter_cpu_cores_for_test {0};
167
#endif
168
169
121
int64_t get_s3_rate_limiter_cpu_cores() {
170
121
#ifdef BE_TEST
171
121
    const int64_t cpu_cores_for_test =
172
121
            s3_rate_limiter_cpu_cores_for_test.load(std::memory_order_relaxed);
173
121
    if (cpu_cores_for_test > 0) {
174
78
        return cpu_cores_for_test;
175
78
    }
176
43
#endif
177
43
    return CpuInfo::num_cores();
178
121
}
179
180
64
int64_t get_s3_qps_per_core(S3RateLimitType type) {
181
64
    switch (type) {
182
32
    case S3RateLimitType::GET:
183
32
        return config::s3_get_qps_per_core;
184
32
    case S3RateLimitType::PUT:
185
32
        return config::s3_put_qps_per_core;
186
0
    default:
187
0
        CHECK(false) << "unknown S3 rate limiter type: " << to_string(type);
188
0
        return -1;
189
64
    }
190
64
}
191
192
64
int64_t get_s3_bytes_per_second_per_core(S3RateLimitType type) {
193
64
    switch (type) {
194
32
    case S3RateLimitType::GET:
195
32
        return config::s3_get_bytes_per_second_per_core;
196
32
    case S3RateLimitType::PUT:
197
32
        return config::s3_put_bytes_per_second_per_core;
198
0
    default:
199
0
        CHECK(false) << "unknown S3 rate limiter type: " << to_string(type);
200
0
        return -1;
201
64
    }
202
64
}
203
204
41
int64_t compute_s3_rate_limit_from_core(int64_t per_core, int64_t cores, int64_t max_rate) {
205
41
    max_rate = max_rate > 0 ? max_rate : std::numeric_limits<int64_t>::max();
206
41
    if (per_core > max_rate / cores) {
207
12
        return max_rate;
208
12
    }
209
29
    return per_core * cores;
210
41
}
211
212
S3RateLimiterConfig get_effective_s3_rate_limiter_config_with_cpu_cores(S3RateLimitType type,
213
228
                                                                        int64_t cpu_cores) {
214
228
    S3RateLimiterConfig result;
215
228
    int64_t per_core;
216
228
    int64_t max_qps;
217
218
228
    switch (type) {
219
115
    case S3RateLimitType::GET:
220
115
        per_core = config::s3_get_qps_per_core;
221
115
        max_qps = config::s3_get_qps_max;
222
115
        if (per_core < 0) {
223
96
            result.token_limit = config::s3_get_token_limit;
224
96
            result.token_per_second = config::s3_get_token_per_second;
225
96
            result.bucket_tokens = config::s3_get_bucket_tokens;
226
96
        }
227
115
        break;
228
113
    case S3RateLimitType::PUT:
229
113
        per_core = config::s3_put_qps_per_core;
230
113
        max_qps = config::s3_put_qps_max;
231
113
        if (per_core < 0) {
232
106
            result.token_limit = config::s3_put_token_limit;
233
106
            result.token_per_second = config::s3_put_token_per_second;
234
106
            result.bucket_tokens = config::s3_put_bucket_tokens;
235
106
        }
236
113
        break;
237
0
    default:
238
0
        CHECK(false) << "unknown S3 rate limiter type: " << to_string(type);
239
0
        return result;
240
228
    }
241
242
228
    if (per_core >= 0) {
243
26
        result.token_per_second = compute_s3_rate_limit_from_core(per_core, cpu_cores, max_qps);
244
26
        result.bucket_tokens = result.token_per_second;
245
26
    }
246
228
    return result;
247
228
}
248
249
S3RateLimiterConfig get_effective_s3_bytes_rate_limiter_config_with_cpu_cores(S3RateLimitType type,
250
223
                                                                              int64_t cpu_cores) {
251
223
    int64_t per_core = 0;
252
223
    int64_t max_bytes_per_second = 0;
253
254
223
    switch (type) {
255
112
    case S3RateLimitType::GET:
256
112
        per_core = config::s3_get_bytes_per_second_per_core;
257
112
        max_bytes_per_second = config::s3_get_bytes_per_second_max;
258
112
        break;
259
111
    case S3RateLimitType::PUT:
260
111
        per_core = config::s3_put_bytes_per_second_per_core;
261
111
        max_bytes_per_second = config::s3_put_bytes_per_second_max;
262
111
        break;
263
0
    default:
264
0
        CHECK(false) << "unknown S3 rate limiter type: " << to_string(type);
265
223
    }
266
267
223
    S3RateLimiterConfig result;
268
223
    if (per_core > 0) {
269
15
        result.token_per_second =
270
15
                compute_s3_rate_limit_from_core(per_core, cpu_cores, max_bytes_per_second);
271
15
        result.bucket_tokens = result.token_per_second;
272
15
    }
273
223
    return result;
274
223
}
275
} // namespace
276
277
bvar::Adder<int64_t> get_bytes_rate_limit_ns("get_bytes_rate_limit_ns");
278
bvar::Adder<int64_t> get_bytes_rate_limit_exceed_req_num("get_bytes_rate_limit_exceed_req_num");
279
bvar::Adder<int64_t> put_bytes_rate_limit_ns("put_bytes_rate_limit_ns");
280
bvar::Adder<int64_t> put_bytes_rate_limit_exceed_req_num("put_bytes_rate_limit_exceed_req_num");
281
282
namespace {
283
284
enum class S3RateLimiterKind {
285
    QPS,
286
    BYTES,
287
};
288
289
struct S3RateLimiterUpdateState {
290
    S3RateLimiterConfig applied_config;
291
    std::mutex update_lock;
292
};
293
294
std::array<std::array<S3RateLimiterUpdateState, 2>, 2> update_states;
295
296
440
S3RateLimiterUpdateState& get_update_state(S3RateLimitType type, S3RateLimiterKind kind) {
297
440
    DCHECK(type == S3RateLimitType::GET || type == S3RateLimitType::PUT);
298
440
    return update_states[static_cast<size_t>(kind)][static_cast<size_t>(type)];
299
440
}
300
301
void update_s3_rate_limiter(S3ClientFactory& factory, S3RateLimitType type, S3RateLimiterKind kind,
302
128
                            int64_t cpu_cores) {
303
128
    auto& state = get_update_state(type, kind);
304
128
    std::lock_guard update_guard(state.update_lock);
305
306
128
    const int64_t per_core = kind == S3RateLimiterKind::BYTES
307
128
                                     ? get_s3_bytes_per_second_per_core(type)
308
128
                                     : get_s3_qps_per_core(type);
309
128
    const auto current =
310
128
            kind == S3RateLimiterKind::BYTES
311
128
                    ? get_effective_s3_bytes_rate_limiter_config_with_cpu_cores(type, cpu_cores)
312
128
                    : get_effective_s3_rate_limiter_config_with_cpu_cores(type, cpu_cores);
313
128
    const bool config_changed = state.applied_config.token_per_second != current.token_per_second ||
314
128
                                state.applied_config.bucket_tokens != current.bucket_tokens ||
315
128
                                state.applied_config.token_limit != current.token_limit;
316
128
    if (!config_changed) {
317
95
        return;
318
95
    }
319
320
33
    auto* limiter = kind == S3RateLimiterKind::BYTES ? factory.bytes_rate_limiter(type)
321
33
                                                     : factory.rate_limiter(type);
322
33
    const int ret =
323
33
            limiter->reset(current.token_per_second, current.bucket_tokens, current.token_limit);
324
33
    if (ret != 0) {
325
0
        LOG(WARNING) << "Failed to reset S3 " << to_string(type)
326
0
                     << (kind == S3RateLimiterKind::BYTES ? " bytes" : " QPS")
327
0
                     << " rate limiter, error code: " << ret;
328
0
        return;
329
0
    }
330
331
33
    const int64_t logged_cpu_cores = per_core > 0 ? cpu_cores : 1;
332
33
    if (kind == S3RateLimiterKind::BYTES) {
333
13
        LOG(INFO) << "Reset S3 " << to_string(type)
334
13
                  << " bytes rate limiter, bytes_per_second=" << current.token_per_second
335
13
                  << ", burst_bytes=" << current.bucket_tokens
336
13
                  << ", cpu_cores=" << logged_cpu_cores;
337
20
    } else if (per_core >= 0) {
338
16
        LOG(INFO) << "Reset S3 " << to_string(type)
339
16
                  << " QPS rate limiter, qps=" << current.token_per_second
340
16
                  << ", burst=" << current.bucket_tokens << ", limit=" << current.token_limit
341
16
                  << ", cpu_cores=" << logged_cpu_cores;
342
16
    }
343
344
33
    state.applied_config = current;
345
33
}
346
347
} // namespace
348
349
239
S3RateLimiterHolder* S3ClientFactory::rate_limiter(S3RateLimitType type) {
350
239
    CHECK(type == S3RateLimitType::GET || type == S3RateLimitType::PUT) << to_string(type);
351
239
    return _rate_limiters[static_cast<size_t>(type)].get();
352
239
}
353
354
214
S3RateLimiterHolder* S3ClientFactory::bytes_rate_limiter(S3RateLimitType type) {
355
214
    CHECK(type == S3RateLimitType::GET || type == S3RateLimitType::PUT) << to_string(type);
356
214
    return _bytes_rate_limiters[static_cast<size_t>(type)].get();
357
214
}
358
359
51
bool acquire_s3_qps_rate_limit(S3RateLimitType type) {
360
51
    if (!config::enable_s3_rate_limiter) {
361
9
        return true;
362
9
    }
363
364
42
    auto& factory = S3ClientFactory::instance();
365
42
    auto* qps_limiter = factory.rate_limiter(type);
366
42
    if (qps_limiter->is_enabled() &&
367
42
        doris::apply_s3_rate_limit(type, qps_limiter, config::s3_rate_limiter_log_interval) < 0) {
368
11
        return false;
369
11
    }
370
31
    return true;
371
42
}
372
373
23
bool acquire_s3_qps_rate_limit(bool is_internal_bucket, S3RateLimitType type) {
374
    // In cloud mode, the shared limiter only protects traffic to Doris internal object storage
375
    // buckets.
376
23
    if (config::is_cloud_mode() && !is_internal_bucket) {
377
5
        return true;
378
5
    }
379
18
    return acquire_s3_qps_rate_limit(type);
380
23
}
381
382
44
bool acquire_s3_bytes_rate_limit(S3RateLimitType type, size_t bytes) {
383
44
    if (!config::enable_s3_rate_limiter || bytes == 0) {
384
13
        return true;
385
13
    }
386
387
31
    auto* bytes_limiter = S3ClientFactory::instance().bytes_rate_limiter(type);
388
31
    if (!bytes_limiter->is_enabled()) {
389
2
        return true;
390
2
    }
391
29
    return bytes_limiter->add(bytes) >= 0;
392
31
}
393
394
19
bool acquire_s3_bytes_rate_limit(bool is_internal_bucket, S3RateLimitType type, size_t bytes) {
395
    // In cloud mode, the shared limiter only protects traffic to Doris internal object storage
396
    // buckets.
397
19
    if (config::is_cloud_mode() && !is_internal_bucket) {
398
4
        return true;
399
4
    }
400
15
    return acquire_s3_bytes_rate_limit(type, bytes);
401
19
}
402
403
32
bool acquire_s3_rate_limit(S3RateLimitType type, size_t bytes) {
404
32
    return acquire_s3_qps_rate_limit(type) && acquire_s3_bytes_rate_limit(type, bytes);
405
32
}
406
407
8
bool acquire_s3_rate_limit(bool is_internal_bucket, S3RateLimitType type, size_t bytes) {
408
8
    return acquire_s3_qps_rate_limit(is_internal_bucket, type) &&
409
8
           acquire_s3_bytes_rate_limit(is_internal_bucket, type, bytes);
410
8
}
411
412
32
void refresh_s3_rate_limiters() {
413
32
    auto& factory = S3ClientFactory::instance();
414
32
    const int64_t cpu_cores = get_s3_rate_limiter_cpu_cores();
415
64
    for (auto type : {S3RateLimitType::GET, S3RateLimitType::PUT}) {
416
64
        update_s3_rate_limiter(factory, type, S3RateLimiterKind::QPS, cpu_cores);
417
64
        update_s3_rate_limiter(factory, type, S3RateLimiterKind::BYTES, cpu_cores);
418
64
    }
419
32
}
420
421
#ifdef BE_TEST
422
8
S3RateLimiterConfig get_effective_s3_rate_limiter_config(S3RateLimitType type) {
423
8
    return get_effective_s3_rate_limiter_config_with_cpu_cores(type,
424
8
                                                               get_s3_rate_limiter_cpu_cores());
425
8
}
426
427
3
S3RateLimiterConfig get_effective_s3_bytes_rate_limiter_config(S3RateLimitType type) {
428
3
    return get_effective_s3_bytes_rate_limiter_config_with_cpu_cores(
429
3
            type, get_s3_rate_limiter_cpu_cores());
430
3
}
431
432
68
void set_s3_rate_limiter_cpu_cores_for_test(int64_t cpu_cores) {
433
68
    s3_rate_limiter_cpu_cores_for_test.store(cpu_cores, std::memory_order_relaxed);
434
68
}
435
436
77
void reset_s3_rate_limiters_from_config_for_test() {
437
77
    auto& factory = S3ClientFactory::instance();
438
77
    const int64_t cpu_cores = get_s3_rate_limiter_cpu_cores();
439
308
    auto reset = [&](S3RateLimitType type, S3RateLimiterKind kind) {
440
308
        auto& state = get_update_state(type, kind);
441
308
        std::lock_guard update_guard(state.update_lock);
442
308
        const auto config =
443
308
                kind == S3RateLimiterKind::BYTES
444
308
                        ? get_effective_s3_bytes_rate_limiter_config_with_cpu_cores(type, cpu_cores)
445
308
                        : get_effective_s3_rate_limiter_config_with_cpu_cores(type, cpu_cores);
446
308
        auto* limiter = kind == S3RateLimiterKind::BYTES ? factory.bytes_rate_limiter(type)
447
308
                                                         : factory.rate_limiter(type);
448
308
        CHECK_EQ(limiter->reset(config.token_per_second, config.bucket_tokens, config.token_limit),
449
308
                 0);
450
308
        state.applied_config = config;
451
308
    };
452
154
    for (auto type : {S3RateLimitType::GET, S3RateLimitType::PUT}) {
453
154
        reset(type, S3RateLimiterKind::QPS);
454
154
        reset(type, S3RateLimiterKind::BYTES);
455
154
    }
456
77
}
457
#endif
458
459
0
int reset_s3_rate_limiter(S3RateLimitType type, size_t max_speed, size_t max_burst, size_t limit) {
460
0
    if (type == S3RateLimitType::UNKNOWN) {
461
0
        return -1;
462
0
    }
463
0
    return S3ClientFactory::instance().rate_limiter(type)->reset(max_speed, max_burst, limit);
464
0
}
465
466
// The analyzer does not model destruction of the process-lifetime singleton's owned resources.
467
1
S3ClientFactory::S3ClientFactory() { // NOLINT(clang-analyzer-cplusplus.NewDeleteLeaks)
468
1
    _aws_options = Aws::SDKOptions {};
469
1
    auto logLevel = static_cast<Aws::Utils::Logging::LogLevel>(config::aws_log_level);
470
1
    _aws_options.loggingOptions.logLevel = logLevel;
471
1
    _aws_options.loggingOptions.logger_create_fn = [logLevel] {
472
1
        return std::make_shared<DorisAWSLogger>(logLevel);
473
1
    };
474
1
    Aws::InitAPI(_aws_options);
475
1
    _ca_cert_file_path = get_valid_ca_cert_path(doris::split(config::ca_cert_file_paths, ";"));
476
1
    const int64_t cpu_cores = get_s3_rate_limiter_cpu_cores();
477
1
    auto get_init_config =
478
1
            get_effective_s3_rate_limiter_config_with_cpu_cores(S3RateLimitType::GET, cpu_cores);
479
1
    auto put_init_config =
480
1
            get_effective_s3_rate_limiter_config_with_cpu_cores(S3RateLimitType::PUT, cpu_cores);
481
1
    _rate_limiters = {
482
1
            std::make_unique<S3RateLimiterHolder>(
483
1
                    get_init_config.token_per_second, get_init_config.bucket_tokens,
484
1
                    get_init_config.token_limit, s3_rate_limiter_metric_func(S3RateLimitType::GET)),
485
1
            std::make_unique<S3RateLimiterHolder>(
486
1
                    put_init_config.token_per_second, put_init_config.bucket_tokens,
487
1
                    put_init_config.token_limit,
488
1
                    s3_rate_limiter_metric_func(S3RateLimitType::PUT))};
489
1
    auto get_bytes_init_config = get_effective_s3_bytes_rate_limiter_config_with_cpu_cores(
490
1
            S3RateLimitType::GET, cpu_cores);
491
1
    auto put_bytes_init_config = get_effective_s3_bytes_rate_limiter_config_with_cpu_cores(
492
1
            S3RateLimitType::PUT, cpu_cores);
493
1
    _bytes_rate_limiters = {
494
1
            std::make_unique<S3RateLimiterHolder>(
495
1
                    get_bytes_init_config.token_per_second, get_bytes_init_config.bucket_tokens,
496
1
                    get_bytes_init_config.token_limit,
497
1
                    metric_func_factory(get_bytes_rate_limit_ns,
498
1
                                        get_bytes_rate_limit_exceed_req_num)),
499
1
            std::make_unique<S3RateLimiterHolder>(
500
1
                    put_bytes_init_config.token_per_second, put_bytes_init_config.bucket_tokens,
501
1
                    put_bytes_init_config.token_limit,
502
1
                    metric_func_factory(put_bytes_rate_limit_ns,
503
1
                                        put_bytes_rate_limit_exceed_req_num))};
504
505
1
    get_update_state(S3RateLimitType::GET, S3RateLimiterKind::QPS).applied_config = get_init_config;
506
1
    get_update_state(S3RateLimitType::PUT, S3RateLimiterKind::QPS).applied_config = put_init_config;
507
1
    get_update_state(S3RateLimitType::GET, S3RateLimiterKind::BYTES).applied_config =
508
1
            get_bytes_init_config;
509
1
    get_update_state(S3RateLimitType::PUT, S3RateLimiterKind::BYTES).applied_config =
510
1
            put_bytes_init_config;
511
512
1
#ifdef USE_AZURE
513
1
    auto azureLogLevel =
514
1
            static_cast<Azure::Core::Diagnostics::Logger::Level>(config::azure_log_level);
515
1
    Azure::Core::Diagnostics::Logger::SetLevel(azureLogLevel);
516
1
    Azure::Core::Diagnostics::Logger::SetListener(
517
1
            [&](Azure::Core::Diagnostics::Logger::Level level, const std::string& message) {
518
0
                switch (level) {
519
0
                case Azure::Core::Diagnostics::Logger::Level::Verbose:
520
0
                    LOG(INFO) << message;
521
0
                    break;
522
0
                case Azure::Core::Diagnostics::Logger::Level::Informational:
523
0
                    LOG(INFO) << message;
524
0
                    break;
525
0
                case Azure::Core::Diagnostics::Logger::Level::Warning:
526
0
                    LOG(WARNING) << message;
527
0
                    break;
528
0
                case Azure::Core::Diagnostics::Logger::Level::Error:
529
0
                    LOG(ERROR) << message;
530
0
                    break;
531
0
                default:
532
0
                    LOG(WARNING) << "Unknown level: " << static_cast<int>(level)
533
0
                                 << ", message: " << message;
534
0
                    break;
535
0
                }
536
0
            });
537
1
#endif
538
1
}
539
540
1
S3ClientFactory::~S3ClientFactory() {
541
1
    Aws::ShutdownAPI(_aws_options);
542
1
}
543
544
248
S3ClientFactory& S3ClientFactory::instance() {
545
248
    static S3ClientFactory ret;
546
248
    return ret;
547
248
}
548
549
19
std::shared_ptr<io::ObjStorageClient> S3ClientFactory::create(const S3ClientConf& s3_conf) {
550
19
    if (!is_s3_conf_valid(s3_conf).ok()) {
551
0
        return nullptr;
552
0
    }
553
554
19
#ifdef BE_TEST
555
19
    {
556
19
        std::lock_guard l(_lock);
557
19
        if (_test_client_creator) {
558
5
            return _test_client_creator(s3_conf);
559
5
        }
560
19
    }
561
14
#endif
562
563
14
    {
564
14
        std::lock_guard l(_lock);
565
14
        auto it = _cache.find(s3_conf);
566
14
        if (it != _cache.end()) {
567
9
            return it->second;
568
9
        }
569
14
    }
570
571
5
    auto obj_client = (s3_conf.provider == io::ObjStorageType::AZURE)
572
5
                              ? _create_azure_client(s3_conf)
573
5
                              : _create_s3_client(s3_conf);
574
575
5
    {
576
5
        std::lock_guard l(_lock);
577
5
        auto [it, _] = _cache.emplace(s3_conf, obj_client);
578
5
        return it->second;
579
14
    }
580
14
}
581
582
#ifdef BE_TEST
583
void S3ClientFactory::set_client_creator_for_test(
584
4
        std::function<std::shared_ptr<io::ObjStorageClient>(const S3ClientConf&)> creator) {
585
4
    std::lock_guard l(_lock);
586
4
    _test_client_creator = std::move(creator);
587
4
}
588
589
13
void S3ClientFactory::clear_client_creator_for_test() {
590
13
    std::lock_guard l(_lock);
591
13
    _test_client_creator = nullptr;
592
13
}
593
#endif
594
595
std::shared_ptr<io::ObjStorageClient> S3ClientFactory::_create_azure_client(
596
0
        const S3ClientConf& s3_conf) {
597
0
#ifdef USE_AZURE
598
0
    auto cred =
599
0
            std::make_shared<Azure::Storage::StorageSharedKeyCredential>(s3_conf.ak, s3_conf.sk);
600
601
0
    const std::string container_name = s3_conf.bucket;
602
0
    std::string uri = fmt::format("{}/{}", s3_conf.endpoint, container_name);
603
0
    if (s3_conf.endpoint.find("://") == std::string::npos) {
604
0
        uri = "https://" + uri;
605
0
    }
606
607
0
    Azure::Storage::Blobs::BlobClientOptions options;
608
0
    options.Retry.StatusCodes.insert(Azure::Core::Http::HttpStatusCode::TooManyRequests);
609
0
    options.Retry.MaxRetries = config::max_s3_client_retry;
610
0
    options.PerRetryPolicies.emplace_back(
611
0
            io::create_azure_bytes_rate_limit_policy(s3_conf.is_internal_bucket));
612
0
    options.PerRetryPolicies.emplace_back(std::make_unique<AzureRetryRecordPolicy>());
613
0
    if (_ca_cert_file_path.empty()) {
614
0
        _ca_cert_file_path = get_valid_ca_cert_path(doris::split(config::ca_cert_file_paths, ";"));
615
0
    }
616
0
    if (!_ca_cert_file_path.empty()) {
617
0
        Azure::Core::Http::CurlTransportOptions curl_options;
618
0
        curl_options.CAInfo = _ca_cert_file_path;
619
0
        options.Transport.Transport =
620
0
                std::make_shared<Azure::Core::Http::CurlTransport>(std::move(curl_options));
621
0
    }
622
623
0
    std::string normalized_uri = normalize_http_uri(uri);
624
0
    VLOG_DEBUG << "uri:" << uri << ", normalized_uri:" << normalized_uri;
625
0
    std::string tls_debug_context = build_azure_tls_debug_context(_ca_cert_file_path);
626
627
0
    auto containerClient = std::make_shared<Azure::Storage::Blobs::BlobContainerClient>(
628
0
            uri, cred, std::move(options));
629
0
    LOG_INFO("create one azure client with {}", s3_conf.to_string());
630
0
    return std::make_shared<io::AzureObjStorageClient>(
631
0
            std::move(containerClient), s3_conf.is_internal_bucket, std::move(tls_debug_context));
632
#else
633
    LOG_FATAL("BE is not compiled with azure support, export BUILD_AZURE=ON before building");
634
    return nullptr;
635
#endif
636
0
}
637
638
std::shared_ptr<Aws::Auth::AWSCredentialsProvider>
639
6
S3ClientFactory::_get_aws_credentials_provider_v1(const S3ClientConf& s3_conf) {
640
6
    if (!s3_conf.ak.empty() && !s3_conf.sk.empty()) {
641
2
        Aws::Auth::AWSCredentials aws_cred(s3_conf.ak, s3_conf.sk);
642
2
        DCHECK(!aws_cred.IsExpiredOrEmpty());
643
2
        if (!s3_conf.token.empty()) {
644
0
            aws_cred.SetSessionToken(s3_conf.token);
645
0
        }
646
2
        return std::make_shared<Aws::Auth::SimpleAWSCredentialsProvider>(std::move(aws_cred));
647
2
    }
648
649
4
    if (s3_conf.cred_provider_type == CredProviderType::InstanceProfile) {
650
2
        if (s3_conf.role_arn.empty()) {
651
1
            return std::make_shared<Aws::Auth::InstanceProfileCredentialsProvider>();
652
1
        }
653
654
1
        Aws::Client::ClientConfiguration clientConfiguration =
655
1
                S3ClientFactory::getClientConfiguration();
656
657
1
        if (_ca_cert_file_path.empty()) {
658
0
            _ca_cert_file_path =
659
0
                    get_valid_ca_cert_path(doris::split(config::ca_cert_file_paths, ";"));
660
0
        }
661
1
        if (!_ca_cert_file_path.empty()) {
662
1
            clientConfiguration.caFile = _ca_cert_file_path;
663
1
        }
664
665
1
        auto stsClient = std::make_shared<Aws::STS::STSClient>(
666
1
                std::make_shared<Aws::Auth::InstanceProfileCredentialsProvider>(),
667
1
                clientConfiguration);
668
669
1
        return std::make_shared<Aws::Auth::STSAssumeRoleCredentialsProvider>(
670
1
                s3_conf.role_arn, Aws::String(), s3_conf.external_id,
671
1
                Aws::Auth::DEFAULT_CREDS_LOAD_FREQ_SECONDS, stsClient);
672
2
    }
673
674
    // Support anonymous access for public datasets when no credentials are provided
675
2
    if (s3_conf.ak.empty() && s3_conf.sk.empty()) {
676
2
        return std::make_shared<Aws::Auth::AnonymousAWSCredentialsProvider>();
677
2
    }
678
679
0
    return std::make_shared<Aws::Auth::DefaultAWSCredentialsProviderChain>();
680
2
}
681
682
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> S3ClientFactory::_create_credentials_provider(
683
18
        CredProviderType type) {
684
18
    switch (type) {
685
2
    case CredProviderType::Env:
686
2
        return std::make_shared<Aws::Auth::EnvironmentAWSCredentialsProvider>();
687
2
    case CredProviderType::SystemProperties:
688
2
        return std::make_shared<Aws::Auth::ProfileConfigFileAWSCredentialsProvider>();
689
3
    case CredProviderType::WebIdentity:
690
3
        return std::make_shared<Aws::Auth::STSAssumeRoleWebIdentityCredentialsProvider>();
691
2
    case CredProviderType::Container:
692
2
        return std::make_shared<Aws::Auth::TaskRoleCredentialsProvider>(
693
2
                Aws::Environment::GetEnv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI").c_str());
694
4
    case CredProviderType::InstanceProfile:
695
4
        return std::make_shared<Aws::Auth::InstanceProfileCredentialsProvider>();
696
2
    case CredProviderType::Anonymous:
697
2
        return std::make_shared<Aws::Auth::AnonymousAWSCredentialsProvider>();
698
3
    case CredProviderType::Default:
699
3
    default:
700
3
        return std::make_shared<CustomAwsCredentialsProviderChain>();
701
18
    }
702
18
}
703
704
std::shared_ptr<Aws::Auth::AWSCredentialsProvider>
705
24
S3ClientFactory::_get_aws_credentials_provider_v2(const S3ClientConf& s3_conf) {
706
24
    if (!s3_conf.ak.empty() && !s3_conf.sk.empty()) {
707
6
        Aws::Auth::AWSCredentials aws_cred(s3_conf.ak, s3_conf.sk);
708
6
        DCHECK(!aws_cred.IsExpiredOrEmpty());
709
6
        if (!s3_conf.token.empty()) {
710
0
            aws_cred.SetSessionToken(s3_conf.token);
711
0
        }
712
6
        return std::make_shared<Aws::Auth::SimpleAWSCredentialsProvider>(std::move(aws_cred));
713
6
    }
714
715
    // Handle role_arn for assume role scenario
716
18
    if (!s3_conf.role_arn.empty()) {
717
8
        Aws::Client::ClientConfiguration clientConfiguration =
718
8
                S3ClientFactory::getClientConfiguration();
719
720
8
        if (_ca_cert_file_path.empty()) {
721
0
            _ca_cert_file_path =
722
0
                    get_valid_ca_cert_path(doris::split(config::ca_cert_file_paths, ";"));
723
0
        }
724
8
        if (!_ca_cert_file_path.empty()) {
725
8
            clientConfiguration.caFile = _ca_cert_file_path;
726
8
        }
727
728
8
        auto baseProvider = _create_credentials_provider(s3_conf.cred_provider_type);
729
8
        auto stsClient = std::make_shared<Aws::STS::STSClient>(baseProvider, clientConfiguration);
730
731
8
        return std::make_shared<Aws::Auth::STSAssumeRoleCredentialsProvider>(
732
8
                s3_conf.role_arn, Aws::String(), s3_conf.external_id,
733
8
                Aws::Auth::DEFAULT_CREDS_LOAD_FREQ_SECONDS, stsClient);
734
8
    }
735
736
    // Return provider based on cred_provider_type
737
10
    return _create_credentials_provider(s3_conf.cred_provider_type);
738
18
}
739
740
std::shared_ptr<Aws::Auth::AWSCredentialsProvider> S3ClientFactory::get_aws_credentials_provider(
741
30
        const S3ClientConf& s3_conf) {
742
30
    if (config::aws_credentials_provider_version == "v2") {
743
24
        return _get_aws_credentials_provider_v2(s3_conf);
744
24
    }
745
6
    return _get_aws_credentials_provider_v1(s3_conf);
746
30
}
747
748
std::shared_ptr<io::ObjStorageClient> S3ClientFactory::_create_s3_client(
749
5
        const S3ClientConf& s3_conf) {
750
5
    TEST_SYNC_POINT_RETURN_WITH_VALUE(
751
4
            "s3_client_factory::create",
752
4
            std::make_shared<io::S3ObjStorageClient>(std::make_shared<Aws::S3::S3Client>(),
753
4
                                                     s3_conf.is_internal_bucket));
754
4
    Aws::Client::ClientConfiguration aws_config = S3ClientFactory::getClientConfiguration();
755
4
    if (s3_conf.need_override_endpoint) {
756
4
        aws_config.endpointOverride = s3_conf.endpoint;
757
4
    }
758
4
    aws_config.region = s3_conf.region;
759
760
4
    if (_ca_cert_file_path.empty()) {
761
0
        _ca_cert_file_path = get_valid_ca_cert_path(doris::split(config::ca_cert_file_paths, ";"));
762
0
    }
763
764
4
    if (!_ca_cert_file_path.empty()) {
765
4
        aws_config.caFile = _ca_cert_file_path;
766
4
    }
767
768
4
    if (s3_conf.max_connections > 0) {
769
0
        aws_config.maxConnections = s3_conf.max_connections;
770
4
    } else {
771
4
        aws_config.maxConnections = 102400;
772
4
    }
773
774
4
    aws_config.requestTimeoutMs = 30000;
775
4
    if (s3_conf.request_timeout_ms > 0) {
776
0
        aws_config.requestTimeoutMs = s3_conf.request_timeout_ms;
777
0
    }
778
779
4
    if (s3_conf.connect_timeout_ms > 0) {
780
0
        aws_config.connectTimeoutMs = s3_conf.connect_timeout_ms;
781
0
    }
782
783
4
    if (config::s3_client_http_scheme == "http") {
784
4
        aws_config.scheme = Aws::Http::Scheme::HTTP;
785
4
    }
786
787
4
    aws_config.retryStrategy = std::make_shared<S3CustomRetryStrategy>(
788
4
            config::max_s3_client_retry /*scaleFactor = 25*/, /*retry_slow_down=*/true);
789
790
4
    std::shared_ptr<Aws::S3::S3Client> new_client = std::make_shared<Aws::S3::S3Client>(
791
4
            get_aws_credentials_provider(s3_conf), std::move(aws_config),
792
4
            Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never,
793
4
            s3_conf.use_virtual_addressing);
794
795
4
    auto obj_client = std::make_shared<io::S3ObjStorageClient>(std::move(new_client),
796
4
                                                               s3_conf.is_internal_bucket);
797
4
    LOG_INFO("create one s3 client with {}", s3_conf.to_string());
798
4
    return obj_client;
799
5
}
800
801
Status S3ClientFactory::convert_properties_to_s3_conf(
802
15
        const std::map<std::string, std::string>& prop, const S3URI& s3_uri, S3Conf* s3_conf) {
803
15
    StringCaseMap<std::string> properties(prop.begin(), prop.end());
804
15
    if (auto it = properties.find(S3_AK); it != properties.end()) {
805
2
        s3_conf->client_conf.ak = it->second;
806
2
    }
807
15
    if (auto it = properties.find(S3_SK); it != properties.end()) {
808
2
        s3_conf->client_conf.sk = it->second;
809
2
    }
810
15
    if (auto it = properties.find(S3_TOKEN); it != properties.end()) {
811
0
        s3_conf->client_conf.token = it->second;
812
0
    }
813
15
    if (auto it = properties.find(S3_ENDPOINT); it != properties.end()) {
814
15
        s3_conf->client_conf.endpoint = it->second;
815
15
    }
816
15
    if (auto it = properties.find(S3_NEED_OVERRIDE_ENDPOINT); it != properties.end()) {
817
0
        s3_conf->client_conf.need_override_endpoint = (it->second == "true");
818
0
    }
819
15
    if (auto it = properties.find(S3_REGION); it != properties.end()) {
820
15
        s3_conf->client_conf.region = it->second;
821
15
    }
822
15
    if (auto it = properties.find(S3_MAX_CONN_SIZE); it != properties.end()) {
823
0
        if (!to_int(it->second, s3_conf->client_conf.max_connections)) {
824
0
            return Status::InvalidArgument("invalid {} value \"{}\"", S3_MAX_CONN_SIZE, it->second);
825
0
        }
826
0
    }
827
15
    if (auto it = properties.find(S3_REQUEST_TIMEOUT_MS); it != properties.end()) {
828
0
        if (!to_int(it->second, s3_conf->client_conf.request_timeout_ms)) {
829
0
            return Status::InvalidArgument("invalid {} value \"{}\"", S3_REQUEST_TIMEOUT_MS,
830
0
                                           it->second);
831
0
        }
832
0
    }
833
15
    if (auto it = properties.find(S3_CONN_TIMEOUT_MS); it != properties.end()) {
834
0
        if (!to_int(it->second, s3_conf->client_conf.connect_timeout_ms)) {
835
0
            return Status::InvalidArgument("invalid {} value \"{}\"", S3_CONN_TIMEOUT_MS,
836
0
                                           it->second);
837
0
        }
838
0
    }
839
15
    if (auto it = properties.find(S3_PROVIDER); it != properties.end()) {
840
        // S3 Provider properties should be case insensitive.
841
0
        if (0 == strcasecmp(it->second.c_str(), AZURE_PROVIDER_STRING)) {
842
0
            s3_conf->client_conf.provider = io::ObjStorageType::AZURE;
843
0
        }
844
0
    }
845
846
15
    if (s3_uri.get_bucket().empty()) {
847
0
        return Status::InvalidArgument("Invalid S3 URI {}, bucket is not specified",
848
0
                                       s3_uri.to_string());
849
0
    }
850
15
    s3_conf->bucket = s3_uri.get_bucket();
851
    // For azure's compatibility
852
15
    s3_conf->client_conf.bucket = s3_uri.get_bucket();
853
15
    s3_conf->prefix = "";
854
855
    // See https://sdk.amazonaws.com/cpp/api/LATEST/class_aws_1_1_s3_1_1_s3_client.html
856
15
    s3_conf->client_conf.use_virtual_addressing = true;
857
15
    if (auto it = properties.find(USE_PATH_STYLE); it != properties.end()) {
858
0
        s3_conf->client_conf.use_virtual_addressing = it->second != "true";
859
0
    }
860
861
15
    if (auto it = properties.find(S3_ROLE_ARN); it != properties.end()) {
862
        // Keep provider type as Default unless explicitly configured by
863
        // AWS_CREDENTIALS_PROVIDER_TYPE, consistent with FE behavior.
864
5
        s3_conf->client_conf.role_arn = it->second;
865
5
    }
866
867
15
    if (auto it = properties.find(S3_EXTERNAL_ID); it != properties.end()) {
868
0
        s3_conf->client_conf.external_id = it->second;
869
0
    }
870
871
15
    if (auto it = properties.find(S3_CREDENTIALS_PROVIDER_TYPE); it != properties.end()) {
872
8
        s3_conf->client_conf.cred_provider_type = cred_provider_type_from_string(it->second);
873
8
    }
874
875
15
    if (auto st = is_s3_conf_valid(s3_conf->client_conf); !st.ok()) {
876
2
        return st;
877
2
    }
878
13
    return Status::OK();
879
15
}
880
881
0
static CredProviderType cred_provider_type_from_thrift(TCredProviderType::type cred_provider_type) {
882
0
    switch (cred_provider_type) {
883
0
    case TCredProviderType::DEFAULT:
884
0
        return CredProviderType::Default;
885
0
    case TCredProviderType::SIMPLE:
886
0
        return CredProviderType::Simple;
887
0
    case TCredProviderType::INSTANCE_PROFILE:
888
0
        return CredProviderType::InstanceProfile;
889
0
    case TCredProviderType::ENV:
890
0
        return CredProviderType::Env;
891
0
    case TCredProviderType::SYSTEM_PROPERTIES:
892
0
        return CredProviderType::SystemProperties;
893
0
    case TCredProviderType::WEB_IDENTITY:
894
0
        return CredProviderType::WebIdentity;
895
0
    case TCredProviderType::CONTAINER:
896
0
        return CredProviderType::Container;
897
0
    case TCredProviderType::ANONYMOUS:
898
0
        return CredProviderType::Anonymous;
899
0
    default:
900
0
        __builtin_unreachable();
901
0
        LOG(WARNING) << "Invalid TCredProviderType value: " << cred_provider_type
902
0
                     << ", use default instead.";
903
0
        return CredProviderType::Default;
904
0
    }
905
0
}
906
907
0
S3Conf S3Conf::get_s3_conf(const cloud::ObjectStoreInfoPB& info) {
908
0
    S3Conf ret {
909
0
            .bucket = info.bucket(),
910
0
            .prefix = info.prefix(),
911
0
            .client_conf {
912
0
                    .endpoint = info.endpoint(),
913
0
                    .region = info.region(),
914
0
                    .ak = info.ak(),
915
0
                    .sk = info.sk(),
916
0
                    .token {},
917
0
                    .bucket = info.bucket(),
918
0
                    .provider = io::ObjStorageType::AWS,
919
0
                    .use_virtual_addressing =
920
0
                            info.has_use_path_style() ? !info.use_path_style() : true,
921
922
0
                    .role_arn = info.role_arn(),
923
0
                    .external_id = info.external_id(),
924
0
                    .is_internal_bucket = true,
925
0
            },
926
0
            .sse_enabled = info.sse_enabled(),
927
0
    };
928
929
0
    if (info.has_cred_provider_type()) {
930
0
        ret.client_conf.cred_provider_type = cred_provider_type_from_pb(info.cred_provider_type());
931
0
    }
932
933
0
    io::ObjStorageType type = io::ObjStorageType::AWS;
934
0
    switch (info.provider()) {
935
0
    case cloud::ObjectStoreInfoPB_Provider_OSS:
936
0
        type = io::ObjStorageType::OSS;
937
0
        break;
938
0
    case cloud::ObjectStoreInfoPB_Provider_S3:
939
0
        type = io::ObjStorageType::AWS;
940
0
        break;
941
0
    case cloud::ObjectStoreInfoPB_Provider_COS:
942
0
        type = io::ObjStorageType::COS;
943
0
        break;
944
0
    case cloud::ObjectStoreInfoPB_Provider_OBS:
945
0
        type = io::ObjStorageType::OBS;
946
0
        break;
947
0
    case cloud::ObjectStoreInfoPB_Provider_BOS:
948
0
        type = io::ObjStorageType::BOS;
949
0
        break;
950
0
    case cloud::ObjectStoreInfoPB_Provider_GCP:
951
0
        type = io::ObjStorageType::GCP;
952
0
        break;
953
0
    case cloud::ObjectStoreInfoPB_Provider_AZURE:
954
0
        type = io::ObjStorageType::AZURE;
955
0
        break;
956
0
    case cloud::ObjectStoreInfoPB_Provider_TOS:
957
0
        type = io::ObjStorageType::TOS;
958
0
        break;
959
0
    default:
960
0
        __builtin_unreachable();
961
0
        LOG_FATAL("unknown provider type {}, info {}", info.provider(), ret.to_string());
962
0
    }
963
0
    ret.client_conf.provider = type;
964
0
    return ret;
965
0
}
966
967
0
S3Conf S3Conf::get_s3_conf(const TS3StorageParam& param) {
968
0
    S3Conf ret {
969
0
            .bucket = param.bucket,
970
0
            .prefix = param.root_path,
971
0
            .client_conf = {
972
0
                    .endpoint = param.endpoint,
973
0
                    .region = param.region,
974
0
                    .ak = param.ak,
975
0
                    .sk = param.sk,
976
0
                    .token = param.token,
977
0
                    .bucket = param.bucket,
978
0
                    .provider = io::ObjStorageType::AWS,
979
0
                    .max_connections = param.max_conn,
980
0
                    .request_timeout_ms = param.request_timeout_ms,
981
0
                    .connect_timeout_ms = param.conn_timeout_ms,
982
                    // When using cold heat separation in minio, user might use ip address directly,
983
                    // which needs enable use_virtual_addressing to true
984
0
                    .use_virtual_addressing = !param.use_path_style,
985
0
                    .role_arn = param.role_arn,
986
0
                    .external_id = param.external_id,
987
0
            }};
988
989
0
    if (param.__isset.cred_provider_type) {
990
0
        ret.client_conf.cred_provider_type =
991
0
                cred_provider_type_from_thrift(param.cred_provider_type);
992
0
    }
993
994
0
    io::ObjStorageType type = io::ObjStorageType::AWS;
995
0
    switch (param.provider) {
996
0
    case TObjStorageType::UNKNOWN:
997
0
        LOG_INFO("Receive one legal storage resource, set provider type to aws, param detail {}",
998
0
                 ret.to_string());
999
0
        type = io::ObjStorageType::AWS;
1000
0
        break;
1001
0
    case TObjStorageType::AWS:
1002
0
        type = io::ObjStorageType::AWS;
1003
0
        break;
1004
0
    case TObjStorageType::AZURE:
1005
0
        type = io::ObjStorageType::AZURE;
1006
0
        break;
1007
0
    case TObjStorageType::BOS:
1008
0
        type = io::ObjStorageType::BOS;
1009
0
        break;
1010
0
    case TObjStorageType::COS:
1011
0
        type = io::ObjStorageType::COS;
1012
0
        break;
1013
0
    case TObjStorageType::OBS:
1014
0
        type = io::ObjStorageType::OBS;
1015
0
        break;
1016
0
    case TObjStorageType::OSS:
1017
0
        type = io::ObjStorageType::OSS;
1018
0
        break;
1019
0
    case TObjStorageType::GCP:
1020
0
        type = io::ObjStorageType::GCP;
1021
0
        break;
1022
0
    case TObjStorageType::TOS:
1023
0
        type = io::ObjStorageType::TOS;
1024
0
        break;
1025
0
    default:
1026
0
        LOG_FATAL("unknown provider type {}, info {}", param.provider, ret.to_string());
1027
0
        __builtin_unreachable();
1028
0
    }
1029
0
    ret.client_conf.provider = type;
1030
0
    return ret;
1031
0
}
1032
1033
16
std::string hide_access_key(const std::string& ak) {
1034
16
    std::string key = ak;
1035
16
    size_t key_len = key.length();
1036
16
    size_t reserved_count;
1037
16
    if (key_len > 7) {
1038
3
        reserved_count = 6;
1039
13
    } else if (key_len > 2) {
1040
6
        reserved_count = key_len - 2;
1041
7
    } else {
1042
7
        reserved_count = 0;
1043
7
    }
1044
1045
16
    size_t x_count = key_len - reserved_count;
1046
16
    size_t left_x_count = (x_count + 1) / 2;
1047
1048
16
    if (left_x_count > 0) {
1049
15
        key.replace(0, left_x_count, left_x_count, 'x');
1050
15
    }
1051
1052
16
    if (x_count - left_x_count > 0) {
1053
14
        key.replace(key_len - (x_count - left_x_count), x_count - left_x_count,
1054
14
                    x_count - left_x_count, 'x');
1055
14
    }
1056
16
    return key;
1057
16
}
1058
1059
} // end namespace doris