This is an automated email from the ASF dual-hosted git repository.

laiyingchun pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new f33757512 [multi-tenancy] add a server key upgrade tool
f33757512 is described below

commit f337575127c8cfb73b684998866478bc7f0a8e1c
Author: kedeng <[email protected]>
AuthorDate: Wed Jun 21 20:14:44 2023 +0800

    [multi-tenancy] add a server key upgrade tool
    
    For the purpose of easily upgrading the server key to the
    tenant key, I have implemented the corresponding function
    in this patch.
    
    With the help of a command-line tool, such as
    `kudu fs upgrade_encryption_key --fs_data_dirs=xx --fs_wal_dir=xx 
--fs_metadata_dir=xx`,
    we can easily complete the metadata upgrade for a single
    tserver. After all tservers have been upgraded, the previous
    server key info will be converted into the encrypted information
    of the default tenant, and the metadata will no longer retain
    the previous server key info.
    
    In addition, after the upgrade is completed, the multi-tenant
    enable flag '--enable_multi_tenancy' needs to be set to true to
    complete the entire upgrade process.
    
    I have also added unit tests for this process to ensure that
    this logic can be executed correctly.
    
    Change-Id: I2fe50fdcdf294b5955756dfcb92d3b627534bcc7
    Reviewed-on: http://gerrit.cloudera.org:8080/20103
    Reviewed-by: Yingchun Lai <[email protected]>
    Tested-by: Yingchun Lai <[email protected]>
---
 src/kudu/fs/fs_manager.cc        |  11 +++++
 src/kudu/fs/fs_manager.h         |  12 +++++
 src/kudu/tools/kudu-tool-test.cc | 104 ++++++++++++++++++++++++++++++++++++++-
 src/kudu/tools/tool_action_fs.cc |  71 ++++++++++++++++++++++++++
 4 files changed, 197 insertions(+), 1 deletion(-)

diff --git a/src/kudu/fs/fs_manager.cc b/src/kudu/fs/fs_manager.cc
index 6eb35c48f..1efe2ce43 100644
--- a/src/kudu/fs/fs_manager.cc
+++ b/src/kudu/fs/fs_manager.cc
@@ -624,6 +624,12 @@ Status FsManager::Open(FsReport* report, Timer* 
read_instance_metadata_files,
   return Status::OK();
 }
 
