]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw/logging: rollover objects when conf changes 65456/head
authorYuval Lifshitz <ylifshit@ibm.com>
Tue, 9 Sep 2025 17:51:29 +0000 (17:51 +0000)
committerYuval Lifshitz <ylifshit@ibm.com>
Wed, 10 Sep 2025 15:12:24 +0000 (15:12 +0000)
and return the name of the flushed object to the client

Fixes: https://tracker.ceph.com/issues/72940
Signed-off-by: Yuval Lifshitz <ylifshit@ibm.com>
doc/radosgw/s3/bucketops.rst
examples/rgw/boto3/service-2.sdk-extras.json
src/rgw/rgw_bucket_logging.cc
src/rgw/rgw_bucket_logging.h
src/rgw/rgw_rest_bucket_logging.cc

index 21f834afdf5dd3e8d487f681ef9f51d17dec97a0..e0c02f292326da84f3a20902c1c4f51e3e28ab13 100644 (file)
@@ -651,7 +651,7 @@ Parameters
 Response Entities
 ~~~~~~~~~~~~~~~~~
 
-Response is XML encoded in the body of the request, in the following format:
+The response is XML encoded in the body of the request, in the following format:
 
 ::
 
@@ -790,7 +790,7 @@ Parameters are XML encoded in the body of the request, in the following format:
 |                               |           | between different source buckets writing log records to the same log bucket.         |          |
 +-------------------------------+-----------+--------------------------------------------------------------------------------------+----------+
 | ``LoggingType``               | String    | The type of logging. Valid values are:                                               | No       |
-|                               |           | ``Standard`` (default) all bucket operations are logged after being perfomed.        |          |
+|                               |           | ``Standard`` (default) all bucket operations are logged after being performed.        |          |
 |                               |           | The log record will contain all fields.                                              |          |
 |                               |           | ``Journal`` only operations that modify and object are logged.                       |          |
 |                               |           | Will record the minimum subset of fields in the log record that is needed            |          |
@@ -800,6 +800,18 @@ Parameters are XML encoded in the body of the request, in the following format:
 |                               |           | object added to the log bucket. Default is 3600 seconds (1 hour).                    |          |
 +-------------------------------+-----------+--------------------------------------------------------------------------------------+----------+
 
+Response Entities
+~~~~~~~~~~~~~~~~~
+
+The response is XML encoded in the body of the request, only if a configuration change triggers flushing of the current logging object.
+In this case it will return the name of the flushed logging object in following format:
+
+::
+
+  <PostBucketLoggingOutput xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
+    <FlushedLoggingObject>string</FlushedLoggingObject>
+  </PostBucketLoggingOutput>
+
 
 HTTP Response
 ~~~~~~~~~~~~~
@@ -864,7 +876,7 @@ Syntax
 Response Entities
 ~~~~~~~~~~~~~~~~~
 
-Response header contains ``Last-Modified`` date/time of the logging configuration.
+The response header contains ``Last-Modified`` date/time of the logging configuration.
 Logging configuration is XML encoded in the body of the response, in the following format:
 
 ::
@@ -931,7 +943,7 @@ Syntax
 Response Entities
 ~~~~~~~~~~~~~~~~~
 
-Response is XML encoded in the body of the request, in the following format:
+The response is XML encoded in the body of the request, in the following format:
 
 ::
 
index 6812900143e1be59f0ef1ee967f66e27a2e770fd..47e369bb585ed915cd3ec3a95c3461e45f94bd4d 100644 (file)
             "output": {"shape": "PostBucketLoggingOutput"},
             "documentationUrl":"https://docs.ceph.com/docs/master/radosgw/s3/bucketops/#post-bucket-logging",
             "documentation":"<p>Flushes the logging objects of the buckets.</p>"
