From 824a7e30c85139ecda47fc20eea15a7982fdab2f Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Sun, 19 Nov 2023 11:32:37 -0500 Subject: [PATCH] test/cls: add ceph_test_cls_user Signed-off-by: Casey Bodley (cherry picked from commit a49757e5c3e7580399802345babd2dde4e3e661b) --- qa/suites/rgw/verify/tasks/cls.yaml | 1 + qa/workunits/cls/test_cls_user.sh | 5 + src/test/CMakeLists.txt | 1 + src/test/cls_user/CMakeLists.txt | 5 + src/test/cls_user/test_cls_user.cc | 211 ++++++++++++++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100755 qa/workunits/cls/test_cls_user.sh create mode 100644 src/test/cls_user/CMakeLists.txt create mode 100644 src/test/cls_user/test_cls_user.cc diff --git a/qa/suites/rgw/verify/tasks/cls.yaml b/qa/suites/rgw/verify/tasks/cls.yaml index 568395a0dd0..8034715353f 100644 --- a/qa/suites/rgw/verify/tasks/cls.yaml +++ b/qa/suites/rgw/verify/tasks/cls.yaml @@ -10,6 +10,7 @@ tasks: - cls/test_cls_rgw_stats.sh - cls/test_cls_cmpomap.sh - cls/test_cls_2pc_queue.sh + - cls/test_cls_user.sh - rgw/test_rgw_gc_log.sh - rgw/test_rgw_obj.sh - rgw/test_librgw_file.sh diff --git a/qa/workunits/cls/test_cls_user.sh b/qa/workunits/cls/test_cls_user.sh new file mode 100755 index 00000000000..9df1f4b9251 --- /dev/null +++ b/qa/workunits/cls/test_cls_user.sh @@ -0,0 +1,5 @@ +#!/bin/sh -e + +ceph_test_cls_user + +exit 0 diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 8f57b2db9b6..a0c8fcfe823 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -49,6 +49,7 @@ if(NOT WIN32) if(WITH_RADOSGW) add_subdirectory(cls_rgw) add_subdirectory(cls_rgw_gc) + add_subdirectory(cls_user) endif(WITH_RADOSGW) add_subdirectory(cls_refcount) add_subdirectory(cls_version) diff --git a/src/test/cls_user/CMakeLists.txt b/src/test/cls_user/CMakeLists.txt new file mode 100644 index 00000000000..9796205d1e6 --- /dev/null +++ b/src/test/cls_user/CMakeLists.txt @@ -0,0 +1,5 @@ +add_executable(ceph_test_cls_user test_cls_user.cc + $) +target_link_libraries(ceph_test_cls_user cls_user_client global + librados ${UNITTEST_LIBS} radostest-cxx) +install(TARGETS ceph_test_cls_user DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/test/cls_user/test_cls_user.cc b/src/test/cls_user/test_cls_user.cc new file mode 100644 index 00000000000..c37f7a8e44c --- /dev/null +++ b/src/test/cls_user/test_cls_user.cc @@ -0,0 +1,211 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright contributors to the Ceph project + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + */ + +#include "cls/user/cls_user_client.h" +#include "test/librados/test_cxx.h" +#include "gtest/gtest.h" + +#include +#include +#include "include/expected.hpp" + +// create/destroy a pool that's shared by all tests in the process +struct RadosEnv : public ::testing::Environment { + static std::optional pool_name; + public: + static librados::Rados rados; + static librados::IoCtx ioctx; + + void SetUp() override { + // create pool + std::string name = get_temp_pool_name(); + ASSERT_EQ("", create_one_pool_pp(name, rados)); + pool_name = name; + ASSERT_EQ(rados.ioctx_create(name.c_str(), ioctx), 0); + } + void TearDown() override { + ioctx.close(); + if (pool_name) { + ASSERT_EQ(destroy_one_pool_pp(*pool_name, rados), 0); + } + } +}; +std::optional RadosEnv::pool_name; +librados::Rados RadosEnv::rados; +librados::IoCtx RadosEnv::ioctx; + +auto *const rados_env = ::testing::AddGlobalTestEnvironment(new RadosEnv); + +// test fixture with helper functions +class ClsAccount : public ::testing::Test { + protected: + librados::IoCtx& ioctx = RadosEnv::ioctx; + + int add(const std::string& oid, const cls_user_account_resource& entry, + bool exclusive, uint32_t limit) + { + librados::ObjectWriteOperation op; + cls_user_account_resource_add(op, entry, exclusive, limit); + return ioctx.operate(oid, &op); + } + + auto get(const std::string& oid, std::string_view name) + -> tl::expected + { + librados::ObjectReadOperation op; + cls_user_account_resource resource; + int r2 = 0; + cls_user_account_resource_get(op, name, resource, &r2); + + int r1 = ioctx.operate(oid, &op, nullptr); + if (r1 < 0) return tl::unexpected(r1); + if (r2 < 0) return tl::unexpected(r2); + return resource; + } + + int rm(const std::string& oid, std::string_view name) + { + librados::ObjectWriteOperation op; + cls_user_account_resource_rm(op, name); + return ioctx.operate(oid, &op); + } + + int list(const std::string& oid, std::string_view marker, + std::string_view path_prefix, uint32_t max_entries, + std::vector& entries, bool& truncated, + std::string& next_marker, int& ret) + { + librados::ObjectReadOperation op; + cls_user_account_resource_list(op, marker, path_prefix, max_entries, + entries, &truncated, &next_marker, &ret); + return ioctx.operate(oid, &op, nullptr); + } + + auto list_all(const std::string& oid, + std::string_view path_prefix = "", + uint32_t max_chunk = 1000) + -> std::vector + { + std::vector all_entries; + std::string marker; + bool truncated = true; + + while (truncated) { + std::vector entries; + std::string next_marker; + int r2 = 0; + int r1 = list(oid, marker, path_prefix, max_chunk, + entries, truncated, next_marker, r2); + if (r1 < 0) throw std::system_error(r1, std::system_category()); + if (r2 < 0) throw std::system_error(r2, std::system_category()); + marker = std::move(next_marker); + std::move(entries.begin(), entries.end(), + std::back_inserter(all_entries)); + } + return all_entries; + } +}; + +template +std::vector make_list(Args&& ...args) +{ + return {std::forward(args)...}; +} + +bool operator==(const cls_user_account_resource& lhs, + const cls_user_account_resource& rhs) +{ + if (lhs.name != rhs.name) { + return false; + } + return lhs.path == rhs.path; + // ignore metadata +} +std::ostream& operator<<(std::ostream& out, const cls_user_account_resource& r) +{ + return out << r.path << r.name; +} + +TEST_F(ClsAccount, add) +{ + const std::string oid = __PRETTY_FUNCTION__; + const auto u1 = cls_user_account_resource{.name = "user1"}; + const auto u2 = cls_user_account_resource{.name = "user2"}; + const auto u3 = cls_user_account_resource{.name = "USER2"}; + EXPECT_EQ(-EUSERS, add(oid, u1, true, 0)); + EXPECT_EQ(0, add(oid, u1, true, 1)); + EXPECT_EQ(-EUSERS, add(oid, u2, true, 1)); + EXPECT_EQ(-EEXIST, add(oid, u1, true, 1)); + EXPECT_EQ(0, add(oid, u1, false, 1)); // allow overwrite at limit + EXPECT_EQ(0, add(oid, u2, true, 2)); + EXPECT_EQ(-EEXIST, add(oid, u3, true, 2)); // case-insensitive match +} + +TEST_F(ClsAccount, get) +{ + const std::string oid = __PRETTY_FUNCTION__; + const auto u1 = cls_user_account_resource{.name = "user1", .path = "A"}; + const auto u2 = cls_user_account_resource{.name = "USER1"}; + EXPECT_EQ(tl::unexpected(-ENOENT), get(oid, u1.name)); + EXPECT_EQ(-EUSERS, add(oid, u1, true, 0)); + EXPECT_EQ(tl::unexpected(-ENOENT), get(oid, u1.name)); + EXPECT_EQ(0, add(oid, u1, true, 1)); + EXPECT_EQ(u1, get(oid, u1.name)); + EXPECT_EQ(0, add(oid, u2, false, 1)); // overwrite with different case + EXPECT_EQ(u2, get(oid, u1.name)); // accessible by the original name +} + +TEST_F(ClsAccount, rm) +{ + const std::string oid = __PRETTY_FUNCTION__; + const auto u1 = cls_user_account_resource{.name = "user1"}; + const auto u2 = cls_user_account_resource{.name = "USER1"}; + EXPECT_EQ(-ENOENT, rm(oid, u1.name)); + ASSERT_EQ(0, add(oid, u1, true, 1)); + ASSERT_EQ(0, rm(oid, u1.name)); + EXPECT_EQ(-ENOENT, rm(oid, u1.name)); + ASSERT_EQ(0, add(oid, u1, true, 1)); + ASSERT_EQ(0, rm(oid, u2.name)); // case-insensitive match +} + +TEST_F(ClsAccount, list) +{ + const std::string oid = __PRETTY_FUNCTION__; + const auto u1 = cls_user_account_resource{.name = "user1", .path = ""}; + const auto u2 = cls_user_account_resource{.name = "User2", .path = "A"}; + const auto u3 = cls_user_account_resource{.name = "user3", .path = "AA"}; + const auto u4 = cls_user_account_resource{.name = "User4", .path = ""}; + const auto u5 = cls_user_account_resource{.name = "USER1", .path = "z"}; + constexpr uint32_t max_users = 1024; + + ASSERT_EQ(0, ioctx.create(oid, true)); + ASSERT_EQ(make_list(), list_all(oid)); + ASSERT_EQ(0, add(oid, u1, true, max_users)); + EXPECT_EQ(make_list(u1), list_all(oid)); + ASSERT_EQ(0, add(oid, u2, true, max_users)); + ASSERT_EQ(0, add(oid, u3, true, max_users)); + ASSERT_EQ(0, add(oid, u4, true, max_users)); + EXPECT_EQ(make_list(u1, u2, u3, u4), list_all(oid, "")); + EXPECT_EQ(make_list(u1, u2, u3, u4), list_all(oid, "", 1)); // paginated + EXPECT_EQ(make_list(u2, u3), list_all(oid, "A")); + EXPECT_EQ(make_list(u2, u3), list_all(oid, "A", 1)); // paginated + EXPECT_EQ(make_list(u3), list_all(oid, "AA")); + EXPECT_EQ(make_list(u3), list_all(oid, "AA", 1)); // paginated + EXPECT_EQ(make_list(), list_all(oid, "AAu")); // don't match AAuser3 + ASSERT_EQ(0, rm(oid, u2.name)); + EXPECT_EQ(make_list(u1, u3, u4), list_all(oid, "")); + EXPECT_EQ(make_list(u1, u3, u4), list_all(oid, "", 1)); // paginated + ASSERT_EQ(0, add(oid, u5, false, max_users)); // overwrite u1 + EXPECT_EQ(make_list(u5, u3, u4), list_all(oid, "")); +} -- 2.39.5