ClickHouse/src/IO/S3Common.cpp

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

202 lines
6.6 KiB
C++
Raw Normal View History

2022-09-15 14:06:22 +00:00
#include <IO/S3Common.h>
2019-12-06 14:37:21 +00:00
2022-09-15 14:06:22 +00:00
#include <Common/Exception.h>
2024-05-19 08:02:06 +00:00
#include <Common/StringUtils.h>
2022-09-15 10:11:09 +00:00
#include <Poco/Util/AbstractConfiguration.h>
#include "config.h"
2022-09-15 10:11:09 +00:00
2019-12-06 14:37:21 +00:00
#if USE_AWS_S3
# include <IO/HTTPHeaderEntries.h>
# include <IO/S3/Client.h>
# include <IO/S3/Requests.h>
2024-01-07 01:16:29 +00:00
# include <Common/quoteString.h>
2022-04-27 15:05:45 +00:00
# include <Common/logger_useful.h>
2019-11-05 07:54:13 +00:00
2022-09-19 18:40:32 +00:00
namespace ProfileEvents
{
extern const Event S3GetObjectAttributes;
extern const Event S3GetObjectMetadata;
2022-09-19 18:40:32 +00:00
extern const Event S3HeadObject;
extern const Event DiskS3GetObjectAttributes;
extern const Event DiskS3GetObjectMetadata;
2022-09-19 18:40:32 +00:00
extern const Event DiskS3HeadObject;
}
namespace DB
{
bool S3Exception::isRetryableError() const
{
/// Looks like these list is quite conservative, add more codes if you wish
static const std::unordered_set<Aws::S3::S3Errors> unretryable_errors = {
Aws::S3::S3Errors::NO_SUCH_KEY,
Aws::S3::S3Errors::ACCESS_DENIED,
Aws::S3::S3Errors::INVALID_ACCESS_KEY_ID,
Aws::S3::S3Errors::INVALID_SIGNATURE,
Aws::S3::S3Errors::NO_SUCH_UPLOAD,
Aws::S3::S3Errors::NO_SUCH_BUCKET,
};
return !unretryable_errors.contains(code);
}
}
2023-02-13 12:28:04 +00:00
namespace DB::ErrorCodes
2019-12-06 15:14:39 +00:00
{
2022-03-21 14:52:26 +00:00
extern const int S3_ERROR;
2019-12-03 01:22:25 +00:00
}
2022-09-15 10:11:09 +00:00
#endif
2022-09-15 10:11:09 +00:00
namespace DB
{
2022-09-15 10:11:09 +00:00
namespace ErrorCodes
{
extern const int INVALID_CONFIG_PARAMETER;
2019-11-05 07:54:13 +00:00
}
2022-09-15 10:11:09 +00:00
namespace S3
{
HTTPHeaderEntries getHTTPHeaders(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config)
{
HTTPHeaderEntries headers;
Poco::Util::AbstractConfiguration::Keys subconfig_keys;
config.keys(config_elem, subconfig_keys);
for (const std::string & subkey : subconfig_keys)
{
if (subkey.starts_with("header"))
{
auto header_str = config.getString(config_elem + "." + subkey);
auto delimiter = header_str.find(':');
if (delimiter == std::string::npos)
throw Exception(ErrorCodes::INVALID_CONFIG_PARAMETER, "Malformed s3 header value");
headers.emplace_back(header_str.substr(0, delimiter), header_str.substr(delimiter + 1, String::npos));
}
}
return headers;
}
ServerSideEncryptionKMSConfig getSSEKMSConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config)
{
ServerSideEncryptionKMSConfig sse_kms_config;
if (config.has(config_elem + ".server_side_encryption_kms_key_id"))
sse_kms_config.key_id = config.getString(config_elem + ".server_side_encryption_kms_key_id");
if (config.has(config_elem + ".server_side_encryption_kms_encryption_context"))
sse_kms_config.encryption_context = config.getString(config_elem + ".server_side_encryption_kms_encryption_context");
if (config.has(config_elem + ".server_side_encryption_kms_bucket_key_enabled"))
sse_kms_config.bucket_key_enabled = config.getBool(config_elem + ".server_side_encryption_kms_bucket_key_enabled");
return sse_kms_config;
}
2022-09-15 14:06:22 +00:00
AuthSettings AuthSettings::loadFromConfig(const std::string & config_elem, const Poco::Util::AbstractConfiguration & config)
2022-09-15 10:11:09 +00:00
{
auto access_key_id = config.getString(config_elem + ".access_key_id", "");
auto secret_access_key = config.getString(config_elem + ".secret_access_key", "");
auto session_token = config.getString(config_elem + ".session_token", "");
2022-09-15 10:11:09 +00:00
auto region = config.getString(config_elem + ".region", "");
auto server_side_encryption_customer_key_base64 = config.getString(config_elem + ".server_side_encryption_customer_key_base64", "");
std::optional<bool> use_environment_credentials;
if (config.has(config_elem + ".use_environment_credentials"))
use_environment_credentials = config.getBool(config_elem + ".use_environment_credentials");
std::optional<bool> use_insecure_imds_request;
if (config.has(config_elem + ".use_insecure_imds_request"))
use_insecure_imds_request = config.getBool(config_elem + ".use_insecure_imds_request");
std::optional<uint64_t> expiration_window_seconds;
if (config.has(config_elem + ".expiration_window_seconds"))
expiration_window_seconds = config.getUInt64(config_elem + ".expiration_window_seconds");
std::optional<bool> no_sign_request;
if (config.has(config_elem + ".no_sign_request"))
no_sign_request = config.getBool(config_elem + ".no_sign_request");
HTTPHeaderEntries headers = getHTTPHeaders(config_elem, config);
ServerSideEncryptionKMSConfig sse_kms_config = getSSEKMSConfig(config_elem, config);
2022-09-15 10:11:09 +00:00
std::unordered_set<std::string> users;
Poco::Util::AbstractConfiguration::Keys keys;
config.keys(config_elem, keys);
for (const auto & key : keys)
{
if (startsWith(key, "user"))
users.insert(config.getString(config_elem + "." + key));
}
2022-09-15 10:11:09 +00:00
return AuthSettings
{
std::move(access_key_id), std::move(secret_access_key), std::move(session_token),
2022-09-15 10:11:09 +00:00
std::move(region),
std::move(server_side_encryption_customer_key_base64),
std::move(sse_kms_config),
2022-09-15 10:11:09 +00:00
std::move(headers),
use_environment_credentials,
use_insecure_imds_request,
expiration_window_seconds,
no_sign_request,
std::move(users)
2022-09-15 10:11:09 +00:00
};
}
2019-12-06 14:37:21 +00:00
bool AuthSettings::canBeUsedByUser(const String & user) const
{
return users.empty() || users.contains(user);
}
2024-01-07 01:16:29 +00:00
bool AuthSettings::hasUpdates(const AuthSettings & other) const
{
AuthSettings copy = *this;
copy.updateFrom(other);
2024-01-07 22:00:26 +00:00
return *this != copy;
2024-01-07 01:16:29 +00:00
}
void AuthSettings::updateFrom(const AuthSettings & from)
{
/// Update with check for emptyness only parameters which
/// can be passed not only from config, but via ast.
if (!from.access_key_id.empty())
access_key_id = from.access_key_id;
if (!from.secret_access_key.empty())
secret_access_key = from.secret_access_key;
if (!from.session_token.empty())
session_token = from.session_token;
if (!from.headers.empty())
headers = from.headers;
if (!from.region.empty())
region = from.region;
server_side_encryption_customer_key_base64 = from.server_side_encryption_customer_key_base64;
server_side_encryption_kms_config = from.server_side_encryption_kms_config;
2023-03-29 11:08:44 +00:00
2023-03-29 12:54:57 +00:00
if (from.use_environment_credentials.has_value())
2023-03-29 11:08:44 +00:00
use_environment_credentials = from.use_environment_credentials;
2023-03-29 12:54:57 +00:00
if (from.use_insecure_imds_request.has_value())
2023-03-29 11:08:44 +00:00
use_insecure_imds_request = from.use_insecure_imds_request;
2023-03-29 12:54:57 +00:00
if (from.expiration_window_seconds.has_value())
2023-03-29 11:08:44 +00:00
expiration_window_seconds = from.expiration_window_seconds;
if (from.no_sign_request.has_value())
2024-01-07 01:16:29 +00:00
no_sign_request = from.no_sign_request;
users.insert(from.users.begin(), from.users.end());
}
2022-09-15 10:11:09 +00:00
}
}