+        },
+                       "PutBucketLogging":{
+               "name":"PutBucketLogging",
+               "http":{
+                       "method":"PUT",
+                       "requestUri":"/{Bucket}?logging"
+               },
+               "input":{"shape":"PutBucketLoggingRequest"},
+          "output": {"shape": "PutBucketLoggingOutput"},
+          "documentationUrl":"https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutBucketLogging.html",
+          "documentation":"<p>Put bucket logging configuration on source bucket.</p>"
         },
         "GetUsageStats":{
             "name":"GetUsageStats",
               }
             }
         },
+        "PutBucketLoggingOutput": {
+            "type":"structure",
+            "members":{
+              "FlushedLoggingObject": {
+                "shape":"FlushedLoggingObject",
+                "documentation":"<p>Name of the pending logging object that was flushed.</p>"
+              }
+            }
+        },
         "FlushedLoggingObject":{
             "type":"string"
         },
index fa3eaf429585a6cbbe54e99e62b59e162942c7c0..871491b6d212e0594097314fa3cce0278a63551e 100644 (file)
@@ -837,14 +837,15 @@ int bucket_deletion_cleanup(const DoutPrefixProvider* dpp,
     }
   }
 
-  return source_bucket_cleanup(dpp, driver, bucket, false, y);
+  return source_bucket_cleanup(dpp, driver, bucket, false, y, nullptr);
 }
 
 int source_bucket_cleanup(const DoutPrefixProvider* dpp,
                                    sal::Driver* driver,
                                    sal::Bucket* bucket,
                                    bool remove_attr,
-                                   optional_yield y) {
+                                   optional_yield y,
+                                   std::string* last_committed) {
   std::optional<configuration> conf;
   if (const int ret = retry_raced_bucket_write(dpp, bucket, [dpp, bucket, &conf, remove_attr, y] {
     auto& attrs = bucket->get_attrs();
@@ -879,7 +880,7 @@ int source_bucket_cleanup(const DoutPrefixProvider* dpp,
     return 0;
   }
   const auto& info = bucket->get_info();
-  if (const int ret = commit_logging_object(*conf, dpp, driver, info.bucket.tenant, y, nullptr); ret < 0) {
+  if (const int ret = commit_logging_object(*conf, dpp, driver, info.bucket.tenant, y, last_committed); ret < 0) {
     ldpp_dout(dpp, 5) << "WARNING: could not commit pending logging object of bucket '" <<
       bucket->get_key() << "' during cleanup. ret = " << ret << dendl;
   } else {
index f27f296f53e0e9698b7005519b7e058fe90351c5..a5cd5552334681b77fb0fce7bf50f1ff4b707a21 100644 (file)
@@ -251,11 +251,13 @@ int bucket_deletion_cleanup(const DoutPrefixProvider* dpp,
 // in addition:
 // any pending log objects should be comitted to the log bucket
 // and the log bucket should be updated to remove the bucket as a source
+// if "last_committed" is not null, it will be set to the name of the last committed object
 int source_bucket_cleanup(const DoutPrefixProvider* dpp,
                                    sal::Driver* driver,
                                    sal::Bucket* bucket,
                                    bool remove_attr,
-                                   optional_yield y);
+                                   optional_yield y,
+                                   std::string* last_committed);
 
 // verify that the target bucket has the correct policy to allow the source bucket to log to it
 // note that this function adds entries to the request state environment
index 28294119456db51b773ba38f33fb2f9a1a59b2c2..6a3f3cd29c0ca1e2b89de87161d2e22f0975fe65 100644 (file)
@@ -111,6 +111,7 @@ public:
   }
 
   void send_response() override {
+    set_req_state_err(s, op_ret);
     dump_errno(s);
     if (mtime) {
       dump_last_modified(s, *mtime);
@@ -123,6 +124,7 @@ public:
     s->formatter->close_section();
     rgw_flush_formatter_and_reset(s, s->formatter);
   }
+
   const char* name() const override { return "get_bucket_logging"; }
   std::string canonical_name() const override { return fmt::format("REST.{}.LOGGING", s->info.method); }
   RGWOpType get_type() override { return RGW_OP_GET_BUCKET_LOGGING; }
@@ -136,6 +138,7 @@ class RGWPutBucketLoggingOp : public RGWDefaultResponseOp {
   // and usd in execute()
   rgw::bucketlogging::configuration configuration;
   std::unique_ptr<rgw::sal::Bucket> target_bucket;
+  std::string old_obj; // used when conf change triggers a rollover
 
   int init_processing(optional_yield y) override {
     if (const auto ret = verify_bucket_logging_params(this, s); ret < 0) {
@@ -234,7 +237,7 @@ class RGWPutBucketLoggingOp : public RGWDefaultResponseOp {
     }
 
     if (!configuration.enabled) {
-      op_ret = rgw::bucketlogging::source_bucket_cleanup(this, driver, src_bucket.get(), true, y);
+      op_ret = rgw::bucketlogging::source_bucket_cleanup(this, driver, src_bucket.get(), true, y, &old_obj);
       return;
     }
 
@@ -306,12 +309,27 @@ class RGWPutBucketLoggingOp : public RGWDefaultResponseOp {
       }
     } else if (*old_conf != configuration) {
       // conf changed - do cleanup
-      if (const auto ret = commit_logging_object(*old_conf, target_bucket, this, y, nullptr); ret < 0) {
-        ldpp_dout(this, 1) << "WARNING: could not commit pending logging object when updating logging configuration of bucket '" <<
-          src_bucket->get_key() << "', ret = " << ret << dendl;
+      RGWObjVersionTracker objv_tracker;
+      std::string obj_name;
+      const auto region = driver->get_zone()->get_zonegroup().get_api_name();
+      if (const auto ret = rollover_logging_object(*old_conf,
+            target_bucket,
+            obj_name,
+            this,
+            region,
+            src_bucket,
+            y,
+            false, // rollover should happen even if commit failed
+            &objv_tracker,
+            &old_obj); ret < 0) {
+        ldpp_dout(this, 1) << "WARNING: failed to flush pending logging object '" << obj_name << "'"
+            << " to target bucket '" << target_bucket_id << "'. "
+            << " last committed object is '" << old_obj <<
+            "' when updating logging configuration of bucket '" << src_bucket->get_key() << ". error: " << ret << dendl;
       } else {
-        ldpp_dout(this, 20) << "INFO: committed pending logging object when updating logging configuration of bucket '" <<
-          src_bucket->get_key() << "'" << dendl;
+        ldpp_dout(this, 20) << "INFO: flushed pending logging object '" << old_obj
+          << "' to target bucket '" << target_bucket_id << "' when updating logging configuration of bucket '"
+          << src_bucket->get_key() << "'" << dendl;
       }
       if (old_conf->target_bucket != configuration.target_bucket) {
         rgw_bucket old_target_bucket_id;
@@ -334,6 +352,19 @@ class RGWPutBucketLoggingOp : public RGWDefaultResponseOp {
       ldpp_dout(this, 20) << "INFO: logging configuration of bucket '" << src_bucket_id << "' did not change" << dendl;
     }
   }
+
+  void send_response() override {
+    set_req_state_err(s, op_ret);
+    dump_errno(s);
+    end_header(s, this, to_mime_type(s->format));
+    if (!old_obj.empty()) {
+      dump_start(s);
+      s->formatter->open_object_section_in_ns("PutBucketLoggingOutput", XMLNS_AWS_S3);
+      s->formatter->dump_string("FlushedLoggingObject", old_obj);
+      s->formatter->close_section();
+      rgw_flush_formatter_and_reset(s, s->formatter);
+    }
+  }
 };
 
 // Post /<bucket name>/?logging
@@ -407,6 +438,7 @@ class RGWPostBucketLoggingOp : public RGWDefaultResponseOp {
   }
 
   void send_response() override {
+    set_req_state_err(s, op_ret);
     dump_errno(s);
     end_header(s, this, to_mime_type(s->format));
     dump_start(s);