]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw/s3: support paginated ListBuckets
authorCasey Bodley <cbodley@redhat.com>
Mon, 28 Jul 2025 20:41:56 +0000 (16:41 -0400)
committerCasey Bodley <cbodley@redhat.com>
Tue, 29 Jul 2025 16:49:47 +0000 (12:49 -0400)
at some point, aws added pagination support to ListBuckets[1] in the
form of query params max-buckets and continuation-token

RGWListBuckets already supported pagination for the swift api, so we
only need to hook up the s3 request/response formats to use it

Fixes: https://tracker.ceph.com/issues/72315
[1] https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html

Signed-off-by: Casey Bodley <cbodley@redhat.com>
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

index 61c3c4107764340d9eb121e7b4b4a5260f7ac2c0..cbc5273afb8b2bcdbb7d69059cc575f0c47c2d25 100644 (file)
@@ -1611,6 +1611,37 @@ void RGWDeleteBucketReplication_ObjStore_S3::send_response()
   dump_start(s);
 }
 
+int RGWListBuckets_ObjStore_S3::get_params(optional_yield y)
+{
+  auto continuation_token = s->info.args.get_optional("continuation-token");
+  if (continuation_token) {
+    marker = *continuation_token;
+  }
+
+  auto max_buckets = s->info.args.get_optional("max-buckets");
+  if (max_buckets) {
+    std::optional value = ceph::parse<int64_t>(*max_buckets);
+    if (!value) {
+      s->err.message = "max-buckets must be an integer";
+      return -EINVAL;
+    }
+    if (*value < 1 || *value > 10000) {
+      s->err.message = "max-buckets must be between 1 and 10000 inclusive";
+      return -EINVAL;
+    }
+    limit = *value;
+  }
+  // TODO: support "prefix" and "bucket-region" params?
+
+  // a request is only considered to be "paginated" if it specifies
+  // either "max-buckets" or "continuation-token"
+  if (!continuation_token && !max_buckets) {
+    limit = -1; /* no limit */
+  }
+
+  return 0;
+}
+
 void RGWListBuckets_ObjStore_S3::send_response_begin(bool has_buckets)
 {
   if (op_ret)
@@ -1643,7 +1674,10 @@ void RGWListBuckets_ObjStore_S3::send_response_data(std::span<const RGWBucketEnt
 void RGWListBuckets_ObjStore_S3::send_response_end()
 {
   if (sent_data) {
-    s->formatter->close_section();
+    s->formatter->close_section(); // Buckets
+    if (!marker.empty()) {
+      s->formatter->dump_string("ContinuationToken", marker);
+    }
     list_all_buckets_end(s);
     rgw_flush_formatter_and_reset(s, s->formatter);
   }
index 4996197692b5187ada6deb1828bdd3046eacfb1b..337f9f4d11a44c5ed62bf91fdef5a6cf9e818104 100644 (file)
@@ -131,10 +131,7 @@ public:
   RGWListBuckets_ObjStore_S3() {}
   ~RGWListBuckets_ObjStore_S3() override {}
 
-  int get_params(optional_yield y) override {
-    limit = -1; /* no limit */
-    return 0;
-  }
+  int get_params(optional_yield y) override;
   void send_response_begin(bool has_buckets) override;
   void send_response_data(std::span<const RGWBucketEnt> buckets) override;
   void send_response_end() override;