Skip to content

Commit 216be6d

Browse files
committed
GH-50126: [C++] Make S3Filesystem consume key-value options
1 parent c9c887c commit 216be6d

3 files changed

Lines changed: 70 additions & 7 deletions

File tree

cpp/src/arrow/filesystem/s3fs.cc

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,52 @@ Result<S3Options> S3Options::FromUri(const std::string& uri_string,
419419
return FromUri(uri, out_path);
420420
}
421421

422+
namespace {
423+
424+
Result<std::string> GetStringOption(const std::string& key, const std::any& value) {
425+
// TODO: Validate this is necessary with tests.
426+
if (const auto* s = std::any_cast<std::string>(&value)) {
427+
return *s;
428+
}
429+
return Status::Invalid("S3 filesystem option '", key, "' must be a std::string");
430+
}
431+
} // namespace
432+
433+
Result<S3Options> S3Options::FromUriAndOptions(
434+
const ::arrow::util::Uri& uri,
435+
const FileSystemFactoryOptions& options, std::string* out_path) {
436+
std::optional<std::string> access_key, secret_key, session_token;
437+
for (const auto& [key, value] : options) {
438+
if (key == "access_key") {
439+
ARROW_ASSIGN_OR_RAISE(access_key, GetStringOption(key, value));
440+
} else if (key == "secret_key") {
441+
ARROW_ASSIGN_OR_RAISE(secret_key, GetStringOption(key, value));
442+
} else if (key == "session_token") {
443+
ARROW_ASSIGN_OR_RAISE(session_token, GetStringOption(key, value));
444+
} else {
445+
return Status::Invalid("Unexpected option for S3 filesystem: '", key, "'");
446+
}
447+
}
448+
449+
if (access_key.has_value() != secret_key.has_value()) {
450+
return Status::Invalid(
451+
"Both 'access_key' and 'secret_key' must be provided together");
452+
}
453+
ARROW_ASSIGN_OR_RAISE(auto s3_options, FromUri(uri, out_path));
454+
if (access_key.has_value()) {
455+
s3_options.ConfigureAccessKey(*access_key, *secret_key, session_token.value_or(""));
456+
}
457+
return s3_options;
458+
}
459+
460+
Result<S3Options> S3Options::FromUriAndOptions(
461+
const std::string& uri_string,
462+
const FileSystemFactoryOptions& options, std::string* out_path) {
463+
Uri uri;
464+
RETURN_NOT_OK(uri.Parse(uri_string));
465+
return FromUriAndOptions(uri, options, out_path);
466+
}
467+
422468
bool S3Options::Equals(const S3Options& other) const {
423469
const int64_t default_metadata_size = default_metadata ? default_metadata->size() : 0;
424470
const bool default_metadata_equals =
@@ -3606,13 +3652,14 @@ auto kS3FileSystemModule = ARROW_REGISTER_FILESYSTEM(
36063652
[](const arrow::util::Uri& uri, const FileSystemFactoryOptions& options,
36073653
const io::IOContext& io_context,
36083654
std::string* out_path) -> Result<std::shared_ptr<fs::FileSystem>> {
3609-
if (!options.empty()) {
3655+
/*if (!options.empty()) {
36103656
return Status::NotImplemented(
36113657
"S3 filesystem factory options are not supported yet, got: ", options.size(),
36123658
" option(s)");
3613-
}
3659+
}*/
36143660
RETURN_NOT_OK(EnsureS3Initialized());
3615-
ARROW_ASSIGN_OR_RAISE(auto s3_options, S3Options::FromUri(uri, out_path));
3661+
ARROW_ASSIGN_OR_RAISE(auto s3_options,
3662+
S3Options::FromUriAndOptions(uri, options, out_path));
36163663
return S3FileSystem::Make(s3_options, io_context);
36173664
},
36183665
[] { DCHECK_OK(EnsureS3Finalized()); });

cpp/src/arrow/filesystem/s3fs.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#pragma once
1919

20+
#include <any>
2021
#include <memory>
2122
#include <string>
2223
#include <vector>
@@ -290,6 +291,22 @@ struct ARROW_EXPORT S3Options {
290291
std::string* out_path = NULLPTR);
291292
static Result<S3Options> FromUri(const std::string& uri,
292293
std::string* out_path = NULLPTR);
294+
295+
/// Equivalent to FromUri() with specific backend options that can't be represented
296+
/// on the URI overlaid on top. Recognized keys:
297+
/// - "access_key" (std::string)
298+
/// - "secret_key" (std::string)
299+
/// - "session_token" (std::string).
300+
/// Options take precedence over the URI; unknown keys return
301+
/// Status::Invalid.
302+
static Result<S3Options> FromUriAndOptions(
303+
const ::arrow::util::Uri& uri,
304+
const FileSystemFactoryOptions& options,
305+
std::string* out_path = NULLPTR);
306+
static Result<S3Options> FromUriAndOptions(
307+
const std::string& uri,
308+
const FileSystemFactoryOptions& options,
309+
std::string* out_path = NULLPTR);
293310
};
294311

295312
/// S3-backed FileSystem implementation.

cpp/src/arrow/filesystem/s3fs_module_test.cc

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,10 @@ TEST(S3Test, FromUri) {
8383
"&allow_bucket_creation=0&allow_bucket_deletion=0");
8484
}
8585

86-
TEST(S3Test, FromUriRejectsOptions) {
86+
TEST(S3Test, FromUriRejectsUnknownOptions) {
8787
FileSystemFactoryOptions options{{"some_option", 1}};
88-
EXPECT_RAISES_WITH_MESSAGE_THAT(
89-
NotImplemented, ::testing::HasSubstr("options are not supported"),
90-
FileSystemFromUriAndOptions("s3://bucket/key", options));
88+
EXPECT_RAISES_WITH_MESSAGE_THAT(Invalid, ::testing::HasSubstr("Unexpected option"),
89+
FileSystemFromUriAndOptions("s3://bucket/key", options));
9190
}
9291

9392
} // namespace arrow::fs

0 commit comments

Comments
 (0)