summaryrefslogtreecommitdiffstats
path: root/src/test/admin_socket.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 18:45:59 +0000
commit19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch)
tree42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/test/admin_socket.cc
parentInitial commit. (diff)
downloadceph-upstream/16.2.11+ds.tar.xz
ceph-upstream/16.2.11+ds.zip
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/admin_socket.cc')
-rw-r--r--src/test/admin_socket.cc336
1 files changed, 336 insertions, 0 deletions
diff --git a/src/test/admin_socket.cc b/src/test/admin_socket.cc
new file mode 100644
index 000000000..859328d9c
--- /dev/null
+++ b/src/test/admin_socket.cc
@@ -0,0 +1,336 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2011 New Dream Network
+ *
+ * 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 "common/ceph_mutex.h"
+#include "common/Cond.h"
+#include "common/admin_socket.h"
+#include "common/admin_socket_client.h"
+#include "common/ceph_argparse.h"
+#include "gtest/gtest.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <string>
+#include <sys/un.h>
+
+class AdminSocketTest
+{
+public:
+ explicit AdminSocketTest(AdminSocket *asokc)
+ : m_asokc(asokc)
+ {
+ }
+ bool init(const std::string &uri) {
+ return m_asokc->init(uri);
+ }
+ string bind_and_listen(const std::string &sock_path, int *fd) {
+ return m_asokc->bind_and_listen(sock_path, fd);
+ }
+ bool shutdown() {
+ m_asokc->shutdown();
+ return true;
+ }
+ AdminSocket *m_asokc;
+};
+
+TEST(AdminSocket, Teardown) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, TeardownSetup) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, SendHelp) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"help\"}", &help));
+ ASSERT_NE(string::npos, help.find("\"list available commands\""));
+ }
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{"
+ " \"prefix\":\"help\","
+ " \"format\":\"xml\","
+ "}", &help));
+ ASSERT_NE(string::npos, help.find(">list available commands<"));
+ }
+ {
+ string help;
+ ASSERT_EQ("", client.do_request("{"
+ " \"prefix\":\"help\","
+ " \"format\":\"UNSUPPORTED\","
+ "}", &help));
+ ASSERT_NE(string::npos, help.find("\"list available commands\""));
+ }
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, SendNoOp) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ string version;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"0\"}", &version));
+ ASSERT_EQ(CEPH_ADMIN_SOCK_VERSION, version);
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+TEST(AdminSocket, SendTooLongRequest) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ string version;
+ string request(16384, 'a');
+ //if admin_socket cannot handle it, segfault will happened.
+ ASSERT_NE("", client.do_request(request, &version));
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+class MyTest : public AdminSocketHook {
+ int call(std::string_view command, const cmdmap_t& cmdmap,
+ Formatter *f,
+ std::ostream& ss,
+ bufferlist& result) override {
+ std::vector<std::string> args;
+ TOPNSPC::common::cmd_getval(cmdmap, "args", args);
+ result.append(command);
+ result.append("|");
+ string resultstr;
+ for (std::vector<std::string>::iterator it = args.begin();
+ it != args.end(); ++it) {
+ if (it != args.begin())
+ resultstr += ' ';
+ resultstr += *it;
+ }
+ result.append(resultstr);
+ return 0;
+ }
+};
+
+TEST(AdminSocket, RegisterCommand) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ std::unique_ptr<AdminSocketHook> my_test_asok = std::make_unique<MyTest>();
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ ASSERT_EQ(0, asoct.m_asokc->register_command("test", my_test_asok.get(), ""));
+ string result;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\"}", &result));
+ ASSERT_EQ("test|", result);
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+class MyTest2 : public AdminSocketHook {
+ int call(std::string_view command, const cmdmap_t& cmdmap,
+ Formatter *f,
+ std::ostream& ss,
+ bufferlist& result) override {
+ std::vector<std::string> args;
+ TOPNSPC::common::cmd_getval(cmdmap, "args", args);
+ result.append(command);
+ result.append("|");
+ string resultstr;
+ for (std::vector<std::string>::iterator it = args.begin();
+ it != args.end(); ++it) {
+ if (it != args.begin())
+ resultstr += ' ';
+ resultstr += *it;
+ }
+ result.append(resultstr);
+ ss << "error stream";
+ return 0;
+ }
+};
+
+TEST(AdminSocket, RegisterCommandPrefixes) {
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ std::unique_ptr<AdminSocketHook> my_test_asok = std::make_unique<MyTest>();
+ std::unique_ptr<AdminSocketHook> my_test2_asok = std::make_unique<MyTest2>();
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_EQ(true, asoct.shutdown());
+ ASSERT_EQ(true, asoct.init(get_rand_socket_path()));
+ AdminSocketClient client(get_rand_socket_path());
+ ASSERT_EQ(0, asoct.m_asokc->register_command("test name=args,type=CephString,n=N", my_test_asok.get(), ""));
+ ASSERT_EQ(0, asoct.m_asokc->register_command("test command name=args,type=CephString,n=N", my_test2_asok.get(), ""));
+ string result;
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\"}", &result));
+ ASSERT_EQ("test|", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\"}", &result));
+ ASSERT_EQ("test command|", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\",\"args\":[\"post\"]}", &result));
+ ASSERT_EQ("test command|post", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test command\",\"args\":[\" post\"]}", &result));
+ ASSERT_EQ("test command| post", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\"this thing\"]}", &result));
+ ASSERT_EQ("test|this thing", result);
+
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\" command post\"]}", &result));
+ ASSERT_EQ("test| command post", result);
+ ASSERT_EQ("", client.do_request("{\"prefix\":\"test\",\"args\":[\" this thing\"]}", &result));
+ ASSERT_EQ("test| this thing", result);
+ ASSERT_EQ(true, asoct.shutdown());
+}
+
+class BlockingHook : public AdminSocketHook {
+public:
+ ceph::mutex _lock = ceph::make_mutex("BlockingHook::_lock");
+ ceph::condition_variable _cond;
+
+ BlockingHook() = default;
+
+ int call(std::string_view command, const cmdmap_t& cmdmap,
+ Formatter *f,
+ std::ostream& ss,
+ bufferlist& result) override {
+ std::unique_lock l{_lock};
+ _cond.wait(l);
+ return 0;
+ }
+};
+
+TEST(AdminSocketClient, Ping) {
+ string path = get_rand_socket_path();
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+ AdminSocketClient client(path);
+ // no socket
+ {
+ bool ok;
+ std::string result = client.ping(&ok);
+#ifndef _WIN32
+// TODO: convert WSA errors.
+ EXPECT_NE(std::string::npos, result.find("No such file or directory"));
+#endif
+ ASSERT_FALSE(ok);
+ }
+ // file exists but does not allow connections (no process, wrong type...)
+ int fd = ::creat(path.c_str(), 0777);
+ ASSERT_TRUE(fd);
+ // On Windows, we won't be able to remove the file unless we close it
+ // first.
+ ASSERT_FALSE(::close(fd));
+ {
+ bool ok;
+ std::string result = client.ping(&ok);
+#ifndef _WIN32
+#if defined(__APPLE__) || defined(__FreeBSD__)
+ const char* errmsg = "Socket operation on non-socket";
+#else
+ const char* errmsg = "Connection refused";
+#endif
+ EXPECT_NE(std::string::npos, result.find(errmsg));
+#endif /* _WIN32 */
+ ASSERT_FALSE(ok);
+ }
+ // a daemon is connected to the socket
+ {
+ AdminSocketTest asoct(asokc.get());
+ ASSERT_TRUE(asoct.init(path));
+ bool ok;
+ std::string result = client.ping(&ok);
+ EXPECT_EQ("", result);
+ ASSERT_TRUE(ok);
+ ASSERT_TRUE(asoct.shutdown());
+ }
+ // hardcoded five seconds timeout prevents infinite blockage
+ {
+ AdminSocketTest asoct(asokc.get());
+ BlockingHook *blocking = new BlockingHook();
+ ASSERT_EQ(0, asoct.m_asokc->register_command("0", blocking, ""));
+ ASSERT_TRUE(asoct.init(path));
+ bool ok;
+ std::string result = client.ping(&ok);
+ #ifndef _WIN32
+ EXPECT_NE(std::string::npos, result.find("Resource temporarily unavailable"));
+ #endif
+ ASSERT_FALSE(ok);
+ {
+ std::lock_guard l{blocking->_lock};
+ blocking->_cond.notify_all();
+ }
+ ASSERT_TRUE(asoct.shutdown());
+ delete blocking;
+ }
+}
+
+TEST(AdminSocket, bind_and_listen) {
+ string path = get_rand_socket_path();
+ std::unique_ptr<AdminSocket> asokc = std::make_unique<AdminSocket>(g_ceph_context);
+
+ AdminSocketTest asoct(asokc.get());
+ // successfull bind
+ {
+ int fd = 0;
+ string message;
+ message = asoct.bind_and_listen(path, &fd);
+ ASSERT_NE(0, fd);
+ ASSERT_EQ("", message);
+ ASSERT_EQ(0, ::compat_closesocket(fd));
+ ASSERT_EQ(0, ::unlink(path.c_str()));
+ }
+ // silently discard an existing file
+ {
+ int fd = 0;
+ string message;
+ int fd2 = ::creat(path.c_str(), 0777);
+ ASSERT_TRUE(fd2);
+ // On Windows, we won't be able to remove the file unless we close it
+ // first.
+ ASSERT_FALSE(::close(fd2));
+ message = asoct.bind_and_listen(path, &fd);
+ ASSERT_NE(0, fd);
+ ASSERT_EQ("", message);
+ ASSERT_EQ(0, ::compat_closesocket(fd));
+ ASSERT_EQ(0, ::unlink(path.c_str()));
+ }
+ // do not take over a live socket
+ {
+ ASSERT_TRUE(asoct.init(path));
+ int fd = 0;
+ string message;
+ message = asoct.bind_and_listen(path, &fd);
+ std::cout << "message: " << message << std::endl;
+ EXPECT_NE(std::string::npos, message.find("File exists"));
+ ASSERT_TRUE(asoct.shutdown());
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: "cd .. ;
+ * make unittest_admin_socket &&
+ * valgrind \
+ * --max-stackframe=20000000 --tool=memcheck \
+ * ./unittest_admin_socket --debug-asok 20 # --gtest_filter=AdminSocket*.*
+ * "
+ * End:
+ */
+