]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw/multisite: forward create_key request to master, fetch the newly created key
authorShilpa Jagannath <smanjara@redhat.com>
Wed, 30 Jul 2025 19:48:32 +0000 (15:48 -0400)
committerThomas Serlin <tserlin@redhat.com>
Mon, 22 Sep 2025 19:18:18 +0000 (15:18 -0400)
and store it on secondary. also, include 'create_date' in the user info response to
help identify timestamp of each key.

Signed-off-by: Shilpa Jagannath <smanjara@redhat.com>
(cherry picked from commit e46f3324791c8b6d82d3c40be4b0803538d9cb61)

PendingReleaseNotes
qa/suites/rgw/multisite/tasks/test_multi.yaml
src/rgw/driver/rados/rgw_rest_user.cc
src/rgw/driver/rados/rgw_user.cc
src/rgw/driver/rados/rgw_user.h
src/rgw/rgw_common.cc

index ef4f558c40203a15b4350e5ea99bbedb23847184..68ba361bca926c799706b193301f5170841ea6af 100644 (file)
@@ -26,6 +26,8 @@
 * RGW: Bucket resharding now does most of its processing before it starts to block
   write operations. This should significantly reduce the client-visible impact
   of resharding on large buckets.
+* RGW: 'create_key' admin API's response only includes the latest key created instead
+        of all keys.
 
 * RBD: All Python APIs that produce timestamps now return "aware" `datetime`
   objects instead of "naive" ones (i.e. those including time zone information
index 422535db6099c0f6dfafaeed22a31a4e651e1ae7..e7a16c99f25951014edd30eced725e30c0c682c2 100644 (file)
@@ -15,3 +15,6 @@ tasks:
 - rgw-multisite-tests:
     config:
       reconfigure_delay: 90
+- radosgw-admin-rest:
+    admin-client: c1.client.0 # create user on master zone
+    rest-client: c2.client.0 # send admin api requests to secondary zone
index 43206f58c83f836722a40c053998b9464072292a..18c0386c15b0efb6c82d5a3b1c7085e1d33a6ee7 100644 (file)
@@ -35,7 +35,13 @@ int fetch_access_keys_from_master(const DoutPrefixProvider* dpp, req_state* s,
   }
 
   RGWUserInfo ui;
-  ui.decode_json(&jp);
+  try {
+    ui.decode_json(&jp);
+  } catch (const JSONDecoder::err& e) {
+    cout << "failed to decode JSON input: " << e.what() << std::endl;
+    return -EINVAL;
+  }
+
   keys = std::move(ui.access_keys);
   create_date = ui.create_date;
   return 0;
@@ -719,9 +725,6 @@ void RGWOp_Key_Create::execute(optional_yield y)
     op_state.access_key_active = active;
   }
 
-  if (gen_key)
-    op_state.set_generate_key();
-
   if (!key_type_str.empty()) {
     int32_t key_type = KEY_TYPE_UNDEFINED;
     if (key_type_str.compare("swift") == 0)
@@ -732,6 +735,34 @@ void RGWOp_Key_Create::execute(optional_yield y)
     op_state.set_key_type(key_type);
   }
 
+  if (!s->penv.site->is_meta_master()) {
+    bufferlist data;
+    JSONParser jp;
+    int ret = rgw_forward_request_to_master(this, *s->penv.site, s->user->get_id(),
+                                            &data, &jp, s->info, s->err, y);
+    if (ret < 0) {
+      ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << ret << dendl;
+      return;
+    }
+
+    RGWAccessKey key;
+    try {
+      key.decode_json(&jp);
+    } catch (const JSONDecoder::err& e) {
+      cout << "failed to decode JSON input: " << e.what() << std::endl;
+      ret = -EINVAL;
+      return;
+    }
+    op_state.op_master_key = std::move(key);
+
+    // set_generate_key() is not set if keys have already been fetched from master zone
+    gen_key = false;
+  }
+
+  if (gen_key) {
+    op_state.set_generate_key();
+  }
+
   op_ret = RGWUserAdminOp_Key::create(s, driver, op_state, flusher, y);
 }
 
@@ -779,6 +810,13 @@ void RGWOp_Key_Remove::execute(optional_yield y)
     op_state.set_key_type(key_type);
   }
 
+  op_ret = rgw_forward_request_to_master(this, *s->penv.site, s->user->get_id(),
+                                         nullptr, nullptr, s->info, s->err, y);
+  if (op_ret < 0) {
+    ldpp_dout(this, 0) << "forward_request_to_master returned ret=" << op_ret << dendl;
+    return;
+  }
+
   op_ret = RGWUserAdminOp_Key::remove(s, driver, op_state, flusher, y);
 }
 
index cce593c6bd507bdf63bb7db5c8c2fc4a5fd99cf3..ac202823c11fdf1edd590a35d3a502679a4e5752 100644 (file)
@@ -106,11 +106,30 @@ static void dump_access_keys_info(Formatter *f, RGWUserInfo &info)
     f->dump_string("access_key", k.id);
     f->dump_string("secret_key", k.key);
     f->dump_bool("active", k.active);
+    encode_json("create_date", k.create_date, f);
     f->close_section();
   }
   f->close_section();
 }
 