+void FsManager::CopyMetadata(
+    unique_ptr<InstanceMetadataPB>* metadata) {
+  shared_lock<rw_spinlock> md_lock(metadata_rwlock_.get_lock());
+  (*metadata)->CopyFrom(*metadata_);
+}
+
 Status FsManager::UpdateMetadata(unique_ptr<InstanceMetadataPB> metadata) {
   // In the event of failure, rollback everything we changed.
   // <string, string>   <=>   <old instance file, backup instance file>
@@ -927,6 +933,11 @@ const string& FsManager::server_key_version() const {
   return CHECK_NOTNULL(metadata_.get())->server_key_version();
 }
 
+const int32_t FsManager::tenants_count() const {
+  shared_lock<rw_spinlock> md_lock(metadata_rwlock_.get_lock());
+  return metadata_->tenants_size();
+}
+
 bool FsManager::is_tenants_exist() const {
   shared_lock<rw_spinlock> md_lock(metadata_rwlock_.get_lock());
   return metadata_->tenants_size() > 0;
diff --git a/src/kudu/fs/fs_manager.h b/src/kudu/fs/fs_manager.h
index b003e6a80..3313d7d19 100644
--- a/src/kudu/fs/fs_manager.h
+++ b/src/kudu/fs/fs_manager.h
@@ -82,6 +82,10 @@ namespace tserver {
 class MiniTabletServerTest_TestFsLayoutEndToEnd_Test;
 } // namespace tserver
 
+namespace tools {
+Status UpdateEncryptionKeyInfo(Env* env);
+} // namespace tools
+
 // Options that control the behavior of FsManager.
 struct FsManagerOpts {
   // Creates a new FsManagerOpts with default values.
@@ -307,10 +311,17 @@ class FsManager {
   // Open() have not been called, this will crash.
   const std::string& uuid() const;
 
+  // Copy the metadata_ to metadata.
+  void CopyMetadata(
+    std::unique_ptr<InstanceMetadataPB>* metadata);
+
   // ==========================================================================
   //  tenant helpers
   // ==========================================================================
 
+  // Use to get the total count of all the tenants.
+  const int32_t tenants_count() const;
+
   // Use to confirm whether there is tenants information in metadata.
   bool is_tenants_exist() const;
 
@@ -390,6 +401,7 @@ class FsManager {
   FRIEND_TEST(fs::FsManagerTestBase, TestOpenWithDuplicateInstanceFiles);
   FRIEND_TEST(tserver::MiniTabletServerTest, TestFsLayoutEndToEnd);
   friend class itest::MiniClusterFsInspector; // for access to directory names
+  friend Status tools::UpdateEncryptionKeyInfo(Env* env); // for update the 
metadata
 
   // Initializes, sanitizes, and canonicalizes the filesystem roots.
   // Determines the correct filesystem root for tablet-specific metadata.
diff --git a/src/kudu/tools/kudu-tool-test.cc b/src/kudu/tools/kudu-tool-test.cc
index 9bcbd91ab..ddcac9dbe 100644
--- a/src/kudu/tools/kudu-tool-test.cc
+++ b/src/kudu/tools/kudu-tool-test.cc
@@ -70,6 +70,7 @@
 #include "kudu/consensus/ref_counted_replicate.h"
 #include "kudu/fs/block_id.h"
 #include "kudu/fs/block_manager.h"
+#include "kudu/fs/dir_manager.h"
 #include "kudu/fs/fs_manager.h"
 #include "kudu/fs/fs_report.h"
 #include "kudu/fs/log_block_manager.h"
@@ -145,6 +146,7 @@ DECLARE_bool(allow_unsafe_replication_factor);
 DECLARE_bool(catalog_manager_check_ts_count_for_create_table);
 DECLARE_bool(enable_tablet_orphaned_block_deletion);
 DECLARE_bool(encrypt_data_at_rest);
+DECLARE_bool(enable_multi_tenancy);
 DECLARE_bool(disable_gflag_filter_logic_for_testing);
 DECLARE_bool(fs_data_dirs_consider_available_space);
 DECLARE_bool(hive_metastore_sasl_enabled);
@@ -1361,8 +1363,9 @@ TEST_F(ToolTest, TestModeHelp) {
         "check.*Kudu filesystem for inconsistencies",
         "dump.*Dump a Kudu filesystem",
         "format.*new Kudu filesystem",
-        "list.*List metadata for on-disk tablets, rowsets, blocks",
+        "list.*List metadata for on-disk tablets, rowsets",
         "update_dirs.*Updates the set of data directories",
+        "upgrade_encryption_key.*Upgrade the encryption key info",
     };
     NO_FATALS(RunTestHelp("fs", kFsModeRegexes));
     NO_FATALS(RunTestHelp("fs not_a_mode", kFsModeRegexes,
@@ -8089,6 +8092,105 @@ TEST_F(ToolTest, 
TestFsSwappingDirectoriesFailsGracefully) {
       wal_root, JoinStrings(new_data_roots, ","))));
 }
 
+TEST_F(ToolTest, 
TestFsUpgradeEncryptionKeyFromServerKeyInfoAndFromTenantKeyInfo) {
+  // Create a cluster with server key info.
+  FLAGS_encrypt_data_at_rest = true;
+
+  NO_FATALS(StartMiniCluster());
+  MiniTabletServer* mts = mini_cluster_->mini_tablet_server(0);
+  const string& wal_root = mts->options()->fs_opts.wal_root;
+  vector<string> data_roots = mts->options()->fs_opts.data_roots;
+  const string& metadata_root = mts->options()->fs_opts.metadata_root;
+
+  // Obtain source encryption server key info.
+  FsManager* fs_manager = mts->server()->fs_manager();
+  ASSERT_FALSE(fs_manager->server_key().empty());
+  const string source_key = fs_manager->server_key();
+  ASSERT_FALSE(fs_manager->server_key_iv().empty());
+  const string source_key_iv = fs_manager->server_key_iv();
+  ASSERT_FALSE(fs_manager->server_key_version().empty());
+  const string source_key_version = fs_manager->server_key_version();
+  // No tenant key info exist.
+  ASSERT_FALSE(fs_manager->is_tenants_exist());
+
+  mts->Shutdown();
+
+  // Do the encryption key info upgrade with the kudu CLI tool.
+  // Providing the necessary arguments, the tool should work.
+  string stdout;
+  NO_FATALS(RunActionStdoutString(Substitute(
+      "fs upgrade_encryption_key --fs_data_dirs=$0 --fs_wal_dir=$1 
--fs_metadata_dir=$2",
+       JoinStrings(data_roots, ","), wal_root, metadata_root), &stdout));
+  SCOPED_TRACE(stdout);
+
+  // This flag needs to set true after the upgrading.
+  FLAGS_enable_multi_tenancy = true;
+  // Start the tserver and check the encryption key info.
+  ASSERT_OK(mts->Start());
+  ASSERT_OK(mts->WaitStarted());
+
+  {
+    FsManager* fs_manager = mts->server()->fs_manager();
+    // The default tenant key info corresponds to the previous server key 
information.
+    ASSERT_TRUE(fs_manager->is_tenants_exist());
+    ASSERT_TRUE(fs_manager->is_tenant_exist(fs::kDefaultTenantName));
+    // Make sure there is only one tenant exist.
+    ASSERT_EQ(1, fs_manager->tenants_count());
+    ASSERT_FALSE(fs_manager->tenant_id(fs::kDefaultTenantName).empty());
+    ASSERT_EQ(fs::kDefaultTenantID, 
fs_manager->tenant_id(fs::kDefaultTenantName));
+    ASSERT_FALSE(fs_manager->tenant_key(fs::kDefaultTenantName).empty());
+    ASSERT_EQ(source_key, fs_manager->tenant_key(fs::kDefaultTenantName));
+    ASSERT_FALSE(fs_manager->tenant_key_iv(fs::kDefaultTenantName).empty());
+    ASSERT_EQ(source_key_iv, 
fs_manager->tenant_key_iv(fs::kDefaultTenantName));
+    
ASSERT_FALSE(fs_manager->tenant_key_version(fs::kDefaultTenantName).empty());
+    ASSERT_EQ(source_key_version, 
fs_manager->tenant_key_version(fs::kDefaultTenantName));
+
+    // There is no server key in the metadata.
+    ASSERT_TRUE(fs_manager->server_key().empty());
+    ASSERT_TRUE(fs_manager->server_key_iv().empty());
+    ASSERT_TRUE(fs_manager->server_key_version().empty());
+  }
+
+  // We are in tenant key version, try to 'upgrade_encryption_key' from tenant 
key info.
+  mts->Shutdown();
+
+  string err;
+  RunActionStderrString(Substitute(
+      "fs upgrade_encryption_key --fs_data_dirs=$0 --fs_wal_dir=$1 
--fs_metadata_dir=$2",
+       JoinStrings(data_roots, ","), wal_root, metadata_root), &err);
+  // This upgrade tool only works on the cluster with server key info exist.
+  ASSERT_STR_CONTAINS(err, "We should not do server key upgrade on a cluster");
+}
+
+TEST_F(ToolTest, TestFsUpgradeEncryptionKeyFromNoEncryptionKeyInfo) {
+  // Create a cluster without server key info.
+  FLAGS_encrypt_data_at_rest = false;
+
+  NO_FATALS(StartMiniCluster());
+  MiniTabletServer* mts = mini_cluster_->mini_tablet_server(0);
+  const string& wal_root = mts->options()->fs_opts.wal_root;
+  vector<string> data_roots = mts->options()->fs_opts.data_roots;
+  const string& metadata_root = mts->options()->fs_opts.metadata_root;
+
+  // No server key info exist.
+  FsManager* fs_manager = mts->server()->fs_manager();
+  ASSERT_TRUE(fs_manager->server_key().empty());
+  ASSERT_TRUE(fs_manager->server_key_iv().empty());
+  ASSERT_TRUE(fs_manager->server_key_version().empty());
+  // No tenant key info exist.
+  ASSERT_FALSE(fs_manager->is_tenants_exist());
+
+  mts->Shutdown();
+
+  // Do the encryption key info upgrade for a tserver without server key with 
the kudu CLI tool.
+  string err;
+  RunActionStderrString(Substitute(
+      "fs upgrade_encryption_key --fs_data_dirs=$0 --fs_wal_dir=$1 
--fs_metadata_dir=$2",
+       JoinStrings(data_roots, ","), wal_root, metadata_root), &err);
+  // This upgrade tool only works on the cluster with server key info exist.
+  ASSERT_STR_CONTAINS(err, "We should not do server key upgrade on a cluster");
+}
+
 TEST_F(ToolTest, TestStartEndMaintenanceMode) {
   NO_FATALS(StartMiniCluster());
   // Perform the steps on a tserver that exists and one that doesn't.
diff --git a/src/kudu/tools/tool_action_fs.cc b/src/kudu/tools/tool_action_fs.cc
index 48f0f7cd8..b0900ec85 100644
--- a/src/kudu/tools/tool_action_fs.cc
+++ b/src/kudu/tools/tool_action_fs.cc
@@ -73,6 +73,7 @@
 #include "kudu/util/flag_validators.h"
 #include "kudu/util/memory/arena.h"
 #include "kudu/util/pb_util.h"
+#include "kudu/util/scoped_cleanup.h"
 #include "kudu/util/slice.h"
 #include "kudu/util/status.h"
 #include "kudu/util/string_case.h"
@@ -161,6 +162,7 @@ using kudu::tablet::TabletDataState;
 using kudu::tablet::TabletMetadata;
 using std::cout;
 using std::endl;
+using std::move;
 using std::nullopt;
 using std::optional;
 using std::shared_ptr;
@@ -173,6 +175,45 @@ using strings::Substitute;
 namespace kudu {
 namespace tools {
 
+Status UpdateEncryptionKeyInfo(Env* env) {
+  FsManagerOpts fs_opts;
+  fs_opts.update_instances = 
UpdateInstanceBehavior::UPDATE_AND_ERROR_ON_FAILURE;
+  fs_opts.skip_block_manager = true;
+  FsManager fs_manager(env, std::move(fs_opts));
+  RETURN_NOT_OK(fs_manager.Open());
+
+  if (fs_manager.server_key().empty()) {
+    return Status::IllegalState(
+        "We should not do server key upgrade on a cluster with data rest 
encryption disabled.");
+  }
+
+  if (fs_manager.is_tenants_exist()) {
+    cout << "This tserver has already in tenant key version, and there is no 
need to upgrade again."
+         << endl;
+    return Status::OK();
+  }
+
+  unique_ptr<InstanceMetadataPB> metadata(new InstanceMetadataPB);
+  fs_manager.CopyMetadata(&metadata);
+
+  // Add the default tenant key with the server key.
+  InstanceMetadataPB::TenantMetadataPB* tenant_metadata = 
metadata->add_tenants();
+  tenant_metadata->set_tenant_name(fs::kDefaultTenantName);
+  tenant_metadata->set_tenant_id(fs::kDefaultTenantID);
+  tenant_metadata->set_tenant_key(fs_manager.server_key());
+  tenant_metadata->set_tenant_key_iv(fs_manager.server_key_iv());
+  tenant_metadata->set_tenant_key_version(fs_manager.server_key_version());
+  // Clear the server key info.
+  metadata->clear_server_key();
+  metadata->clear_server_key_iv();
+  metadata->clear_server_key_version();
+
+  // Write the new metadata to disk.
+  RETURN_NOT_OK(fs_manager.UpdateMetadata(move(metadata)));
+
+  return Status::OK();
+}
+
 namespace {
 
 Status Check(const RunnerContext& /*context*/) {
@@ -452,6 +493,22 @@ Status Update(const RunnerContext& /*context*/) {
   return fs.Open();
 }
 
+Status UpgradeEncryptionKey(const RunnerContext& /*context*/) {
+  Env* env = Env::Default();
+  const string& filename = "/tmp/kudu_tserver-metadata-upgrade-instance.lock";
+  FileLock* file_lock;
+  KUDU_RETURN_NOT_OK_PREPEND(env->LockFile(filename, &file_lock),
+                             "Could not lock instance file. Make sure that "
+                             "this tool is not already running.");
+  auto unlock = MakeScopedCleanup([&]() {
+    env->UnlockFile(file_lock);
+  });
+
+  RETURN_NOT_OK(UpdateEncryptionKeyInfo(env));
+
+  return Status::OK();
+}
+
 namespace {
 
 // The 'kudu fs list' column fields.
@@ -999,6 +1056,19 @@ unique_ptr<Mode> BuildFsMode() {
       .AddOptionalParameter("h")
       .Build();
 
+  unique_ptr<Action> upgrade_encryption_key =
+      ActionBuilder("upgrade_encryption_key", &UpgradeEncryptionKey)
+      .Description("Upgrade the encryption key info in metadata")
+      .ExtraDescription("Upgrade the server key to the tenant key which 
belongs to the default "
+                        "tenant. This feature only works on a cluster with 
data rest encryption "
+                        "enabled.\n\n"
+                        "Note: this function is exclusively for use with the 
Kudu CLI tool and can "
+                        "only be used once per tserver.")
+      .AddOptionalParameter("fs_data_dirs")
+      .AddOptionalParameter("fs_metadata_dir")
+      .AddOptionalParameter("fs_wal_dir")
+      .Build();
+
   return ModeBuilder("fs")
       .Description("Operate on a local Kudu filesystem")
       .AddMode(BuildFsDumpMode())
@@ -1006,6 +1076,7 @@ unique_ptr<Mode> BuildFsMode() {
       .AddAction(std::move(format))
       .AddAction(std::move(list))
       .AddAction(std::move(update))
+      .AddAction(std::move(upgrade_encryption_key))
       .Build();
 }
 

Reply via email to