diff options
Diffstat (limited to 'src/test/rgw/rgw_cr_test.cc')
-rw-r--r-- | src/test/rgw/rgw_cr_test.cc | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/src/test/rgw/rgw_cr_test.cc b/src/test/rgw/rgw_cr_test.cc new file mode 100644 index 000000000..dc5d25d23 --- /dev/null +++ b/src/test/rgw/rgw_cr_test.cc @@ -0,0 +1,343 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#include <cerrno> +#include <iostream> +#include <sstream> +#include <string> + +#include <fmt/format.h> + +#include "include/rados/librados.hpp" + +#include "common/common_init.h" +#include "common/config.h" +#include "common/ceph_argparse.h" +#include "common/debug.h" + +#include "rgw_coroutine.h" +#include "rgw_cr_rados.h" +#include "rgw_sal.h" +#include "rgw_sal_rados.h" + +#include "gtest/gtest.h" + +using namespace std::literals; + +static constexpr auto dout_subsys = ceph_subsys_rgw; + +static rgw::sal::RadosStore* store = nullptr; + +static const DoutPrefixProvider* dpp() { + struct GlobalPrefix : public DoutPrefixProvider { + CephContext *get_cct() const override { return g_ceph_context; } + unsigned get_subsys() const override { return dout_subsys; } + std::ostream& gen_prefix(std::ostream& out) const override { return out; } + }; + static GlobalPrefix global_dpp; + return &global_dpp; +} + +class StoreDestructor { + rgw::sal::Driver* driver; +public: + explicit StoreDestructor(rgw::sal::RadosStore* _s) : driver(_s) {} + ~StoreDestructor() { + DriverManager::close_storage(store); + } +}; + +struct TempPool { + inline static uint64_t num = 0; + std::string name = + fmt::format("{}-{}-{}", ::time(nullptr), ::getpid(),num++); + + TempPool() { + auto r = store->getRados()->get_rados_handle()->pool_create(name.c_str()); + assert(r == 0); + } + + ~TempPool() { + auto r = store->getRados()->get_rados_handle()->pool_delete(name.c_str()); + assert(r == 0); + } + + operator rgw_pool() { + return { name }; + } + + operator librados::IoCtx() { + librados::IoCtx ioctx; + auto r = store->getRados()->get_rados_handle()->ioctx_create(name.c_str(), + ioctx); + assert(r == 0); + return ioctx; + } +}; + +int run(RGWCoroutine* cr) { + RGWCoroutinesManager cr_mgr{store->ctx(), + store->getRados()->get_cr_registry()}; + std::list<RGWCoroutinesStack *> stacks; + auto stack = new RGWCoroutinesStack(store->ctx(), &cr_mgr); + stack->call(cr); + stacks.push_back(stack); + return cr_mgr.run(dpp(), stacks); +} + +TEST(ReadAttrs, Unfiltered) { + TempPool pool; + ceph::bufferlist bl; + auto dummy = "Dummy attribute value"s; + encode(dummy, bl); + const std::map<std::string, ceph::bufferlist> ref_attrs{ + { "foo"s, bl }, { "bar"s, bl }, { "baz"s, bl } + }; + auto oid = "object"s; + { + librados::IoCtx ioctx(pool); + librados::ObjectWriteOperation op; + op.setxattr("foo", bl); + op.setxattr("bar", bl); + op.setxattr("baz", bl); + auto r = ioctx.operate(oid, &op); + ASSERT_EQ(0, r); + } + std::map<std::string, ceph::bufferlist> attrs; + auto r = run(new RGWSimpleRadosReadAttrsCR(dpp(), store, {pool, oid}, &attrs, + true)); + ASSERT_EQ(0, r); + ASSERT_EQ(ref_attrs, attrs); +} + +TEST(ReadAttrs, Filtered) { + TempPool pool; + ceph::bufferlist bl; + auto dummy = "Dummy attribute value"s; + encode(dummy, bl); + const std::map<std::string, ceph::bufferlist> ref_attrs{ + { RGW_ATTR_PREFIX "foo"s, bl }, + { RGW_ATTR_PREFIX "bar"s, bl }, + { RGW_ATTR_PREFIX "baz"s, bl } + }; + auto oid = "object"s; + { + librados::IoCtx ioctx(pool); + librados::ObjectWriteOperation op; + op.setxattr(RGW_ATTR_PREFIX "foo", bl); + op.setxattr(RGW_ATTR_PREFIX "bar", bl); + op.setxattr(RGW_ATTR_PREFIX "baz", bl); + op.setxattr("oneOfTheseThingsIsNotLikeTheOthers", bl); + auto r = ioctx.operate(oid, &op); + ASSERT_EQ(0, r); + } + std::map<std::string, ceph::bufferlist> attrs; + auto r = run(new RGWSimpleRadosReadAttrsCR(dpp(), store, {pool, oid}, &attrs, + false)); + ASSERT_EQ(0, r); + ASSERT_EQ(ref_attrs, attrs); +} + +TEST(Read, Dne) { + TempPool pool; + std::string result; + auto r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, "doesnotexist"}, + &result, false)); + ASSERT_EQ(-ENOENT, r); +} + +TEST(Read, Read) { + TempPool pool; + auto data = "I am test data!"sv; + auto oid = "object"s; + { + bufferlist bl; + encode(data, bl); + librados::IoCtx ioctx(pool); + auto r = ioctx.write_full(oid, bl); + ASSERT_EQ(0, r); + } + std::string result; + auto r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result, + false)); + ASSERT_EQ(0, r); + ASSERT_EQ(result, data); +} + +TEST(Read, ReadVersion) { + TempPool pool; + auto data = "I am test data!"sv; + auto oid = "object"s; + RGWObjVersionTracker wobjv; + { + bufferlist bl; + encode(data, bl); + librados::IoCtx ioctx(pool); + librados::ObjectWriteOperation op; + wobjv.generate_new_write_ver(store->ctx()); + wobjv.prepare_op_for_write(&op); + op.write_full(bl); + auto r = ioctx.operate(oid, &op); + EXPECT_EQ(0, r); + wobjv.apply_write(); + } + RGWObjVersionTracker robjv; + std::string result; + auto r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result, + false, &robjv)); + ASSERT_EQ(0, r); + ASSERT_EQ(result, data); + data = "I am NEW test data!"; + { + bufferlist bl; + encode(data, bl); + librados::IoCtx ioctx(pool); + librados::ObjectWriteOperation op; + wobjv.generate_new_write_ver(store->ctx()); + wobjv.prepare_op_for_write(&op); + op.write_full(bl); + r = ioctx.operate(oid, &op); + EXPECT_EQ(0, r); + wobjv.apply_write(); + } + result.clear(); + r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result, false, + &robjv)); + ASSERT_EQ(-ECANCELED, r); + ASSERT_TRUE(result.empty()); + + robjv.clear(); + r = run(new RGWSimpleRadosReadCR(dpp(), store, {pool, oid}, &result, false, + &robjv)); + ASSERT_EQ(0, r); + ASSERT_EQ(result, data); + ASSERT_EQ(wobjv.read_version, robjv.read_version); +} + +TEST(Write, Exclusive) { + TempPool pool; + auto oid = "object"s; + { + bufferlist bl; + bl.append("I'm some data!"s); + librados::IoCtx ioctx(pool); + auto r = ioctx.write_full(oid, bl); + ASSERT_EQ(0, r); + } + auto r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid}, + "I am some DIFFERENT data!"s, nullptr, + true)); + ASSERT_EQ(-EEXIST, r); +} + +TEST(Write, Write) { + TempPool pool; + auto oid = "object"s; + auto data = "I'm some data!"s; + auto r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid}, + data, nullptr, true)); + ASSERT_EQ(0, r); + bufferlist bl; + librados::IoCtx ioctx(pool); + ioctx.read(oid, bl, 0, 0); + ASSERT_EQ(0, r); + std::string result; + decode(result, bl); + ASSERT_EQ(data, result); +} + +TEST(Write, ObjV) { + TempPool pool; + auto oid = "object"s; + RGWObjVersionTracker objv; + objv.generate_new_write_ver(store->ctx()); + auto r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid}, + "I'm some data!"s, &objv, + true)); + RGWObjVersionTracker interfering_objv(objv); + r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid}, + "I'm some newer, better data!"s, + &interfering_objv, false)); + ASSERT_EQ(0, r); + r = run(new RGWSimpleRadosWriteCR(dpp(), store, {pool, oid}, + "I'm some treacherous, obsolete data!"s, + &objv, false)); + ASSERT_EQ(-ECANCELED, r); +} + +TEST(WriteAttrs, Attrs) { + TempPool pool; + auto oid = "object"s; + bufferlist bl; + bl.append("I'm some data."); + std::map<std::string, bufferlist> wrattrs { + { "foo", bl }, { "bar", bl }, { "baz", bl } + }; + auto r = run(new RGWSimpleRadosWriteAttrsCR(dpp(), store, {pool, oid}, + wrattrs, nullptr, true)); + ASSERT_EQ(0, r); + std::map<std::string, bufferlist> rdattrs; + librados::IoCtx ioctx(pool); + r = ioctx.getxattrs(oid, rdattrs); + ASSERT_EQ(0, r); + ASSERT_EQ(wrattrs, rdattrs); +} + +TEST(WriteAttrs, Empty) { + TempPool pool; + auto oid = "object"s; + bufferlist bl; + std::map<std::string, bufferlist> wrattrs { + { "foo", bl }, { "bar", bl }, { "baz", bl } + }; + // With an empty bufferlist all attributes should be skipped. + auto r = run(new RGWSimpleRadosWriteAttrsCR(dpp(), store, {pool, oid}, + wrattrs, nullptr, true)); + ASSERT_EQ(0, r); + std::map<std::string, bufferlist> rdattrs; + librados::IoCtx ioctx(pool); + r = ioctx.getxattrs(oid, rdattrs); + ASSERT_EQ(0, r); + ASSERT_TRUE(rdattrs.empty()); +} + +int main(int argc, const char **argv) +{ + auto args = argv_to_vec(argc, argv); + auto cct = rgw_global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT, + CODE_ENVIRONMENT_UTILITY, 0); + + // for region -> zonegroup conversion (must happen before common_init_finish()) + if (!g_conf()->rgw_region.empty() && g_conf()->rgw_zonegroup.empty()) { + g_conf().set_val_or_die("rgw_zonegroup", g_conf()->rgw_region.c_str()); + } + + /* common_init_finish needs to be called after g_conf().set_val() */ + common_init_finish(g_ceph_context); + + + DriverManager::Config cfg = DriverManager::get_config(true, g_ceph_context); + + store = static_cast<rgw::sal::RadosStore*>( + DriverManager::get_storage(dpp(), + g_ceph_context, + cfg, + false, + false, + false, + false, + false, + true, + false)); + if (!store) { + std::cerr << "couldn't init storage provider" << std::endl; + return 5; //EIO + } + StoreDestructor store_destructor(static_cast<rgw::sal::RadosStore*>(store)); + + std::string pool{"rgw_cr_test"}; + store->getRados()->create_pool(dpp(), pool); + + testing::InitGoogleTest(); + return RUN_ALL_TESTS(); +} |