+
+static void dump_master_key(Formatter *f, RGWUserInfo &info)
+{
+  f->open_object_section("user_info");
+  RGWAccessKey& k = info.master_key;
+  const char *sep = (k.subuser.empty() ? "" : ":");
+  const char *subuser = (k.subuser.empty() ? "" : k.subuser.c_str());
+  string s;
+  info.user_id.to_str(s);
+  f->dump_format("user", "%s%s%s", s.c_str(), sep, subuser);
+  f->dump_string("access_key", k.id);
+  f->dump_string("secret_key", k.key);
+  f->dump_bool("active", k.active);
+  encode_json("create_date", k.create_date, f);
+  f->close_section();
+}
+
+
 static void dump_swift_keys_info(Formatter *f, RGWUserInfo &info)
 {
   map<string, RGWAccessKey>::iterator kiter;
@@ -264,6 +283,7 @@ int RGWAccessKeyPool::init(RGWUserAdminOpState& op_state)
 
   swift_keys = op_state.get_swift_keys();
   access_keys = op_state.get_access_keys();
+  master_key = op_state.get_master_key();
 
   keys_allowed = true;
 
@@ -344,6 +364,11 @@ map<std::string, RGWAccessKey>* RGWUserAdminOpState::get_access_keys()
   return &user->get_info().access_keys;
 }
 
+RGWAccessKey *RGWUserAdminOpState::get_master_key()
+{
+  return &user->get_info().master_key;
+}
+
 map<std::string, RGWSubUser>* RGWUserAdminOpState::get_subusers()
 {
   return &user->get_info().subusers;
@@ -489,7 +514,7 @@ int RGWAccessKeyPool::check_op(RGWUserAdminOpState& op_state,
 
   /* see if the access key was specified */
   if (key_type == KEY_TYPE_S3 && !op_state.will_gen_access() && 
-      op_state.get_access_key().empty()) {
+      op_state.get_access_key().empty() && op_state.get_master_access_key().empty()) {
     set_err_msg(err_msg, "empty access key");
     return -ERR_INVALID_ACCESS_KEY;
   }
@@ -591,7 +616,7 @@ int RGWAccessKeyPool::generate_key(const DoutPrefixProvider *dpp, RGWUserAdminOp
 
   //Secret key
   if (!gen_secret) {
-    if (op_state.get_secret_key().empty()) {
+    if (op_state.get_secret_key().empty() && op_state.get_master_secret_key().empty()) {
       set_err_msg(err_msg, "empty secret key");
       return -ERR_INVALID_SECRET_KEY;
     }
@@ -625,8 +650,6 @@ int RGWAccessKeyPool::generate_key(const DoutPrefixProvider *dpp, RGWUserAdminOp
   }
 
   // finally create the new key
-  new_key.id = id;
-  new_key.key = key;
 
   if (op_state.create_date) {
     new_key.create_date = *op_state.create_date;
@@ -635,7 +658,16 @@ int RGWAccessKeyPool::generate_key(const DoutPrefixProvider *dpp, RGWUserAdminOp
   }
 
   if (key_type == KEY_TYPE_S3) {
-    access_keys->emplace(id, new_key);
+    if (!op_state.get_master_access_key().empty()) {
+      // use the key fetched from master
+      new_key = op_state.op_master_key;
+      access_keys->emplace(new_key.id, new_key);
+    } else {
+      new_key.id = id;
+      new_key.key = key;
+      access_keys->emplace(id, new_key);
+    }
+    *master_key = new_key;
   } else if (key_type == KEY_TYPE_SWIFT) {
     swift_keys->emplace(id, new_key);
   }
@@ -2610,8 +2642,9 @@ int RGWUserAdminOp_Key::create(const DoutPrefixProvider *dpp,
     if (key_type == KEY_TYPE_SWIFT)
       dump_swift_keys_info(formatter, info);
 
-    else if (key_type == KEY_TYPE_S3)
-      dump_access_keys_info(formatter, info);
+    else if (key_type == KEY_TYPE_S3) {
+      dump_master_key(formatter, info);
+    }
 
     flusher.flush();
   }
index 4ae7d13eff75d162c78aee7c76d1f5ffcfdbcfad..bda4858b2967101a4036a4cbef6f3cc73a821e32 100644 (file)
@@ -145,8 +145,10 @@ struct RGWUserAdminOpState {
   // key_attributes
   std::string id; // access key
   std::string key; // secret key
-  // access keys fetched for a user in the middle of an op
+  // access keys fetched for a user in the middle of a create_user op
   std::map<std::string, RGWAccessKey> op_access_keys;
+  // access key fetched for a user in the middle of a create_key op
+  RGWAccessKey op_master_key;
   int32_t key_type{-1};
   bool access_key_exist = false;
   std::optional<bool> access_key_active;
@@ -445,6 +447,8 @@ struct RGWUserAdminOpState {
   std::string get_subuser() { return subuser; }
   std::string get_access_key() { return id; }
   std::string get_secret_key() { return key; }
+  std::string get_master_access_key() { return op_master_key.id; }
+  std::string get_master_secret_key() { return op_master_key.key; }
   std::string get_caps() { return caps; }
   std::string get_user_email() { return user_email; }
   std::string get_display_name() { return display_name; }
index 50057d144ee687eb879c4dd322f8037486be896f..290f73d0c7e1d004467435f172f660ab68fda39b 100644 (file)
@@ -2852,7 +2852,6 @@ void RGWUserInfo::dump(Formatter *f) const
   encode_json_map("subusers", NULL, "subuser", NULL, user_info_dump_subuser,(void *)this, subusers, f);
   encode_json_map("keys", NULL, "key", NULL, user_info_dump_key,(void *)this, access_keys, f);
   encode_json_map("swift_keys", NULL, "key", NULL, user_info_dump_swift_key,(void *)this, swift_keys, f);
-
   encode_json("caps", caps, f);
 
   char buf[256];